From 0d122e5e9bb168e953fbab3b18bbb28299b881d6 Mon Sep 17 00:00:00 2001 From: unknown <331404948@qq.com> Date: Tue, 22 Oct 2024 18:34:21 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E7=BB=83=E4=B9=A0=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/api.js | 3 + config/request.js | 4 +- main.js | 3 + manifest.json | 2 +- node_modules/.bin/vue-demi-fix | 15 + node_modules/.bin/vue-demi-fix.cmd | 17 + node_modules/.bin/vue-demi-fix.ps1 | 18 + node_modules/.bin/vue-demi-switch | 15 + node_modules/.bin/vue-demi-switch.cmd | 17 + node_modules/.bin/vue-demi-switch.ps1 | 18 + node_modules/@vue/devtools-api/lib/cjs/api/api.js | 2 + node_modules/@vue/devtools-api/lib/cjs/api/app.js | 2 + .../@vue/devtools-api/lib/cjs/api/component.js | 2 + .../@vue/devtools-api/lib/cjs/api/context.js | 2 + .../@vue/devtools-api/lib/cjs/api/hooks.js | 2 + .../@vue/devtools-api/lib/cjs/api/index.js | 22 + node_modules/@vue/devtools-api/lib/cjs/api/util.js | 2 + node_modules/@vue/devtools-api/lib/cjs/const.js | 5 + node_modules/@vue/devtools-api/lib/cjs/env.js | 17 + node_modules/@vue/devtools-api/lib/cjs/index.js | 45 + node_modules/@vue/devtools-api/lib/cjs/plugin.js | 2 + node_modules/@vue/devtools-api/lib/cjs/proxy.js | 111 + node_modules/@vue/devtools-api/lib/cjs/time.js | 28 + .../@vue/devtools-api/lib/esm/api/api.d.ts | 108 + node_modules/@vue/devtools-api/lib/esm/api/api.js | 1 + .../@vue/devtools-api/lib/esm/api/app.d.ts | 1 + node_modules/@vue/devtools-api/lib/esm/api/app.js | 1 + .../@vue/devtools-api/lib/esm/api/component.d.ts | 78 + .../@vue/devtools-api/lib/esm/api/component.js | 1 + .../@vue/devtools-api/lib/esm/api/context.d.ts | 5 + .../@vue/devtools-api/lib/esm/api/context.js | 1 + .../@vue/devtools-api/lib/esm/api/hooks.d.ts | 180 ++ .../@vue/devtools-api/lib/esm/api/hooks.js | 1 + .../@vue/devtools-api/lib/esm/api/index.d.ts | 6 + .../@vue/devtools-api/lib/esm/api/index.js | 6 + .../@vue/devtools-api/lib/esm/api/util.d.ts | 4 + node_modules/@vue/devtools-api/lib/esm/api/util.js | 1 + node_modules/@vue/devtools-api/lib/esm/const.d.ts | 2 + node_modules/@vue/devtools-api/lib/esm/const.js | 2 + node_modules/@vue/devtools-api/lib/esm/env.d.ts | 15 + node_modules/@vue/devtools-api/lib/esm/env.js | 12 + node_modules/@vue/devtools-api/lib/esm/index.d.ts | 18 + node_modules/@vue/devtools-api/lib/esm/index.js | 27 + node_modules/@vue/devtools-api/lib/esm/plugin.d.ts | 47 + node_modules/@vue/devtools-api/lib/esm/plugin.js | 1 + node_modules/@vue/devtools-api/lib/esm/proxy.d.ts | 20 + node_modules/@vue/devtools-api/lib/esm/proxy.js | 107 + node_modules/@vue/devtools-api/lib/esm/time.d.ts | 2 + node_modules/@vue/devtools-api/lib/esm/time.js | 23 + node_modules/@vue/devtools-api/package.json | 67 + node_modules/pinia/LICENSE | 21 + node_modules/pinia/README.md | 24 + node_modules/pinia/dist/pinia.cjs | 2059 ++++++++++++++++++ node_modules/pinia/dist/pinia.d.ts | 1064 ++++++++++ node_modules/pinia/dist/pinia.esm-browser.js | 2035 ++++++++++++++++++ node_modules/pinia/dist/pinia.iife.js | 2221 ++++++++++++++++++++ node_modules/pinia/dist/pinia.iife.prod.js | 6 + node_modules/pinia/dist/pinia.mjs | 2043 ++++++++++++++++++ node_modules/pinia/dist/pinia.prod.cjs | 844 ++++++++ node_modules/pinia/index.cjs | 7 + node_modules/pinia/index.js | 7 + node_modules/pinia/package.json | 126 ++ node_modules/vue-demi/LICENSE | 21 + node_modules/vue-demi/README.md | 229 ++ node_modules/vue-demi/bin/vue-demi-fix.js | 3 + node_modules/vue-demi/bin/vue-demi-switch.js | 3 + node_modules/vue-demi/lib/index.cjs | 29 + node_modules/vue-demi/lib/index.d.ts | 22 + node_modules/vue-demi/lib/index.iife.js | 126 ++ node_modules/vue-demi/lib/index.mjs | 34 + node_modules/vue-demi/lib/v2.7/index.cjs | 60 + node_modules/vue-demi/lib/v2.7/index.d.ts | 38 + node_modules/vue-demi/lib/v2.7/index.mjs | 80 + node_modules/vue-demi/lib/v2/index.cjs | 34 + node_modules/vue-demi/lib/v2/index.d.ts | 33 + node_modules/vue-demi/lib/v2/index.mjs | 49 + node_modules/vue-demi/lib/v3/index.cjs | 29 + node_modules/vue-demi/lib/v3/index.d.ts | 22 + node_modules/vue-demi/lib/v3/index.mjs | 34 + node_modules/vue-demi/package.json | 83 + node_modules/vue-demi/scripts/postinstall.js | 19 + node_modules/vue-demi/scripts/switch-cli.js | 18 + node_modules/vue-demi/scripts/utils.js | 62 + package-lock.json | 33 +- package.json | 12 +- pages/exercises/brushQuestions/brushQuestions.vue | 358 +++- pages/index/index.vue | 6 + store/index.js | 9 + store/modules/car.js | 18 + 89 files changed, 12906 insertions(+), 106 deletions(-) create mode 100644 node_modules/.bin/vue-demi-fix create mode 100644 node_modules/.bin/vue-demi-fix.cmd create mode 100644 node_modules/.bin/vue-demi-fix.ps1 create mode 100644 node_modules/.bin/vue-demi-switch create mode 100644 node_modules/.bin/vue-demi-switch.cmd create mode 100644 node_modules/.bin/vue-demi-switch.ps1 create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/api.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/app.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/component.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/context.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/hooks.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/index.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/api/util.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/const.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/env.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/index.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/plugin.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/proxy.js create mode 100644 node_modules/@vue/devtools-api/lib/cjs/time.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/api.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/api.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/app.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/app.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/component.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/component.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/context.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/context.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/hooks.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/hooks.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/index.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/index.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/util.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/api/util.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/const.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/const.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/env.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/env.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/index.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/index.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/plugin.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/plugin.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/proxy.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/proxy.js create mode 100644 node_modules/@vue/devtools-api/lib/esm/time.d.ts create mode 100644 node_modules/@vue/devtools-api/lib/esm/time.js create mode 100644 node_modules/@vue/devtools-api/package.json create mode 100644 node_modules/pinia/LICENSE create mode 100644 node_modules/pinia/README.md create mode 100644 node_modules/pinia/dist/pinia.cjs create mode 100644 node_modules/pinia/dist/pinia.d.ts create mode 100644 node_modules/pinia/dist/pinia.esm-browser.js create mode 100644 node_modules/pinia/dist/pinia.iife.js create mode 100644 node_modules/pinia/dist/pinia.iife.prod.js create mode 100644 node_modules/pinia/dist/pinia.mjs create mode 100644 node_modules/pinia/dist/pinia.prod.cjs create mode 100644 node_modules/pinia/index.cjs create mode 100644 node_modules/pinia/index.js create mode 100644 node_modules/pinia/package.json create mode 100644 node_modules/vue-demi/LICENSE create mode 100644 node_modules/vue-demi/README.md create mode 100644 node_modules/vue-demi/bin/vue-demi-fix.js create mode 100644 node_modules/vue-demi/bin/vue-demi-switch.js create mode 100644 node_modules/vue-demi/lib/index.cjs create mode 100644 node_modules/vue-demi/lib/index.d.ts create mode 100644 node_modules/vue-demi/lib/index.iife.js create mode 100644 node_modules/vue-demi/lib/index.mjs create mode 100644 node_modules/vue-demi/lib/v2.7/index.cjs create mode 100644 node_modules/vue-demi/lib/v2.7/index.d.ts create mode 100644 node_modules/vue-demi/lib/v2.7/index.mjs create mode 100644 node_modules/vue-demi/lib/v2/index.cjs create mode 100644 node_modules/vue-demi/lib/v2/index.d.ts create mode 100644 node_modules/vue-demi/lib/v2/index.mjs create mode 100644 node_modules/vue-demi/lib/v3/index.cjs create mode 100644 node_modules/vue-demi/lib/v3/index.d.ts create mode 100644 node_modules/vue-demi/lib/v3/index.mjs create mode 100644 node_modules/vue-demi/package.json create mode 100644 node_modules/vue-demi/scripts/postinstall.js create mode 100644 node_modules/vue-demi/scripts/switch-cli.js create mode 100644 node_modules/vue-demi/scripts/utils.js create mode 100644 store/index.js create mode 100644 store/modules/car.js diff --git a/config/api.js b/config/api.js index 45443f2..5dc1f6d 100644 --- a/config/api.js +++ b/config/api.js @@ -19,6 +19,9 @@ export const logOut = (data={}) => http.post('member/auth/logout', data, {custom export const startQuestionApi = (data) => http.post('questionBank/startQuestion', data,) // 提交答案 export const submitAnswerResultApi = (data) => http.post('questionBank/submitAnswerResult.do', data,) + // 上一题-下一题-直接跳到几题 + export const getQuestionApi = (data) => http.post('questionBank/getQuestion', data,) + diff --git a/config/request.js b/config/request.js index 698f8ae..acb3e16 100644 --- a/config/request.js +++ b/config/request.js @@ -35,8 +35,8 @@ let secretKey = '22d90e09d1374f0f9e4accd07d333e55' if(noToken&&config.header.Authorization) { delete config.header.Authorization } - console.log('config') - console.log(config) + // console.log('config') + // console.log(config) return config }, config => { // 可使用async await 做异步操作 return Promise.reject(config) diff --git a/main.js b/main.js index bf6c7d1..a249d0f 100644 --- a/main.js +++ b/main.js @@ -2,6 +2,8 @@ import App from './App' import uviewPlus from '@/uni_modules/uview-plus' import request from './config/request.js' import { goPage } from '@/utils/utils.js' +import {setupStore} from '@/store'; + // // #ifndef VUE3 // import Vue from 'vue' // import './uni.promisify.adaptor' @@ -21,6 +23,7 @@ export function createApp() { app.config.globalProperties.$goPage = goPage app.use(uviewPlus) request(app) + setupStore(app); return { app } diff --git a/manifest.json b/manifest.json index 4959641..0a287fb 100644 --- a/manifest.json +++ b/manifest.json @@ -75,7 +75,7 @@ "disableHostCheck" : true, "proxy" : { "/api" : { - "target" : "http://192.168.1.204:8081/", + "target" : "http://192.168.1.214:8081/", // "target" : "http://192.168.1.40:8099/", "changeOrigin" : true, "secure" : true, diff --git a/node_modules/.bin/vue-demi-fix b/node_modules/.bin/vue-demi-fix new file mode 100644 index 0000000..e05d388 --- /dev/null +++ b/node_modules/.bin/vue-demi-fix @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../vue-demi/bin/vue-demi-fix.js" "$@" + ret=$? +else + node "$basedir/../vue-demi/bin/vue-demi-fix.js" "$@" + ret=$? +fi +exit $ret diff --git a/node_modules/.bin/vue-demi-fix.cmd b/node_modules/.bin/vue-demi-fix.cmd new file mode 100644 index 0000000..8f40137 --- /dev/null +++ b/node_modules/.bin/vue-demi-fix.cmd @@ -0,0 +1,17 @@ +@ECHO off +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +"%_prog%" "%dp0%\..\vue-demi\bin\vue-demi-fix.js" %* +ENDLOCAL +EXIT /b %errorlevel% +:find_dp0 +SET dp0=%~dp0 +EXIT /b diff --git a/node_modules/.bin/vue-demi-fix.ps1 b/node_modules/.bin/vue-demi-fix.ps1 new file mode 100644 index 0000000..0972a40 --- /dev/null +++ b/node_modules/.bin/vue-demi-fix.ps1 @@ -0,0 +1,18 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + & "$basedir/node$exe" "$basedir/../vue-demi/bin/vue-demi-fix.js" $args + $ret=$LASTEXITCODE +} else { + & "node$exe" "$basedir/../vue-demi/bin/vue-demi-fix.js" $args + $ret=$LASTEXITCODE +} +exit $ret diff --git a/node_modules/.bin/vue-demi-switch b/node_modules/.bin/vue-demi-switch new file mode 100644 index 0000000..6f80ef6 --- /dev/null +++ b/node_modules/.bin/vue-demi-switch @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../vue-demi/bin/vue-demi-switch.js" "$@" + ret=$? +else + node "$basedir/../vue-demi/bin/vue-demi-switch.js" "$@" + ret=$? +fi +exit $ret diff --git a/node_modules/.bin/vue-demi-switch.cmd b/node_modules/.bin/vue-demi-switch.cmd new file mode 100644 index 0000000..d355008 --- /dev/null +++ b/node_modules/.bin/vue-demi-switch.cmd @@ -0,0 +1,17 @@ +@ECHO off +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +"%_prog%" "%dp0%\..\vue-demi\bin\vue-demi-switch.js" %* +ENDLOCAL +EXIT /b %errorlevel% +:find_dp0 +SET dp0=%~dp0 +EXIT /b diff --git a/node_modules/.bin/vue-demi-switch.ps1 b/node_modules/.bin/vue-demi-switch.ps1 new file mode 100644 index 0000000..4ffe298 --- /dev/null +++ b/node_modules/.bin/vue-demi-switch.ps1 @@ -0,0 +1,18 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + & "$basedir/node$exe" "$basedir/../vue-demi/bin/vue-demi-switch.js" $args + $ret=$LASTEXITCODE +} else { + & "node$exe" "$basedir/../vue-demi/bin/vue-demi-switch.js" $args + $ret=$LASTEXITCODE +} +exit $ret diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/api.js b/node_modules/@vue/devtools-api/lib/cjs/api/api.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/api.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/app.js b/node_modules/@vue/devtools-api/lib/cjs/api/app.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/app.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/component.js b/node_modules/@vue/devtools-api/lib/cjs/api/component.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/component.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/context.js b/node_modules/@vue/devtools-api/lib/cjs/api/context.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/context.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/hooks.js b/node_modules/@vue/devtools-api/lib/cjs/api/hooks.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/hooks.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/index.js b/node_modules/@vue/devtools-api/lib/cjs/api/index.js new file mode 100644 index 0000000..22f7589 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/index.js @@ -0,0 +1,22 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./api.js"), exports); +__exportStar(require("./app.js"), exports); +__exportStar(require("./component.js"), exports); +__exportStar(require("./context.js"), exports); +__exportStar(require("./hooks.js"), exports); +__exportStar(require("./util.js"), exports); diff --git a/node_modules/@vue/devtools-api/lib/cjs/api/util.js b/node_modules/@vue/devtools-api/lib/cjs/api/util.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/api/util.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/const.js b/node_modules/@vue/devtools-api/lib/cjs/const.js new file mode 100644 index 0000000..84d408c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/const.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HOOK_PLUGIN_SETTINGS_SET = exports.HOOK_SETUP = void 0; +exports.HOOK_SETUP = 'devtools-plugin:setup'; +exports.HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'; diff --git a/node_modules/@vue/devtools-api/lib/cjs/env.js b/node_modules/@vue/devtools-api/lib/cjs/env.js new file mode 100644 index 0000000..e6c7ebd --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/env.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isProxyAvailable = exports.getTarget = exports.getDevtoolsGlobalHook = void 0; +function getDevtoolsGlobalHook() { + return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__; +} +exports.getDevtoolsGlobalHook = getDevtoolsGlobalHook; +function getTarget() { + // @ts-expect-error navigator and windows are not available in all environments + return (typeof navigator !== 'undefined' && typeof window !== 'undefined') + ? window + : typeof globalThis !== 'undefined' + ? globalThis + : {}; +} +exports.getTarget = getTarget; +exports.isProxyAvailable = typeof Proxy === 'function'; diff --git a/node_modules/@vue/devtools-api/lib/cjs/index.js b/node_modules/@vue/devtools-api/lib/cjs/index.js new file mode 100644 index 0000000..f8b5fc3 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/index.js @@ -0,0 +1,45 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setupDevtoolsPlugin = void 0; +const env_js_1 = require("./env.js"); +const const_js_1 = require("./const.js"); +const proxy_js_1 = require("./proxy.js"); +__exportStar(require("./api/index.js"), exports); +__exportStar(require("./plugin.js"), exports); +__exportStar(require("./time.js"), exports); +function setupDevtoolsPlugin(pluginDescriptor, setupFn) { + const descriptor = pluginDescriptor; + const target = (0, env_js_1.getTarget)(); + const hook = (0, env_js_1.getDevtoolsGlobalHook)(); + const enableProxy = env_js_1.isProxyAvailable && descriptor.enableEarlyProxy; + if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) { + hook.emit(const_js_1.HOOK_SETUP, pluginDescriptor, setupFn); + } + else { + const proxy = enableProxy ? new proxy_js_1.ApiProxy(descriptor, hook) : null; + const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []; + list.push({ + pluginDescriptor: descriptor, + setupFn, + proxy, + }); + if (proxy) { + setupFn(proxy.proxiedTarget); + } + } +} +exports.setupDevtoolsPlugin = setupDevtoolsPlugin; diff --git a/node_modules/@vue/devtools-api/lib/cjs/plugin.js b/node_modules/@vue/devtools-api/lib/cjs/plugin.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/plugin.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/@vue/devtools-api/lib/cjs/proxy.js b/node_modules/@vue/devtools-api/lib/cjs/proxy.js new file mode 100644 index 0000000..731c9ac --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/proxy.js @@ -0,0 +1,111 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApiProxy = void 0; +const const_js_1 = require("./const.js"); +const time_js_1 = require("./time.js"); +class ApiProxy { + constructor(plugin, hook) { + this.target = null; + this.targetQueue = []; + this.onQueue = []; + this.plugin = plugin; + this.hook = hook; + const defaultSettings = {}; + if (plugin.settings) { + for (const id in plugin.settings) { + const item = plugin.settings[id]; + defaultSettings[id] = item.defaultValue; + } + } + const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`; + let currentSettings = Object.assign({}, defaultSettings); + try { + const raw = localStorage.getItem(localSettingsSaveId); + const data = JSON.parse(raw); + Object.assign(currentSettings, data); + } + catch (e) { + // noop + } + this.fallbacks = { + getSettings() { + return currentSettings; + }, + setSettings(value) { + try { + localStorage.setItem(localSettingsSaveId, JSON.stringify(value)); + } + catch (e) { + // noop + } + currentSettings = value; + }, + now() { + return (0, time_js_1.now)(); + }, + }; + if (hook) { + hook.on(const_js_1.HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => { + if (pluginId === this.plugin.id) { + this.fallbacks.setSettings(value); + } + }); + } + this.proxiedOn = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target.on[prop]; + } + else { + return (...args) => { + this.onQueue.push({ + method: prop, + args, + }); + }; + } + }, + }); + this.proxiedTarget = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target[prop]; + } + else if (prop === 'on') { + return this.proxiedOn; + } + else if (Object.keys(this.fallbacks).includes(prop)) { + return (...args) => { + this.targetQueue.push({ + method: prop, + args, + resolve: () => { }, + }); + return this.fallbacks[prop](...args); + }; + } + else { + return (...args) => { + return new Promise((resolve) => { + this.targetQueue.push({ + method: prop, + args, + resolve, + }); + }); + }; + } + }, + }); + } + async setRealTarget(target) { + this.target = target; + for (const item of this.onQueue) { + this.target.on[item.method](...item.args); + } + for (const item of this.targetQueue) { + item.resolve(await this.target[item.method](...item.args)); + } + } +} +exports.ApiProxy = ApiProxy; diff --git a/node_modules/@vue/devtools-api/lib/cjs/time.js b/node_modules/@vue/devtools-api/lib/cjs/time.js new file mode 100644 index 0000000..78d7f67 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/cjs/time.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.now = exports.isPerformanceSupported = void 0; +let supported; +let perf; +function isPerformanceSupported() { + var _a; + if (supported !== undefined) { + return supported; + } + if (typeof window !== 'undefined' && window.performance) { + supported = true; + perf = window.performance; + } + else if (typeof globalThis !== 'undefined' && ((_a = globalThis.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) { + supported = true; + perf = globalThis.perf_hooks.performance; + } + else { + supported = false; + } + return supported; +} +exports.isPerformanceSupported = isPerformanceSupported; +function now() { + return isPerformanceSupported() ? perf.now() : Date.now(); +} +exports.now = now; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/api.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/api.d.ts new file mode 100644 index 0000000..2e5494c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/api.d.ts @@ -0,0 +1,108 @@ +import type { ComponentBounds, Hookable } from './hooks.js'; +import type { Context } from './context.js'; +import type { ComponentInstance, ComponentState, StateBase } from './component.js'; +import type { App } from './app.js'; +import type { ID } from './util.js'; +export interface DevtoolsPluginApi { + on: Hookable; + notifyComponentUpdate: (instance?: ComponentInstance) => void; + addTimelineLayer: (options: TimelineLayerOptions) => void; + addTimelineEvent: (options: TimelineEventOptions) => void; + addInspector: (options: CustomInspectorOptions) => void; + sendInspectorTree: (inspectorId: string) => void; + sendInspectorState: (inspectorId: string) => void; + selectInspectorNode: (inspectorId: string, nodeId: string) => void; + getComponentBounds: (instance: ComponentInstance) => Promise; + getComponentName: (instance: ComponentInstance) => Promise; + getComponentInstances: (app: App) => Promise; + highlightElement: (instance: ComponentInstance) => void; + unhighlightElement: () => void; + getSettings: (pluginId?: string) => TSettings; + now: () => number; + /** + * @private + */ + setSettings: (values: TSettings) => void; +} +export interface AppRecord { + id: string; + name: string; + instanceMap: Map; + rootInstance: ComponentInstance; +} +export interface TimelineLayerOptions { + id: string; + label: string; + color: number; + skipScreenshots?: boolean; + groupsOnly?: boolean; + ignoreNoDurationGroups?: boolean; + screenshotOverlayRender?: (event: TimelineEvent & ScreenshotOverlayEvent, ctx: ScreenshotOverlayRenderContext) => ScreenshotOverlayRenderResult | Promise; +} +export interface ScreenshotOverlayEvent { + layerId: string; + renderMeta: any; +} +export interface ScreenshotOverlayRenderContext { + screenshot: ScreenshotData; + events: (TimelineEvent & ScreenshotOverlayEvent)[]; + index: number; +} +export type ScreenshotOverlayRenderResult = HTMLElement | string | false; +export interface ScreenshotData { + time: number; +} +export interface TimelineEventOptions { + layerId: string; + event: TimelineEvent; + all?: boolean; +} +export interface TimelineEvent { + time: number; + data: TData; + logType?: 'default' | 'warning' | 'error'; + meta?: TMeta; + groupId?: ID; + title?: string; + subtitle?: string; +} +export interface TimelineMarkerOptions { + id: string; + time: number; + color: number; + label: string; + all?: boolean; +} +export interface CustomInspectorOptions { + id: string; + label: string; + icon?: string; + treeFilterPlaceholder?: string; + stateFilterPlaceholder?: string; + noSelectionText?: string; + actions?: { + icon: string; + tooltip?: string; + action: () => void | Promise; + }[]; + nodeActions?: { + icon: string; + tooltip?: string; + action: (nodeId: string) => void | Promise; + }[]; +} +export interface CustomInspectorNode { + id: string; + label: string; + children?: CustomInspectorNode[]; + tags?: InspectorNodeTag[]; +} +export interface InspectorNodeTag { + label: string; + textColor: number; + backgroundColor: number; + tooltip?: string; +} +export interface CustomInspectorState { + [key: string]: (StateBase | Omit)[]; +} diff --git a/node_modules/@vue/devtools-api/lib/esm/api/api.js b/node_modules/@vue/devtools-api/lib/esm/api/api.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/api.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/app.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/app.d.ts new file mode 100644 index 0000000..a10f02f --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/app.d.ts @@ -0,0 +1 @@ +export type App = any; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/app.js b/node_modules/@vue/devtools-api/lib/esm/api/app.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/app.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/component.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/component.d.ts new file mode 100644 index 0000000..84fcba7 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/component.d.ts @@ -0,0 +1,78 @@ +import type { InspectorNodeTag } from './api.js'; +import type { ID } from './util.js'; +export type ComponentInstance = any; +export interface ComponentTreeNode { + uid: ID; + id: string; + name: string; + renderKey: string | number; + inactive: boolean; + isFragment: boolean; + hasChildren: boolean; + children: ComponentTreeNode[]; + domOrder?: number[]; + consoleId?: string; + isRouterView?: boolean; + macthedRouteSegment?: string; + tags: InspectorNodeTag[]; + autoOpen: boolean; + meta?: any; +} +export interface InspectedComponentData { + id: string; + name: string; + file: string; + state: ComponentState[]; + functional?: boolean; +} +export interface StateBase { + key: string; + value: any; + editable?: boolean; + objectType?: 'ref' | 'reactive' | 'computed' | 'other'; + raw?: string; +} +export interface ComponentStateBase extends StateBase { + type: string; +} +export interface ComponentPropState extends ComponentStateBase { + meta?: { + type: string; + required: boolean; + /** Vue 1 only */ + mode?: 'default' | 'sync' | 'once'; + }; +} +export type ComponentBuiltinCustomStateTypes = 'function' | 'map' | 'set' | 'reference' | 'component' | 'component-definition' | 'router' | 'store'; +export interface ComponentCustomState extends ComponentStateBase { + value: CustomState; +} +export interface CustomState { + _custom: { + type: ComponentBuiltinCustomStateTypes | string; + objectType?: string; + display?: string; + tooltip?: string; + value?: any; + abstract?: boolean; + file?: string; + uid?: number; + readOnly?: boolean; + /** Configure immediate child fields */ + fields?: { + abstract?: boolean; + }; + id?: any; + actions?: { + icon: string; + tooltip?: string; + action: () => void | Promise; + }[]; + /** internal */ + _reviveId?: number; + }; +} +export type ComponentState = ComponentStateBase | ComponentPropState | ComponentCustomState; +export interface ComponentDevtoolsOptions { + hide?: boolean; +} diff --git a/node_modules/@vue/devtools-api/lib/esm/api/component.js b/node_modules/@vue/devtools-api/lib/esm/api/component.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/component.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/context.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/context.d.ts new file mode 100644 index 0000000..29388f1 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/context.d.ts @@ -0,0 +1,5 @@ +import type { AppRecord } from './api.js'; +export interface Context { + currentTab: string; + currentAppRecord: AppRecord; +} diff --git a/node_modules/@vue/devtools-api/lib/esm/api/context.js b/node_modules/@vue/devtools-api/lib/esm/api/context.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/context.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/hooks.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/hooks.d.ts new file mode 100644 index 0000000..4e5d7cc --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/hooks.d.ts @@ -0,0 +1,180 @@ +import type { ComponentDevtoolsOptions, ComponentInstance, ComponentTreeNode, InspectedComponentData } from './component.js'; +import type { App } from './app.js'; +import type { CustomInspectorNode, CustomInspectorState, TimelineEvent } from './api.js'; +export declare const enum Hooks { + TRANSFORM_CALL = "transformCall", + GET_APP_RECORD_NAME = "getAppRecordName", + GET_APP_ROOT_INSTANCE = "getAppRootInstance", + REGISTER_APPLICATION = "registerApplication", + WALK_COMPONENT_TREE = "walkComponentTree", + VISIT_COMPONENT_TREE = "visitComponentTree", + WALK_COMPONENT_PARENTS = "walkComponentParents", + INSPECT_COMPONENT = "inspectComponent", + GET_COMPONENT_BOUNDS = "getComponentBounds", + GET_COMPONENT_NAME = "getComponentName", + GET_COMPONENT_INSTANCES = "getComponentInstances", + GET_ELEMENT_COMPONENT = "getElementComponent", + GET_COMPONENT_ROOT_ELEMENTS = "getComponentRootElements", + EDIT_COMPONENT_STATE = "editComponentState", + GET_COMPONENT_DEVTOOLS_OPTIONS = "getAppDevtoolsOptions", + GET_COMPONENT_RENDER_CODE = "getComponentRenderCode", + INSPECT_TIMELINE_EVENT = "inspectTimelineEvent", + TIMELINE_CLEARED = "timelineCleared", + GET_INSPECTOR_TREE = "getInspectorTree", + GET_INSPECTOR_STATE = "getInspectorState", + EDIT_INSPECTOR_STATE = "editInspectorState", + SET_PLUGIN_SETTINGS = "setPluginSettings" +} +export interface ComponentBounds { + left: number; + top: number; + width: number; + height: number; +} +export interface HookPayloads { + [Hooks.TRANSFORM_CALL]: { + callName: string; + inArgs: any[]; + outArgs: any[]; + }; + [Hooks.GET_APP_RECORD_NAME]: { + app: App; + name: string; + }; + [Hooks.GET_APP_ROOT_INSTANCE]: { + app: App; + root: ComponentInstance; + }; + [Hooks.REGISTER_APPLICATION]: { + app: App; + }; + [Hooks.WALK_COMPONENT_TREE]: { + componentInstance: ComponentInstance; + componentTreeData: ComponentTreeNode[]; + maxDepth: number; + filter: string; + recursively: boolean; + }; + [Hooks.VISIT_COMPONENT_TREE]: { + app: App; + componentInstance: ComponentInstance; + treeNode: ComponentTreeNode; + filter: string; + }; + [Hooks.WALK_COMPONENT_PARENTS]: { + componentInstance: ComponentInstance; + parentInstances: ComponentInstance[]; + }; + [Hooks.INSPECT_COMPONENT]: { + app: App; + componentInstance: ComponentInstance; + instanceData: InspectedComponentData; + }; + [Hooks.GET_COMPONENT_BOUNDS]: { + componentInstance: ComponentInstance; + bounds: ComponentBounds; + }; + [Hooks.GET_COMPONENT_NAME]: { + componentInstance: ComponentInstance; + name: string; + }; + [Hooks.GET_COMPONENT_INSTANCES]: { + app: App; + componentInstances: ComponentInstance[]; + }; + [Hooks.GET_ELEMENT_COMPONENT]: { + element: HTMLElement | any; + componentInstance: ComponentInstance; + }; + [Hooks.GET_COMPONENT_ROOT_ELEMENTS]: { + componentInstance: ComponentInstance; + rootElements: (HTMLElement | any)[]; + }; + [Hooks.EDIT_COMPONENT_STATE]: { + app: App; + componentInstance: ComponentInstance; + path: string[]; + type: string; + state: EditStatePayload; + set: (object: any, path?: string | (string[]), value?: any, cb?: (object: any, field: string, value: any) => void) => void; + }; + [Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS]: { + componentInstance: ComponentInstance; + options: ComponentDevtoolsOptions; + }; + [Hooks.GET_COMPONENT_RENDER_CODE]: { + componentInstance: ComponentInstance; + code: string; + }; + [Hooks.INSPECT_TIMELINE_EVENT]: { + app: App; + layerId: string; + event: TimelineEvent; + all?: boolean; + data: any; + }; + [Hooks.TIMELINE_CLEARED]: Record; + [Hooks.GET_INSPECTOR_TREE]: { + app: App; + inspectorId: string; + filter: string; + rootNodes: CustomInspectorNode[]; + }; + [Hooks.GET_INSPECTOR_STATE]: { + app: App; + inspectorId: string; + nodeId: string; + state: CustomInspectorState; + }; + [Hooks.EDIT_INSPECTOR_STATE]: { + app: App; + inspectorId: string; + nodeId: string; + path: string[]; + type: string; + state: EditStatePayload; + set: (object: any, path?: string | (string[]), value?: any, cb?: (object: any, field: string, value: any) => void) => void; + }; + [Hooks.SET_PLUGIN_SETTINGS]: { + app: App; + pluginId: string; + key: string; + newValue: any; + oldValue: any; + settings: any; + }; +} +export type EditStatePayload = { + value: any; + newKey?: string | null; + remove?: undefined | false; +} | { + value?: undefined; + newKey?: undefined; + remove: true; +}; +export type HookHandler = (payload: TPayload, ctx: TContext) => void | Promise; +export interface Hookable { + transformCall: (handler: HookHandler) => any; + getAppRecordName: (handler: HookHandler) => any; + getAppRootInstance: (handler: HookHandler) => any; + registerApplication: (handler: HookHandler) => any; + walkComponentTree: (handler: HookHandler) => any; + visitComponentTree: (handler: HookHandler) => any; + walkComponentParents: (handler: HookHandler) => any; + inspectComponent: (handler: HookHandler) => any; + getComponentBounds: (handler: HookHandler) => any; + getComponentName: (handler: HookHandler) => any; + getComponentInstances: (handler: HookHandler) => any; + getElementComponent: (handler: HookHandler) => any; + getComponentRootElements: (handler: HookHandler) => any; + editComponentState: (handler: HookHandler) => any; + getComponentDevtoolsOptions: (handler: HookHandler) => any; + getComponentRenderCode: (handler: HookHandler) => any; + inspectTimelineEvent: (handler: HookHandler) => any; + timelineCleared: (handler: HookHandler) => any; + getInspectorTree: (handler: HookHandler) => any; + getInspectorState: (handler: HookHandler) => any; + editInspectorState: (handler: HookHandler) => any; + setPluginSettings: (handler: HookHandler) => any; +} diff --git a/node_modules/@vue/devtools-api/lib/esm/api/hooks.js b/node_modules/@vue/devtools-api/lib/esm/api/hooks.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/hooks.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/index.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/index.d.ts new file mode 100644 index 0000000..70d09d2 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/index.d.ts @@ -0,0 +1,6 @@ +export * from './api.js'; +export * from './app.js'; +export * from './component.js'; +export * from './context.js'; +export * from './hooks.js'; +export * from './util.js'; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/index.js b/node_modules/@vue/devtools-api/lib/esm/api/index.js new file mode 100644 index 0000000..70d09d2 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/index.js @@ -0,0 +1,6 @@ +export * from './api.js'; +export * from './app.js'; +export * from './component.js'; +export * from './context.js'; +export * from './hooks.js'; +export * from './util.js'; diff --git a/node_modules/@vue/devtools-api/lib/esm/api/util.d.ts b/node_modules/@vue/devtools-api/lib/esm/api/util.d.ts new file mode 100644 index 0000000..f8ca920 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/util.d.ts @@ -0,0 +1,4 @@ +export type ID = number | string; +export interface WithId { + id: ID; +} diff --git a/node_modules/@vue/devtools-api/lib/esm/api/util.js b/node_modules/@vue/devtools-api/lib/esm/api/util.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/api/util.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/const.d.ts b/node_modules/@vue/devtools-api/lib/esm/const.d.ts new file mode 100644 index 0000000..289b254 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/const.d.ts @@ -0,0 +1,2 @@ +export declare const HOOK_SETUP = "devtools-plugin:setup"; +export declare const HOOK_PLUGIN_SETTINGS_SET = "plugin:settings:set"; diff --git a/node_modules/@vue/devtools-api/lib/esm/const.js b/node_modules/@vue/devtools-api/lib/esm/const.js new file mode 100644 index 0000000..872ea3e --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/const.js @@ -0,0 +1,2 @@ +export const HOOK_SETUP = 'devtools-plugin:setup'; +export const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'; diff --git a/node_modules/@vue/devtools-api/lib/esm/env.d.ts b/node_modules/@vue/devtools-api/lib/esm/env.d.ts new file mode 100644 index 0000000..045d95f --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/env.d.ts @@ -0,0 +1,15 @@ +import type { ApiProxy } from './proxy.js'; +import type { PluginDescriptor, SetupFunction } from './index.js'; +export interface PluginQueueItem { + pluginDescriptor: PluginDescriptor; + setupFn: SetupFunction; + proxy?: ApiProxy; +} +interface GlobalTarget { + __VUE_DEVTOOLS_PLUGINS__?: PluginQueueItem[]; + __VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__?: boolean; +} +export declare function getDevtoolsGlobalHook(): any; +export declare function getTarget(): GlobalTarget; +export declare const isProxyAvailable: boolean; +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/env.js b/node_modules/@vue/devtools-api/lib/esm/env.js new file mode 100644 index 0000000..a7a74b1 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/env.js @@ -0,0 +1,12 @@ +export function getDevtoolsGlobalHook() { + return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__; +} +export function getTarget() { + // @ts-expect-error navigator and windows are not available in all environments + return (typeof navigator !== 'undefined' && typeof window !== 'undefined') + ? window + : typeof globalThis !== 'undefined' + ? globalThis + : {}; +} +export const isProxyAvailable = typeof Proxy === 'function'; diff --git a/node_modules/@vue/devtools-api/lib/esm/index.d.ts b/node_modules/@vue/devtools-api/lib/esm/index.d.ts new file mode 100644 index 0000000..162518b --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/index.d.ts @@ -0,0 +1,18 @@ +import type { DevtoolsPluginApi } from './api/index.js'; +import type { ExtractSettingsTypes, PluginDescriptor, PluginSettingsItem } from './plugin.js'; +export * from './api/index.js'; +export * from './plugin.js'; +export * from './time.js'; +export { PluginQueueItem } from './env.js'; +type Cast = A extends B ? A : B; +type Narrowable = string | number | bigint | boolean; +type Narrow = Cast; +})>; +type Exact = { + [K in keyof C]: K extends keyof T ? T[K] : never; +}; +export type SetupFunction = (api: DevtoolsPluginApi) => void; +export declare function setupDevtoolsPlugin, TSettings = ExtractSettingsTypes ? S : Record : Record>>(pluginDescriptor: Narrow, setupFn: SetupFunction): void; diff --git a/node_modules/@vue/devtools-api/lib/esm/index.js b/node_modules/@vue/devtools-api/lib/esm/index.js new file mode 100644 index 0000000..c7a50ec --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/index.js @@ -0,0 +1,27 @@ +import { getDevtoolsGlobalHook, getTarget, isProxyAvailable } from './env.js'; +import { HOOK_SETUP } from './const.js'; +import { ApiProxy } from './proxy.js'; +export * from './api/index.js'; +export * from './plugin.js'; +export * from './time.js'; +export function setupDevtoolsPlugin(pluginDescriptor, setupFn) { + const descriptor = pluginDescriptor; + const target = getTarget(); + const hook = getDevtoolsGlobalHook(); + const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy; + if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) { + hook.emit(HOOK_SETUP, pluginDescriptor, setupFn); + } + else { + const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null; + const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []; + list.push({ + pluginDescriptor: descriptor, + setupFn, + proxy, + }); + if (proxy) { + setupFn(proxy.proxiedTarget); + } + } +} diff --git a/node_modules/@vue/devtools-api/lib/esm/plugin.d.ts b/node_modules/@vue/devtools-api/lib/esm/plugin.d.ts new file mode 100644 index 0000000..52322ca --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/plugin.d.ts @@ -0,0 +1,47 @@ +import type { App } from './api/index.js'; +export interface PluginDescriptor { + id: string; + label: string; + app: App; + packageName?: string; + homepage?: string; + componentStateTypes?: string[]; + logo?: string; + disableAppScope?: boolean; + disablePluginScope?: boolean; + /** + * Run the plugin setup and expose the api even if the devtools is not opened yet. + * Useful to record timeline events early. + */ + enableEarlyProxy?: boolean; + settings?: Record; +} +export type PluginSettingsItem = { + label: string; + description?: string; +} & ({ + type: 'boolean'; + defaultValue: boolean; +} | { + type: 'choice'; + defaultValue: string | number; + options: { + value: string | number; + label: string; + }[]; + component?: 'select' | 'button-group'; +} | { + type: 'text'; + defaultValue: string; +}); +type InferSettingsType = [T] extends [{ + type: 'boolean'; +}] ? boolean : [T] extends [{ + type: 'choice'; +}] ? T['options'][number]['value'] : [T] extends [{ + type: 'text'; +}] ? string : unknown; +export type ExtractSettingsTypes> = { + [K in keyof O]: InferSettingsType; +}; +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/plugin.js b/node_modules/@vue/devtools-api/lib/esm/plugin.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/plugin.js @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/proxy.d.ts b/node_modules/@vue/devtools-api/lib/esm/proxy.d.ts new file mode 100644 index 0000000..38ff867 --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/proxy.d.ts @@ -0,0 +1,20 @@ +import type { Context, DevtoolsPluginApi, Hookable } from './api/index.js'; +import type { PluginDescriptor } from './plugin.js'; +interface QueueItem { + method: string; + args: any[]; + resolve?: (value?: any) => void; +} +export declare class ApiProxy = DevtoolsPluginApi> { + target: TTarget | null; + targetQueue: QueueItem[]; + proxiedTarget: TTarget; + onQueue: QueueItem[]; + proxiedOn: Hookable; + plugin: PluginDescriptor; + hook: any; + fallbacks: Record; + constructor(plugin: PluginDescriptor, hook: any); + setRealTarget(target: TTarget): Promise; +} +export {}; diff --git a/node_modules/@vue/devtools-api/lib/esm/proxy.js b/node_modules/@vue/devtools-api/lib/esm/proxy.js new file mode 100644 index 0000000..3cce0ff --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/proxy.js @@ -0,0 +1,107 @@ +import { HOOK_PLUGIN_SETTINGS_SET } from './const.js'; +import { now } from './time.js'; +export class ApiProxy { + constructor(plugin, hook) { + this.target = null; + this.targetQueue = []; + this.onQueue = []; + this.plugin = plugin; + this.hook = hook; + const defaultSettings = {}; + if (plugin.settings) { + for (const id in plugin.settings) { + const item = plugin.settings[id]; + defaultSettings[id] = item.defaultValue; + } + } + const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`; + let currentSettings = Object.assign({}, defaultSettings); + try { + const raw = localStorage.getItem(localSettingsSaveId); + const data = JSON.parse(raw); + Object.assign(currentSettings, data); + } + catch (e) { + // noop + } + this.fallbacks = { + getSettings() { + return currentSettings; + }, + setSettings(value) { + try { + localStorage.setItem(localSettingsSaveId, JSON.stringify(value)); + } + catch (e) { + // noop + } + currentSettings = value; + }, + now() { + return now(); + }, + }; + if (hook) { + hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => { + if (pluginId === this.plugin.id) { + this.fallbacks.setSettings(value); + } + }); + } + this.proxiedOn = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target.on[prop]; + } + else { + return (...args) => { + this.onQueue.push({ + method: prop, + args, + }); + }; + } + }, + }); + this.proxiedTarget = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target[prop]; + } + else if (prop === 'on') { + return this.proxiedOn; + } + else if (Object.keys(this.fallbacks).includes(prop)) { + return (...args) => { + this.targetQueue.push({ + method: prop, + args, + resolve: () => { }, + }); + return this.fallbacks[prop](...args); + }; + } + else { + return (...args) => { + return new Promise((resolve) => { + this.targetQueue.push({ + method: prop, + args, + resolve, + }); + }); + }; + } + }, + }); + } + async setRealTarget(target) { + this.target = target; + for (const item of this.onQueue) { + this.target.on[item.method](...item.args); + } + for (const item of this.targetQueue) { + item.resolve(await this.target[item.method](...item.args)); + } + } +} diff --git a/node_modules/@vue/devtools-api/lib/esm/time.d.ts b/node_modules/@vue/devtools-api/lib/esm/time.d.ts new file mode 100644 index 0000000..1aebade --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/time.d.ts @@ -0,0 +1,2 @@ +export declare function isPerformanceSupported(): boolean; +export declare function now(): number; diff --git a/node_modules/@vue/devtools-api/lib/esm/time.js b/node_modules/@vue/devtools-api/lib/esm/time.js new file mode 100644 index 0000000..fd15ffe --- /dev/null +++ b/node_modules/@vue/devtools-api/lib/esm/time.js @@ -0,0 +1,23 @@ +let supported; +let perf; +export function isPerformanceSupported() { + var _a; + if (supported !== undefined) { + return supported; + } + if (typeof window !== 'undefined' && window.performance) { + supported = true; + perf = window.performance; + } + else if (typeof globalThis !== 'undefined' && ((_a = globalThis.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) { + supported = true; + perf = globalThis.perf_hooks.performance; + } + else { + supported = false; + } + return supported; +} +export function now() { + return isPerformanceSupported() ? perf.now() : Date.now(); +} diff --git a/node_modules/@vue/devtools-api/package.json b/node_modules/@vue/devtools-api/package.json new file mode 100644 index 0000000..ffec6b4 --- /dev/null +++ b/node_modules/@vue/devtools-api/package.json @@ -0,0 +1,67 @@ +{ + "_from": "@vue/devtools-api@^6.6.3", + "_id": "@vue/devtools-api@6.6.4", + "_inBundle": false, + "_integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "_location": "/@vue/devtools-api", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "@vue/devtools-api@^6.6.3", + "name": "@vue/devtools-api", + "escapedName": "@vue%2fdevtools-api", + "scope": "@vue", + "rawSpec": "^6.6.3", + "saveSpec": null, + "fetchSpec": "^6.6.3" + }, + "_requiredBy": [ + "/pinia" + ], + "_resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "_shasum": "cbe97fe0162b365edc1dba80e173f90492535343", + "_spec": "@vue/devtools-api@^6.6.3", + "_where": "D:\\work\\questionbankvue3\\node_modules\\pinia", + "author": { + "name": "Guillaume Chau" + }, + "browser": "lib/esm/index.js", + "bugs": { + "url": "https://github.com/vuejs/vue-devtools/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Interact with the Vue devtools from the page", + "devDependencies": { + "@types/node": "^20.11.16", + "@types/webpack-env": "^1.15.1", + "typescript": "^5.3.3" + }, + "files": [ + "lib/cjs", + "lib/esm" + ], + "homepage": "https://github.com/vuejs/vue-devtools#readme", + "license": "MIT", + "main": "lib/cjs/index.js", + "module": "lib/esm/index.js", + "name": "@vue/devtools-api", + "publishConfig": { + "access": "public" + }, + "repository": { + "url": "git+https://github.com/vuejs/vue-devtools.git", + "type": "git", + "directory": "packages/api" + }, + "scripts": { + "build": "rimraf lib && yarn build:esm && yarn build:cjs", + "build:cjs": "tsc --module commonjs --outDir lib/cjs", + "build:esm": "tsc --module es2015 --outDir lib/esm -d", + "build:watch": "yarn tsc --module es2015 --outDir lib/esm -d -w --sourceMap" + }, + "sideEffects": false, + "types": "lib/esm/index.d.ts", + "version": "6.6.4" +} diff --git a/node_modules/pinia/LICENSE b/node_modules/pinia/LICENSE new file mode 100644 index 0000000..0c77562 --- /dev/null +++ b/node_modules/pinia/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-present Eduardo San Martin Morote + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/pinia/README.md b/node_modules/pinia/README.md new file mode 100644 index 0000000..d73d322 --- /dev/null +++ b/node_modules/pinia/README.md @@ -0,0 +1,24 @@ +

+ + Pinia logo + +

+ +# Pinia + +> Intuitive, type safe and flexible Store for Vue + +## 👉 [Demo with Vue 3 on StackBlitz](https://stackblitz.com/github/piniajs/example-vue-3-vite) + +## Help me keep working on this project 💚 + +- [Become a Sponsor on GitHub](https://github.com/sponsors/posva) +- [One-time donation via PayPal](https://paypal.me/posva) + +## Documentation + +To learn more about Pinia, check [its documentation](https://pinia.vuejs.org). + +## License + +[MIT](http://opensource.org/licenses/MIT) diff --git a/node_modules/pinia/dist/pinia.cjs b/node_modules/pinia/dist/pinia.cjs new file mode 100644 index 0000000..b639feb --- /dev/null +++ b/node_modules/pinia/dist/pinia.cjs @@ -0,0 +1,2059 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +'use strict'; + +var vueDemi = require('vue-demi'); +var devtoolsApi = require('@vue/devtools-api'); + +/** + * setActivePinia must be called to handle SSR at the top of functions like + * `fetch`, `setup`, `serverPrefetch` and others + */ +let activePinia; +/** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ +// @ts-expect-error: cannot constrain the type of the return +const setActivePinia = (pinia) => (activePinia = pinia); +/** + * Get the currently active pinia if there is any. + */ +const getActivePinia = () => (vueDemi.hasInjectionContext() && vueDemi.inject(piniaSymbol)) || activePinia; +const piniaSymbol = ((process.env.NODE_ENV !== 'production') ? Symbol('pinia') : /* istanbul ignore next */ Symbol()); + +function isPlainObject( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +o) { + return (o && + typeof o === 'object' && + Object.prototype.toString.call(o) === '[object Object]' && + typeof o.toJSON !== 'function'); +} +// type DeepReadonly = { readonly [P in keyof T]: DeepReadonly } +// TODO: can we change these to numbers? +/** + * Possible types for SubscriptionCallback + */ +exports.MutationType = void 0; +(function (MutationType) { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + MutationType["direct"] = "direct"; + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + MutationType["patchObject"] = "patch object"; + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + MutationType["patchFunction"] = "patch function"; + // maybe reset? for $state = {} and $reset +})(exports.MutationType || (exports.MutationType = {})); + +const IS_CLIENT = typeof window !== 'undefined'; + +/* + * FileSaver.js A saveAs() FileSaver implementation. + * + * Originally by Eli Grey, adapted as an ESM module by Eduardo San Martin + * Morote. + * + * License : MIT + */ +// The one and only way of getting global scope in all environments +// https://stackoverflow.com/q/3277182/1008999 +const _global = /*#__PURE__*/ (() => typeof window === 'object' && window.window === window + ? window + : typeof self === 'object' && self.self === self + ? self + : typeof global === 'object' && global.global === global + ? global + : typeof globalThis === 'object' + ? globalThis + : { HTMLElement: null })(); +function bom(blob, { autoBom = false } = {}) { + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (autoBom && + /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xfeff), blob], { type: blob.type }); + } + return blob; +} +function download(url, name, opts) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.onload = function () { + saveAs(xhr.response, name, opts); + }; + xhr.onerror = function () { + console.error('could not download file'); + }; + xhr.send(); +} +function corsEnabled(url) { + const xhr = new XMLHttpRequest(); + // use sync to avoid popup blocker + xhr.open('HEAD', url, false); + try { + xhr.send(); + } + catch (e) { } + return xhr.status >= 200 && xhr.status <= 299; +} +// `a.click()` doesn't work for all browsers (#465) +function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')); + } + catch (e) { + const evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); + node.dispatchEvent(evt); + } +} +const _navigator = typeof navigator === 'object' ? navigator : { userAgent: '' }; +// Detect WebView inside a native macOS app by ruling out all browsers +// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too +// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos +const isMacOSWebView = /*#__PURE__*/ (() => /Macintosh/.test(_navigator.userAgent) && + /AppleWebKit/.test(_navigator.userAgent) && + !/Safari/.test(_navigator.userAgent))(); +const saveAs = !IS_CLIENT + ? () => { } // noop + : // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView or mini program + typeof HTMLAnchorElement !== 'undefined' && + 'download' in HTMLAnchorElement.prototype && + !isMacOSWebView + ? downloadSaveAs + : // Use msSaveOrOpenBlob as a second approach + 'msSaveOrOpenBlob' in _navigator + ? msSaveAs + : // Fallback to using FileReader and a popup + fileSaverSaveAs; +function downloadSaveAs(blob, name = 'download', opts) { + const a = document.createElement('a'); + a.download = name; + a.rel = 'noopener'; // tabnabbing + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + if (typeof blob === 'string') { + // Support regular links + a.href = blob; + if (a.origin !== location.origin) { + if (corsEnabled(a.href)) { + download(blob, name, opts); + } + else { + a.target = '_blank'; + click(a); + } + } + else { + click(a); + } + } + else { + // Support blobs + a.href = URL.createObjectURL(blob); + setTimeout(function () { + URL.revokeObjectURL(a.href); + }, 4e4); // 40s + setTimeout(function () { + click(a); + }, 0); + } +} +function msSaveAs(blob, name = 'download', opts) { + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts); + } + else { + const a = document.createElement('a'); + a.href = blob; + a.target = '_blank'; + setTimeout(function () { + click(a); + }); + } + } + else { + // @ts-ignore: works on windows + navigator.msSaveOrOpenBlob(bom(blob, opts), name); + } +} +function fileSaverSaveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank'); + if (popup) { + popup.document.title = popup.document.body.innerText = 'downloading...'; + } + if (typeof blob === 'string') + return download(blob, name, opts); + const force = blob.type === 'application/octet-stream'; + const isSafari = /constructor/i.test(String(_global.HTMLElement)) || 'safari' in _global; + const isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); + if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && + typeof FileReader !== 'undefined') { + // Safari doesn't allow downloading of blob URLs + const reader = new FileReader(); + reader.onloadend = function () { + let url = reader.result; + if (typeof url !== 'string') { + popup = null; + throw new Error('Wrong reader.result type'); + } + url = isChromeIOS + ? url + : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); + if (popup) { + popup.location.href = url; + } + else { + location.assign(url); + } + popup = null; // reverse-tabnabbing #460 + }; + reader.readAsDataURL(blob); + } + else { + const url = URL.createObjectURL(blob); + if (popup) + popup.location.assign(url); + else + location.href = url; + popup = null; // reverse-tabnabbing #460 + setTimeout(function () { + URL.revokeObjectURL(url); + }, 4e4); // 40s + } +} + +/** + * Shows a toast or console.log + * + * @param message - message to log + * @param type - different color of the tooltip + */ +function toastMessage(message, type) { + const piniaMessage = '🍍 ' + message; + if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') { + // No longer available :( + __VUE_DEVTOOLS_TOAST__(piniaMessage, type); + } + else if (type === 'error') { + console.error(piniaMessage); + } + else if (type === 'warn') { + console.warn(piniaMessage); + } + else { + console.log(piniaMessage); + } +} +function isPinia(o) { + return '_a' in o && 'install' in o; +} + +/** + * This file contain devtools actions, they are not Pinia actions. + */ +// --- +function checkClipboardAccess() { + if (!('clipboard' in navigator)) { + toastMessage(`Your browser doesn't support the Clipboard API`, 'error'); + return true; + } +} +function checkNotFocusedError(error) { + if (error instanceof Error && + error.message.toLowerCase().includes('document is not focused')) { + toastMessage('You need to activate the "Emulate a focused page" setting in the "Rendering" panel of devtools.', 'warn'); + return true; + } + return false; +} +async function actionGlobalCopyState(pinia) { + if (checkClipboardAccess()) + return; + try { + await navigator.clipboard.writeText(JSON.stringify(pinia.state.value)); + toastMessage('Global state copied to clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to serialize the state. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalPasteState(pinia) { + if (checkClipboardAccess()) + return; + try { + loadStoresState(pinia, JSON.parse(await navigator.clipboard.readText())); + toastMessage('Global state pasted from clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to deserialize the state from clipboard. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalSaveState(pinia) { + try { + saveAs(new Blob([JSON.stringify(pinia.state.value)], { + type: 'text/plain;charset=utf-8', + }), 'pinia-state.json'); + } + catch (error) { + toastMessage(`Failed to export the state as JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +let fileInput; +function getFileOpener() { + if (!fileInput) { + fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + } + function openFile() { + return new Promise((resolve, reject) => { + fileInput.onchange = async () => { + const files = fileInput.files; + if (!files) + return resolve(null); + const file = files.item(0); + if (!file) + return resolve(null); + return resolve({ text: await file.text(), file }); + }; + // @ts-ignore: TODO: changed from 4.3 to 4.4 + fileInput.oncancel = () => resolve(null); + fileInput.onerror = reject; + fileInput.click(); + }); + } + return openFile; +} +async function actionGlobalOpenStateFile(pinia) { + try { + const open = getFileOpener(); + const result = await open(); + if (!result) + return; + const { text, file } = result; + loadStoresState(pinia, JSON.parse(text)); + toastMessage(`Global state imported from "${file.name}".`); + } + catch (error) { + toastMessage(`Failed to import the state from JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +function loadStoresState(pinia, state) { + for (const key in state) { + const storeState = pinia.state.value[key]; + // store is already instantiated, patch it + if (storeState) { + Object.assign(storeState, state[key]); + } + else { + // store is not instantiated, set the initial state + pinia.state.value[key] = state[key]; + } + } +} + +function formatDisplay(display) { + return { + _custom: { + display, + }, + }; +} +const PINIA_ROOT_LABEL = '🍍 Pinia (root)'; +const PINIA_ROOT_ID = '_root'; +function formatStoreForInspectorTree(store) { + return isPinia(store) + ? { + id: PINIA_ROOT_ID, + label: PINIA_ROOT_LABEL, + } + : { + id: store.$id, + label: store.$id, + }; +} +function formatStoreForInspectorState(store) { + if (isPinia(store)) { + const storeNames = Array.from(store._s.keys()); + const storeMap = store._s; + const state = { + state: storeNames.map((storeId) => ({ + editable: true, + key: storeId, + value: store.state.value[storeId], + })), + getters: storeNames + .filter((id) => storeMap.get(id)._getters) + .map((id) => { + const store = storeMap.get(id); + return { + editable: false, + key: id, + value: store._getters.reduce((getters, key) => { + getters[key] = store[key]; + return getters; + }, {}), + }; + }), + }; + return state; + } + const state = { + state: Object.keys(store.$state).map((key) => ({ + editable: true, + key, + value: store.$state[key], + })), + }; + // avoid adding empty getters + if (store._getters && store._getters.length) { + state.getters = store._getters.map((getterName) => ({ + editable: false, + key: getterName, + value: store[getterName], + })); + } + if (store._customProperties.size) { + state.customProperties = Array.from(store._customProperties).map((key) => ({ + editable: true, + key, + value: store[key], + })); + } + return state; +} +function formatEventData(events) { + if (!events) + return {}; + if (Array.isArray(events)) { + // TODO: handle add and delete for arrays and objects + return events.reduce((data, event) => { + data.keys.push(event.key); + data.operations.push(event.type); + data.oldValue[event.key] = event.oldValue; + data.newValue[event.key] = event.newValue; + return data; + }, { + oldValue: {}, + keys: [], + operations: [], + newValue: {}, + }); + } + else { + return { + operation: formatDisplay(events.type), + key: formatDisplay(events.key), + oldValue: events.oldValue, + newValue: events.newValue, + }; + } +} +function formatMutationType(type) { + switch (type) { + case exports.MutationType.direct: + return 'mutation'; + case exports.MutationType.patchFunction: + return '$patch'; + case exports.MutationType.patchObject: + return '$patch'; + default: + return 'unknown'; + } +} + +// timeline can be paused when directly changing the state +let isTimelineActive = true; +const componentStateTypes = []; +const MUTATIONS_LAYER_ID = 'pinia:mutations'; +const INSPECTOR_ID = 'pinia'; +const { assign: assign$1 } = Object; +/** + * Gets the displayed name of a store in devtools + * + * @param id - id of the store + * @returns a formatted string + */ +const getStoreType = (id) => '🍍 ' + id; +/** + * Add the pinia plugin without any store. Allows displaying a Pinia plugin tab + * as soon as it is added to the application. + * + * @param app - Vue application + * @param pinia - pinia instance + */ +function registerPiniaDevtools(app, pinia) { + devtoolsApi.setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + }, (api) => { + if (typeof api.now !== 'function') { + toastMessage('You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); + } + api.addTimelineLayer({ + id: MUTATIONS_LAYER_ID, + label: `Pinia 🍍`, + color: 0xe5df88, + }); + api.addInspector({ + id: INSPECTOR_ID, + label: 'Pinia 🍍', + icon: 'storage', + treeFilterPlaceholder: 'Search stores', + actions: [ + { + icon: 'content_copy', + action: () => { + actionGlobalCopyState(pinia); + }, + tooltip: 'Serialize and copy the state', + }, + { + icon: 'content_paste', + action: async () => { + await actionGlobalPasteState(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Replace the state with the content of your clipboard', + }, + { + icon: 'save', + action: () => { + actionGlobalSaveState(pinia); + }, + tooltip: 'Save the state as a JSON file', + }, + { + icon: 'folder_open', + action: async () => { + await actionGlobalOpenStateFile(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Import the state from a JSON file', + }, + ], + nodeActions: [ + { + icon: 'restore', + tooltip: 'Reset the state (with "$reset")', + action: (nodeId) => { + const store = pinia._s.get(nodeId); + if (!store) { + toastMessage(`Cannot reset "${nodeId}" store because it wasn't found.`, 'warn'); + } + else if (typeof store.$reset !== 'function') { + toastMessage(`Cannot reset "${nodeId}" store because it doesn't have a "$reset" method implemented.`, 'warn'); + } + else { + store.$reset(); + toastMessage(`Store "${nodeId}" reset.`); + } + }, + }, + ], + }); + api.on.inspectComponent((payload, ctx) => { + const proxy = (payload.componentInstance && + payload.componentInstance.proxy); + if (proxy && proxy._pStores) { + const piniaStores = payload.componentInstance.proxy._pStores; + Object.values(piniaStores).forEach((store) => { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'state', + editable: true, + value: store._isOptionsAPI + ? { + _custom: { + value: vueDemi.toRaw(store.$state), + actions: [ + { + icon: 'restore', + tooltip: 'Reset the state of this store', + action: () => store.$reset(), + }, + ], + }, + } + : // NOTE: workaround to unwrap transferred refs + Object.keys(store.$state).reduce((state, key) => { + state[key] = store.$state[key]; + return state; + }, {}), + }); + if (store._getters && store._getters.length) { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'getters', + editable: false, + value: store._getters.reduce((getters, key) => { + try { + getters[key] = store[key]; + } + catch (error) { + // @ts-expect-error: we just want to show it in devtools + getters[key] = error; + } + return getters; + }, {}), + }); + } + }); + } + }); + api.on.getInspectorTree((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + let stores = [pinia]; + stores = stores.concat(Array.from(pinia._s.values())); + payload.rootNodes = (payload.filter + ? stores.filter((store) => '$id' in store + ? store.$id + .toLowerCase() + .includes(payload.filter.toLowerCase()) + : PINIA_ROOT_LABEL.toLowerCase().includes(payload.filter.toLowerCase())) + : stores).map(formatStoreForInspectorTree); + } + }); + // Expose pinia instance as $pinia to window + globalThis.$pinia = pinia; + api.on.getInspectorState((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + // this could be the selected store restored for a different project + // so it's better not to say anything here + return; + } + if (inspectedStore) { + // Expose selected store as $store to window + if (payload.nodeId !== PINIA_ROOT_ID) + globalThis.$store = vueDemi.toRaw(inspectedStore); + payload.state = formatStoreForInspectorState(inspectedStore); + } + } + }); + api.on.editInspectorState((payload, ctx) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + return toastMessage(`store "${payload.nodeId}" not found`, 'error'); + } + const { path } = payload; + if (!isPinia(inspectedStore)) { + // access only the state + if (path.length !== 1 || + !inspectedStore._customProperties.has(path[0]) || + path[0] in inspectedStore.$state) { + path.unshift('$state'); + } + } + else { + // Root access, we can omit the `.value` because the devtools API does it for us + path.unshift('state'); + } + isTimelineActive = false; + payload.set(inspectedStore, path, payload.state.value); + isTimelineActive = true; + } + }); + api.on.editComponentState((payload) => { + if (payload.type.startsWith('🍍')) { + const storeId = payload.type.replace(/^🍍\s*/, ''); + const store = pinia._s.get(storeId); + if (!store) { + return toastMessage(`store "${storeId}" not found`, 'error'); + } + const { path } = payload; + if (path[0] !== 'state') { + return toastMessage(`Invalid path for store "${storeId}":\n${path}\nOnly state can be modified.`); + } + // rewrite the first entry to be able to directly set the state as + // well as any other path + path[0] = '$state'; + isTimelineActive = false; + payload.set(store, path, payload.state.value); + isTimelineActive = true; + } + }); + }); +} +function addStoreToDevtools(app, store) { + if (!componentStateTypes.includes(getStoreType(store.$id))) { + componentStateTypes.push(getStoreType(store.$id)); + } + devtoolsApi.setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + settings: { + logStoreChanges: { + label: 'Notify about new/deleted stores', + type: 'boolean', + defaultValue: true, + }, + // useEmojis: { + // label: 'Use emojis in messages ⚡️', + // type: 'boolean', + // defaultValue: true, + // }, + }, + }, (api) => { + // gracefully handle errors + const now = typeof api.now === 'function' ? api.now.bind(api) : Date.now; + store.$onAction(({ after, onError, name, args }) => { + const groupId = runningActionId++; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛫 ' + name, + subtitle: 'start', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + }, + groupId, + }, + }); + after((result) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛬 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + result, + }, + groupId, + }, + }); + }); + onError((error) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + logType: 'error', + title: '💥 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + error, + }, + groupId, + }, + }); + }); + }, true); + store._customProperties.forEach((name) => { + vueDemi.watch(() => vueDemi.unref(store[name]), (newValue, oldValue) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (isTimelineActive) { + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: 'Change', + subtitle: name, + data: { + newValue, + oldValue, + }, + groupId: activeAction, + }, + }); + } + }, { deep: true }); + }); + store.$subscribe(({ events, type }, state) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (!isTimelineActive) + return; + // rootStore.state[store.id] = state + const eventData = { + time: now(), + title: formatMutationType(type), + data: assign$1({ store: formatDisplay(store.$id) }, formatEventData(events)), + groupId: activeAction, + }; + if (type === exports.MutationType.patchFunction) { + eventData.subtitle = '⤵️'; + } + else if (type === exports.MutationType.patchObject) { + eventData.subtitle = '🧩'; + } + else if (events && !Array.isArray(events)) { + eventData.subtitle = events.type; + } + if (events) { + eventData.data['rawEvent(s)'] = { + _custom: { + display: 'DebuggerEvent', + type: 'object', + tooltip: 'raw DebuggerEvent[]', + value: events, + }, + }; + } + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: eventData, + }); + }, { detached: true, flush: 'sync' }); + const hotUpdate = store._hotUpdate; + store._hotUpdate = vueDemi.markRaw((newStore) => { + hotUpdate(newStore); + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🔥 ' + store.$id, + subtitle: 'HMR update', + data: { + store: formatDisplay(store.$id), + info: formatDisplay(`HMR update`), + }, + }, + }); + // update the devtools too + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }); + const { $dispose } = store; + store.$dispose = () => { + $dispose(); + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`Disposed "${store.$id}" store 🗑`); + }; + // trigger an update so it can display new registered stores + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`"${store.$id}" store installed 🆕`); + }); +} +let runningActionId = 0; +let activeAction; +/** + * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the + * context of all actions, allowing us to set `runningAction` on each access and effectively associating any state + * mutation to the action. + * + * @param store - store to patch + * @param actionNames - list of actionst to patch + */ +function patchActionForGrouping(store, actionNames, wrapWithProxy) { + // original actions of the store as they are given by pinia. We are going to override them + const actions = actionNames.reduce((storeActions, actionName) => { + // use toRaw to avoid tracking #541 + storeActions[actionName] = vueDemi.toRaw(store)[actionName]; + return storeActions; + }, {}); + for (const actionName in actions) { + store[actionName] = function () { + // the running action id is incremented in a before action hook + const _actionId = runningActionId; + const trackedStore = wrapWithProxy + ? new Proxy(store, { + get(...args) { + activeAction = _actionId; + return Reflect.get(...args); + }, + set(...args) { + activeAction = _actionId; + return Reflect.set(...args); + }, + }) + : store; + // For Setup Stores we need https://github.com/tc39/proposal-async-context + activeAction = _actionId; + const retValue = actions[actionName].apply(trackedStore, arguments); + // this is safer as async actions in Setup Stores would associate mutations done outside of the action + activeAction = undefined; + return retValue; + }; + } +} +/** + * pinia.use(devtoolsPlugin) + */ +function devtoolsPlugin({ app, store, options }) { + // HMR module + if (store.$id.startsWith('__hot:')) { + return; + } + // detect option api vs setup api + store._isOptionsAPI = !!options.state; + // Do not overwrite actions mocked by @pinia/testing (#2298) + if (!store._p._testing) { + patchActionForGrouping(store, Object.keys(options.actions), store._isOptionsAPI); + // Upgrade the HMR to also update the new actions + const originalHotUpdate = store._hotUpdate; + vueDemi.toRaw(store)._hotUpdate = function (newStore) { + originalHotUpdate.apply(this, arguments); + patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions), !!store._isOptionsAPI); + }; + } + addStoreToDevtools(app, + // FIXME: is there a way to allow the assignment from Store to StoreGeneric? + store); +} + +/** + * Creates a Pinia instance to be used by the application + */ +function createPinia() { + const scope = vueDemi.effectScope(true); + // NOTE: here we could check the window object for a state and directly set it + // if there is anything like it with Vue 3 SSR + const state = scope.run(() => vueDemi.ref({})); + let _p = []; + // plugins added before calling app.use(pinia) + let toBeInstalled = []; + const pinia = vueDemi.markRaw({ + install(app) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + if (!vueDemi.isVue2) { + pinia._a = app; + app.provide(piniaSymbol, pinia); + app.config.globalProperties.$pinia = pinia; + /* istanbul ignore else */ + if ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + registerPiniaDevtools(app, pinia); + } + toBeInstalled.forEach((plugin) => _p.push(plugin)); + toBeInstalled = []; + } + }, + use(plugin) { + if (!this._a && !vueDemi.isVue2) { + toBeInstalled.push(plugin); + } + else { + _p.push(plugin); + } + return this; + }, + _p, + // it's actually undefined here + // @ts-expect-error + _a: null, + _e: scope, + _s: new Map(), + state, + }); + // pinia devtools rely on dev only features so they cannot be forced unless + // the dev build of Vue is used. Avoid old browsers like IE11. + if ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && typeof Proxy !== 'undefined') { + pinia.use(devtoolsPlugin); + } + return pinia; +} +/** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ +function disposePinia(pinia) { + pinia._e.stop(); + pinia._s.clear(); + pinia._p.splice(0); + pinia.state.value = {}; + // @ts-expect-error: non valid + pinia._a = null; +} + +/** + * Checks if a function is a `StoreDefinition`. + * + * @param fn - object to test + * @returns true if `fn` is a StoreDefinition + */ +const isUseStore = (fn) => { + return typeof fn === 'function' && typeof fn.$id === 'string'; +}; +/** + * Mutates in place `newState` with `oldState` to _hot update_ it. It will + * remove any key not existing in `newState` and recursively merge plain + * objects. + * + * @param newState - new state object to be patched + * @param oldState - old state that should be used to patch newState + * @returns - newState + */ +function patchObject(newState, oldState) { + // no need to go through symbols because they cannot be serialized anyway + for (const key in oldState) { + const subPatch = oldState[key]; + // skip the whole sub tree + if (!(key in newState)) { + continue; + } + const targetValue = newState[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + !vueDemi.isRef(subPatch) && + !vueDemi.isReactive(subPatch)) { + newState[key] = patchObject(targetValue, subPatch); + } + else { + // objects are either a bit more complex (e.g. refs) or primitives, so we + // just set the whole thing + if (vueDemi.isVue2) { + vueDemi.set(newState, key, subPatch); + } + else { + newState[key] = subPatch; + } + } + } + return newState; +} +/** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ +function acceptHMRUpdate(initialUseStore, hot) { + // strip as much as possible from iife.prod + if (!(process.env.NODE_ENV !== 'production')) { + return () => { }; + } + return (newModule) => { + const pinia = hot.data.pinia || initialUseStore._pinia; + if (!pinia) { + // this store is still not used + return; + } + // preserve the pinia instance across loads + hot.data.pinia = pinia; + // console.log('got data', newStore) + for (const exportName in newModule) { + const useStore = newModule[exportName]; + // console.log('checking for', exportName) + if (isUseStore(useStore) && pinia._s.has(useStore.$id)) { + // console.log('Accepting update for', useStore.$id) + const id = useStore.$id; + if (id !== initialUseStore.$id) { + console.warn(`The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`); + // return import.meta.hot.invalidate() + return hot.invalidate(); + } + const existingStore = pinia._s.get(id); + if (!existingStore) { + console.log(`[Pinia]: skipping hmr because store doesn't exist yet`); + return; + } + useStore(pinia, existingStore); + } + } + }; +} + +const noop = () => { }; +function addSubscription(subscriptions, callback, detached, onCleanup = noop) { + subscriptions.push(callback); + const removeSubscription = () => { + const idx = subscriptions.indexOf(callback); + if (idx > -1) { + subscriptions.splice(idx, 1); + onCleanup(); + } + }; + if (!detached && vueDemi.getCurrentScope()) { + vueDemi.onScopeDispose(removeSubscription); + } + return removeSubscription; +} +function triggerSubscriptions(subscriptions, ...args) { + subscriptions.slice().forEach((callback) => { + callback(...args); + }); +} + +const fallbackRunWithContext = (fn) => fn(); +/** + * Marks a function as an action for `$onAction` + * @internal + */ +const ACTION_MARKER = Symbol(); +/** + * Action name symbol. Allows to add a name to an action after defining it + * @internal + */ +const ACTION_NAME = Symbol(); +function mergeReactiveObjects(target, patchToApply) { + // Handle Map instances + if (target instanceof Map && patchToApply instanceof Map) { + patchToApply.forEach((value, key) => target.set(key, value)); + } + else if (target instanceof Set && patchToApply instanceof Set) { + // Handle Set instances + patchToApply.forEach(target.add, target); + } + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + if (!patchToApply.hasOwnProperty(key)) + continue; + const subPatch = patchToApply[key]; + const targetValue = target[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + target.hasOwnProperty(key) && + !vueDemi.isRef(subPatch) && + !vueDemi.isReactive(subPatch)) { + // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might + // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that + // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`. + target[key] = mergeReactiveObjects(targetValue, subPatch); + } + else { + // @ts-expect-error: subPatch is a valid value + target[key] = subPatch; + } + } + return target; +} +const skipHydrateSymbol = (process.env.NODE_ENV !== 'production') + ? Symbol('pinia:skipHydration') + : /* istanbul ignore next */ Symbol(); +const skipHydrateMap = /*#__PURE__*/ new WeakMap(); +/** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ +function skipHydrate(obj) { + return vueDemi.isVue2 + ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work... + /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj + : Object.defineProperty(obj, skipHydrateSymbol, {}); +} +/** + * Returns whether a value should be hydrated + * + * @param obj - target variable + * @returns true if `obj` should be hydrated + */ +function shouldHydrate(obj) { + return vueDemi.isVue2 + ? /* istanbul ignore next */ !skipHydrateMap.has(obj) + : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol); +} +const { assign } = Object; +function isComputed(o) { + return !!(vueDemi.isRef(o) && o.effect); +} +function createOptionsStore(id, options, pinia, hot) { + const { state, actions, getters } = options; + const initialState = pinia.state.value[id]; + let store; + function setup() { + if (!initialState && (!(process.env.NODE_ENV !== 'production') || !hot)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, id, state ? state() : {}); + } + else { + pinia.state.value[id] = state ? state() : {}; + } + } + // avoid creating a state in pinia.state.value + const localState = (process.env.NODE_ENV !== 'production') && hot + ? // use ref() to unwrap refs inside state TODO: check if this is still necessary + vueDemi.toRefs(vueDemi.ref(state ? state() : {}).value) + : vueDemi.toRefs(pinia.state.value[id]); + return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { + if ((process.env.NODE_ENV !== 'production') && name in localState) { + console.warn(`[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with "${name}" in store "${id}".`); + } + computedGetters[name] = vueDemi.markRaw(vueDemi.computed(() => { + setActivePinia(pinia); + // it was created just before + const store = pinia._s.get(id); + // allow cross using stores + /* istanbul ignore if */ + if (vueDemi.isVue2 && !store._r) + return; + // @ts-expect-error + // return getters![name].call(context, context) + // TODO: avoid reading the getter while assigning with a global variable + return getters[name].call(store, store); + })); + return computedGetters; + }, {})); + } + store = createSetupStore(id, setup, options, pinia, hot, true); + return store; +} +function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { + let scope; + const optionsForPlugin = assign({ actions: {} }, options); + /* istanbul ignore if */ + if ((process.env.NODE_ENV !== 'production') && !pinia._e.active) { + throw new Error('Pinia destroyed'); + } + // watcher options for $subscribe + const $subscribeOptions = { deep: true }; + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production') && !vueDemi.isVue2) { + $subscribeOptions.onTrigger = (event) => { + /* istanbul ignore else */ + if (isListening) { + debuggerEvents = event; + // avoid triggering this while the store is being built and the state is being set in pinia + } + else if (isListening == false && !store._hotUpdating) { + // let patch send all the events together later + /* istanbul ignore else */ + if (Array.isArray(debuggerEvents)) { + debuggerEvents.push(event); + } + else { + console.error('🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'); + } + } + }; + } + // internal state + let isListening; // set to true at the end + let isSyncListening; // set to true at the end + let subscriptions = []; + let actionSubscriptions = []; + let debuggerEvents; + const initialState = pinia.state.value[$id]; + // avoid setting the state for option stores if it is set + // by the setup + if (!isOptionsStore && !initialState && (!(process.env.NODE_ENV !== 'production') || !hot)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, $id, {}); + } + else { + pinia.state.value[$id] = {}; + } + } + const hotState = vueDemi.ref({}); + // avoid triggering too many listeners + // https://github.com/vuejs/pinia/issues/1129 + let activeListener; + function $patch(partialStateOrMutator) { + let subscriptionMutation; + isListening = isSyncListening = false; + // reset the debugger events since patches are sync + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + debuggerEvents = []; + } + if (typeof partialStateOrMutator === 'function') { + partialStateOrMutator(pinia.state.value[$id]); + subscriptionMutation = { + type: exports.MutationType.patchFunction, + storeId: $id, + events: debuggerEvents, + }; + } + else { + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); + subscriptionMutation = { + type: exports.MutationType.patchObject, + payload: partialStateOrMutator, + storeId: $id, + events: debuggerEvents, + }; + } + const myListenerId = (activeListener = Symbol()); + vueDemi.nextTick().then(() => { + if (activeListener === myListenerId) { + isListening = true; + } + }); + isSyncListening = true; + // because we paused the watcher, we need to manually call the subscriptions + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); + } + const $reset = isOptionsStore + ? function $reset() { + const { state } = options; + const newState = state ? state() : {}; + // we use a patch to group all changes into one single subscription + this.$patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, newState); + }); + } + : /* istanbul ignore next */ + (process.env.NODE_ENV !== 'production') + ? () => { + throw new Error(`🍍: Store "${$id}" is built using the setup syntax and does not implement $reset().`); + } + : noop; + function $dispose() { + scope.stop(); + subscriptions = []; + actionSubscriptions = []; + pinia._s.delete($id); + } + /** + * Helper that wraps function so it can be tracked with $onAction + * @param fn - action to wrap + * @param name - name of the action + */ + const action = (fn, name = '') => { + if (ACTION_MARKER in fn) { + fn[ACTION_NAME] = name; + return fn; + } + const wrappedAction = function () { + setActivePinia(pinia); + const args = Array.from(arguments); + const afterCallbackList = []; + const onErrorCallbackList = []; + function after(callback) { + afterCallbackList.push(callback); + } + function onError(callback) { + onErrorCallbackList.push(callback); + } + // @ts-expect-error + triggerSubscriptions(actionSubscriptions, { + args, + name: wrappedAction[ACTION_NAME], + store, + after, + onError, + }); + let ret; + try { + ret = fn.apply(this && this.$id === $id ? this : store, args); + // handle sync errors + } + catch (error) { + triggerSubscriptions(onErrorCallbackList, error); + throw error; + } + if (ret instanceof Promise) { + return ret + .then((value) => { + triggerSubscriptions(afterCallbackList, value); + return value; + }) + .catch((error) => { + triggerSubscriptions(onErrorCallbackList, error); + return Promise.reject(error); + }); + } + // trigger after callbacks + triggerSubscriptions(afterCallbackList, ret); + return ret; + }; + wrappedAction[ACTION_MARKER] = true; + wrappedAction[ACTION_NAME] = name; // will be set later + // @ts-expect-error: we are intentionally limiting the returned type to just Fn + // because all the added properties are internals that are exposed through `$onAction()` only + return wrappedAction; + }; + const _hmrPayload = /*#__PURE__*/ vueDemi.markRaw({ + actions: {}, + getters: {}, + state: [], + hotState, + }); + const partialStore = { + _p: pinia, + // _s: scope, + $id, + $onAction: addSubscription.bind(null, actionSubscriptions), + $patch, + $reset, + $subscribe(callback, options = {}) { + const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher()); + const stopWatcher = scope.run(() => vueDemi.watch(() => pinia.state.value[$id], (state) => { + if (options.flush === 'sync' ? isSyncListening : isListening) { + callback({ + storeId: $id, + type: exports.MutationType.direct, + events: debuggerEvents, + }, state); + } + }, assign({}, $subscribeOptions, options))); + return removeSubscription; + }, + $dispose, + }; + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // start as non ready + partialStore._r = false; + } + const store = vueDemi.reactive((process.env.NODE_ENV !== 'production') || ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) + ? assign({ + _hmrPayload, + _customProperties: vueDemi.markRaw(new Set()), // devtools custom properties + }, partialStore + // must be added later + // setupStore + ) + : partialStore); + // store the partial store now so the setup of stores can instantiate each other before they are finished without + // creating infinite loops. + pinia._s.set($id, store); + const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext; + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped + const setupStore = runWithContext(() => pinia._e.run(() => (scope = vueDemi.effectScope()).run(() => setup({ action })))); + // overwrite existing actions to support $onAction + for (const key in setupStore) { + const prop = setupStore[key]; + if ((vueDemi.isRef(prop) && !isComputed(prop)) || vueDemi.isReactive(prop)) { + // mark it as a piece of state to be serialized + if ((process.env.NODE_ENV !== 'production') && hot) { + vueDemi.set(hotState.value, key, vueDemi.toRef(setupStore, key)); + // createOptionStore directly sets the state in pinia.state.value so we + // can just skip that + } + else if (!isOptionsStore) { + // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created + if (initialState && shouldHydrate(prop)) { + if (vueDemi.isRef(prop)) { + prop.value = initialState[key]; + } + else { + // probably a reactive object, lets recursively assign + // @ts-expect-error: prop is unknown + mergeReactiveObjects(prop, initialState[key]); + } + } + // transfer the ref to the pinia state to keep everything in sync + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value[$id], key, prop); + } + else { + pinia.state.value[$id][key] = prop; + } + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + _hmrPayload.state.push(key); + } + // action + } + else if (typeof prop === 'function') { + const actionValue = (process.env.NODE_ENV !== 'production') && hot ? prop : action(prop, key); + // this a hot module replacement store because the hotUpdate method needs + // to do it with the right context + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(setupStore, key, actionValue); + } + else { + // @ts-expect-error + setupStore[key] = actionValue; + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + _hmrPayload.actions[key] = prop; + } + // list actions so they can be used in plugins + // @ts-expect-error + optionsForPlugin.actions[key] = prop; + } + else if ((process.env.NODE_ENV !== 'production')) { + // add getters for devtools + if (isComputed(prop)) { + _hmrPayload.getters[key] = isOptionsStore + ? // @ts-expect-error + options.getters[key] + : prop; + if (IS_CLIENT) { + const getters = setupStore._getters || + // @ts-expect-error: same + (setupStore._getters = vueDemi.markRaw([])); + getters.push(key); + } + } + } + } + // add the state, getters, and action properties + /* istanbul ignore if */ + if (vueDemi.isVue2) { + Object.keys(setupStore).forEach((key) => { + vueDemi.set(store, key, setupStore[key]); + }); + } + else { + assign(store, setupStore); + // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object. + // Make `storeToRefs()` work with `reactive()` #799 + assign(vueDemi.toRaw(store), setupStore); + } + // use this instead of a computed with setter to be able to create it anywhere + // without linking the computed lifespan to wherever the store is first + // created. + Object.defineProperty(store, '$state', { + get: () => ((process.env.NODE_ENV !== 'production') && hot ? hotState.value : pinia.state.value[$id]), + set: (state) => { + /* istanbul ignore if */ + if ((process.env.NODE_ENV !== 'production') && hot) { + throw new Error('cannot set hotState'); + } + $patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, state); + }); + }, + }); + // add the hotUpdate before plugins to allow them to override it + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + store._hotUpdate = vueDemi.markRaw((newStore) => { + store._hotUpdating = true; + newStore._hmrPayload.state.forEach((stateKey) => { + if (stateKey in store.$state) { + const newStateTarget = newStore.$state[stateKey]; + const oldStateSource = store.$state[stateKey]; + if (typeof newStateTarget === 'object' && + isPlainObject(newStateTarget) && + isPlainObject(oldStateSource)) { + patchObject(newStateTarget, oldStateSource); + } + else { + // transfer the ref + newStore.$state[stateKey] = oldStateSource; + } + } + // patch direct access properties to allow store.stateProperty to work as + // store.$state.stateProperty + vueDemi.set(store, stateKey, vueDemi.toRef(newStore.$state, stateKey)); + }); + // remove deleted state properties + Object.keys(store.$state).forEach((stateKey) => { + if (!(stateKey in newStore.$state)) { + vueDemi.del(store, stateKey); + } + }); + // avoid devtools logging this as a mutation + isListening = false; + isSyncListening = false; + pinia.state.value[$id] = vueDemi.toRef(newStore._hmrPayload, 'hotState'); + isSyncListening = true; + vueDemi.nextTick().then(() => { + isListening = true; + }); + for (const actionName in newStore._hmrPayload.actions) { + const actionFn = newStore[actionName]; + vueDemi.set(store, actionName, action(actionFn, actionName)); + } + // TODO: does this work in both setup and option store? + for (const getterName in newStore._hmrPayload.getters) { + const getter = newStore._hmrPayload.getters[getterName]; + const getterValue = isOptionsStore + ? // special handling of options api + vueDemi.computed(() => { + setActivePinia(pinia); + return getter.call(store, store); + }) + : getter; + vueDemi.set(store, getterName, getterValue); + } + // remove deleted getters + Object.keys(store._hmrPayload.getters).forEach((key) => { + if (!(key in newStore._hmrPayload.getters)) { + vueDemi.del(store, key); + } + }); + // remove old actions + Object.keys(store._hmrPayload.actions).forEach((key) => { + if (!(key in newStore._hmrPayload.actions)) { + vueDemi.del(store, key); + } + }); + // update the values used in devtools and to allow deleting new properties later on + store._hmrPayload = newStore._hmrPayload; + store._getters = newStore._getters; + store._hotUpdating = false; + }); + } + if ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + const nonEnumerable = { + writable: true, + configurable: true, + // avoid warning on devtools trying to display this property + enumerable: false, + }; + ['_p', '_hmrPayload', '_getters', '_customProperties'].forEach((p) => { + Object.defineProperty(store, p, assign({ value: store[p] }, nonEnumerable)); + }); + } + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // mark the store as ready before plugins + store._r = true; + } + // apply all plugins + pinia._p.forEach((extender) => { + /* istanbul ignore else */ + if ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + const extensions = scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + })); + Object.keys(extensions || {}).forEach((key) => store._customProperties.add(key)); + assign(store, extensions); + } + else { + assign(store, scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + }))); + } + }); + if ((process.env.NODE_ENV !== 'production') && + store.$state && + typeof store.$state === 'object' && + typeof store.$state.constructor === 'function' && + !store.$state.constructor.toString().includes('[native code]')) { + console.warn(`[🍍]: The "state" must be a plain object. It cannot be\n` + + `\tstate: () => new MyClass()\n` + + `Found in store "${store.$id}".`); + } + // only apply hydrate to option stores with an initial state in pinia + if (initialState && + isOptionsStore && + options.hydrate) { + options.hydrate(store.$state, initialState); + } + isListening = true; + isSyncListening = true; + return store; +} +// improves tree shaking +/*#__NO_SIDE_EFFECTS__*/ +function defineStore( +// TODO: add proper types from above +idOrOptions, setup, setupOptions) { + let id; + let options; + const isSetupStore = typeof setup === 'function'; + if (typeof idOrOptions === 'string') { + id = idOrOptions; + // the option store setup will contain the actual options in this case + options = isSetupStore ? setupOptions : setup; + } + else { + options = idOrOptions; + id = idOrOptions.id; + if ((process.env.NODE_ENV !== 'production') && typeof id !== 'string') { + throw new Error(`[🍍]: "defineStore()" must be passed a store id as its first argument.`); + } + } + function useStore(pinia, hot) { + const hasContext = vueDemi.hasInjectionContext(); + pinia = + // in test mode, ignore the argument provided as we can always retrieve a + // pinia instance with getActivePinia() + ((process.env.NODE_ENV === 'test') && activePinia && activePinia._testing ? null : pinia) || + (hasContext ? vueDemi.inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + if ((process.env.NODE_ENV !== 'production') && !activePinia) { + throw new Error(`[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?\n` + + `See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.\n` + + `This will fail in production.`); + } + pinia = activePinia; + if (!pinia._s.has(id)) { + // creating the store registers it in `pinia._s` + if (isSetupStore) { + createSetupStore(id, setup, options, pinia); + } + else { + createOptionsStore(id, options, pinia); + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + // @ts-expect-error: not the right inferred type + useStore._pinia = pinia; + } + } + const store = pinia._s.get(id); + if ((process.env.NODE_ENV !== 'production') && hot) { + const hotId = '__hot:' + id; + const newStore = isSetupStore + ? createSetupStore(hotId, setup, options, pinia, true) + : createOptionsStore(hotId, assign({}, options), pinia, true); + hot._hotUpdate(newStore); + // cleanup the state properties and the store from the cache + delete pinia.state.value[hotId]; + pinia._s.delete(hotId); + } + if ((process.env.NODE_ENV !== 'production') && IS_CLIENT) { + const currentInstance = vueDemi.getCurrentInstance(); + // save stores in instances to access them devtools + if (currentInstance && + currentInstance.proxy && + // avoid adding stores that are just built for hot module replacement + !hot) { + const vm = currentInstance.proxy; + const cache = '_pStores' in vm ? vm._pStores : (vm._pStores = {}); + cache[id] = store; + } + } + // StoreGeneric cannot be casted towards Store + return store; + } + useStore.$id = id; + return useStore; +} + +let mapStoreSuffix = 'Store'; +/** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ +function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS +) { + mapStoreSuffix = suffix; +} +/** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ +function mapStores(...stores) { + if ((process.env.NODE_ENV !== 'production') && Array.isArray(stores[0])) { + console.warn(`[🍍]: Directly pass all stores to "mapStores()" without putting them in an array:\n` + + `Replace\n` + + `\tmapStores([useAuthStore, useCartStore])\n` + + `with\n` + + `\tmapStores(useAuthStore, useCartStore)\n` + + `This will fail in production if not fixed.`); + stores = stores[0]; + } + return stores.reduce((reduced, useStore) => { + // @ts-expect-error: $id is added by defineStore + reduced[useStore.$id + mapStoreSuffix] = function () { + return useStore(this.$pinia); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + reduced[key] = function () { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function () { + const store = useStore(this.$pinia); + const storeKey = keysOrMapper[key]; + // for some reason TS is unable to infer the type of storeKey to be a + // function + return typeof storeKey === 'function' + ? storeKey.call(this, store) + : // @ts-expect-error: FIXME: should work? + store[storeKey]; + }; + return reduced; + }, {}); +} +/** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ +const mapGetters = mapState; +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapActions(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key](...args); + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]](...args); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapWritableState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[key] = value); + }, + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[keysOrMapper[key]] = value); + }, + }; + return reduced; + }, {}); +} + +/** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ +function storeToRefs(store) { + // See https://github.com/vuejs/pinia/issues/852 + // It's easier to just use toRefs() even if it includes more stuff + if (vueDemi.isVue2) { + // @ts-expect-error: toRefs include methods and others + return vueDemi.toRefs(store); + } + else { + store = vueDemi.toRaw(store); + const refs = {}; + for (const key in store) { + const value = store[key]; + if (vueDemi.isRef(value) || vueDemi.isReactive(value)) { + // @ts-expect-error: the key is state or getter + refs[key] = + // --- + vueDemi.toRef(store, key); + } + } + return refs; + } +} + +/** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ +const PiniaVuePlugin = function (_Vue) { + // Equivalent of + // app.config.globalProperties.$pinia = pinia + _Vue.mixin({ + beforeCreate() { + const options = this.$options; + if (options.pinia) { + const pinia = options.pinia; + // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31 + /* istanbul ignore else */ + if (!this._provided) { + const provideCache = {}; + Object.defineProperty(this, '_provided', { + get: () => provideCache, + set: (v) => Object.assign(provideCache, v), + }); + } + this._provided[piniaSymbol] = pinia; + // propagate the pinia instance in an SSR friendly way + // avoid adding it to nuxt twice + /* istanbul ignore else */ + if (!this.$pinia) { + this.$pinia = pinia; + } + pinia._a = this; + if (IS_CLIENT) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + } + if ((((process.env.NODE_ENV !== 'production') || false) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + registerPiniaDevtools(pinia._a, pinia); + } + } + else if (!this.$pinia && options.parent && options.parent.$pinia) { + this.$pinia = options.parent.$pinia; + } + }, + destroyed() { + delete this._pStores; + }, + }); +}; + +exports.PiniaVuePlugin = PiniaVuePlugin; +exports.acceptHMRUpdate = acceptHMRUpdate; +exports.createPinia = createPinia; +exports.defineStore = defineStore; +exports.disposePinia = disposePinia; +exports.getActivePinia = getActivePinia; +exports.mapActions = mapActions; +exports.mapGetters = mapGetters; +exports.mapState = mapState; +exports.mapStores = mapStores; +exports.mapWritableState = mapWritableState; +exports.setActivePinia = setActivePinia; +exports.setMapStoreSuffix = setMapStoreSuffix; +exports.skipHydrate = skipHydrate; +exports.storeToRefs = storeToRefs; diff --git a/node_modules/pinia/dist/pinia.d.ts b/node_modules/pinia/dist/pinia.d.ts new file mode 100644 index 0000000..8878391 --- /dev/null +++ b/node_modules/pinia/dist/pinia.d.ts @@ -0,0 +1,1064 @@ +import { App } from 'vue-demi'; +import { ComputedRef } from 'vue-demi'; +import type { DebuggerEvent } from 'vue-demi'; +import { EffectScope } from 'vue-demi'; +import type { Plugin as Plugin_2 } from 'vue-demi'; +import { Ref } from 'vue-demi'; +import { ToRef } from 'vue-demi'; +import { ToRefs } from 'vue-demi'; +import { UnwrapRef } from 'vue-demi'; +import type { WatchOptions } from 'vue-demi'; +import { WritableComputedRef } from 'vue-demi'; + +/** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ +export declare function acceptHMRUpdate = _GettersTree, A = _ActionsTree>(initialUseStore: StoreDefinition, hot: any): (newModule: any) => any; + +/** + * Type of an object of Actions. For internal usage only. + * For internal use **only** + */ +export declare type _ActionsTree = Record; + +export declare type _Awaited = T extends null | undefined ? T : T extends object & { + then(onfulfilled: infer F): any; +} ? F extends (value: infer V, ...args: any) => any ? _Awaited : never : T; + +/** + * Creates a Pinia instance to be used by the application + */ +export declare function createPinia(): Pinia; + +/** + * Recursive `Partial`. Used by {@link Store['$patch']}. + * + * For internal use **only** + */ +export declare type _DeepPartial = { + [K in keyof T]?: _DeepPartial; +}; + +/** + * Options parameter of `defineStore()` for setup stores. Can be extended to + * augment stores with the plugin API. @see {@link DefineStoreOptionsBase}. + */ +export declare interface DefineSetupStoreOptions extends DefineStoreOptionsBase> { + /** + * Extracted actions. Added by useStore(). SHOULD NOT be added by the user when + * creating the store. Can be used in plugins to get the list of actions in a + * store defined with a setup function. Note this is always defined + */ + actions?: A; +} + +/** + * Creates a `useStore` function that retrieves the store instance + * + * @param id - id of the store (must be unique) + * @param options - options to define the store + */ +export declare function defineStore = {}, A = {}>(id: Id, options: Omit, 'id'>): StoreDefinition; + +/** + * Creates a `useStore` function that retrieves the store instance + * + * @param options - options to define the store + */ +export declare function defineStore = {}, A = {}>(options: DefineStoreOptions): StoreDefinition; + +/** + * Creates a `useStore` function that retrieves the store instance + * + * @param id - id of the store (must be unique) + * @param storeSetup - function that defines the store + * @param options - extra options + */ +export declare function defineStore(id: Id, storeSetup: (helpers: SetupStoreHelpers) => SS, options?: DefineSetupStoreOptions, _ExtractGettersFromSetupStore, _ExtractActionsFromSetupStore>): StoreDefinition, _ExtractGettersFromSetupStore, _ExtractActionsFromSetupStore>; + +/** + * Options parameter of `defineStore()` for option stores. Can be extended to + * augment stores with the plugin API. @see {@link DefineStoreOptionsBase}. + */ +export declare interface DefineStoreOptions extends DefineStoreOptionsBase> { + /** + * Unique string key to identify the store across the application. + */ + id: Id; + /** + * Function to create a fresh state. **Must be an arrow function** to ensure + * correct typings! + */ + state?: () => S; + /** + * Optional object of getters. + */ + getters?: G & ThisType & _StoreWithGetters & PiniaCustomProperties> & _GettersTree; + /** + * Optional object of actions. + */ + actions?: A & ThisType & _StoreWithState & _StoreWithGetters & PiniaCustomProperties>; + /** + * Allows hydrating the store during SSR when complex state (like client side only refs) are used in the store + * definition and copying the value from `pinia.state` isn't enough. + * + * @example + * If in your `state`, you use any `customRef`s, any `computed`s, or any `ref`s that have a different value on + * Server and Client, you need to manually hydrate them. e.g., a custom ref that is stored in the local + * storage: + * + * ```ts + * const useStore = defineStore('main', { + * state: () => ({ + * n: useLocalStorage('key', 0) + * }), + * hydrate(storeState, initialState) { + * // @ts-expect-error: https://github.com/microsoft/TypeScript/issues/43826 + * storeState.n = useLocalStorage('key', 0) + * } + * }) + * ``` + * + * @param storeState - the current state in the store + * @param initialState - initialState + */ + hydrate?(storeState: UnwrapRef, initialState: UnwrapRef): void; +} + +/** + * Options passed to `defineStore()` that are common between option and setup + * stores. Extend this interface if you want to add custom options to both kinds + * of stores. + */ +export declare interface DefineStoreOptionsBase { +} + +/** + * Available `options` when creating a pinia plugin. + */ +export declare interface DefineStoreOptionsInPlugin extends Omit, 'id' | 'actions'> { + /** + * Extracted object of actions. Added by useStore() when the store is built + * using the setup API, otherwise uses the one passed to `defineStore()`. + * Defaults to an empty object if no actions are defined. + */ + actions: A; +} + +/** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ +export declare function disposePinia(pinia: Pinia): void; + +/** + * For internal use **only** + */ +export declare type _ExtractActionsFromSetupStore = SS extends undefined | void ? {} : Pick>; + +/** + * Type that enables refactoring through IDE. + * For internal use **only** + */ +export declare type _ExtractActionsFromSetupStore_Keys = keyof { + [K in keyof SS as SS[K] extends _Method ? K : never]: any; +}; + +/** + * For internal use **only** + */ +export declare type _ExtractGettersFromSetupStore = SS extends undefined | void ? {} : Pick>; + +/** + * Type that enables refactoring through IDE. + * For internal use **only** + */ +export declare type _ExtractGettersFromSetupStore_Keys = keyof { + [K in keyof SS as SS[K] extends ComputedRef ? K : never]: any; +}; + +/** + * For internal use **only** + */ +export declare type _ExtractStateFromSetupStore = SS extends undefined | void ? {} : Pick>; + +/** + * Type that enables refactoring through IDE. + * For internal use **only** + */ +export declare type _ExtractStateFromSetupStore_Keys = keyof { + [K in keyof SS as SS[K] extends _Method | ComputedRef ? never : K]: any; +}; + +/** + * Get the currently active pinia if there is any. + */ +export declare const getActivePinia: () => Pinia | undefined; + +/** + * Type of an object of Getters that infers the argument. For internal usage only. + * For internal use **only** + */ +export declare type _GettersTree = Record & UnwrapRef>) => any) | (() => any)>; + +/** + * Internal utility type + */ +declare type _IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; + +/** + * Internal utility type + */ +declare type _IsReadonly = _IfEquals<{ + [P in K]: T[P]; +}, { + -readonly [P in K]: T[P]; +}, false, // Property is not readonly if they are the same +true>; + +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. The values of the object are the actions while the keys are + * the names of the resulting methods. + * + * @example + * ```js + * export default { + * methods: { + * // other methods properties + * // useCounterStore has two actions named `increment` and `setCount` + * ...mapActions(useCounterStore, { more: 'increment', setIt: 'setCount' }) + * }, + * + * created() { + * this.more() + * this.setIt(2) + * } + * } + * ``` + * + * @param useStore - store to map from + * @param keyMapper - object to define new names for the actions + */ +export declare function mapActions, A, KeyMapper extends Record>(useStore: StoreDefinition, keyMapper: KeyMapper): _MapActionsObjectReturn; + +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @example + * ```js + * export default { + * methods: { + * // other methods properties + * ...mapActions(useCounterStore, ['increment', 'setCount']) + * }, + * + * created() { + * this.increment() + * this.setCount(2) // pass arguments as usual + * } + * } + * ``` + * + * @param useStore - store to map from + * @param keys - array of action names to map + */ +export declare function mapActions, A>(useStore: StoreDefinition, keys: Array): _MapActionsReturn; + +/** + * For internal use **only** + */ +export declare type _MapActionsObjectReturn> = { + [key in keyof T]: A[T[key]]; +}; + +/** + * For internal use **only** + */ +export declare type _MapActionsReturn = { + [key in keyof A]: A[key]; +}; + +/** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ +export declare const mapGetters: typeof mapState; + +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. The values of the object are the state properties/getters + * while the keys are the names of the resulting computed properties. + * Optionally, you can also pass a custom function that will receive the store + * as its first argument. Note that while it has access to the component + * instance via `this`, it won't be typed. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * // useCounterStore has a state property named `count` and a getter `double` + * ...mapState(useCounterStore, { + * n: 'count', + * triple: store => store.n * 3, + * // note we can't use an arrow function if we want to use `this` + * custom(store) { + * return this.someComponentValue + store.n + * }, + * doubleN: 'double' + * }) + * }, + * + * created() { + * this.n // 2 + * this.doubleN // 4 + * } + * } + * ``` + * + * @param useStore - store to map from + * @param keyMapper - object of state properties or getters + */ +export declare function mapState | { + [key: string]: ComputedRef; +}, A, KeyMapper extends Record) => any)>>(useStore: StoreDefinition, keyMapper: KeyMapper): _MapStateObjectReturn; + +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapState(useCounterStore, ['count', 'double']) + * }, + * + * created() { + * this.count // 2 + * this.double // 4 + * } + * } + * ``` + * + * @param useStore - store to map from + * @param keys - array of state properties or getters + */ +export declare function mapState | { + [key: string]: ComputedRef; +}, A, Keys extends keyof S | keyof G>(useStore: StoreDefinition, keys: readonly Keys[]): _MapStateReturn; + +/** + * For internal use **only** + */ +export declare type _MapStateObjectReturn | { + [key: string]: ComputedRef; +}, A, T extends Record) => any)> = {}> = { + [key in keyof T]: () => T[key] extends (store: any) => infer R ? R : T[key] extends keyof Store ? Store[T[key]] : never; +}; + +/** + * For internal use **only** + */ +export declare type _MapStateReturn | { + [key: string]: ComputedRef; +}, Keys extends keyof S | keyof G = keyof S | keyof G> = { + [key in Keys]: key extends keyof Store ? () => Store[key] : never; +}; + +/** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ +export declare function mapStores(...stores: [...Stores]): _Spread; + +/** + * Interface to allow customizing map helpers. Extend this interface with the + * following properties: + * + * - `suffix`: string. Affects the suffix of `mapStores()`, defaults to `Store`. + */ +export declare interface MapStoresCustomization { +} + +/** + * Same as `mapState()` but creates computed setters as well so the state can be + * modified. Differently from `mapState()`, only `state` properties can be + * added. + * + * @param useStore - store to map from + * @param keyMapper - object of state properties + */ +export declare function mapWritableState, A, KeyMapper extends Record>(useStore: StoreDefinition, keyMapper: KeyMapper): _MapWritableStateObjectReturn; + +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keys - array of state properties + */ +export declare function mapWritableState, A, Keys extends keyof S>(useStore: StoreDefinition, keys: readonly Keys[]): { + [K in Keys]: { + get: () => S[K]; + set: (value: S[K]) => any; + }; +}; + +/** + * For internal use **only** + */ +export declare type _MapWritableStateObjectReturn> = { + [key in keyof T]: { + get: () => S[T[key]]; + set: (value: S[T[key]]) => any; + }; +}; + +/** + * For internal use **only** + */ +export declare type _MapWritableStateReturn = { + [key in keyof S]: { + get: () => S[key]; + set: (value: S[key]) => any; + }; +}; + +/** + * Generic type for a function that can infer arguments and return type + * + * For internal use **only** + */ +export declare type _Method = (...args: any[]) => any; + +/** + * Possible types for SubscriptionCallback + */ +export declare enum MutationType { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + direct = "direct", + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + patchObject = "patch object", + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + patchFunction = "patch function" +} + +/** + * Every application must own its own pinia to be able to create stores + */ +export declare interface Pinia { + install: (app: App) => void; + /** + * root state + */ + state: Ref>; + /** + * Adds a store plugin to extend every store + * + * @param plugin - store plugin to add + */ + use(plugin: PiniaPlugin): Pinia; + /* Excluded from this release type: _p */ + /* Excluded from this release type: _a */ + /* Excluded from this release type: _e */ + /* Excluded from this release type: _s */ + /* Excluded from this release type: _testing */ +} + +/** + * Interface to be extended by the user when they add properties through plugins. + */ +export declare interface PiniaCustomProperties, A = _ActionsTree> { +} + +/** + * Properties that are added to every `store.$state` by `pinia.use()`. + */ +export declare interface PiniaCustomStateProperties { +} + +/** + * Plugin to extend every store. + */ +export declare interface PiniaPlugin { + /** + * Plugin to extend every store. Returns an object to extend the store or + * nothing. + * + * @param context - Context + */ + (context: PiniaPluginContext): Partial | void; +} + +/** + * Context argument passed to Pinia plugins. + */ +export declare interface PiniaPluginContext, A = _ActionsTree> { + /** + * pinia instance. + */ + pinia: Pinia; + /** + * Current app created with `Vue.createApp()`. + */ + app: App; + /** + * Current store being extended. + */ + store: Store; + /** + * Initial options defining the store when calling `defineStore()`. + */ + options: DefineStoreOptionsInPlugin; +} + +/** + * Plugin to extend every store. + * @deprecated use PiniaPlugin instead + */ +export declare type PiniaStorePlugin = PiniaPlugin; + +/** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ +export declare const PiniaVuePlugin: Plugin_2; + +declare interface _SetActivePinia { + (pinia: Pinia): Pinia; + (pinia: undefined): undefined; + (pinia: Pinia | undefined): Pinia | undefined; +} + +/** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ +export declare const setActivePinia: _SetActivePinia; + +/** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ +export declare function setMapStoreSuffix(suffix: MapStoresCustomization extends Record<'suffix', infer Suffix> ? Suffix : string): void; + +/** + * Return type of `defineStore()` with a setup function. + * - `Id` is a string literal of the store's name + * - `SS` is the return type of the setup function + * @see {@link StoreDefinition} + */ +export declare interface SetupStoreDefinition extends StoreDefinition, _ExtractGettersFromSetupStore, _ExtractActionsFromSetupStore> { +} + +declare interface SetupStoreHelpers { + action: (fn: Fn) => Fn; +} + +/** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ +export declare function skipHydrate(obj: T): T; + +/** + * For internal use **only**. + */ +export declare type _Spread = A extends [infer L, ...infer R] ? _StoreObject & _Spread : unknown; + +/** + * Generic state of a Store + */ +export declare type StateTree = Record; + +/** + * Store type to build a store. + */ +export declare type Store = _StoreWithState & UnwrapRef & _StoreWithGetters & (_ActionsTree extends A ? {} : A) & PiniaCustomProperties & PiniaCustomStateProperties; + +/** + * Extract the actions of a store type. Works with both a Setup Store or an + * Options Store. + */ +export declare type StoreActions = SS extends Store, infer A> ? A : _ExtractActionsFromSetupStore; + +/** + * Return type of `defineStore()`. Function that allows instantiating a store. + */ +export declare interface StoreDefinition, A = _ActionsTree> { + /** + * Returns a store, creates it if necessary. + * + * @param pinia - Pinia instance to retrieve the store + * @param hot - dev only hot module replacement + */ + (pinia?: Pinia | null | undefined, hot?: StoreGeneric): Store; + /** + * Id of the store. Used by map helpers. + */ + $id: Id; + /* Excluded from this release type: _pinia */ +} + +/** + * Generic and type-unsafe version of Store. Doesn't fail on access with + * strings, making it much easier to write generic functions that do not care + * about the kind of store that is passed. + */ +export declare type StoreGeneric = Store, _ActionsTree>; + +/** + * Extract the getters of a store type. Works with both a Setup Store or an + * Options Store. + */ +export declare type StoreGetters = SS extends Store ? _StoreWithGetters : _ExtractGettersFromSetupStore; + +/** + * For internal use **only**. + */ +export declare type _StoreObject = S extends StoreDefinition ? { + [Id in `${Ids}${MapStoresCustomization extends Record<'suffix', infer Suffix> ? Suffix : 'Store'}`]: () => Store ? Suffix : 'Store'}` ? RealId : string, State, Getters, Actions>; +} : {}; + +/** + * Argument of `store.$onAction()` + */ +export declare type StoreOnActionListener = (context: StoreOnActionListenerContext) => void; + +/** + * Context object passed to callbacks of `store.$onAction(context => {})` + * TODO: should have only the Id, the Store and Actions to generate the proper object + */ +export declare type StoreOnActionListenerContext = _ActionsTree extends A ? _StoreOnActionListenerContext : { + [Name in keyof A]: Name extends string ? _StoreOnActionListenerContext, Name, A> : never; +}[keyof A]; + +/** + * Actual type for {@link StoreOnActionListenerContext}. Exists for refactoring + * purposes. For internal use only. + * For internal use **only** + */ +export declare interface _StoreOnActionListenerContext { + /** + * Name of the action + */ + name: ActionName; + /** + * Store that is invoking the action + */ + store: Store; + /** + * Parameters passed to the action + */ + args: A extends Record ? Parameters : unknown[]; + /** + * Sets up a hook once the action is finished. It receives the return value + * of the action, if it's a Promise, it will be unwrapped. + */ + after: (callback: A extends Record ? (resolvedReturn: _Awaited>) => void : () => void) => void; + /** + * Sets up a hook if the action fails. Return `false` to catch the error and + * stop it from propagating. + */ + onError: (callback: (error: unknown) => void) => void; +} + +/** + * Properties of a store. + */ +export declare interface StoreProperties { + /** + * Unique identifier of the store + */ + $id: Id; + /* Excluded from this release type: _p */ + /* Excluded from this release type: _getters */ + /* Excluded from this release type: _isOptionsAPI */ + /** + * Used by devtools plugin to retrieve properties added with plugins. Removed + * in production. Can be used by the user to add property keys of the store + * that should be displayed in devtools. + */ + _customProperties: Set; + /* Excluded from this release type: _hotUpdate */ + /* Excluded from this release type: _hotUpdating */ + /* Excluded from this release type: _hmrPayload */ +} + +/** + * Extract the state of a store type. Works with both a Setup Store or an + * Options Store. Note this unwraps refs. + */ +export declare type StoreState = SS extends Store, _ActionsTree> ? UnwrapRef : _ExtractStateFromSetupStore; + +/** + * Extracts the return type for `storeToRefs`. + * Will convert any `getters` into `ComputedRef`. + */ +declare type StoreToRefs = _ToStateRefs & ToRefs>> & _ToComputedRefs>; + +/** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ +export declare function storeToRefs(store: SS): StoreToRefs; + +/** + * Store augmented for actions. For internal usage only. + * For internal use **only** + */ +export declare type _StoreWithActions = { + [k in keyof A]: A[k] extends (...args: infer P) => infer R ? (...args: P) => R : never; +}; + +/** + * Store augmented with getters. For internal usage only. + * For internal use **only** + */ +export declare type _StoreWithGetters = _StoreWithGetters_Readonly & _StoreWithGetters_Writable; + +/** + * Store augmented with readonly getters. For internal usage **only**. + */ +declare type _StoreWithGetters_Readonly = { + readonly [K in keyof G as G[K] extends (...args: any[]) => any ? K : ComputedRef extends G[K] ? K : never]: G[K] extends (...args: any[]) => infer R ? R : UnwrapRef; +}; + +/** + * Store augmented with writable getters. For internal usage **only**. + */ +declare type _StoreWithGetters_Writable = { + [K in keyof G as G[K] extends WritableComputedRef ? K : never]: G[K] extends WritableComputedRef ? R : never; +}; + +/** + * Base store with state and functions. Should not be used directly. + */ +export declare interface _StoreWithState extends StoreProperties { + /** + * State of the Store. Setting it will internally call `$patch()` to update the state. + */ + $state: UnwrapRef & PiniaCustomStateProperties; + /** + * Applies a state patch to current state. Allows passing nested values + * + * @param partialState - patch to apply to the state + */ + $patch(partialState: _DeepPartial>): void; + /** + * Group multiple changes into one function. Useful when mutating objects like + * Sets or arrays and applying an object patch isn't practical, e.g. appending + * to an array. The function passed to `$patch()` **must be synchronous**. + * + * @param stateMutator - function that mutates `state`, cannot be asynchronous + */ + $patch) => any>(stateMutator: ReturnType extends Promise ? never : F): void; + /** + * Resets the store to its initial state by building a new state object. + */ + $reset(): void; + /** + * Setups a callback to be called whenever the state changes. It also returns a function to remove the callback. Note + * that when calling `store.$subscribe()` inside of a component, it will be automatically cleaned up when the + * component gets unmounted unless `detached` is set to true. + * + * @param callback - callback passed to the watcher + * @param options - `watch` options + `detached` to detach the subscription from the context (usually a component) + * this is called from. Note that the `flush` option does not affect calls to `store.$patch()`. + * @returns function that removes the watcher + */ + $subscribe(callback: SubscriptionCallback, options?: { + detached?: boolean; + } & WatchOptions): () => void; + /** + * Setups a callback to be called every time an action is about to get + * invoked. The callback receives an object with all the relevant information + * of the invoked action: + * - `store`: the store it is invoked on + * - `name`: The name of the action + * - `args`: The parameters passed to the action + * + * On top of these, it receives two functions that allow setting up a callback + * once the action finishes or when it fails. + * + * It also returns a function to remove the callback. Note than when calling + * `store.$onAction()` inside of a component, it will be automatically cleaned + * up when the component gets unmounted unless `detached` is set to true. + * + * @example + * + *```js + *store.$onAction(({ after, onError }) => { + * // Here you could share variables between all of the hooks as well as + * // setting up watchers and clean them up + * after((resolvedValue) => { + * // can be used to cleanup side effects + * . // `resolvedValue` is the value returned by the action, if it's a + * . // Promise, it will be the resolved value instead of the Promise + * }) + * onError((error) => { + * // can be used to pass up errors + * }) + *}) + *``` + * + * @param callback - callback called before every action + * @param detached - detach the subscription from the context this is called from + * @returns function that removes the watcher + */ + $onAction(callback: StoreOnActionListener, detached?: boolean): () => void; + /** + * Stops the associated effect scope of the store and remove it from the store + * registry. Plugins can override this method to cleanup any added effects. + * e.g. devtools plugin stops displaying disposed stores from devtools. + * Note this doesn't delete the state of the store, you have to do it manually with + * `delete pinia.state.value[store.$id]` if you want to. If you don't and the + * store is used again, it will reuse the previous state. + */ + $dispose(): void; + /* Excluded from this release type: _r */ +} + +/** + * Callback of a subscription + */ +export declare type SubscriptionCallback = ( +/** + * Object with information relative to the store mutation that triggered the + * subscription. + */ +mutation: SubscriptionCallbackMutation, +/** + * State of the store when the subscription is triggered. Same as + * `store.$state`. + */ +state: UnwrapRef) => void; + +/** + * Context object passed to a subscription callback. + */ +export declare type SubscriptionCallbackMutation = SubscriptionCallbackMutationDirect | SubscriptionCallbackMutationPatchObject | SubscriptionCallbackMutationPatchFunction; + +/** + * Base type for the context passed to a subscription callback. Internal type. + */ +export declare interface _SubscriptionCallbackMutationBase { + /** + * Type of the mutation. + */ + type: MutationType; + /** + * `id` of the store doing the mutation. + */ + storeId: string; + /** + * 🔴 DEV ONLY, DO NOT use for production code. Different mutation calls. Comes from + * https://vuejs.org/guide/extras/reactivity-in-depth.html#reactivity-debugging and allows to track mutations in + * devtools and plugins **during development only**. + */ + events?: DebuggerEvent[] | DebuggerEvent; +} + +/** + * Context passed to a subscription callback when directly mutating the state of + * a store with `store.someState = newValue` or `store.$state.someState = + * newValue`. + */ +export declare interface SubscriptionCallbackMutationDirect extends _SubscriptionCallbackMutationBase { + type: MutationType.direct; + events: DebuggerEvent; +} + +/** + * Context passed to a subscription callback when `store.$patch()` is called + * with a function. + */ +export declare interface SubscriptionCallbackMutationPatchFunction extends _SubscriptionCallbackMutationBase { + type: MutationType.patchFunction; + events: DebuggerEvent[]; +} + +/** + * Context passed to a subscription callback when `store.$patch()` is called + * with an object. + */ +export declare interface SubscriptionCallbackMutationPatchObject extends _SubscriptionCallbackMutationBase { + type: MutationType.patchObject; + events: DebuggerEvent[]; + /** + * Object passed to `store.$patch()`. + */ + payload: _DeepPartial>; +} + +/** + * Extracts the getters of a store while keeping writable and readonly properties. **Internal type DO NOT USE**. + */ +declare type _ToComputedRefs = { + [K in keyof SS]: true extends _IsReadonly ? ComputedRef : WritableComputedRef; +}; + +/** + * Extracts the refs of a state object from a store. If the state value is a Ref or type that extends ref, it will be kept as is. + * Otherwise, it will be converted into a Ref. **Internal type DO NOT USE**. + */ +declare type _ToStateRefs = SS extends Store, _ActionsTree> ? UnwrappedState extends _UnwrapAll> ? { + [K in Key]: ToRef; +} : ToRefs : ToRefs>; + +/** + * Type that enables refactoring through IDE. + * For internal use **only** + */ +export declare type _UnwrapAll = { + [K in keyof SS]: UnwrapRef; +}; + +export { } + +// Extensions of Vue types to be appended manually +// https://github.com/microsoft/rushstack/issues/2090 +// https://github.com/microsoft/rushstack/issues/1709 + +// @ts-ignore: works on Vue 2, fails in Vue 3 +declare module 'vue/types/vue' { + interface Vue { + /** + * Currently installed pinia instance. + */ + $pinia: Pinia + + /** + * Cache of stores instantiated by the current instance. Used by map + * helpers. Used internally by Pinia. + * + * @internal + */ + _pStores?: Record + } +} + +// @ts-ignore: works on Vue 2, fails in Vue 3 +declare module 'vue/types/options' { + interface ComponentOptions { + /** + * Pinia instance to install in your application. Should be passed to the + * root Vue. + */ + pinia?: Pinia + } +} + +/** + * NOTE: Used to be `@vue/runtime-core` but it break types from time to time. Then, in Vue docs, we started recommending + * to use `vue` instead of `@vue/runtime-core` but that broke others' types so we reverted it. Now, local types do not + * work if we use `@vue/runtime-core` so we are using `vue` again. + */ +// @ts-ignore: works on Vue 3, fails in Vue 2 +declare module 'vue' { + // This seems to be needed to not break auto import types based on the order + // https://github.com/vuejs/pinia/pull/2730 + interface GlobalComponents {} + interface ComponentCustomProperties { + /** + * Access to the application's Pinia + */ + $pinia: Pinia + + /** + * Cache of stores instantiated by the current instance. Used by devtools to + * list currently used stores. Used internally by Pinia. + * + * @internal + */ + _pStores?: Record + } +} + +// normally this is only needed in .d.ts files +export {} diff --git a/node_modules/pinia/dist/pinia.esm-browser.js b/node_modules/pinia/dist/pinia.esm-browser.js new file mode 100644 index 0000000..a240529 --- /dev/null +++ b/node_modules/pinia/dist/pinia.esm-browser.js @@ -0,0 +1,2035 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +import { hasInjectionContext, inject, toRaw, watch, unref, markRaw, effectScope, ref, isVue2, isRef, isReactive, set, getCurrentScope, onScopeDispose, getCurrentInstance, reactive, toRef, del, nextTick, computed, toRefs } from 'vue-demi'; +import { setupDevtoolsPlugin } from '@vue/devtools-api'; + +/** + * setActivePinia must be called to handle SSR at the top of functions like + * `fetch`, `setup`, `serverPrefetch` and others + */ +let activePinia; +/** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ +// @ts-expect-error: cannot constrain the type of the return +const setActivePinia = (pinia) => (activePinia = pinia); +/** + * Get the currently active pinia if there is any. + */ +const getActivePinia = () => (hasInjectionContext() && inject(piniaSymbol)) || activePinia; +const piniaSymbol = (Symbol('pinia') ); + +function isPlainObject( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +o) { + return (o && + typeof o === 'object' && + Object.prototype.toString.call(o) === '[object Object]' && + typeof o.toJSON !== 'function'); +} +// type DeepReadonly = { readonly [P in keyof T]: DeepReadonly } +// TODO: can we change these to numbers? +/** + * Possible types for SubscriptionCallback + */ +var MutationType; +(function (MutationType) { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + MutationType["direct"] = "direct"; + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + MutationType["patchObject"] = "patch object"; + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + MutationType["patchFunction"] = "patch function"; + // maybe reset? for $state = {} and $reset +})(MutationType || (MutationType = {})); + +const IS_CLIENT = typeof window !== 'undefined'; + +/* + * FileSaver.js A saveAs() FileSaver implementation. + * + * Originally by Eli Grey, adapted as an ESM module by Eduardo San Martin + * Morote. + * + * License : MIT + */ +// The one and only way of getting global scope in all environments +// https://stackoverflow.com/q/3277182/1008999 +const _global = /*#__PURE__*/ (() => typeof window === 'object' && window.window === window + ? window + : typeof self === 'object' && self.self === self + ? self + : typeof global === 'object' && global.global === global + ? global + : typeof globalThis === 'object' + ? globalThis + : { HTMLElement: null })(); +function bom(blob, { autoBom = false } = {}) { + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (autoBom && + /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xfeff), blob], { type: blob.type }); + } + return blob; +} +function download(url, name, opts) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.onload = function () { + saveAs(xhr.response, name, opts); + }; + xhr.onerror = function () { + console.error('could not download file'); + }; + xhr.send(); +} +function corsEnabled(url) { + const xhr = new XMLHttpRequest(); + // use sync to avoid popup blocker + xhr.open('HEAD', url, false); + try { + xhr.send(); + } + catch (e) { } + return xhr.status >= 200 && xhr.status <= 299; +} +// `a.click()` doesn't work for all browsers (#465) +function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')); + } + catch (e) { + const evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); + node.dispatchEvent(evt); + } +} +const _navigator = typeof navigator === 'object' ? navigator : { userAgent: '' }; +// Detect WebView inside a native macOS app by ruling out all browsers +// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too +// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos +const isMacOSWebView = /*#__PURE__*/ (() => /Macintosh/.test(_navigator.userAgent) && + /AppleWebKit/.test(_navigator.userAgent) && + !/Safari/.test(_navigator.userAgent))(); +const saveAs = !IS_CLIENT + ? () => { } // noop + : // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView or mini program + typeof HTMLAnchorElement !== 'undefined' && + 'download' in HTMLAnchorElement.prototype && + !isMacOSWebView + ? downloadSaveAs + : // Use msSaveOrOpenBlob as a second approach + 'msSaveOrOpenBlob' in _navigator + ? msSaveAs + : // Fallback to using FileReader and a popup + fileSaverSaveAs; +function downloadSaveAs(blob, name = 'download', opts) { + const a = document.createElement('a'); + a.download = name; + a.rel = 'noopener'; // tabnabbing + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + if (typeof blob === 'string') { + // Support regular links + a.href = blob; + if (a.origin !== location.origin) { + if (corsEnabled(a.href)) { + download(blob, name, opts); + } + else { + a.target = '_blank'; + click(a); + } + } + else { + click(a); + } + } + else { + // Support blobs + a.href = URL.createObjectURL(blob); + setTimeout(function () { + URL.revokeObjectURL(a.href); + }, 4e4); // 40s + setTimeout(function () { + click(a); + }, 0); + } +} +function msSaveAs(blob, name = 'download', opts) { + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts); + } + else { + const a = document.createElement('a'); + a.href = blob; + a.target = '_blank'; + setTimeout(function () { + click(a); + }); + } + } + else { + // @ts-ignore: works on windows + navigator.msSaveOrOpenBlob(bom(blob, opts), name); + } +} +function fileSaverSaveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank'); + if (popup) { + popup.document.title = popup.document.body.innerText = 'downloading...'; + } + if (typeof blob === 'string') + return download(blob, name, opts); + const force = blob.type === 'application/octet-stream'; + const isSafari = /constructor/i.test(String(_global.HTMLElement)) || 'safari' in _global; + const isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); + if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && + typeof FileReader !== 'undefined') { + // Safari doesn't allow downloading of blob URLs + const reader = new FileReader(); + reader.onloadend = function () { + let url = reader.result; + if (typeof url !== 'string') { + popup = null; + throw new Error('Wrong reader.result type'); + } + url = isChromeIOS + ? url + : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); + if (popup) { + popup.location.href = url; + } + else { + location.assign(url); + } + popup = null; // reverse-tabnabbing #460 + }; + reader.readAsDataURL(blob); + } + else { + const url = URL.createObjectURL(blob); + if (popup) + popup.location.assign(url); + else + location.href = url; + popup = null; // reverse-tabnabbing #460 + setTimeout(function () { + URL.revokeObjectURL(url); + }, 4e4); // 40s + } +} + +/** + * Shows a toast or console.log + * + * @param message - message to log + * @param type - different color of the tooltip + */ +function toastMessage(message, type) { + const piniaMessage = '🍍 ' + message; + if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') { + // No longer available :( + __VUE_DEVTOOLS_TOAST__(piniaMessage, type); + } + else if (type === 'error') { + console.error(piniaMessage); + } + else if (type === 'warn') { + console.warn(piniaMessage); + } + else { + console.log(piniaMessage); + } +} +function isPinia(o) { + return '_a' in o && 'install' in o; +} + +/** + * This file contain devtools actions, they are not Pinia actions. + */ +// --- +function checkClipboardAccess() { + if (!('clipboard' in navigator)) { + toastMessage(`Your browser doesn't support the Clipboard API`, 'error'); + return true; + } +} +function checkNotFocusedError(error) { + if (error instanceof Error && + error.message.toLowerCase().includes('document is not focused')) { + toastMessage('You need to activate the "Emulate a focused page" setting in the "Rendering" panel of devtools.', 'warn'); + return true; + } + return false; +} +async function actionGlobalCopyState(pinia) { + if (checkClipboardAccess()) + return; + try { + await navigator.clipboard.writeText(JSON.stringify(pinia.state.value)); + toastMessage('Global state copied to clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to serialize the state. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalPasteState(pinia) { + if (checkClipboardAccess()) + return; + try { + loadStoresState(pinia, JSON.parse(await navigator.clipboard.readText())); + toastMessage('Global state pasted from clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to deserialize the state from clipboard. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalSaveState(pinia) { + try { + saveAs(new Blob([JSON.stringify(pinia.state.value)], { + type: 'text/plain;charset=utf-8', + }), 'pinia-state.json'); + } + catch (error) { + toastMessage(`Failed to export the state as JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +let fileInput; +function getFileOpener() { + if (!fileInput) { + fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + } + function openFile() { + return new Promise((resolve, reject) => { + fileInput.onchange = async () => { + const files = fileInput.files; + if (!files) + return resolve(null); + const file = files.item(0); + if (!file) + return resolve(null); + return resolve({ text: await file.text(), file }); + }; + // @ts-ignore: TODO: changed from 4.3 to 4.4 + fileInput.oncancel = () => resolve(null); + fileInput.onerror = reject; + fileInput.click(); + }); + } + return openFile; +} +async function actionGlobalOpenStateFile(pinia) { + try { + const open = getFileOpener(); + const result = await open(); + if (!result) + return; + const { text, file } = result; + loadStoresState(pinia, JSON.parse(text)); + toastMessage(`Global state imported from "${file.name}".`); + } + catch (error) { + toastMessage(`Failed to import the state from JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +function loadStoresState(pinia, state) { + for (const key in state) { + const storeState = pinia.state.value[key]; + // store is already instantiated, patch it + if (storeState) { + Object.assign(storeState, state[key]); + } + else { + // store is not instantiated, set the initial state + pinia.state.value[key] = state[key]; + } + } +} + +function formatDisplay(display) { + return { + _custom: { + display, + }, + }; +} +const PINIA_ROOT_LABEL = '🍍 Pinia (root)'; +const PINIA_ROOT_ID = '_root'; +function formatStoreForInspectorTree(store) { + return isPinia(store) + ? { + id: PINIA_ROOT_ID, + label: PINIA_ROOT_LABEL, + } + : { + id: store.$id, + label: store.$id, + }; +} +function formatStoreForInspectorState(store) { + if (isPinia(store)) { + const storeNames = Array.from(store._s.keys()); + const storeMap = store._s; + const state = { + state: storeNames.map((storeId) => ({ + editable: true, + key: storeId, + value: store.state.value[storeId], + })), + getters: storeNames + .filter((id) => storeMap.get(id)._getters) + .map((id) => { + const store = storeMap.get(id); + return { + editable: false, + key: id, + value: store._getters.reduce((getters, key) => { + getters[key] = store[key]; + return getters; + }, {}), + }; + }), + }; + return state; + } + const state = { + state: Object.keys(store.$state).map((key) => ({ + editable: true, + key, + value: store.$state[key], + })), + }; + // avoid adding empty getters + if (store._getters && store._getters.length) { + state.getters = store._getters.map((getterName) => ({ + editable: false, + key: getterName, + value: store[getterName], + })); + } + if (store._customProperties.size) { + state.customProperties = Array.from(store._customProperties).map((key) => ({ + editable: true, + key, + value: store[key], + })); + } + return state; +} +function formatEventData(events) { + if (!events) + return {}; + if (Array.isArray(events)) { + // TODO: handle add and delete for arrays and objects + return events.reduce((data, event) => { + data.keys.push(event.key); + data.operations.push(event.type); + data.oldValue[event.key] = event.oldValue; + data.newValue[event.key] = event.newValue; + return data; + }, { + oldValue: {}, + keys: [], + operations: [], + newValue: {}, + }); + } + else { + return { + operation: formatDisplay(events.type), + key: formatDisplay(events.key), + oldValue: events.oldValue, + newValue: events.newValue, + }; + } +} +function formatMutationType(type) { + switch (type) { + case MutationType.direct: + return 'mutation'; + case MutationType.patchFunction: + return '$patch'; + case MutationType.patchObject: + return '$patch'; + default: + return 'unknown'; + } +} + +// timeline can be paused when directly changing the state +let isTimelineActive = true; +const componentStateTypes = []; +const MUTATIONS_LAYER_ID = 'pinia:mutations'; +const INSPECTOR_ID = 'pinia'; +const { assign: assign$1 } = Object; +/** + * Gets the displayed name of a store in devtools + * + * @param id - id of the store + * @returns a formatted string + */ +const getStoreType = (id) => '🍍 ' + id; +/** + * Add the pinia plugin without any store. Allows displaying a Pinia plugin tab + * as soon as it is added to the application. + * + * @param app - Vue application + * @param pinia - pinia instance + */ +function registerPiniaDevtools(app, pinia) { + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + }, (api) => { + if (typeof api.now !== 'function') { + toastMessage('You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); + } + api.addTimelineLayer({ + id: MUTATIONS_LAYER_ID, + label: `Pinia 🍍`, + color: 0xe5df88, + }); + api.addInspector({ + id: INSPECTOR_ID, + label: 'Pinia 🍍', + icon: 'storage', + treeFilterPlaceholder: 'Search stores', + actions: [ + { + icon: 'content_copy', + action: () => { + actionGlobalCopyState(pinia); + }, + tooltip: 'Serialize and copy the state', + }, + { + icon: 'content_paste', + action: async () => { + await actionGlobalPasteState(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Replace the state with the content of your clipboard', + }, + { + icon: 'save', + action: () => { + actionGlobalSaveState(pinia); + }, + tooltip: 'Save the state as a JSON file', + }, + { + icon: 'folder_open', + action: async () => { + await actionGlobalOpenStateFile(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Import the state from a JSON file', + }, + ], + nodeActions: [ + { + icon: 'restore', + tooltip: 'Reset the state (with "$reset")', + action: (nodeId) => { + const store = pinia._s.get(nodeId); + if (!store) { + toastMessage(`Cannot reset "${nodeId}" store because it wasn't found.`, 'warn'); + } + else if (typeof store.$reset !== 'function') { + toastMessage(`Cannot reset "${nodeId}" store because it doesn't have a "$reset" method implemented.`, 'warn'); + } + else { + store.$reset(); + toastMessage(`Store "${nodeId}" reset.`); + } + }, + }, + ], + }); + api.on.inspectComponent((payload, ctx) => { + const proxy = (payload.componentInstance && + payload.componentInstance.proxy); + if (proxy && proxy._pStores) { + const piniaStores = payload.componentInstance.proxy._pStores; + Object.values(piniaStores).forEach((store) => { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'state', + editable: true, + value: store._isOptionsAPI + ? { + _custom: { + value: toRaw(store.$state), + actions: [ + { + icon: 'restore', + tooltip: 'Reset the state of this store', + action: () => store.$reset(), + }, + ], + }, + } + : // NOTE: workaround to unwrap transferred refs + Object.keys(store.$state).reduce((state, key) => { + state[key] = store.$state[key]; + return state; + }, {}), + }); + if (store._getters && store._getters.length) { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'getters', + editable: false, + value: store._getters.reduce((getters, key) => { + try { + getters[key] = store[key]; + } + catch (error) { + // @ts-expect-error: we just want to show it in devtools + getters[key] = error; + } + return getters; + }, {}), + }); + } + }); + } + }); + api.on.getInspectorTree((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + let stores = [pinia]; + stores = stores.concat(Array.from(pinia._s.values())); + payload.rootNodes = (payload.filter + ? stores.filter((store) => '$id' in store + ? store.$id + .toLowerCase() + .includes(payload.filter.toLowerCase()) + : PINIA_ROOT_LABEL.toLowerCase().includes(payload.filter.toLowerCase())) + : stores).map(formatStoreForInspectorTree); + } + }); + // Expose pinia instance as $pinia to window + globalThis.$pinia = pinia; + api.on.getInspectorState((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + // this could be the selected store restored for a different project + // so it's better not to say anything here + return; + } + if (inspectedStore) { + // Expose selected store as $store to window + if (payload.nodeId !== PINIA_ROOT_ID) + globalThis.$store = toRaw(inspectedStore); + payload.state = formatStoreForInspectorState(inspectedStore); + } + } + }); + api.on.editInspectorState((payload, ctx) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + return toastMessage(`store "${payload.nodeId}" not found`, 'error'); + } + const { path } = payload; + if (!isPinia(inspectedStore)) { + // access only the state + if (path.length !== 1 || + !inspectedStore._customProperties.has(path[0]) || + path[0] in inspectedStore.$state) { + path.unshift('$state'); + } + } + else { + // Root access, we can omit the `.value` because the devtools API does it for us + path.unshift('state'); + } + isTimelineActive = false; + payload.set(inspectedStore, path, payload.state.value); + isTimelineActive = true; + } + }); + api.on.editComponentState((payload) => { + if (payload.type.startsWith('🍍')) { + const storeId = payload.type.replace(/^🍍\s*/, ''); + const store = pinia._s.get(storeId); + if (!store) { + return toastMessage(`store "${storeId}" not found`, 'error'); + } + const { path } = payload; + if (path[0] !== 'state') { + return toastMessage(`Invalid path for store "${storeId}":\n${path}\nOnly state can be modified.`); + } + // rewrite the first entry to be able to directly set the state as + // well as any other path + path[0] = '$state'; + isTimelineActive = false; + payload.set(store, path, payload.state.value); + isTimelineActive = true; + } + }); + }); +} +function addStoreToDevtools(app, store) { + if (!componentStateTypes.includes(getStoreType(store.$id))) { + componentStateTypes.push(getStoreType(store.$id)); + } + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + settings: { + logStoreChanges: { + label: 'Notify about new/deleted stores', + type: 'boolean', + defaultValue: true, + }, + // useEmojis: { + // label: 'Use emojis in messages ⚡️', + // type: 'boolean', + // defaultValue: true, + // }, + }, + }, (api) => { + // gracefully handle errors + const now = typeof api.now === 'function' ? api.now.bind(api) : Date.now; + store.$onAction(({ after, onError, name, args }) => { + const groupId = runningActionId++; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛫 ' + name, + subtitle: 'start', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + }, + groupId, + }, + }); + after((result) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛬 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + result, + }, + groupId, + }, + }); + }); + onError((error) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + logType: 'error', + title: '💥 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + error, + }, + groupId, + }, + }); + }); + }, true); + store._customProperties.forEach((name) => { + watch(() => unref(store[name]), (newValue, oldValue) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (isTimelineActive) { + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: 'Change', + subtitle: name, + data: { + newValue, + oldValue, + }, + groupId: activeAction, + }, + }); + } + }, { deep: true }); + }); + store.$subscribe(({ events, type }, state) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (!isTimelineActive) + return; + // rootStore.state[store.id] = state + const eventData = { + time: now(), + title: formatMutationType(type), + data: assign$1({ store: formatDisplay(store.$id) }, formatEventData(events)), + groupId: activeAction, + }; + if (type === MutationType.patchFunction) { + eventData.subtitle = '⤵️'; + } + else if (type === MutationType.patchObject) { + eventData.subtitle = '🧩'; + } + else if (events && !Array.isArray(events)) { + eventData.subtitle = events.type; + } + if (events) { + eventData.data['rawEvent(s)'] = { + _custom: { + display: 'DebuggerEvent', + type: 'object', + tooltip: 'raw DebuggerEvent[]', + value: events, + }, + }; + } + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: eventData, + }); + }, { detached: true, flush: 'sync' }); + const hotUpdate = store._hotUpdate; + store._hotUpdate = markRaw((newStore) => { + hotUpdate(newStore); + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🔥 ' + store.$id, + subtitle: 'HMR update', + data: { + store: formatDisplay(store.$id), + info: formatDisplay(`HMR update`), + }, + }, + }); + // update the devtools too + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }); + const { $dispose } = store; + store.$dispose = () => { + $dispose(); + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`Disposed "${store.$id}" store 🗑`); + }; + // trigger an update so it can display new registered stores + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`"${store.$id}" store installed 🆕`); + }); +} +let runningActionId = 0; +let activeAction; +/** + * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the + * context of all actions, allowing us to set `runningAction` on each access and effectively associating any state + * mutation to the action. + * + * @param store - store to patch + * @param actionNames - list of actionst to patch + */ +function patchActionForGrouping(store, actionNames, wrapWithProxy) { + // original actions of the store as they are given by pinia. We are going to override them + const actions = actionNames.reduce((storeActions, actionName) => { + // use toRaw to avoid tracking #541 + storeActions[actionName] = toRaw(store)[actionName]; + return storeActions; + }, {}); + for (const actionName in actions) { + store[actionName] = function () { + // the running action id is incremented in a before action hook + const _actionId = runningActionId; + const trackedStore = wrapWithProxy + ? new Proxy(store, { + get(...args) { + activeAction = _actionId; + return Reflect.get(...args); + }, + set(...args) { + activeAction = _actionId; + return Reflect.set(...args); + }, + }) + : store; + // For Setup Stores we need https://github.com/tc39/proposal-async-context + activeAction = _actionId; + const retValue = actions[actionName].apply(trackedStore, arguments); + // this is safer as async actions in Setup Stores would associate mutations done outside of the action + activeAction = undefined; + return retValue; + }; + } +} +/** + * pinia.use(devtoolsPlugin) + */ +function devtoolsPlugin({ app, store, options }) { + // HMR module + if (store.$id.startsWith('__hot:')) { + return; + } + // detect option api vs setup api + store._isOptionsAPI = !!options.state; + // Do not overwrite actions mocked by @pinia/testing (#2298) + if (!store._p._testing) { + patchActionForGrouping(store, Object.keys(options.actions), store._isOptionsAPI); + // Upgrade the HMR to also update the new actions + const originalHotUpdate = store._hotUpdate; + toRaw(store)._hotUpdate = function (newStore) { + originalHotUpdate.apply(this, arguments); + patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions), !!store._isOptionsAPI); + }; + } + addStoreToDevtools(app, + // FIXME: is there a way to allow the assignment from Store to StoreGeneric? + store); +} + +/** + * Creates a Pinia instance to be used by the application + */ +function createPinia() { + const scope = effectScope(true); + // NOTE: here we could check the window object for a state and directly set it + // if there is anything like it with Vue 3 SSR + const state = scope.run(() => ref({})); + let _p = []; + // plugins added before calling app.use(pinia) + let toBeInstalled = []; + const pinia = markRaw({ + install(app) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + if (!isVue2) { + pinia._a = app; + app.provide(piniaSymbol, pinia); + app.config.globalProperties.$pinia = pinia; + /* istanbul ignore else */ + if (IS_CLIENT) { + registerPiniaDevtools(app, pinia); + } + toBeInstalled.forEach((plugin) => _p.push(plugin)); + toBeInstalled = []; + } + }, + use(plugin) { + if (!this._a && !isVue2) { + toBeInstalled.push(plugin); + } + else { + _p.push(plugin); + } + return this; + }, + _p, + // it's actually undefined here + // @ts-expect-error + _a: null, + _e: scope, + _s: new Map(), + state, + }); + // pinia devtools rely on dev only features so they cannot be forced unless + // the dev build of Vue is used. Avoid old browsers like IE11. + if (typeof Proxy !== 'undefined') { + pinia.use(devtoolsPlugin); + } + return pinia; +} +/** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ +function disposePinia(pinia) { + pinia._e.stop(); + pinia._s.clear(); + pinia._p.splice(0); + pinia.state.value = {}; + // @ts-expect-error: non valid + pinia._a = null; +} + +/** + * Checks if a function is a `StoreDefinition`. + * + * @param fn - object to test + * @returns true if `fn` is a StoreDefinition + */ +const isUseStore = (fn) => { + return typeof fn === 'function' && typeof fn.$id === 'string'; +}; +/** + * Mutates in place `newState` with `oldState` to _hot update_ it. It will + * remove any key not existing in `newState` and recursively merge plain + * objects. + * + * @param newState - new state object to be patched + * @param oldState - old state that should be used to patch newState + * @returns - newState + */ +function patchObject(newState, oldState) { + // no need to go through symbols because they cannot be serialized anyway + for (const key in oldState) { + const subPatch = oldState[key]; + // skip the whole sub tree + if (!(key in newState)) { + continue; + } + const targetValue = newState[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + !isRef(subPatch) && + !isReactive(subPatch)) { + newState[key] = patchObject(targetValue, subPatch); + } + else { + // objects are either a bit more complex (e.g. refs) or primitives, so we + // just set the whole thing + if (isVue2) { + set(newState, key, subPatch); + } + else { + newState[key] = subPatch; + } + } + } + return newState; +} +/** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ +function acceptHMRUpdate(initialUseStore, hot) { + return (newModule) => { + const pinia = hot.data.pinia || initialUseStore._pinia; + if (!pinia) { + // this store is still not used + return; + } + // preserve the pinia instance across loads + hot.data.pinia = pinia; + // console.log('got data', newStore) + for (const exportName in newModule) { + const useStore = newModule[exportName]; + // console.log('checking for', exportName) + if (isUseStore(useStore) && pinia._s.has(useStore.$id)) { + // console.log('Accepting update for', useStore.$id) + const id = useStore.$id; + if (id !== initialUseStore.$id) { + console.warn(`The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`); + // return import.meta.hot.invalidate() + return hot.invalidate(); + } + const existingStore = pinia._s.get(id); + if (!existingStore) { + console.log(`[Pinia]: skipping hmr because store doesn't exist yet`); + return; + } + useStore(pinia, existingStore); + } + } + }; +} + +const noop = () => { }; +function addSubscription(subscriptions, callback, detached, onCleanup = noop) { + subscriptions.push(callback); + const removeSubscription = () => { + const idx = subscriptions.indexOf(callback); + if (idx > -1) { + subscriptions.splice(idx, 1); + onCleanup(); + } + }; + if (!detached && getCurrentScope()) { + onScopeDispose(removeSubscription); + } + return removeSubscription; +} +function triggerSubscriptions(subscriptions, ...args) { + subscriptions.slice().forEach((callback) => { + callback(...args); + }); +} + +const fallbackRunWithContext = (fn) => fn(); +/** + * Marks a function as an action for `$onAction` + * @internal + */ +const ACTION_MARKER = Symbol(); +/** + * Action name symbol. Allows to add a name to an action after defining it + * @internal + */ +const ACTION_NAME = Symbol(); +function mergeReactiveObjects(target, patchToApply) { + // Handle Map instances + if (target instanceof Map && patchToApply instanceof Map) { + patchToApply.forEach((value, key) => target.set(key, value)); + } + else if (target instanceof Set && patchToApply instanceof Set) { + // Handle Set instances + patchToApply.forEach(target.add, target); + } + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + if (!patchToApply.hasOwnProperty(key)) + continue; + const subPatch = patchToApply[key]; + const targetValue = target[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + target.hasOwnProperty(key) && + !isRef(subPatch) && + !isReactive(subPatch)) { + // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might + // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that + // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`. + target[key] = mergeReactiveObjects(targetValue, subPatch); + } + else { + // @ts-expect-error: subPatch is a valid value + target[key] = subPatch; + } + } + return target; +} +const skipHydrateSymbol = Symbol('pinia:skipHydration') + ; +const skipHydrateMap = /*#__PURE__*/ new WeakMap(); +/** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ +function skipHydrate(obj) { + return isVue2 + ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work... + /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj + : Object.defineProperty(obj, skipHydrateSymbol, {}); +} +/** + * Returns whether a value should be hydrated + * + * @param obj - target variable + * @returns true if `obj` should be hydrated + */ +function shouldHydrate(obj) { + return isVue2 + ? /* istanbul ignore next */ !skipHydrateMap.has(obj) + : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol); +} +const { assign } = Object; +function isComputed(o) { + return !!(isRef(o) && o.effect); +} +function createOptionsStore(id, options, pinia, hot) { + const { state, actions, getters } = options; + const initialState = pinia.state.value[id]; + let store; + function setup() { + if (!initialState && (!hot)) { + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value, id, state ? state() : {}); + } + else { + pinia.state.value[id] = state ? state() : {}; + } + } + // avoid creating a state in pinia.state.value + const localState = hot + ? // use ref() to unwrap refs inside state TODO: check if this is still necessary + toRefs(ref(state ? state() : {}).value) + : toRefs(pinia.state.value[id]); + return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { + if (name in localState) { + console.warn(`[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with "${name}" in store "${id}".`); + } + computedGetters[name] = markRaw(computed(() => { + setActivePinia(pinia); + // it was created just before + const store = pinia._s.get(id); + // allow cross using stores + /* istanbul ignore if */ + if (isVue2 && !store._r) + return; + // @ts-expect-error + // return getters![name].call(context, context) + // TODO: avoid reading the getter while assigning with a global variable + return getters[name].call(store, store); + })); + return computedGetters; + }, {})); + } + store = createSetupStore(id, setup, options, pinia, hot, true); + return store; +} +function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { + let scope; + const optionsForPlugin = assign({ actions: {} }, options); + /* istanbul ignore if */ + if (!pinia._e.active) { + throw new Error('Pinia destroyed'); + } + // watcher options for $subscribe + const $subscribeOptions = { deep: true }; + /* istanbul ignore else */ + if (!isVue2) { + $subscribeOptions.onTrigger = (event) => { + /* istanbul ignore else */ + if (isListening) { + debuggerEvents = event; + // avoid triggering this while the store is being built and the state is being set in pinia + } + else if (isListening == false && !store._hotUpdating) { + // let patch send all the events together later + /* istanbul ignore else */ + if (Array.isArray(debuggerEvents)) { + debuggerEvents.push(event); + } + else { + console.error('🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'); + } + } + }; + } + // internal state + let isListening; // set to true at the end + let isSyncListening; // set to true at the end + let subscriptions = []; + let actionSubscriptions = []; + let debuggerEvents; + const initialState = pinia.state.value[$id]; + // avoid setting the state for option stores if it is set + // by the setup + if (!isOptionsStore && !initialState && (!hot)) { + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value, $id, {}); + } + else { + pinia.state.value[$id] = {}; + } + } + const hotState = ref({}); + // avoid triggering too many listeners + // https://github.com/vuejs/pinia/issues/1129 + let activeListener; + function $patch(partialStateOrMutator) { + let subscriptionMutation; + isListening = isSyncListening = false; + // reset the debugger events since patches are sync + /* istanbul ignore else */ + { + debuggerEvents = []; + } + if (typeof partialStateOrMutator === 'function') { + partialStateOrMutator(pinia.state.value[$id]); + subscriptionMutation = { + type: MutationType.patchFunction, + storeId: $id, + events: debuggerEvents, + }; + } + else { + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); + subscriptionMutation = { + type: MutationType.patchObject, + payload: partialStateOrMutator, + storeId: $id, + events: debuggerEvents, + }; + } + const myListenerId = (activeListener = Symbol()); + nextTick().then(() => { + if (activeListener === myListenerId) { + isListening = true; + } + }); + isSyncListening = true; + // because we paused the watcher, we need to manually call the subscriptions + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); + } + const $reset = isOptionsStore + ? function $reset() { + const { state } = options; + const newState = state ? state() : {}; + // we use a patch to group all changes into one single subscription + this.$patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, newState); + }); + } + : /* istanbul ignore next */ + () => { + throw new Error(`🍍: Store "${$id}" is built using the setup syntax and does not implement $reset().`); + } + ; + function $dispose() { + scope.stop(); + subscriptions = []; + actionSubscriptions = []; + pinia._s.delete($id); + } + /** + * Helper that wraps function so it can be tracked with $onAction + * @param fn - action to wrap + * @param name - name of the action + */ + const action = (fn, name = '') => { + if (ACTION_MARKER in fn) { + fn[ACTION_NAME] = name; + return fn; + } + const wrappedAction = function () { + setActivePinia(pinia); + const args = Array.from(arguments); + const afterCallbackList = []; + const onErrorCallbackList = []; + function after(callback) { + afterCallbackList.push(callback); + } + function onError(callback) { + onErrorCallbackList.push(callback); + } + // @ts-expect-error + triggerSubscriptions(actionSubscriptions, { + args, + name: wrappedAction[ACTION_NAME], + store, + after, + onError, + }); + let ret; + try { + ret = fn.apply(this && this.$id === $id ? this : store, args); + // handle sync errors + } + catch (error) { + triggerSubscriptions(onErrorCallbackList, error); + throw error; + } + if (ret instanceof Promise) { + return ret + .then((value) => { + triggerSubscriptions(afterCallbackList, value); + return value; + }) + .catch((error) => { + triggerSubscriptions(onErrorCallbackList, error); + return Promise.reject(error); + }); + } + // trigger after callbacks + triggerSubscriptions(afterCallbackList, ret); + return ret; + }; + wrappedAction[ACTION_MARKER] = true; + wrappedAction[ACTION_NAME] = name; // will be set later + // @ts-expect-error: we are intentionally limiting the returned type to just Fn + // because all the added properties are internals that are exposed through `$onAction()` only + return wrappedAction; + }; + const _hmrPayload = /*#__PURE__*/ markRaw({ + actions: {}, + getters: {}, + state: [], + hotState, + }); + const partialStore = { + _p: pinia, + // _s: scope, + $id, + $onAction: addSubscription.bind(null, actionSubscriptions), + $patch, + $reset, + $subscribe(callback, options = {}) { + const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher()); + const stopWatcher = scope.run(() => watch(() => pinia.state.value[$id], (state) => { + if (options.flush === 'sync' ? isSyncListening : isListening) { + callback({ + storeId: $id, + type: MutationType.direct, + events: debuggerEvents, + }, state); + } + }, assign({}, $subscribeOptions, options))); + return removeSubscription; + }, + $dispose, + }; + /* istanbul ignore if */ + if (isVue2) { + // start as non ready + partialStore._r = false; + } + const store = reactive(assign({ + _hmrPayload, + _customProperties: markRaw(new Set()), // devtools custom properties + }, partialStore + // must be added later + // setupStore + ) + ); + // store the partial store now so the setup of stores can instantiate each other before they are finished without + // creating infinite loops. + pinia._s.set($id, store); + const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext; + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped + const setupStore = runWithContext(() => pinia._e.run(() => (scope = effectScope()).run(() => setup({ action })))); + // overwrite existing actions to support $onAction + for (const key in setupStore) { + const prop = setupStore[key]; + if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) { + // mark it as a piece of state to be serialized + if (hot) { + set(hotState.value, key, toRef(setupStore, key)); + // createOptionStore directly sets the state in pinia.state.value so we + // can just skip that + } + else if (!isOptionsStore) { + // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created + if (initialState && shouldHydrate(prop)) { + if (isRef(prop)) { + prop.value = initialState[key]; + } + else { + // probably a reactive object, lets recursively assign + // @ts-expect-error: prop is unknown + mergeReactiveObjects(prop, initialState[key]); + } + } + // transfer the ref to the pinia state to keep everything in sync + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value[$id], key, prop); + } + else { + pinia.state.value[$id][key] = prop; + } + } + /* istanbul ignore else */ + { + _hmrPayload.state.push(key); + } + // action + } + else if (typeof prop === 'function') { + const actionValue = hot ? prop : action(prop, key); + // this a hot module replacement store because the hotUpdate method needs + // to do it with the right context + /* istanbul ignore if */ + if (isVue2) { + set(setupStore, key, actionValue); + } + else { + // @ts-expect-error + setupStore[key] = actionValue; + } + /* istanbul ignore else */ + { + _hmrPayload.actions[key] = prop; + } + // list actions so they can be used in plugins + // @ts-expect-error + optionsForPlugin.actions[key] = prop; + } + else { + // add getters for devtools + if (isComputed(prop)) { + _hmrPayload.getters[key] = isOptionsStore + ? // @ts-expect-error + options.getters[key] + : prop; + if (IS_CLIENT) { + const getters = setupStore._getters || + // @ts-expect-error: same + (setupStore._getters = markRaw([])); + getters.push(key); + } + } + } + } + // add the state, getters, and action properties + /* istanbul ignore if */ + if (isVue2) { + Object.keys(setupStore).forEach((key) => { + set(store, key, setupStore[key]); + }); + } + else { + assign(store, setupStore); + // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object. + // Make `storeToRefs()` work with `reactive()` #799 + assign(toRaw(store), setupStore); + } + // use this instead of a computed with setter to be able to create it anywhere + // without linking the computed lifespan to wherever the store is first + // created. + Object.defineProperty(store, '$state', { + get: () => (hot ? hotState.value : pinia.state.value[$id]), + set: (state) => { + /* istanbul ignore if */ + if (hot) { + throw new Error('cannot set hotState'); + } + $patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, state); + }); + }, + }); + // add the hotUpdate before plugins to allow them to override it + /* istanbul ignore else */ + { + store._hotUpdate = markRaw((newStore) => { + store._hotUpdating = true; + newStore._hmrPayload.state.forEach((stateKey) => { + if (stateKey in store.$state) { + const newStateTarget = newStore.$state[stateKey]; + const oldStateSource = store.$state[stateKey]; + if (typeof newStateTarget === 'object' && + isPlainObject(newStateTarget) && + isPlainObject(oldStateSource)) { + patchObject(newStateTarget, oldStateSource); + } + else { + // transfer the ref + newStore.$state[stateKey] = oldStateSource; + } + } + // patch direct access properties to allow store.stateProperty to work as + // store.$state.stateProperty + set(store, stateKey, toRef(newStore.$state, stateKey)); + }); + // remove deleted state properties + Object.keys(store.$state).forEach((stateKey) => { + if (!(stateKey in newStore.$state)) { + del(store, stateKey); + } + }); + // avoid devtools logging this as a mutation + isListening = false; + isSyncListening = false; + pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState'); + isSyncListening = true; + nextTick().then(() => { + isListening = true; + }); + for (const actionName in newStore._hmrPayload.actions) { + const actionFn = newStore[actionName]; + set(store, actionName, action(actionFn, actionName)); + } + // TODO: does this work in both setup and option store? + for (const getterName in newStore._hmrPayload.getters) { + const getter = newStore._hmrPayload.getters[getterName]; + const getterValue = isOptionsStore + ? // special handling of options api + computed(() => { + setActivePinia(pinia); + return getter.call(store, store); + }) + : getter; + set(store, getterName, getterValue); + } + // remove deleted getters + Object.keys(store._hmrPayload.getters).forEach((key) => { + if (!(key in newStore._hmrPayload.getters)) { + del(store, key); + } + }); + // remove old actions + Object.keys(store._hmrPayload.actions).forEach((key) => { + if (!(key in newStore._hmrPayload.actions)) { + del(store, key); + } + }); + // update the values used in devtools and to allow deleting new properties later on + store._hmrPayload = newStore._hmrPayload; + store._getters = newStore._getters; + store._hotUpdating = false; + }); + } + if (IS_CLIENT) { + const nonEnumerable = { + writable: true, + configurable: true, + // avoid warning on devtools trying to display this property + enumerable: false, + }; + ['_p', '_hmrPayload', '_getters', '_customProperties'].forEach((p) => { + Object.defineProperty(store, p, assign({ value: store[p] }, nonEnumerable)); + }); + } + /* istanbul ignore if */ + if (isVue2) { + // mark the store as ready before plugins + store._r = true; + } + // apply all plugins + pinia._p.forEach((extender) => { + /* istanbul ignore else */ + if (IS_CLIENT) { + const extensions = scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + })); + Object.keys(extensions || {}).forEach((key) => store._customProperties.add(key)); + assign(store, extensions); + } + else { + assign(store, scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + }))); + } + }); + if (store.$state && + typeof store.$state === 'object' && + typeof store.$state.constructor === 'function' && + !store.$state.constructor.toString().includes('[native code]')) { + console.warn(`[🍍]: The "state" must be a plain object. It cannot be\n` + + `\tstate: () => new MyClass()\n` + + `Found in store "${store.$id}".`); + } + // only apply hydrate to option stores with an initial state in pinia + if (initialState && + isOptionsStore && + options.hydrate) { + options.hydrate(store.$state, initialState); + } + isListening = true; + isSyncListening = true; + return store; +} +// improves tree shaking +/*#__NO_SIDE_EFFECTS__*/ +function defineStore( +// TODO: add proper types from above +idOrOptions, setup, setupOptions) { + let id; + let options; + const isSetupStore = typeof setup === 'function'; + if (typeof idOrOptions === 'string') { + id = idOrOptions; + // the option store setup will contain the actual options in this case + options = isSetupStore ? setupOptions : setup; + } + else { + options = idOrOptions; + id = idOrOptions.id; + if (typeof id !== 'string') { + throw new Error(`[🍍]: "defineStore()" must be passed a store id as its first argument.`); + } + } + function useStore(pinia, hot) { + const hasContext = hasInjectionContext(); + pinia = + // in test mode, ignore the argument provided as we can always retrieve a + // pinia instance with getActivePinia() + (pinia) || + (hasContext ? inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + if (!activePinia) { + throw new Error(`[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?\n` + + `See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.\n` + + `This will fail in production.`); + } + pinia = activePinia; + if (!pinia._s.has(id)) { + // creating the store registers it in `pinia._s` + if (isSetupStore) { + createSetupStore(id, setup, options, pinia); + } + else { + createOptionsStore(id, options, pinia); + } + /* istanbul ignore else */ + { + // @ts-expect-error: not the right inferred type + useStore._pinia = pinia; + } + } + const store = pinia._s.get(id); + if (hot) { + const hotId = '__hot:' + id; + const newStore = isSetupStore + ? createSetupStore(hotId, setup, options, pinia, true) + : createOptionsStore(hotId, assign({}, options), pinia, true); + hot._hotUpdate(newStore); + // cleanup the state properties and the store from the cache + delete pinia.state.value[hotId]; + pinia._s.delete(hotId); + } + if (IS_CLIENT) { + const currentInstance = getCurrentInstance(); + // save stores in instances to access them devtools + if (currentInstance && + currentInstance.proxy && + // avoid adding stores that are just built for hot module replacement + !hot) { + const vm = currentInstance.proxy; + const cache = '_pStores' in vm ? vm._pStores : (vm._pStores = {}); + cache[id] = store; + } + } + // StoreGeneric cannot be casted towards Store + return store; + } + useStore.$id = id; + return useStore; +} + +let mapStoreSuffix = 'Store'; +/** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ +function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS +) { + mapStoreSuffix = suffix; +} +/** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ +function mapStores(...stores) { + if (Array.isArray(stores[0])) { + console.warn(`[🍍]: Directly pass all stores to "mapStores()" without putting them in an array:\n` + + `Replace\n` + + `\tmapStores([useAuthStore, useCartStore])\n` + + `with\n` + + `\tmapStores(useAuthStore, useCartStore)\n` + + `This will fail in production if not fixed.`); + stores = stores[0]; + } + return stores.reduce((reduced, useStore) => { + // @ts-expect-error: $id is added by defineStore + reduced[useStore.$id + mapStoreSuffix] = function () { + return useStore(this.$pinia); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + reduced[key] = function () { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function () { + const store = useStore(this.$pinia); + const storeKey = keysOrMapper[key]; + // for some reason TS is unable to infer the type of storeKey to be a + // function + return typeof storeKey === 'function' + ? storeKey.call(this, store) + : // @ts-expect-error: FIXME: should work? + store[storeKey]; + }; + return reduced; + }, {}); +} +/** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ +const mapGetters = mapState; +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapActions(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key](...args); + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]](...args); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapWritableState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[key] = value); + }, + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[keysOrMapper[key]] = value); + }, + }; + return reduced; + }, {}); +} + +/** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ +function storeToRefs(store) { + // See https://github.com/vuejs/pinia/issues/852 + // It's easier to just use toRefs() even if it includes more stuff + if (isVue2) { + // @ts-expect-error: toRefs include methods and others + return toRefs(store); + } + else { + store = toRaw(store); + const refs = {}; + for (const key in store) { + const value = store[key]; + if (isRef(value) || isReactive(value)) { + // @ts-expect-error: the key is state or getter + refs[key] = + // --- + toRef(store, key); + } + } + return refs; + } +} + +/** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ +const PiniaVuePlugin = function (_Vue) { + // Equivalent of + // app.config.globalProperties.$pinia = pinia + _Vue.mixin({ + beforeCreate() { + const options = this.$options; + if (options.pinia) { + const pinia = options.pinia; + // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31 + /* istanbul ignore else */ + if (!this._provided) { + const provideCache = {}; + Object.defineProperty(this, '_provided', { + get: () => provideCache, + set: (v) => Object.assign(provideCache, v), + }); + } + this._provided[piniaSymbol] = pinia; + // propagate the pinia instance in an SSR friendly way + // avoid adding it to nuxt twice + /* istanbul ignore else */ + if (!this.$pinia) { + this.$pinia = pinia; + } + pinia._a = this; + if (IS_CLIENT) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + } + if (IS_CLIENT) { + registerPiniaDevtools(pinia._a, pinia); + } + } + else if (!this.$pinia && options.parent && options.parent.$pinia) { + this.$pinia = options.parent.$pinia; + } + }, + destroyed() { + delete this._pStores; + }, + }); +}; + +export { MutationType, PiniaVuePlugin, acceptHMRUpdate, createPinia, defineStore, disposePinia, getActivePinia, mapActions, mapGetters, mapState, mapStores, mapWritableState, setActivePinia, setMapStoreSuffix, skipHydrate, storeToRefs }; diff --git a/node_modules/pinia/dist/pinia.iife.js b/node_modules/pinia/dist/pinia.iife.js new file mode 100644 index 0000000..0e89d54 --- /dev/null +++ b/node_modules/pinia/dist/pinia.iife.js @@ -0,0 +1,2221 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +var Pinia = (function (exports, vueDemi) { + 'use strict'; + + /** + * setActivePinia must be called to handle SSR at the top of functions like + * `fetch`, `setup`, `serverPrefetch` and others + */ + let activePinia; + /** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ + // @ts-expect-error: cannot constrain the type of the return + const setActivePinia = (pinia) => (activePinia = pinia); + /** + * Get the currently active pinia if there is any. + */ + const getActivePinia = () => (vueDemi.hasInjectionContext() && vueDemi.inject(piniaSymbol)) || activePinia; + const piniaSymbol = (Symbol('pinia') ); + + function getDevtoolsGlobalHook() { + return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__; + } + function getTarget() { + // @ts-expect-error navigator and windows are not available in all environments + return (typeof navigator !== 'undefined' && typeof window !== 'undefined') + ? window + : typeof globalThis !== 'undefined' + ? globalThis + : {}; + } + const isProxyAvailable = typeof Proxy === 'function'; + + const HOOK_SETUP = 'devtools-plugin:setup'; + const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'; + + let supported; + let perf; + function isPerformanceSupported() { + var _a; + if (supported !== undefined) { + return supported; + } + if (typeof window !== 'undefined' && window.performance) { + supported = true; + perf = window.performance; + } + else if (typeof globalThis !== 'undefined' && ((_a = globalThis.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) { + supported = true; + perf = globalThis.perf_hooks.performance; + } + else { + supported = false; + } + return supported; + } + function now() { + return isPerformanceSupported() ? perf.now() : Date.now(); + } + + class ApiProxy { + constructor(plugin, hook) { + this.target = null; + this.targetQueue = []; + this.onQueue = []; + this.plugin = plugin; + this.hook = hook; + const defaultSettings = {}; + if (plugin.settings) { + for (const id in plugin.settings) { + const item = plugin.settings[id]; + defaultSettings[id] = item.defaultValue; + } + } + const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`; + let currentSettings = Object.assign({}, defaultSettings); + try { + const raw = localStorage.getItem(localSettingsSaveId); + const data = JSON.parse(raw); + Object.assign(currentSettings, data); + } + catch (e) { + // noop + } + this.fallbacks = { + getSettings() { + return currentSettings; + }, + setSettings(value) { + try { + localStorage.setItem(localSettingsSaveId, JSON.stringify(value)); + } + catch (e) { + // noop + } + currentSettings = value; + }, + now() { + return now(); + }, + }; + if (hook) { + hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => { + if (pluginId === this.plugin.id) { + this.fallbacks.setSettings(value); + } + }); + } + this.proxiedOn = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target.on[prop]; + } + else { + return (...args) => { + this.onQueue.push({ + method: prop, + args, + }); + }; + } + }, + }); + this.proxiedTarget = new Proxy({}, { + get: (_target, prop) => { + if (this.target) { + return this.target[prop]; + } + else if (prop === 'on') { + return this.proxiedOn; + } + else if (Object.keys(this.fallbacks).includes(prop)) { + return (...args) => { + this.targetQueue.push({ + method: prop, + args, + resolve: () => { }, + }); + return this.fallbacks[prop](...args); + }; + } + else { + return (...args) => { + return new Promise((resolve) => { + this.targetQueue.push({ + method: prop, + args, + resolve, + }); + }); + }; + } + }, + }); + } + async setRealTarget(target) { + this.target = target; + for (const item of this.onQueue) { + this.target.on[item.method](...item.args); + } + for (const item of this.targetQueue) { + item.resolve(await this.target[item.method](...item.args)); + } + } + } + + function setupDevtoolsPlugin(pluginDescriptor, setupFn) { + const descriptor = pluginDescriptor; + const target = getTarget(); + const hook = getDevtoolsGlobalHook(); + const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy; + if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) { + hook.emit(HOOK_SETUP, pluginDescriptor, setupFn); + } + else { + const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null; + const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []; + list.push({ + pluginDescriptor: descriptor, + setupFn, + proxy, + }); + if (proxy) { + setupFn(proxy.proxiedTarget); + } + } + } + + function isPlainObject( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + o) { + return (o && + typeof o === 'object' && + Object.prototype.toString.call(o) === '[object Object]' && + typeof o.toJSON !== 'function'); + } + // type DeepReadonly = { readonly [P in keyof T]: DeepReadonly } + // TODO: can we change these to numbers? + /** + * Possible types for SubscriptionCallback + */ + exports.MutationType = void 0; + (function (MutationType) { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + MutationType["direct"] = "direct"; + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + MutationType["patchObject"] = "patch object"; + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + MutationType["patchFunction"] = "patch function"; + // maybe reset? for $state = {} and $reset + })(exports.MutationType || (exports.MutationType = {})); + + const IS_CLIENT = typeof window !== 'undefined'; + + /* + * FileSaver.js A saveAs() FileSaver implementation. + * + * Originally by Eli Grey, adapted as an ESM module by Eduardo San Martin + * Morote. + * + * License : MIT + */ + // The one and only way of getting global scope in all environments + // https://stackoverflow.com/q/3277182/1008999 + const _global = /*#__PURE__*/ (() => typeof window === 'object' && window.window === window + ? window + : typeof self === 'object' && self.self === self + ? self + : typeof global === 'object' && global.global === global + ? global + : typeof globalThis === 'object' + ? globalThis + : { HTMLElement: null })(); + function bom(blob, { autoBom = false } = {}) { + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (autoBom && + /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xfeff), blob], { type: blob.type }); + } + return blob; + } + function download(url, name, opts) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.onload = function () { + saveAs(xhr.response, name, opts); + }; + xhr.onerror = function () { + console.error('could not download file'); + }; + xhr.send(); + } + function corsEnabled(url) { + const xhr = new XMLHttpRequest(); + // use sync to avoid popup blocker + xhr.open('HEAD', url, false); + try { + xhr.send(); + } + catch (e) { } + return xhr.status >= 200 && xhr.status <= 299; + } + // `a.click()` doesn't work for all browsers (#465) + function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')); + } + catch (e) { + const evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); + node.dispatchEvent(evt); + } + } + const _navigator = typeof navigator === 'object' ? navigator : { userAgent: '' }; + // Detect WebView inside a native macOS app by ruling out all browsers + // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too + // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos + const isMacOSWebView = /*#__PURE__*/ (() => /Macintosh/.test(_navigator.userAgent) && + /AppleWebKit/.test(_navigator.userAgent) && + !/Safari/.test(_navigator.userAgent))(); + const saveAs = !IS_CLIENT + ? () => { } // noop + : // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView or mini program + typeof HTMLAnchorElement !== 'undefined' && + 'download' in HTMLAnchorElement.prototype && + !isMacOSWebView + ? downloadSaveAs + : // Use msSaveOrOpenBlob as a second approach + 'msSaveOrOpenBlob' in _navigator + ? msSaveAs + : // Fallback to using FileReader and a popup + fileSaverSaveAs; + function downloadSaveAs(blob, name = 'download', opts) { + const a = document.createElement('a'); + a.download = name; + a.rel = 'noopener'; // tabnabbing + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + if (typeof blob === 'string') { + // Support regular links + a.href = blob; + if (a.origin !== location.origin) { + if (corsEnabled(a.href)) { + download(blob, name, opts); + } + else { + a.target = '_blank'; + click(a); + } + } + else { + click(a); + } + } + else { + // Support blobs + a.href = URL.createObjectURL(blob); + setTimeout(function () { + URL.revokeObjectURL(a.href); + }, 4e4); // 40s + setTimeout(function () { + click(a); + }, 0); + } + } + function msSaveAs(blob, name = 'download', opts) { + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts); + } + else { + const a = document.createElement('a'); + a.href = blob; + a.target = '_blank'; + setTimeout(function () { + click(a); + }); + } + } + else { + // @ts-ignore: works on windows + navigator.msSaveOrOpenBlob(bom(blob, opts), name); + } + } + function fileSaverSaveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank'); + if (popup) { + popup.document.title = popup.document.body.innerText = 'downloading...'; + } + if (typeof blob === 'string') + return download(blob, name, opts); + const force = blob.type === 'application/octet-stream'; + const isSafari = /constructor/i.test(String(_global.HTMLElement)) || 'safari' in _global; + const isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); + if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && + typeof FileReader !== 'undefined') { + // Safari doesn't allow downloading of blob URLs + const reader = new FileReader(); + reader.onloadend = function () { + let url = reader.result; + if (typeof url !== 'string') { + popup = null; + throw new Error('Wrong reader.result type'); + } + url = isChromeIOS + ? url + : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); + if (popup) { + popup.location.href = url; + } + else { + location.assign(url); + } + popup = null; // reverse-tabnabbing #460 + }; + reader.readAsDataURL(blob); + } + else { + const url = URL.createObjectURL(blob); + if (popup) + popup.location.assign(url); + else + location.href = url; + popup = null; // reverse-tabnabbing #460 + setTimeout(function () { + URL.revokeObjectURL(url); + }, 4e4); // 40s + } + } + + /** + * Shows a toast or console.log + * + * @param message - message to log + * @param type - different color of the tooltip + */ + function toastMessage(message, type) { + const piniaMessage = '🍍 ' + message; + if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') { + // No longer available :( + __VUE_DEVTOOLS_TOAST__(piniaMessage, type); + } + else if (type === 'error') { + console.error(piniaMessage); + } + else if (type === 'warn') { + console.warn(piniaMessage); + } + else { + console.log(piniaMessage); + } + } + function isPinia(o) { + return '_a' in o && 'install' in o; + } + + /** + * This file contain devtools actions, they are not Pinia actions. + */ + // --- + function checkClipboardAccess() { + if (!('clipboard' in navigator)) { + toastMessage(`Your browser doesn't support the Clipboard API`, 'error'); + return true; + } + } + function checkNotFocusedError(error) { + if (error instanceof Error && + error.message.toLowerCase().includes('document is not focused')) { + toastMessage('You need to activate the "Emulate a focused page" setting in the "Rendering" panel of devtools.', 'warn'); + return true; + } + return false; + } + async function actionGlobalCopyState(pinia) { + if (checkClipboardAccess()) + return; + try { + await navigator.clipboard.writeText(JSON.stringify(pinia.state.value)); + toastMessage('Global state copied to clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to serialize the state. Check the console for more details.`, 'error'); + console.error(error); + } + } + async function actionGlobalPasteState(pinia) { + if (checkClipboardAccess()) + return; + try { + loadStoresState(pinia, JSON.parse(await navigator.clipboard.readText())); + toastMessage('Global state pasted from clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to deserialize the state from clipboard. Check the console for more details.`, 'error'); + console.error(error); + } + } + async function actionGlobalSaveState(pinia) { + try { + saveAs(new Blob([JSON.stringify(pinia.state.value)], { + type: 'text/plain;charset=utf-8', + }), 'pinia-state.json'); + } + catch (error) { + toastMessage(`Failed to export the state as JSON. Check the console for more details.`, 'error'); + console.error(error); + } + } + let fileInput; + function getFileOpener() { + if (!fileInput) { + fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + } + function openFile() { + return new Promise((resolve, reject) => { + fileInput.onchange = async () => { + const files = fileInput.files; + if (!files) + return resolve(null); + const file = files.item(0); + if (!file) + return resolve(null); + return resolve({ text: await file.text(), file }); + }; + // @ts-ignore: TODO: changed from 4.3 to 4.4 + fileInput.oncancel = () => resolve(null); + fileInput.onerror = reject; + fileInput.click(); + }); + } + return openFile; + } + async function actionGlobalOpenStateFile(pinia) { + try { + const open = getFileOpener(); + const result = await open(); + if (!result) + return; + const { text, file } = result; + loadStoresState(pinia, JSON.parse(text)); + toastMessage(`Global state imported from "${file.name}".`); + } + catch (error) { + toastMessage(`Failed to import the state from JSON. Check the console for more details.`, 'error'); + console.error(error); + } + } + function loadStoresState(pinia, state) { + for (const key in state) { + const storeState = pinia.state.value[key]; + // store is already instantiated, patch it + if (storeState) { + Object.assign(storeState, state[key]); + } + else { + // store is not instantiated, set the initial state + pinia.state.value[key] = state[key]; + } + } + } + + function formatDisplay(display) { + return { + _custom: { + display, + }, + }; + } + const PINIA_ROOT_LABEL = '🍍 Pinia (root)'; + const PINIA_ROOT_ID = '_root'; + function formatStoreForInspectorTree(store) { + return isPinia(store) + ? { + id: PINIA_ROOT_ID, + label: PINIA_ROOT_LABEL, + } + : { + id: store.$id, + label: store.$id, + }; + } + function formatStoreForInspectorState(store) { + if (isPinia(store)) { + const storeNames = Array.from(store._s.keys()); + const storeMap = store._s; + const state = { + state: storeNames.map((storeId) => ({ + editable: true, + key: storeId, + value: store.state.value[storeId], + })), + getters: storeNames + .filter((id) => storeMap.get(id)._getters) + .map((id) => { + const store = storeMap.get(id); + return { + editable: false, + key: id, + value: store._getters.reduce((getters, key) => { + getters[key] = store[key]; + return getters; + }, {}), + }; + }), + }; + return state; + } + const state = { + state: Object.keys(store.$state).map((key) => ({ + editable: true, + key, + value: store.$state[key], + })), + }; + // avoid adding empty getters + if (store._getters && store._getters.length) { + state.getters = store._getters.map((getterName) => ({ + editable: false, + key: getterName, + value: store[getterName], + })); + } + if (store._customProperties.size) { + state.customProperties = Array.from(store._customProperties).map((key) => ({ + editable: true, + key, + value: store[key], + })); + } + return state; + } + function formatEventData(events) { + if (!events) + return {}; + if (Array.isArray(events)) { + // TODO: handle add and delete for arrays and objects + return events.reduce((data, event) => { + data.keys.push(event.key); + data.operations.push(event.type); + data.oldValue[event.key] = event.oldValue; + data.newValue[event.key] = event.newValue; + return data; + }, { + oldValue: {}, + keys: [], + operations: [], + newValue: {}, + }); + } + else { + return { + operation: formatDisplay(events.type), + key: formatDisplay(events.key), + oldValue: events.oldValue, + newValue: events.newValue, + }; + } + } + function formatMutationType(type) { + switch (type) { + case exports.MutationType.direct: + return 'mutation'; + case exports.MutationType.patchFunction: + return '$patch'; + case exports.MutationType.patchObject: + return '$patch'; + default: + return 'unknown'; + } + } + + // timeline can be paused when directly changing the state + let isTimelineActive = true; + const componentStateTypes = []; + const MUTATIONS_LAYER_ID = 'pinia:mutations'; + const INSPECTOR_ID = 'pinia'; + const { assign: assign$1 } = Object; + /** + * Gets the displayed name of a store in devtools + * + * @param id - id of the store + * @returns a formatted string + */ + const getStoreType = (id) => '🍍 ' + id; + /** + * Add the pinia plugin without any store. Allows displaying a Pinia plugin tab + * as soon as it is added to the application. + * + * @param app - Vue application + * @param pinia - pinia instance + */ + function registerPiniaDevtools(app, pinia) { + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + }, (api) => { + if (typeof api.now !== 'function') { + toastMessage('You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); + } + api.addTimelineLayer({ + id: MUTATIONS_LAYER_ID, + label: `Pinia 🍍`, + color: 0xe5df88, + }); + api.addInspector({ + id: INSPECTOR_ID, + label: 'Pinia 🍍', + icon: 'storage', + treeFilterPlaceholder: 'Search stores', + actions: [ + { + icon: 'content_copy', + action: () => { + actionGlobalCopyState(pinia); + }, + tooltip: 'Serialize and copy the state', + }, + { + icon: 'content_paste', + action: async () => { + await actionGlobalPasteState(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Replace the state with the content of your clipboard', + }, + { + icon: 'save', + action: () => { + actionGlobalSaveState(pinia); + }, + tooltip: 'Save the state as a JSON file', + }, + { + icon: 'folder_open', + action: async () => { + await actionGlobalOpenStateFile(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Import the state from a JSON file', + }, + ], + nodeActions: [ + { + icon: 'restore', + tooltip: 'Reset the state (with "$reset")', + action: (nodeId) => { + const store = pinia._s.get(nodeId); + if (!store) { + toastMessage(`Cannot reset "${nodeId}" store because it wasn't found.`, 'warn'); + } + else if (typeof store.$reset !== 'function') { + toastMessage(`Cannot reset "${nodeId}" store because it doesn't have a "$reset" method implemented.`, 'warn'); + } + else { + store.$reset(); + toastMessage(`Store "${nodeId}" reset.`); + } + }, + }, + ], + }); + api.on.inspectComponent((payload, ctx) => { + const proxy = (payload.componentInstance && + payload.componentInstance.proxy); + if (proxy && proxy._pStores) { + const piniaStores = payload.componentInstance.proxy._pStores; + Object.values(piniaStores).forEach((store) => { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'state', + editable: true, + value: store._isOptionsAPI + ? { + _custom: { + value: vueDemi.toRaw(store.$state), + actions: [ + { + icon: 'restore', + tooltip: 'Reset the state of this store', + action: () => store.$reset(), + }, + ], + }, + } + : // NOTE: workaround to unwrap transferred refs + Object.keys(store.$state).reduce((state, key) => { + state[key] = store.$state[key]; + return state; + }, {}), + }); + if (store._getters && store._getters.length) { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'getters', + editable: false, + value: store._getters.reduce((getters, key) => { + try { + getters[key] = store[key]; + } + catch (error) { + // @ts-expect-error: we just want to show it in devtools + getters[key] = error; + } + return getters; + }, {}), + }); + } + }); + } + }); + api.on.getInspectorTree((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + let stores = [pinia]; + stores = stores.concat(Array.from(pinia._s.values())); + payload.rootNodes = (payload.filter + ? stores.filter((store) => '$id' in store + ? store.$id + .toLowerCase() + .includes(payload.filter.toLowerCase()) + : PINIA_ROOT_LABEL.toLowerCase().includes(payload.filter.toLowerCase())) + : stores).map(formatStoreForInspectorTree); + } + }); + // Expose pinia instance as $pinia to window + globalThis.$pinia = pinia; + api.on.getInspectorState((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + // this could be the selected store restored for a different project + // so it's better not to say anything here + return; + } + if (inspectedStore) { + // Expose selected store as $store to window + if (payload.nodeId !== PINIA_ROOT_ID) + globalThis.$store = vueDemi.toRaw(inspectedStore); + payload.state = formatStoreForInspectorState(inspectedStore); + } + } + }); + api.on.editInspectorState((payload, ctx) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + return toastMessage(`store "${payload.nodeId}" not found`, 'error'); + } + const { path } = payload; + if (!isPinia(inspectedStore)) { + // access only the state + if (path.length !== 1 || + !inspectedStore._customProperties.has(path[0]) || + path[0] in inspectedStore.$state) { + path.unshift('$state'); + } + } + else { + // Root access, we can omit the `.value` because the devtools API does it for us + path.unshift('state'); + } + isTimelineActive = false; + payload.set(inspectedStore, path, payload.state.value); + isTimelineActive = true; + } + }); + api.on.editComponentState((payload) => { + if (payload.type.startsWith('🍍')) { + const storeId = payload.type.replace(/^🍍\s*/, ''); + const store = pinia._s.get(storeId); + if (!store) { + return toastMessage(`store "${storeId}" not found`, 'error'); + } + const { path } = payload; + if (path[0] !== 'state') { + return toastMessage(`Invalid path for store "${storeId}":\n${path}\nOnly state can be modified.`); + } + // rewrite the first entry to be able to directly set the state as + // well as any other path + path[0] = '$state'; + isTimelineActive = false; + payload.set(store, path, payload.state.value); + isTimelineActive = true; + } + }); + }); + } + function addStoreToDevtools(app, store) { + if (!componentStateTypes.includes(getStoreType(store.$id))) { + componentStateTypes.push(getStoreType(store.$id)); + } + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + settings: { + logStoreChanges: { + label: 'Notify about new/deleted stores', + type: 'boolean', + defaultValue: true, + }, + // useEmojis: { + // label: 'Use emojis in messages ⚡️', + // type: 'boolean', + // defaultValue: true, + // }, + }, + }, (api) => { + // gracefully handle errors + const now = typeof api.now === 'function' ? api.now.bind(api) : Date.now; + store.$onAction(({ after, onError, name, args }) => { + const groupId = runningActionId++; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛫 ' + name, + subtitle: 'start', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + }, + groupId, + }, + }); + after((result) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛬 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + result, + }, + groupId, + }, + }); + }); + onError((error) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + logType: 'error', + title: '💥 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + error, + }, + groupId, + }, + }); + }); + }, true); + store._customProperties.forEach((name) => { + vueDemi.watch(() => vueDemi.unref(store[name]), (newValue, oldValue) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (isTimelineActive) { + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: 'Change', + subtitle: name, + data: { + newValue, + oldValue, + }, + groupId: activeAction, + }, + }); + } + }, { deep: true }); + }); + store.$subscribe(({ events, type }, state) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (!isTimelineActive) + return; + // rootStore.state[store.id] = state + const eventData = { + time: now(), + title: formatMutationType(type), + data: assign$1({ store: formatDisplay(store.$id) }, formatEventData(events)), + groupId: activeAction, + }; + if (type === exports.MutationType.patchFunction) { + eventData.subtitle = '⤵️'; + } + else if (type === exports.MutationType.patchObject) { + eventData.subtitle = '🧩'; + } + else if (events && !Array.isArray(events)) { + eventData.subtitle = events.type; + } + if (events) { + eventData.data['rawEvent(s)'] = { + _custom: { + display: 'DebuggerEvent', + type: 'object', + tooltip: 'raw DebuggerEvent[]', + value: events, + }, + }; + } + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: eventData, + }); + }, { detached: true, flush: 'sync' }); + const hotUpdate = store._hotUpdate; + store._hotUpdate = vueDemi.markRaw((newStore) => { + hotUpdate(newStore); + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🔥 ' + store.$id, + subtitle: 'HMR update', + data: { + store: formatDisplay(store.$id), + info: formatDisplay(`HMR update`), + }, + }, + }); + // update the devtools too + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }); + const { $dispose } = store; + store.$dispose = () => { + $dispose(); + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`Disposed "${store.$id}" store 🗑`); + }; + // trigger an update so it can display new registered stores + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`"${store.$id}" store installed 🆕`); + }); + } + let runningActionId = 0; + let activeAction; + /** + * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the + * context of all actions, allowing us to set `runningAction` on each access and effectively associating any state + * mutation to the action. + * + * @param store - store to patch + * @param actionNames - list of actionst to patch + */ + function patchActionForGrouping(store, actionNames, wrapWithProxy) { + // original actions of the store as they are given by pinia. We are going to override them + const actions = actionNames.reduce((storeActions, actionName) => { + // use toRaw to avoid tracking #541 + storeActions[actionName] = vueDemi.toRaw(store)[actionName]; + return storeActions; + }, {}); + for (const actionName in actions) { + store[actionName] = function () { + // the running action id is incremented in a before action hook + const _actionId = runningActionId; + const trackedStore = wrapWithProxy + ? new Proxy(store, { + get(...args) { + activeAction = _actionId; + return Reflect.get(...args); + }, + set(...args) { + activeAction = _actionId; + return Reflect.set(...args); + }, + }) + : store; + // For Setup Stores we need https://github.com/tc39/proposal-async-context + activeAction = _actionId; + const retValue = actions[actionName].apply(trackedStore, arguments); + // this is safer as async actions in Setup Stores would associate mutations done outside of the action + activeAction = undefined; + return retValue; + }; + } + } + /** + * pinia.use(devtoolsPlugin) + */ + function devtoolsPlugin({ app, store, options }) { + // HMR module + if (store.$id.startsWith('__hot:')) { + return; + } + // detect option api vs setup api + store._isOptionsAPI = !!options.state; + // Do not overwrite actions mocked by @pinia/testing (#2298) + if (!store._p._testing) { + patchActionForGrouping(store, Object.keys(options.actions), store._isOptionsAPI); + // Upgrade the HMR to also update the new actions + const originalHotUpdate = store._hotUpdate; + vueDemi.toRaw(store)._hotUpdate = function (newStore) { + originalHotUpdate.apply(this, arguments); + patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions), !!store._isOptionsAPI); + }; + } + addStoreToDevtools(app, + // FIXME: is there a way to allow the assignment from Store to StoreGeneric? + store); + } + + /** + * Creates a Pinia instance to be used by the application + */ + function createPinia() { + const scope = vueDemi.effectScope(true); + // NOTE: here we could check the window object for a state and directly set it + // if there is anything like it with Vue 3 SSR + const state = scope.run(() => vueDemi.ref({})); + let _p = []; + // plugins added before calling app.use(pinia) + let toBeInstalled = []; + const pinia = vueDemi.markRaw({ + install(app) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + if (!vueDemi.isVue2) { + pinia._a = app; + app.provide(piniaSymbol, pinia); + app.config.globalProperties.$pinia = pinia; + /* istanbul ignore else */ + if (IS_CLIENT) { + registerPiniaDevtools(app, pinia); + } + toBeInstalled.forEach((plugin) => _p.push(plugin)); + toBeInstalled = []; + } + }, + use(plugin) { + if (!this._a && !vueDemi.isVue2) { + toBeInstalled.push(plugin); + } + else { + _p.push(plugin); + } + return this; + }, + _p, + // it's actually undefined here + // @ts-expect-error + _a: null, + _e: scope, + _s: new Map(), + state, + }); + // pinia devtools rely on dev only features so they cannot be forced unless + // the dev build of Vue is used. Avoid old browsers like IE11. + if (typeof Proxy !== 'undefined') { + pinia.use(devtoolsPlugin); + } + return pinia; + } + /** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ + function disposePinia(pinia) { + pinia._e.stop(); + pinia._s.clear(); + pinia._p.splice(0); + pinia.state.value = {}; + // @ts-expect-error: non valid + pinia._a = null; + } + + /** + * Checks if a function is a `StoreDefinition`. + * + * @param fn - object to test + * @returns true if `fn` is a StoreDefinition + */ + const isUseStore = (fn) => { + return typeof fn === 'function' && typeof fn.$id === 'string'; + }; + /** + * Mutates in place `newState` with `oldState` to _hot update_ it. It will + * remove any key not existing in `newState` and recursively merge plain + * objects. + * + * @param newState - new state object to be patched + * @param oldState - old state that should be used to patch newState + * @returns - newState + */ + function patchObject(newState, oldState) { + // no need to go through symbols because they cannot be serialized anyway + for (const key in oldState) { + const subPatch = oldState[key]; + // skip the whole sub tree + if (!(key in newState)) { + continue; + } + const targetValue = newState[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + !vueDemi.isRef(subPatch) && + !vueDemi.isReactive(subPatch)) { + newState[key] = patchObject(targetValue, subPatch); + } + else { + // objects are either a bit more complex (e.g. refs) or primitives, so we + // just set the whole thing + if (vueDemi.isVue2) { + vueDemi.set(newState, key, subPatch); + } + else { + newState[key] = subPatch; + } + } + } + return newState; + } + /** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ + function acceptHMRUpdate(initialUseStore, hot) { + return (newModule) => { + const pinia = hot.data.pinia || initialUseStore._pinia; + if (!pinia) { + // this store is still not used + return; + } + // preserve the pinia instance across loads + hot.data.pinia = pinia; + // console.log('got data', newStore) + for (const exportName in newModule) { + const useStore = newModule[exportName]; + // console.log('checking for', exportName) + if (isUseStore(useStore) && pinia._s.has(useStore.$id)) { + // console.log('Accepting update for', useStore.$id) + const id = useStore.$id; + if (id !== initialUseStore.$id) { + console.warn(`The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`); + // return import.meta.hot.invalidate() + return hot.invalidate(); + } + const existingStore = pinia._s.get(id); + if (!existingStore) { + console.log(`[Pinia]: skipping hmr because store doesn't exist yet`); + return; + } + useStore(pinia, existingStore); + } + } + }; + } + + const noop = () => { }; + function addSubscription(subscriptions, callback, detached, onCleanup = noop) { + subscriptions.push(callback); + const removeSubscription = () => { + const idx = subscriptions.indexOf(callback); + if (idx > -1) { + subscriptions.splice(idx, 1); + onCleanup(); + } + }; + if (!detached && vueDemi.getCurrentScope()) { + vueDemi.onScopeDispose(removeSubscription); + } + return removeSubscription; + } + function triggerSubscriptions(subscriptions, ...args) { + subscriptions.slice().forEach((callback) => { + callback(...args); + }); + } + + const fallbackRunWithContext = (fn) => fn(); + /** + * Marks a function as an action for `$onAction` + * @internal + */ + const ACTION_MARKER = Symbol(); + /** + * Action name symbol. Allows to add a name to an action after defining it + * @internal + */ + const ACTION_NAME = Symbol(); + function mergeReactiveObjects(target, patchToApply) { + // Handle Map instances + if (target instanceof Map && patchToApply instanceof Map) { + patchToApply.forEach((value, key) => target.set(key, value)); + } + else if (target instanceof Set && patchToApply instanceof Set) { + // Handle Set instances + patchToApply.forEach(target.add, target); + } + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + if (!patchToApply.hasOwnProperty(key)) + continue; + const subPatch = patchToApply[key]; + const targetValue = target[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + target.hasOwnProperty(key) && + !vueDemi.isRef(subPatch) && + !vueDemi.isReactive(subPatch)) { + // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might + // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that + // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`. + target[key] = mergeReactiveObjects(targetValue, subPatch); + } + else { + // @ts-expect-error: subPatch is a valid value + target[key] = subPatch; + } + } + return target; + } + const skipHydrateSymbol = Symbol('pinia:skipHydration') + ; + const skipHydrateMap = /*#__PURE__*/ new WeakMap(); + /** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ + function skipHydrate(obj) { + return vueDemi.isVue2 + ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work... + /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj + : Object.defineProperty(obj, skipHydrateSymbol, {}); + } + /** + * Returns whether a value should be hydrated + * + * @param obj - target variable + * @returns true if `obj` should be hydrated + */ + function shouldHydrate(obj) { + return vueDemi.isVue2 + ? /* istanbul ignore next */ !skipHydrateMap.has(obj) + : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol); + } + const { assign } = Object; + function isComputed(o) { + return !!(vueDemi.isRef(o) && o.effect); + } + function createOptionsStore(id, options, pinia, hot) { + const { state, actions, getters } = options; + const initialState = pinia.state.value[id]; + let store; + function setup() { + if (!initialState && (!hot)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, id, state ? state() : {}); + } + else { + pinia.state.value[id] = state ? state() : {}; + } + } + // avoid creating a state in pinia.state.value + const localState = hot + ? // use ref() to unwrap refs inside state TODO: check if this is still necessary + vueDemi.toRefs(vueDemi.ref(state ? state() : {}).value) + : vueDemi.toRefs(pinia.state.value[id]); + return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { + if (name in localState) { + console.warn(`[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with "${name}" in store "${id}".`); + } + computedGetters[name] = vueDemi.markRaw(vueDemi.computed(() => { + setActivePinia(pinia); + // it was created just before + const store = pinia._s.get(id); + // allow cross using stores + /* istanbul ignore if */ + if (vueDemi.isVue2 && !store._r) + return; + // @ts-expect-error + // return getters![name].call(context, context) + // TODO: avoid reading the getter while assigning with a global variable + return getters[name].call(store, store); + })); + return computedGetters; + }, {})); + } + store = createSetupStore(id, setup, options, pinia, hot, true); + return store; + } + function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { + let scope; + const optionsForPlugin = assign({ actions: {} }, options); + /* istanbul ignore if */ + if (!pinia._e.active) { + throw new Error('Pinia destroyed'); + } + // watcher options for $subscribe + const $subscribeOptions = { deep: true }; + /* istanbul ignore else */ + if (!vueDemi.isVue2) { + $subscribeOptions.onTrigger = (event) => { + /* istanbul ignore else */ + if (isListening) { + debuggerEvents = event; + // avoid triggering this while the store is being built and the state is being set in pinia + } + else if (isListening == false && !store._hotUpdating) { + // let patch send all the events together later + /* istanbul ignore else */ + if (Array.isArray(debuggerEvents)) { + debuggerEvents.push(event); + } + else { + console.error('🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'); + } + } + }; + } + // internal state + let isListening; // set to true at the end + let isSyncListening; // set to true at the end + let subscriptions = []; + let actionSubscriptions = []; + let debuggerEvents; + const initialState = pinia.state.value[$id]; + // avoid setting the state for option stores if it is set + // by the setup + if (!isOptionsStore && !initialState && (!hot)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, $id, {}); + } + else { + pinia.state.value[$id] = {}; + } + } + const hotState = vueDemi.ref({}); + // avoid triggering too many listeners + // https://github.com/vuejs/pinia/issues/1129 + let activeListener; + function $patch(partialStateOrMutator) { + let subscriptionMutation; + isListening = isSyncListening = false; + // reset the debugger events since patches are sync + /* istanbul ignore else */ + { + debuggerEvents = []; + } + if (typeof partialStateOrMutator === 'function') { + partialStateOrMutator(pinia.state.value[$id]); + subscriptionMutation = { + type: exports.MutationType.patchFunction, + storeId: $id, + events: debuggerEvents, + }; + } + else { + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); + subscriptionMutation = { + type: exports.MutationType.patchObject, + payload: partialStateOrMutator, + storeId: $id, + events: debuggerEvents, + }; + } + const myListenerId = (activeListener = Symbol()); + vueDemi.nextTick().then(() => { + if (activeListener === myListenerId) { + isListening = true; + } + }); + isSyncListening = true; + // because we paused the watcher, we need to manually call the subscriptions + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); + } + const $reset = isOptionsStore + ? function $reset() { + const { state } = options; + const newState = state ? state() : {}; + // we use a patch to group all changes into one single subscription + this.$patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, newState); + }); + } + : /* istanbul ignore next */ + () => { + throw new Error(`🍍: Store "${$id}" is built using the setup syntax and does not implement $reset().`); + } + ; + function $dispose() { + scope.stop(); + subscriptions = []; + actionSubscriptions = []; + pinia._s.delete($id); + } + /** + * Helper that wraps function so it can be tracked with $onAction + * @param fn - action to wrap + * @param name - name of the action + */ + const action = (fn, name = '') => { + if (ACTION_MARKER in fn) { + fn[ACTION_NAME] = name; + return fn; + } + const wrappedAction = function () { + setActivePinia(pinia); + const args = Array.from(arguments); + const afterCallbackList = []; + const onErrorCallbackList = []; + function after(callback) { + afterCallbackList.push(callback); + } + function onError(callback) { + onErrorCallbackList.push(callback); + } + // @ts-expect-error + triggerSubscriptions(actionSubscriptions, { + args, + name: wrappedAction[ACTION_NAME], + store, + after, + onError, + }); + let ret; + try { + ret = fn.apply(this && this.$id === $id ? this : store, args); + // handle sync errors + } + catch (error) { + triggerSubscriptions(onErrorCallbackList, error); + throw error; + } + if (ret instanceof Promise) { + return ret + .then((value) => { + triggerSubscriptions(afterCallbackList, value); + return value; + }) + .catch((error) => { + triggerSubscriptions(onErrorCallbackList, error); + return Promise.reject(error); + }); + } + // trigger after callbacks + triggerSubscriptions(afterCallbackList, ret); + return ret; + }; + wrappedAction[ACTION_MARKER] = true; + wrappedAction[ACTION_NAME] = name; // will be set later + // @ts-expect-error: we are intentionally limiting the returned type to just Fn + // because all the added properties are internals that are exposed through `$onAction()` only + return wrappedAction; + }; + const _hmrPayload = /*#__PURE__*/ vueDemi.markRaw({ + actions: {}, + getters: {}, + state: [], + hotState, + }); + const partialStore = { + _p: pinia, + // _s: scope, + $id, + $onAction: addSubscription.bind(null, actionSubscriptions), + $patch, + $reset, + $subscribe(callback, options = {}) { + const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher()); + const stopWatcher = scope.run(() => vueDemi.watch(() => pinia.state.value[$id], (state) => { + if (options.flush === 'sync' ? isSyncListening : isListening) { + callback({ + storeId: $id, + type: exports.MutationType.direct, + events: debuggerEvents, + }, state); + } + }, assign({}, $subscribeOptions, options))); + return removeSubscription; + }, + $dispose, + }; + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // start as non ready + partialStore._r = false; + } + const store = vueDemi.reactive(assign({ + _hmrPayload, + _customProperties: vueDemi.markRaw(new Set()), // devtools custom properties + }, partialStore + // must be added later + // setupStore + ) + ); + // store the partial store now so the setup of stores can instantiate each other before they are finished without + // creating infinite loops. + pinia._s.set($id, store); + const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext; + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped + const setupStore = runWithContext(() => pinia._e.run(() => (scope = vueDemi.effectScope()).run(() => setup({ action })))); + // overwrite existing actions to support $onAction + for (const key in setupStore) { + const prop = setupStore[key]; + if ((vueDemi.isRef(prop) && !isComputed(prop)) || vueDemi.isReactive(prop)) { + // mark it as a piece of state to be serialized + if (hot) { + vueDemi.set(hotState.value, key, vueDemi.toRef(setupStore, key)); + // createOptionStore directly sets the state in pinia.state.value so we + // can just skip that + } + else if (!isOptionsStore) { + // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created + if (initialState && shouldHydrate(prop)) { + if (vueDemi.isRef(prop)) { + prop.value = initialState[key]; + } + else { + // probably a reactive object, lets recursively assign + // @ts-expect-error: prop is unknown + mergeReactiveObjects(prop, initialState[key]); + } + } + // transfer the ref to the pinia state to keep everything in sync + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value[$id], key, prop); + } + else { + pinia.state.value[$id][key] = prop; + } + } + /* istanbul ignore else */ + { + _hmrPayload.state.push(key); + } + // action + } + else if (typeof prop === 'function') { + const actionValue = hot ? prop : action(prop, key); + // this a hot module replacement store because the hotUpdate method needs + // to do it with the right context + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(setupStore, key, actionValue); + } + else { + // @ts-expect-error + setupStore[key] = actionValue; + } + /* istanbul ignore else */ + { + _hmrPayload.actions[key] = prop; + } + // list actions so they can be used in plugins + // @ts-expect-error + optionsForPlugin.actions[key] = prop; + } + else { + // add getters for devtools + if (isComputed(prop)) { + _hmrPayload.getters[key] = isOptionsStore + ? // @ts-expect-error + options.getters[key] + : prop; + if (IS_CLIENT) { + const getters = setupStore._getters || + // @ts-expect-error: same + (setupStore._getters = vueDemi.markRaw([])); + getters.push(key); + } + } + } + } + // add the state, getters, and action properties + /* istanbul ignore if */ + if (vueDemi.isVue2) { + Object.keys(setupStore).forEach((key) => { + vueDemi.set(store, key, setupStore[key]); + }); + } + else { + assign(store, setupStore); + // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object. + // Make `storeToRefs()` work with `reactive()` #799 + assign(vueDemi.toRaw(store), setupStore); + } + // use this instead of a computed with setter to be able to create it anywhere + // without linking the computed lifespan to wherever the store is first + // created. + Object.defineProperty(store, '$state', { + get: () => (hot ? hotState.value : pinia.state.value[$id]), + set: (state) => { + /* istanbul ignore if */ + if (hot) { + throw new Error('cannot set hotState'); + } + $patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, state); + }); + }, + }); + // add the hotUpdate before plugins to allow them to override it + /* istanbul ignore else */ + { + store._hotUpdate = vueDemi.markRaw((newStore) => { + store._hotUpdating = true; + newStore._hmrPayload.state.forEach((stateKey) => { + if (stateKey in store.$state) { + const newStateTarget = newStore.$state[stateKey]; + const oldStateSource = store.$state[stateKey]; + if (typeof newStateTarget === 'object' && + isPlainObject(newStateTarget) && + isPlainObject(oldStateSource)) { + patchObject(newStateTarget, oldStateSource); + } + else { + // transfer the ref + newStore.$state[stateKey] = oldStateSource; + } + } + // patch direct access properties to allow store.stateProperty to work as + // store.$state.stateProperty + vueDemi.set(store, stateKey, vueDemi.toRef(newStore.$state, stateKey)); + }); + // remove deleted state properties + Object.keys(store.$state).forEach((stateKey) => { + if (!(stateKey in newStore.$state)) { + vueDemi.del(store, stateKey); + } + }); + // avoid devtools logging this as a mutation + isListening = false; + isSyncListening = false; + pinia.state.value[$id] = vueDemi.toRef(newStore._hmrPayload, 'hotState'); + isSyncListening = true; + vueDemi.nextTick().then(() => { + isListening = true; + }); + for (const actionName in newStore._hmrPayload.actions) { + const actionFn = newStore[actionName]; + vueDemi.set(store, actionName, action(actionFn, actionName)); + } + // TODO: does this work in both setup and option store? + for (const getterName in newStore._hmrPayload.getters) { + const getter = newStore._hmrPayload.getters[getterName]; + const getterValue = isOptionsStore + ? // special handling of options api + vueDemi.computed(() => { + setActivePinia(pinia); + return getter.call(store, store); + }) + : getter; + vueDemi.set(store, getterName, getterValue); + } + // remove deleted getters + Object.keys(store._hmrPayload.getters).forEach((key) => { + if (!(key in newStore._hmrPayload.getters)) { + vueDemi.del(store, key); + } + }); + // remove old actions + Object.keys(store._hmrPayload.actions).forEach((key) => { + if (!(key in newStore._hmrPayload.actions)) { + vueDemi.del(store, key); + } + }); + // update the values used in devtools and to allow deleting new properties later on + store._hmrPayload = newStore._hmrPayload; + store._getters = newStore._getters; + store._hotUpdating = false; + }); + } + if (IS_CLIENT) { + const nonEnumerable = { + writable: true, + configurable: true, + // avoid warning on devtools trying to display this property + enumerable: false, + }; + ['_p', '_hmrPayload', '_getters', '_customProperties'].forEach((p) => { + Object.defineProperty(store, p, assign({ value: store[p] }, nonEnumerable)); + }); + } + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // mark the store as ready before plugins + store._r = true; + } + // apply all plugins + pinia._p.forEach((extender) => { + /* istanbul ignore else */ + if (IS_CLIENT) { + const extensions = scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + })); + Object.keys(extensions || {}).forEach((key) => store._customProperties.add(key)); + assign(store, extensions); + } + else { + assign(store, scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + }))); + } + }); + if (store.$state && + typeof store.$state === 'object' && + typeof store.$state.constructor === 'function' && + !store.$state.constructor.toString().includes('[native code]')) { + console.warn(`[🍍]: The "state" must be a plain object. It cannot be\n` + + `\tstate: () => new MyClass()\n` + + `Found in store "${store.$id}".`); + } + // only apply hydrate to option stores with an initial state in pinia + if (initialState && + isOptionsStore && + options.hydrate) { + options.hydrate(store.$state, initialState); + } + isListening = true; + isSyncListening = true; + return store; + } + // improves tree shaking + /*#__NO_SIDE_EFFECTS__*/ + function defineStore( + // TODO: add proper types from above + idOrOptions, setup, setupOptions) { + let id; + let options; + const isSetupStore = typeof setup === 'function'; + if (typeof idOrOptions === 'string') { + id = idOrOptions; + // the option store setup will contain the actual options in this case + options = isSetupStore ? setupOptions : setup; + } + else { + options = idOrOptions; + id = idOrOptions.id; + if (typeof id !== 'string') { + throw new Error(`[🍍]: "defineStore()" must be passed a store id as its first argument.`); + } + } + function useStore(pinia, hot) { + const hasContext = vueDemi.hasInjectionContext(); + pinia = + // in test mode, ignore the argument provided as we can always retrieve a + // pinia instance with getActivePinia() + (pinia) || + (hasContext ? vueDemi.inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + if (!activePinia) { + throw new Error(`[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?\n` + + `See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.\n` + + `This will fail in production.`); + } + pinia = activePinia; + if (!pinia._s.has(id)) { + // creating the store registers it in `pinia._s` + if (isSetupStore) { + createSetupStore(id, setup, options, pinia); + } + else { + createOptionsStore(id, options, pinia); + } + /* istanbul ignore else */ + { + // @ts-expect-error: not the right inferred type + useStore._pinia = pinia; + } + } + const store = pinia._s.get(id); + if (hot) { + const hotId = '__hot:' + id; + const newStore = isSetupStore + ? createSetupStore(hotId, setup, options, pinia, true) + : createOptionsStore(hotId, assign({}, options), pinia, true); + hot._hotUpdate(newStore); + // cleanup the state properties and the store from the cache + delete pinia.state.value[hotId]; + pinia._s.delete(hotId); + } + if (IS_CLIENT) { + const currentInstance = vueDemi.getCurrentInstance(); + // save stores in instances to access them devtools + if (currentInstance && + currentInstance.proxy && + // avoid adding stores that are just built for hot module replacement + !hot) { + const vm = currentInstance.proxy; + const cache = '_pStores' in vm ? vm._pStores : (vm._pStores = {}); + cache[id] = store; + } + } + // StoreGeneric cannot be casted towards Store + return store; + } + useStore.$id = id; + return useStore; + } + + let mapStoreSuffix = 'Store'; + /** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ + function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS + ) { + mapStoreSuffix = suffix; + } + /** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ + function mapStores(...stores) { + if (Array.isArray(stores[0])) { + console.warn(`[🍍]: Directly pass all stores to "mapStores()" without putting them in an array:\n` + + `Replace\n` + + `\tmapStores([useAuthStore, useCartStore])\n` + + `with\n` + + `\tmapStores(useAuthStore, useCartStore)\n` + + `This will fail in production if not fixed.`); + stores = stores[0]; + } + return stores.reduce((reduced, useStore) => { + // @ts-expect-error: $id is added by defineStore + reduced[useStore.$id + mapStoreSuffix] = function () { + return useStore(this.$pinia); + }; + return reduced; + }, {}); + } + /** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ + function mapState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + reduced[key] = function () { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function () { + const store = useStore(this.$pinia); + const storeKey = keysOrMapper[key]; + // for some reason TS is unable to infer the type of storeKey to be a + // function + return typeof storeKey === 'function' + ? storeKey.call(this, store) + : // @ts-expect-error: FIXME: should work? + store[storeKey]; + }; + return reduced; + }, {}); + } + /** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ + const mapGetters = mapState; + /** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ + function mapActions(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key](...args); + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]](...args); + }; + return reduced; + }, {}); + } + /** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ + function mapWritableState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[key] = value); + }, + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[keysOrMapper[key]] = value); + }, + }; + return reduced; + }, {}); + } + + /** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ + function storeToRefs(store) { + // See https://github.com/vuejs/pinia/issues/852 + // It's easier to just use toRefs() even if it includes more stuff + if (vueDemi.isVue2) { + // @ts-expect-error: toRefs include methods and others + return vueDemi.toRefs(store); + } + else { + store = vueDemi.toRaw(store); + const refs = {}; + for (const key in store) { + const value = store[key]; + if (vueDemi.isRef(value) || vueDemi.isReactive(value)) { + // @ts-expect-error: the key is state or getter + refs[key] = + // --- + vueDemi.toRef(store, key); + } + } + return refs; + } + } + + /** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ + const PiniaVuePlugin = function (_Vue) { + // Equivalent of + // app.config.globalProperties.$pinia = pinia + _Vue.mixin({ + beforeCreate() { + const options = this.$options; + if (options.pinia) { + const pinia = options.pinia; + // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31 + /* istanbul ignore else */ + if (!this._provided) { + const provideCache = {}; + Object.defineProperty(this, '_provided', { + get: () => provideCache, + set: (v) => Object.assign(provideCache, v), + }); + } + this._provided[piniaSymbol] = pinia; + // propagate the pinia instance in an SSR friendly way + // avoid adding it to nuxt twice + /* istanbul ignore else */ + if (!this.$pinia) { + this.$pinia = pinia; + } + pinia._a = this; + if (IS_CLIENT) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + } + if (IS_CLIENT) { + registerPiniaDevtools(pinia._a, pinia); + } + } + else if (!this.$pinia && options.parent && options.parent.$pinia) { + this.$pinia = options.parent.$pinia; + } + }, + destroyed() { + delete this._pStores; + }, + }); + }; + + exports.PiniaVuePlugin = PiniaVuePlugin; + exports.acceptHMRUpdate = acceptHMRUpdate; + exports.createPinia = createPinia; + exports.defineStore = defineStore; + exports.disposePinia = disposePinia; + exports.getActivePinia = getActivePinia; + exports.mapActions = mapActions; + exports.mapGetters = mapGetters; + exports.mapState = mapState; + exports.mapStores = mapStores; + exports.mapWritableState = mapWritableState; + exports.setActivePinia = setActivePinia; + exports.setMapStoreSuffix = setMapStoreSuffix; + exports.skipHydrate = skipHydrate; + exports.storeToRefs = storeToRefs; + + return exports; + +})({}, VueDemi); diff --git a/node_modules/pinia/dist/pinia.iife.prod.js b/node_modules/pinia/dist/pinia.iife.prod.js new file mode 100644 index 0000000..d852c61 --- /dev/null +++ b/node_modules/pinia/dist/pinia.iife.prod.js @@ -0,0 +1,6 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +var Pinia=function(t,e){"use strict";let n;const i=t=>n=t,s=Symbol();function o(t){return t&&"object"==typeof t&&"[object Object]"===Object.prototype.toString.call(t)&&"function"!=typeof t.toJSON}var r;t.MutationType=void 0,(r=t.MutationType||(t.MutationType={})).direct="direct",r.patchObject="patch object",r.patchFunction="patch function";const c="undefined"!=typeof window;const a=()=>{};function u(t,n,i,s=a){t.push(n);const o=()=>{const e=t.indexOf(n);e>-1&&(t.splice(e,1),s())};return!i&&e.getCurrentScope()&&e.onScopeDispose(o),o}function p(t,...e){t.slice().forEach((t=>{t(...e)}))}const f=t=>t(),h=Symbol(),l=Symbol();function d(t,n){t instanceof Map&&n instanceof Map?n.forEach(((e,n)=>t.set(n,e))):t instanceof Set&&n instanceof Set&&n.forEach(t.add,t);for(const i in n){if(!n.hasOwnProperty(i))continue;const s=n[i],r=t[i];t[i]=o(r)&&o(s)&&t.hasOwnProperty(i)&&!e.isRef(s)&&!e.isReactive(s)?d(r,s):s}return t}const y=Symbol(),v=new WeakMap;const{assign:$}=Object;function _(n,s,r={},c,_,b){let j;const S=$({actions:{}},r),m={deep:!0};let O,g,R,P=[],V=[];const w=c.state.value[n];let M;function A(i){let s;O=g=!1,"function"==typeof i?(i(c.state.value[n]),s={type:t.MutationType.patchFunction,storeId:n,events:R}):(d(c.state.value[n],i),s={type:t.MutationType.patchObject,payload:i,storeId:n,events:R});const o=M=Symbol();e.nextTick().then((()=>{M===o&&(O=!0)})),g=!0,p(P,s,c.state.value[n])}b||w||(e.isVue2?e.set(c.state.value,n,{}):c.state.value[n]={}),e.ref({});const k=b?function(){const{state:t}=r,e=t?t():{};this.$patch((t=>{$(t,e)}))}:a;const T=(t,e="")=>{if(h in t)return t[l]=e,t;const s=function(){i(c);const e=Array.from(arguments),o=[],r=[];let a;p(V,{args:e,name:s[l],store:E,after:function(t){o.push(t)},onError:function(t){r.push(t)}});try{a=t.apply(this&&this.$id===n?this:E,e)}catch(t){throw p(r,t),t}return a instanceof Promise?a.then((t=>(p(o,t),t))).catch((t=>(p(r,t),Promise.reject(t)))):(p(o,a),a)};return s[h]=!0,s[l]=e,s},x={_p:c,$id:n,$onAction:u.bind(null,V),$patch:A,$reset:k,$subscribe(i,s={}){const o=u(P,i,s.detached,(()=>r())),r=j.run((()=>e.watch((()=>c.state.value[n]),(e=>{("sync"===s.flush?g:O)&&i({storeId:n,type:t.MutationType.direct,events:R},e)}),$({},m,s))));return o},$dispose:function(){j.stop(),P=[],V=[],c._s.delete(n)}};e.isVue2&&(x._r=!1);const E=e.reactive(x);c._s.set(n,E);const C=(c._a&&c._a.runWithContext||f)((()=>c._e.run((()=>(j=e.effectScope()).run((()=>s({action:T})))))));for(const t in C){const i=C[t];if(e.isRef(i)&&(!e.isRef(W=i)||!W.effect)||e.isReactive(i))b||(!w||(I=i,e.isVue2?v.has(I):o(I)&&I.hasOwnProperty(y))||(e.isRef(i)?i.value=w[t]:d(i,w[t])),e.isVue2?e.set(c.state.value[n],t,i):c.state.value[n][t]=i);else if("function"==typeof i){const n=T(i,t);e.isVue2?e.set(C,t,n):C[t]=n,S.actions[t]=i}}var I,W;return e.isVue2?Object.keys(C).forEach((t=>{e.set(E,t,C[t])})):($(E,C),$(e.toRaw(E),C)),Object.defineProperty(E,"$state",{get:()=>c.state.value[n],set:t=>{A((e=>{$(e,t)}))}}),e.isVue2&&(E._r=!0),c._p.forEach((t=>{$(E,j.run((()=>t({store:E,app:c._a,pinia:c,options:S}))))})),w&&b&&r.hydrate&&r.hydrate(E.$state,w),O=!0,g=!0,E}let b="Store";function j(t,e){return Array.isArray(e)?e.reduce(((e,n)=>(e[n]=function(){return t(this.$pinia)[n]},e)),{}):Object.keys(e).reduce(((n,i)=>(n[i]=function(){const n=t(this.$pinia),s=e[i];return"function"==typeof s?s.call(this,n):n[s]},n)),{})}const S=j;return t.PiniaVuePlugin=function(t){t.mixin({beforeCreate(){const t=this.$options;if(t.pinia){const e=t.pinia;if(!this._provided){const t={};Object.defineProperty(this,"_provided",{get:()=>t,set:e=>Object.assign(t,e)})}this._provided[s]=e,this.$pinia||(this.$pinia=e),e._a=this,c&&i(e)}else!this.$pinia&&t.parent&&t.parent.$pinia&&(this.$pinia=t.parent.$pinia)},destroyed(){delete this._pStores}})},t.acceptHMRUpdate=function(t,e){return()=>{}},t.createPinia=function(){const t=e.effectScope(!0),n=t.run((()=>e.ref({})));let o=[],r=[];const c=e.markRaw({install(t){i(c),e.isVue2||(c._a=t,t.provide(s,c),t.config.globalProperties.$pinia=c,r.forEach((t=>o.push(t))),r=[])},use(t){return this._a||e.isVue2?o.push(t):r.push(t),this},_p:o,_a:null,_e:t,_s:new Map,state:n});return c},t.defineStore=function(t,o,r){let c,a;const u="function"==typeof o;function p(t,r){const p=e.hasInjectionContext();(t=t||(p?e.inject(s,null):null))&&i(t),(t=n)._s.has(c)||(u?_(c,o,a,t):function(t,n,s){const{state:o,actions:r,getters:c}=n,a=s.state.value[t];let u;u=_(t,(function(){a||(e.isVue2?e.set(s.state.value,t,o?o():{}):s.state.value[t]=o?o():{});const n=e.toRefs(s.state.value[t]);return $(n,r,Object.keys(c||{}).reduce(((n,o)=>(n[o]=e.markRaw(e.computed((()=>{i(s);const n=s._s.get(t);if(!e.isVue2||n._r)return c[o].call(n,n)}))),n)),{}))}),n,s,0,!0)}(c,a,t));return t._s.get(c)}return"string"==typeof t?(c=t,a=u?r:o):(a=t,c=t.id),p.$id=c,p},t.disposePinia=function(t){t._e.stop(),t._s.clear(),t._p.splice(0),t.state.value={},t._a=null},t.getActivePinia=()=>e.hasInjectionContext()&&e.inject(s)||n,t.mapActions=function(t,e){return Array.isArray(e)?e.reduce(((e,n)=>(e[n]=function(...e){return t(this.$pinia)[n](...e)},e)),{}):Object.keys(e).reduce(((n,i)=>(n[i]=function(...n){return t(this.$pinia)[e[i]](...n)},n)),{})},t.mapGetters=S,t.mapState=j,t.mapStores=function(...t){return t.reduce(((t,e)=>(t[e.$id+b]=function(){return e(this.$pinia)},t)),{})},t.mapWritableState=function(t,e){return Array.isArray(e)?e.reduce(((e,n)=>(e[n]={get(){return t(this.$pinia)[n]},set(e){return t(this.$pinia)[n]=e}},e)),{}):Object.keys(e).reduce(((n,i)=>(n[i]={get(){return t(this.$pinia)[e[i]]},set(n){return t(this.$pinia)[e[i]]=n}},n)),{})},t.setActivePinia=i,t.setMapStoreSuffix=function(t){b=t},t.skipHydrate=function(t){return e.isVue2?v.set(t,1)&&t:Object.defineProperty(t,y,{})},t.storeToRefs=function(t){if(e.isVue2)return e.toRefs(t);{t=e.toRaw(t);const n={};for(const i in t){const s=t[i];(e.isRef(s)||e.isReactive(s))&&(n[i]=e.toRef(t,i))}return n}},t}({},VueDemi); diff --git a/node_modules/pinia/dist/pinia.mjs b/node_modules/pinia/dist/pinia.mjs new file mode 100644 index 0000000..c87d925 --- /dev/null +++ b/node_modules/pinia/dist/pinia.mjs @@ -0,0 +1,2043 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +import { hasInjectionContext, inject, toRaw, watch, unref, markRaw, effectScope, ref, isVue2, isRef, isReactive, set, getCurrentScope, onScopeDispose, getCurrentInstance, reactive, toRef, del, nextTick, computed, toRefs } from 'vue-demi'; +import { setupDevtoolsPlugin } from '@vue/devtools-api'; + +/** + * setActivePinia must be called to handle SSR at the top of functions like + * `fetch`, `setup`, `serverPrefetch` and others + */ +let activePinia; +/** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ +// @ts-expect-error: cannot constrain the type of the return +const setActivePinia = (pinia) => (activePinia = pinia); +/** + * Get the currently active pinia if there is any. + */ +const getActivePinia = () => (hasInjectionContext() && inject(piniaSymbol)) || activePinia; +const piniaSymbol = ((process.env.NODE_ENV !== 'production') ? Symbol('pinia') : /* istanbul ignore next */ Symbol()); + +function isPlainObject( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +o) { + return (o && + typeof o === 'object' && + Object.prototype.toString.call(o) === '[object Object]' && + typeof o.toJSON !== 'function'); +} +// type DeepReadonly = { readonly [P in keyof T]: DeepReadonly } +// TODO: can we change these to numbers? +/** + * Possible types for SubscriptionCallback + */ +var MutationType; +(function (MutationType) { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + MutationType["direct"] = "direct"; + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + MutationType["patchObject"] = "patch object"; + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + MutationType["patchFunction"] = "patch function"; + // maybe reset? for $state = {} and $reset +})(MutationType || (MutationType = {})); + +const IS_CLIENT = typeof window !== 'undefined'; + +/* + * FileSaver.js A saveAs() FileSaver implementation. + * + * Originally by Eli Grey, adapted as an ESM module by Eduardo San Martin + * Morote. + * + * License : MIT + */ +// The one and only way of getting global scope in all environments +// https://stackoverflow.com/q/3277182/1008999 +const _global = /*#__PURE__*/ (() => typeof window === 'object' && window.window === window + ? window + : typeof self === 'object' && self.self === self + ? self + : typeof global === 'object' && global.global === global + ? global + : typeof globalThis === 'object' + ? globalThis + : { HTMLElement: null })(); +function bom(blob, { autoBom = false } = {}) { + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (autoBom && + /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xfeff), blob], { type: blob.type }); + } + return blob; +} +function download(url, name, opts) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.onload = function () { + saveAs(xhr.response, name, opts); + }; + xhr.onerror = function () { + console.error('could not download file'); + }; + xhr.send(); +} +function corsEnabled(url) { + const xhr = new XMLHttpRequest(); + // use sync to avoid popup blocker + xhr.open('HEAD', url, false); + try { + xhr.send(); + } + catch (e) { } + return xhr.status >= 200 && xhr.status <= 299; +} +// `a.click()` doesn't work for all browsers (#465) +function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')); + } + catch (e) { + const evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); + node.dispatchEvent(evt); + } +} +const _navigator = typeof navigator === 'object' ? navigator : { userAgent: '' }; +// Detect WebView inside a native macOS app by ruling out all browsers +// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too +// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos +const isMacOSWebView = /*#__PURE__*/ (() => /Macintosh/.test(_navigator.userAgent) && + /AppleWebKit/.test(_navigator.userAgent) && + !/Safari/.test(_navigator.userAgent))(); +const saveAs = !IS_CLIENT + ? () => { } // noop + : // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView or mini program + typeof HTMLAnchorElement !== 'undefined' && + 'download' in HTMLAnchorElement.prototype && + !isMacOSWebView + ? downloadSaveAs + : // Use msSaveOrOpenBlob as a second approach + 'msSaveOrOpenBlob' in _navigator + ? msSaveAs + : // Fallback to using FileReader and a popup + fileSaverSaveAs; +function downloadSaveAs(blob, name = 'download', opts) { + const a = document.createElement('a'); + a.download = name; + a.rel = 'noopener'; // tabnabbing + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + if (typeof blob === 'string') { + // Support regular links + a.href = blob; + if (a.origin !== location.origin) { + if (corsEnabled(a.href)) { + download(blob, name, opts); + } + else { + a.target = '_blank'; + click(a); + } + } + else { + click(a); + } + } + else { + // Support blobs + a.href = URL.createObjectURL(blob); + setTimeout(function () { + URL.revokeObjectURL(a.href); + }, 4e4); // 40s + setTimeout(function () { + click(a); + }, 0); + } +} +function msSaveAs(blob, name = 'download', opts) { + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts); + } + else { + const a = document.createElement('a'); + a.href = blob; + a.target = '_blank'; + setTimeout(function () { + click(a); + }); + } + } + else { + // @ts-ignore: works on windows + navigator.msSaveOrOpenBlob(bom(blob, opts), name); + } +} +function fileSaverSaveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank'); + if (popup) { + popup.document.title = popup.document.body.innerText = 'downloading...'; + } + if (typeof blob === 'string') + return download(blob, name, opts); + const force = blob.type === 'application/octet-stream'; + const isSafari = /constructor/i.test(String(_global.HTMLElement)) || 'safari' in _global; + const isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); + if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && + typeof FileReader !== 'undefined') { + // Safari doesn't allow downloading of blob URLs + const reader = new FileReader(); + reader.onloadend = function () { + let url = reader.result; + if (typeof url !== 'string') { + popup = null; + throw new Error('Wrong reader.result type'); + } + url = isChromeIOS + ? url + : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); + if (popup) { + popup.location.href = url; + } + else { + location.assign(url); + } + popup = null; // reverse-tabnabbing #460 + }; + reader.readAsDataURL(blob); + } + else { + const url = URL.createObjectURL(blob); + if (popup) + popup.location.assign(url); + else + location.href = url; + popup = null; // reverse-tabnabbing #460 + setTimeout(function () { + URL.revokeObjectURL(url); + }, 4e4); // 40s + } +} + +/** + * Shows a toast or console.log + * + * @param message - message to log + * @param type - different color of the tooltip + */ +function toastMessage(message, type) { + const piniaMessage = '🍍 ' + message; + if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') { + // No longer available :( + __VUE_DEVTOOLS_TOAST__(piniaMessage, type); + } + else if (type === 'error') { + console.error(piniaMessage); + } + else if (type === 'warn') { + console.warn(piniaMessage); + } + else { + console.log(piniaMessage); + } +} +function isPinia(o) { + return '_a' in o && 'install' in o; +} + +/** + * This file contain devtools actions, they are not Pinia actions. + */ +// --- +function checkClipboardAccess() { + if (!('clipboard' in navigator)) { + toastMessage(`Your browser doesn't support the Clipboard API`, 'error'); + return true; + } +} +function checkNotFocusedError(error) { + if (error instanceof Error && + error.message.toLowerCase().includes('document is not focused')) { + toastMessage('You need to activate the "Emulate a focused page" setting in the "Rendering" panel of devtools.', 'warn'); + return true; + } + return false; +} +async function actionGlobalCopyState(pinia) { + if (checkClipboardAccess()) + return; + try { + await navigator.clipboard.writeText(JSON.stringify(pinia.state.value)); + toastMessage('Global state copied to clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to serialize the state. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalPasteState(pinia) { + if (checkClipboardAccess()) + return; + try { + loadStoresState(pinia, JSON.parse(await navigator.clipboard.readText())); + toastMessage('Global state pasted from clipboard.'); + } + catch (error) { + if (checkNotFocusedError(error)) + return; + toastMessage(`Failed to deserialize the state from clipboard. Check the console for more details.`, 'error'); + console.error(error); + } +} +async function actionGlobalSaveState(pinia) { + try { + saveAs(new Blob([JSON.stringify(pinia.state.value)], { + type: 'text/plain;charset=utf-8', + }), 'pinia-state.json'); + } + catch (error) { + toastMessage(`Failed to export the state as JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +let fileInput; +function getFileOpener() { + if (!fileInput) { + fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + } + function openFile() { + return new Promise((resolve, reject) => { + fileInput.onchange = async () => { + const files = fileInput.files; + if (!files) + return resolve(null); + const file = files.item(0); + if (!file) + return resolve(null); + return resolve({ text: await file.text(), file }); + }; + // @ts-ignore: TODO: changed from 4.3 to 4.4 + fileInput.oncancel = () => resolve(null); + fileInput.onerror = reject; + fileInput.click(); + }); + } + return openFile; +} +async function actionGlobalOpenStateFile(pinia) { + try { + const open = getFileOpener(); + const result = await open(); + if (!result) + return; + const { text, file } = result; + loadStoresState(pinia, JSON.parse(text)); + toastMessage(`Global state imported from "${file.name}".`); + } + catch (error) { + toastMessage(`Failed to import the state from JSON. Check the console for more details.`, 'error'); + console.error(error); + } +} +function loadStoresState(pinia, state) { + for (const key in state) { + const storeState = pinia.state.value[key]; + // store is already instantiated, patch it + if (storeState) { + Object.assign(storeState, state[key]); + } + else { + // store is not instantiated, set the initial state + pinia.state.value[key] = state[key]; + } + } +} + +function formatDisplay(display) { + return { + _custom: { + display, + }, + }; +} +const PINIA_ROOT_LABEL = '🍍 Pinia (root)'; +const PINIA_ROOT_ID = '_root'; +function formatStoreForInspectorTree(store) { + return isPinia(store) + ? { + id: PINIA_ROOT_ID, + label: PINIA_ROOT_LABEL, + } + : { + id: store.$id, + label: store.$id, + }; +} +function formatStoreForInspectorState(store) { + if (isPinia(store)) { + const storeNames = Array.from(store._s.keys()); + const storeMap = store._s; + const state = { + state: storeNames.map((storeId) => ({ + editable: true, + key: storeId, + value: store.state.value[storeId], + })), + getters: storeNames + .filter((id) => storeMap.get(id)._getters) + .map((id) => { + const store = storeMap.get(id); + return { + editable: false, + key: id, + value: store._getters.reduce((getters, key) => { + getters[key] = store[key]; + return getters; + }, {}), + }; + }), + }; + return state; + } + const state = { + state: Object.keys(store.$state).map((key) => ({ + editable: true, + key, + value: store.$state[key], + })), + }; + // avoid adding empty getters + if (store._getters && store._getters.length) { + state.getters = store._getters.map((getterName) => ({ + editable: false, + key: getterName, + value: store[getterName], + })); + } + if (store._customProperties.size) { + state.customProperties = Array.from(store._customProperties).map((key) => ({ + editable: true, + key, + value: store[key], + })); + } + return state; +} +function formatEventData(events) { + if (!events) + return {}; + if (Array.isArray(events)) { + // TODO: handle add and delete for arrays and objects + return events.reduce((data, event) => { + data.keys.push(event.key); + data.operations.push(event.type); + data.oldValue[event.key] = event.oldValue; + data.newValue[event.key] = event.newValue; + return data; + }, { + oldValue: {}, + keys: [], + operations: [], + newValue: {}, + }); + } + else { + return { + operation: formatDisplay(events.type), + key: formatDisplay(events.key), + oldValue: events.oldValue, + newValue: events.newValue, + }; + } +} +function formatMutationType(type) { + switch (type) { + case MutationType.direct: + return 'mutation'; + case MutationType.patchFunction: + return '$patch'; + case MutationType.patchObject: + return '$patch'; + default: + return 'unknown'; + } +} + +// timeline can be paused when directly changing the state +let isTimelineActive = true; +const componentStateTypes = []; +const MUTATIONS_LAYER_ID = 'pinia:mutations'; +const INSPECTOR_ID = 'pinia'; +const { assign: assign$1 } = Object; +/** + * Gets the displayed name of a store in devtools + * + * @param id - id of the store + * @returns a formatted string + */ +const getStoreType = (id) => '🍍 ' + id; +/** + * Add the pinia plugin without any store. Allows displaying a Pinia plugin tab + * as soon as it is added to the application. + * + * @param app - Vue application + * @param pinia - pinia instance + */ +function registerPiniaDevtools(app, pinia) { + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + }, (api) => { + if (typeof api.now !== 'function') { + toastMessage('You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); + } + api.addTimelineLayer({ + id: MUTATIONS_LAYER_ID, + label: `Pinia 🍍`, + color: 0xe5df88, + }); + api.addInspector({ + id: INSPECTOR_ID, + label: 'Pinia 🍍', + icon: 'storage', + treeFilterPlaceholder: 'Search stores', + actions: [ + { + icon: 'content_copy', + action: () => { + actionGlobalCopyState(pinia); + }, + tooltip: 'Serialize and copy the state', + }, + { + icon: 'content_paste', + action: async () => { + await actionGlobalPasteState(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Replace the state with the content of your clipboard', + }, + { + icon: 'save', + action: () => { + actionGlobalSaveState(pinia); + }, + tooltip: 'Save the state as a JSON file', + }, + { + icon: 'folder_open', + action: async () => { + await actionGlobalOpenStateFile(pinia); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }, + tooltip: 'Import the state from a JSON file', + }, + ], + nodeActions: [ + { + icon: 'restore', + tooltip: 'Reset the state (with "$reset")', + action: (nodeId) => { + const store = pinia._s.get(nodeId); + if (!store) { + toastMessage(`Cannot reset "${nodeId}" store because it wasn't found.`, 'warn'); + } + else if (typeof store.$reset !== 'function') { + toastMessage(`Cannot reset "${nodeId}" store because it doesn't have a "$reset" method implemented.`, 'warn'); + } + else { + store.$reset(); + toastMessage(`Store "${nodeId}" reset.`); + } + }, + }, + ], + }); + api.on.inspectComponent((payload, ctx) => { + const proxy = (payload.componentInstance && + payload.componentInstance.proxy); + if (proxy && proxy._pStores) { + const piniaStores = payload.componentInstance.proxy._pStores; + Object.values(piniaStores).forEach((store) => { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'state', + editable: true, + value: store._isOptionsAPI + ? { + _custom: { + value: toRaw(store.$state), + actions: [ + { + icon: 'restore', + tooltip: 'Reset the state of this store', + action: () => store.$reset(), + }, + ], + }, + } + : // NOTE: workaround to unwrap transferred refs + Object.keys(store.$state).reduce((state, key) => { + state[key] = store.$state[key]; + return state; + }, {}), + }); + if (store._getters && store._getters.length) { + payload.instanceData.state.push({ + type: getStoreType(store.$id), + key: 'getters', + editable: false, + value: store._getters.reduce((getters, key) => { + try { + getters[key] = store[key]; + } + catch (error) { + // @ts-expect-error: we just want to show it in devtools + getters[key] = error; + } + return getters; + }, {}), + }); + } + }); + } + }); + api.on.getInspectorTree((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + let stores = [pinia]; + stores = stores.concat(Array.from(pinia._s.values())); + payload.rootNodes = (payload.filter + ? stores.filter((store) => '$id' in store + ? store.$id + .toLowerCase() + .includes(payload.filter.toLowerCase()) + : PINIA_ROOT_LABEL.toLowerCase().includes(payload.filter.toLowerCase())) + : stores).map(formatStoreForInspectorTree); + } + }); + // Expose pinia instance as $pinia to window + globalThis.$pinia = pinia; + api.on.getInspectorState((payload) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + // this could be the selected store restored for a different project + // so it's better not to say anything here + return; + } + if (inspectedStore) { + // Expose selected store as $store to window + if (payload.nodeId !== PINIA_ROOT_ID) + globalThis.$store = toRaw(inspectedStore); + payload.state = formatStoreForInspectorState(inspectedStore); + } + } + }); + api.on.editInspectorState((payload, ctx) => { + if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { + const inspectedStore = payload.nodeId === PINIA_ROOT_ID + ? pinia + : pinia._s.get(payload.nodeId); + if (!inspectedStore) { + return toastMessage(`store "${payload.nodeId}" not found`, 'error'); + } + const { path } = payload; + if (!isPinia(inspectedStore)) { + // access only the state + if (path.length !== 1 || + !inspectedStore._customProperties.has(path[0]) || + path[0] in inspectedStore.$state) { + path.unshift('$state'); + } + } + else { + // Root access, we can omit the `.value` because the devtools API does it for us + path.unshift('state'); + } + isTimelineActive = false; + payload.set(inspectedStore, path, payload.state.value); + isTimelineActive = true; + } + }); + api.on.editComponentState((payload) => { + if (payload.type.startsWith('🍍')) { + const storeId = payload.type.replace(/^🍍\s*/, ''); + const store = pinia._s.get(storeId); + if (!store) { + return toastMessage(`store "${storeId}" not found`, 'error'); + } + const { path } = payload; + if (path[0] !== 'state') { + return toastMessage(`Invalid path for store "${storeId}":\n${path}\nOnly state can be modified.`); + } + // rewrite the first entry to be able to directly set the state as + // well as any other path + path[0] = '$state'; + isTimelineActive = false; + payload.set(store, path, payload.state.value); + isTimelineActive = true; + } + }); + }); +} +function addStoreToDevtools(app, store) { + if (!componentStateTypes.includes(getStoreType(store.$id))) { + componentStateTypes.push(getStoreType(store.$id)); + } + setupDevtoolsPlugin({ + id: 'dev.esm.pinia', + label: 'Pinia 🍍', + logo: 'https://pinia.vuejs.org/logo.svg', + packageName: 'pinia', + homepage: 'https://pinia.vuejs.org', + componentStateTypes, + app, + settings: { + logStoreChanges: { + label: 'Notify about new/deleted stores', + type: 'boolean', + defaultValue: true, + }, + // useEmojis: { + // label: 'Use emojis in messages ⚡️', + // type: 'boolean', + // defaultValue: true, + // }, + }, + }, (api) => { + // gracefully handle errors + const now = typeof api.now === 'function' ? api.now.bind(api) : Date.now; + store.$onAction(({ after, onError, name, args }) => { + const groupId = runningActionId++; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛫 ' + name, + subtitle: 'start', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + }, + groupId, + }, + }); + after((result) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🛬 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + result, + }, + groupId, + }, + }); + }); + onError((error) => { + activeAction = undefined; + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + logType: 'error', + title: '💥 ' + name, + subtitle: 'end', + data: { + store: formatDisplay(store.$id), + action: formatDisplay(name), + args, + error, + }, + groupId, + }, + }); + }); + }, true); + store._customProperties.forEach((name) => { + watch(() => unref(store[name]), (newValue, oldValue) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (isTimelineActive) { + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: 'Change', + subtitle: name, + data: { + newValue, + oldValue, + }, + groupId: activeAction, + }, + }); + } + }, { deep: true }); + }); + store.$subscribe(({ events, type }, state) => { + api.notifyComponentUpdate(); + api.sendInspectorState(INSPECTOR_ID); + if (!isTimelineActive) + return; + // rootStore.state[store.id] = state + const eventData = { + time: now(), + title: formatMutationType(type), + data: assign$1({ store: formatDisplay(store.$id) }, formatEventData(events)), + groupId: activeAction, + }; + if (type === MutationType.patchFunction) { + eventData.subtitle = '⤵️'; + } + else if (type === MutationType.patchObject) { + eventData.subtitle = '🧩'; + } + else if (events && !Array.isArray(events)) { + eventData.subtitle = events.type; + } + if (events) { + eventData.data['rawEvent(s)'] = { + _custom: { + display: 'DebuggerEvent', + type: 'object', + tooltip: 'raw DebuggerEvent[]', + value: events, + }, + }; + } + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: eventData, + }); + }, { detached: true, flush: 'sync' }); + const hotUpdate = store._hotUpdate; + store._hotUpdate = markRaw((newStore) => { + hotUpdate(newStore); + api.addTimelineEvent({ + layerId: MUTATIONS_LAYER_ID, + event: { + time: now(), + title: '🔥 ' + store.$id, + subtitle: 'HMR update', + data: { + store: formatDisplay(store.$id), + info: formatDisplay(`HMR update`), + }, + }, + }); + // update the devtools too + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + }); + const { $dispose } = store; + store.$dispose = () => { + $dispose(); + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`Disposed "${store.$id}" store 🗑`); + }; + // trigger an update so it can display new registered stores + api.notifyComponentUpdate(); + api.sendInspectorTree(INSPECTOR_ID); + api.sendInspectorState(INSPECTOR_ID); + api.getSettings().logStoreChanges && + toastMessage(`"${store.$id}" store installed 🆕`); + }); +} +let runningActionId = 0; +let activeAction; +/** + * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the + * context of all actions, allowing us to set `runningAction` on each access and effectively associating any state + * mutation to the action. + * + * @param store - store to patch + * @param actionNames - list of actionst to patch + */ +function patchActionForGrouping(store, actionNames, wrapWithProxy) { + // original actions of the store as they are given by pinia. We are going to override them + const actions = actionNames.reduce((storeActions, actionName) => { + // use toRaw to avoid tracking #541 + storeActions[actionName] = toRaw(store)[actionName]; + return storeActions; + }, {}); + for (const actionName in actions) { + store[actionName] = function () { + // the running action id is incremented in a before action hook + const _actionId = runningActionId; + const trackedStore = wrapWithProxy + ? new Proxy(store, { + get(...args) { + activeAction = _actionId; + return Reflect.get(...args); + }, + set(...args) { + activeAction = _actionId; + return Reflect.set(...args); + }, + }) + : store; + // For Setup Stores we need https://github.com/tc39/proposal-async-context + activeAction = _actionId; + const retValue = actions[actionName].apply(trackedStore, arguments); + // this is safer as async actions in Setup Stores would associate mutations done outside of the action + activeAction = undefined; + return retValue; + }; + } +} +/** + * pinia.use(devtoolsPlugin) + */ +function devtoolsPlugin({ app, store, options }) { + // HMR module + if (store.$id.startsWith('__hot:')) { + return; + } + // detect option api vs setup api + store._isOptionsAPI = !!options.state; + // Do not overwrite actions mocked by @pinia/testing (#2298) + if (!store._p._testing) { + patchActionForGrouping(store, Object.keys(options.actions), store._isOptionsAPI); + // Upgrade the HMR to also update the new actions + const originalHotUpdate = store._hotUpdate; + toRaw(store)._hotUpdate = function (newStore) { + originalHotUpdate.apply(this, arguments); + patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions), !!store._isOptionsAPI); + }; + } + addStoreToDevtools(app, + // FIXME: is there a way to allow the assignment from Store to StoreGeneric? + store); +} + +/** + * Creates a Pinia instance to be used by the application + */ +function createPinia() { + const scope = effectScope(true); + // NOTE: here we could check the window object for a state and directly set it + // if there is anything like it with Vue 3 SSR + const state = scope.run(() => ref({})); + let _p = []; + // plugins added before calling app.use(pinia) + let toBeInstalled = []; + const pinia = markRaw({ + install(app) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + if (!isVue2) { + pinia._a = app; + app.provide(piniaSymbol, pinia); + app.config.globalProperties.$pinia = pinia; + /* istanbul ignore else */ + if ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + registerPiniaDevtools(app, pinia); + } + toBeInstalled.forEach((plugin) => _p.push(plugin)); + toBeInstalled = []; + } + }, + use(plugin) { + if (!this._a && !isVue2) { + toBeInstalled.push(plugin); + } + else { + _p.push(plugin); + } + return this; + }, + _p, + // it's actually undefined here + // @ts-expect-error + _a: null, + _e: scope, + _s: new Map(), + state, + }); + // pinia devtools rely on dev only features so they cannot be forced unless + // the dev build of Vue is used. Avoid old browsers like IE11. + if ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && typeof Proxy !== 'undefined') { + pinia.use(devtoolsPlugin); + } + return pinia; +} +/** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ +function disposePinia(pinia) { + pinia._e.stop(); + pinia._s.clear(); + pinia._p.splice(0); + pinia.state.value = {}; + // @ts-expect-error: non valid + pinia._a = null; +} + +/** + * Checks if a function is a `StoreDefinition`. + * + * @param fn - object to test + * @returns true if `fn` is a StoreDefinition + */ +const isUseStore = (fn) => { + return typeof fn === 'function' && typeof fn.$id === 'string'; +}; +/** + * Mutates in place `newState` with `oldState` to _hot update_ it. It will + * remove any key not existing in `newState` and recursively merge plain + * objects. + * + * @param newState - new state object to be patched + * @param oldState - old state that should be used to patch newState + * @returns - newState + */ +function patchObject(newState, oldState) { + // no need to go through symbols because they cannot be serialized anyway + for (const key in oldState) { + const subPatch = oldState[key]; + // skip the whole sub tree + if (!(key in newState)) { + continue; + } + const targetValue = newState[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + !isRef(subPatch) && + !isReactive(subPatch)) { + newState[key] = patchObject(targetValue, subPatch); + } + else { + // objects are either a bit more complex (e.g. refs) or primitives, so we + // just set the whole thing + if (isVue2) { + set(newState, key, subPatch); + } + else { + newState[key] = subPatch; + } + } + } + return newState; +} +/** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ +function acceptHMRUpdate(initialUseStore, hot) { + // strip as much as possible from iife.prod + if (!(process.env.NODE_ENV !== 'production')) { + return () => { }; + } + return (newModule) => { + const pinia = hot.data.pinia || initialUseStore._pinia; + if (!pinia) { + // this store is still not used + return; + } + // preserve the pinia instance across loads + hot.data.pinia = pinia; + // console.log('got data', newStore) + for (const exportName in newModule) { + const useStore = newModule[exportName]; + // console.log('checking for', exportName) + if (isUseStore(useStore) && pinia._s.has(useStore.$id)) { + // console.log('Accepting update for', useStore.$id) + const id = useStore.$id; + if (id !== initialUseStore.$id) { + console.warn(`The id of the store changed from "${initialUseStore.$id}" to "${id}". Reloading.`); + // return import.meta.hot.invalidate() + return hot.invalidate(); + } + const existingStore = pinia._s.get(id); + if (!existingStore) { + console.log(`[Pinia]: skipping hmr because store doesn't exist yet`); + return; + } + useStore(pinia, existingStore); + } + } + }; +} + +const noop = () => { }; +function addSubscription(subscriptions, callback, detached, onCleanup = noop) { + subscriptions.push(callback); + const removeSubscription = () => { + const idx = subscriptions.indexOf(callback); + if (idx > -1) { + subscriptions.splice(idx, 1); + onCleanup(); + } + }; + if (!detached && getCurrentScope()) { + onScopeDispose(removeSubscription); + } + return removeSubscription; +} +function triggerSubscriptions(subscriptions, ...args) { + subscriptions.slice().forEach((callback) => { + callback(...args); + }); +} + +const fallbackRunWithContext = (fn) => fn(); +/** + * Marks a function as an action for `$onAction` + * @internal + */ +const ACTION_MARKER = Symbol(); +/** + * Action name symbol. Allows to add a name to an action after defining it + * @internal + */ +const ACTION_NAME = Symbol(); +function mergeReactiveObjects(target, patchToApply) { + // Handle Map instances + if (target instanceof Map && patchToApply instanceof Map) { + patchToApply.forEach((value, key) => target.set(key, value)); + } + else if (target instanceof Set && patchToApply instanceof Set) { + // Handle Set instances + patchToApply.forEach(target.add, target); + } + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + if (!patchToApply.hasOwnProperty(key)) + continue; + const subPatch = patchToApply[key]; + const targetValue = target[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + target.hasOwnProperty(key) && + !isRef(subPatch) && + !isReactive(subPatch)) { + // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might + // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that + // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`. + target[key] = mergeReactiveObjects(targetValue, subPatch); + } + else { + // @ts-expect-error: subPatch is a valid value + target[key] = subPatch; + } + } + return target; +} +const skipHydrateSymbol = (process.env.NODE_ENV !== 'production') + ? Symbol('pinia:skipHydration') + : /* istanbul ignore next */ Symbol(); +const skipHydrateMap = /*#__PURE__*/ new WeakMap(); +/** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ +function skipHydrate(obj) { + return isVue2 + ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work... + /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj + : Object.defineProperty(obj, skipHydrateSymbol, {}); +} +/** + * Returns whether a value should be hydrated + * + * @param obj - target variable + * @returns true if `obj` should be hydrated + */ +function shouldHydrate(obj) { + return isVue2 + ? /* istanbul ignore next */ !skipHydrateMap.has(obj) + : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol); +} +const { assign } = Object; +function isComputed(o) { + return !!(isRef(o) && o.effect); +} +function createOptionsStore(id, options, pinia, hot) { + const { state, actions, getters } = options; + const initialState = pinia.state.value[id]; + let store; + function setup() { + if (!initialState && (!(process.env.NODE_ENV !== 'production') || !hot)) { + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value, id, state ? state() : {}); + } + else { + pinia.state.value[id] = state ? state() : {}; + } + } + // avoid creating a state in pinia.state.value + const localState = (process.env.NODE_ENV !== 'production') && hot + ? // use ref() to unwrap refs inside state TODO: check if this is still necessary + toRefs(ref(state ? state() : {}).value) + : toRefs(pinia.state.value[id]); + return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { + if ((process.env.NODE_ENV !== 'production') && name in localState) { + console.warn(`[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with "${name}" in store "${id}".`); + } + computedGetters[name] = markRaw(computed(() => { + setActivePinia(pinia); + // it was created just before + const store = pinia._s.get(id); + // allow cross using stores + /* istanbul ignore if */ + if (isVue2 && !store._r) + return; + // @ts-expect-error + // return getters![name].call(context, context) + // TODO: avoid reading the getter while assigning with a global variable + return getters[name].call(store, store); + })); + return computedGetters; + }, {})); + } + store = createSetupStore(id, setup, options, pinia, hot, true); + return store; +} +function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { + let scope; + const optionsForPlugin = assign({ actions: {} }, options); + /* istanbul ignore if */ + if ((process.env.NODE_ENV !== 'production') && !pinia._e.active) { + throw new Error('Pinia destroyed'); + } + // watcher options for $subscribe + const $subscribeOptions = { deep: true }; + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production') && !isVue2) { + $subscribeOptions.onTrigger = (event) => { + /* istanbul ignore else */ + if (isListening) { + debuggerEvents = event; + // avoid triggering this while the store is being built and the state is being set in pinia + } + else if (isListening == false && !store._hotUpdating) { + // let patch send all the events together later + /* istanbul ignore else */ + if (Array.isArray(debuggerEvents)) { + debuggerEvents.push(event); + } + else { + console.error('🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'); + } + } + }; + } + // internal state + let isListening; // set to true at the end + let isSyncListening; // set to true at the end + let subscriptions = []; + let actionSubscriptions = []; + let debuggerEvents; + const initialState = pinia.state.value[$id]; + // avoid setting the state for option stores if it is set + // by the setup + if (!isOptionsStore && !initialState && (!(process.env.NODE_ENV !== 'production') || !hot)) { + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value, $id, {}); + } + else { + pinia.state.value[$id] = {}; + } + } + const hotState = ref({}); + // avoid triggering too many listeners + // https://github.com/vuejs/pinia/issues/1129 + let activeListener; + function $patch(partialStateOrMutator) { + let subscriptionMutation; + isListening = isSyncListening = false; + // reset the debugger events since patches are sync + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + debuggerEvents = []; + } + if (typeof partialStateOrMutator === 'function') { + partialStateOrMutator(pinia.state.value[$id]); + subscriptionMutation = { + type: MutationType.patchFunction, + storeId: $id, + events: debuggerEvents, + }; + } + else { + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); + subscriptionMutation = { + type: MutationType.patchObject, + payload: partialStateOrMutator, + storeId: $id, + events: debuggerEvents, + }; + } + const myListenerId = (activeListener = Symbol()); + nextTick().then(() => { + if (activeListener === myListenerId) { + isListening = true; + } + }); + isSyncListening = true; + // because we paused the watcher, we need to manually call the subscriptions + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); + } + const $reset = isOptionsStore + ? function $reset() { + const { state } = options; + const newState = state ? state() : {}; + // we use a patch to group all changes into one single subscription + this.$patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, newState); + }); + } + : /* istanbul ignore next */ + (process.env.NODE_ENV !== 'production') + ? () => { + throw new Error(`🍍: Store "${$id}" is built using the setup syntax and does not implement $reset().`); + } + : noop; + function $dispose() { + scope.stop(); + subscriptions = []; + actionSubscriptions = []; + pinia._s.delete($id); + } + /** + * Helper that wraps function so it can be tracked with $onAction + * @param fn - action to wrap + * @param name - name of the action + */ + const action = (fn, name = '') => { + if (ACTION_MARKER in fn) { + fn[ACTION_NAME] = name; + return fn; + } + const wrappedAction = function () { + setActivePinia(pinia); + const args = Array.from(arguments); + const afterCallbackList = []; + const onErrorCallbackList = []; + function after(callback) { + afterCallbackList.push(callback); + } + function onError(callback) { + onErrorCallbackList.push(callback); + } + // @ts-expect-error + triggerSubscriptions(actionSubscriptions, { + args, + name: wrappedAction[ACTION_NAME], + store, + after, + onError, + }); + let ret; + try { + ret = fn.apply(this && this.$id === $id ? this : store, args); + // handle sync errors + } + catch (error) { + triggerSubscriptions(onErrorCallbackList, error); + throw error; + } + if (ret instanceof Promise) { + return ret + .then((value) => { + triggerSubscriptions(afterCallbackList, value); + return value; + }) + .catch((error) => { + triggerSubscriptions(onErrorCallbackList, error); + return Promise.reject(error); + }); + } + // trigger after callbacks + triggerSubscriptions(afterCallbackList, ret); + return ret; + }; + wrappedAction[ACTION_MARKER] = true; + wrappedAction[ACTION_NAME] = name; // will be set later + // @ts-expect-error: we are intentionally limiting the returned type to just Fn + // because all the added properties are internals that are exposed through `$onAction()` only + return wrappedAction; + }; + const _hmrPayload = /*#__PURE__*/ markRaw({ + actions: {}, + getters: {}, + state: [], + hotState, + }); + const partialStore = { + _p: pinia, + // _s: scope, + $id, + $onAction: addSubscription.bind(null, actionSubscriptions), + $patch, + $reset, + $subscribe(callback, options = {}) { + const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher()); + const stopWatcher = scope.run(() => watch(() => pinia.state.value[$id], (state) => { + if (options.flush === 'sync' ? isSyncListening : isListening) { + callback({ + storeId: $id, + type: MutationType.direct, + events: debuggerEvents, + }, state); + } + }, assign({}, $subscribeOptions, options))); + return removeSubscription; + }, + $dispose, + }; + /* istanbul ignore if */ + if (isVue2) { + // start as non ready + partialStore._r = false; + } + const store = reactive((process.env.NODE_ENV !== 'production') || ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) + ? assign({ + _hmrPayload, + _customProperties: markRaw(new Set()), // devtools custom properties + }, partialStore + // must be added later + // setupStore + ) + : partialStore); + // store the partial store now so the setup of stores can instantiate each other before they are finished without + // creating infinite loops. + pinia._s.set($id, store); + const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext; + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped + const setupStore = runWithContext(() => pinia._e.run(() => (scope = effectScope()).run(() => setup({ action })))); + // overwrite existing actions to support $onAction + for (const key in setupStore) { + const prop = setupStore[key]; + if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) { + // mark it as a piece of state to be serialized + if ((process.env.NODE_ENV !== 'production') && hot) { + set(hotState.value, key, toRef(setupStore, key)); + // createOptionStore directly sets the state in pinia.state.value so we + // can just skip that + } + else if (!isOptionsStore) { + // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created + if (initialState && shouldHydrate(prop)) { + if (isRef(prop)) { + prop.value = initialState[key]; + } + else { + // probably a reactive object, lets recursively assign + // @ts-expect-error: prop is unknown + mergeReactiveObjects(prop, initialState[key]); + } + } + // transfer the ref to the pinia state to keep everything in sync + /* istanbul ignore if */ + if (isVue2) { + set(pinia.state.value[$id], key, prop); + } + else { + pinia.state.value[$id][key] = prop; + } + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + _hmrPayload.state.push(key); + } + // action + } + else if (typeof prop === 'function') { + const actionValue = (process.env.NODE_ENV !== 'production') && hot ? prop : action(prop, key); + // this a hot module replacement store because the hotUpdate method needs + // to do it with the right context + /* istanbul ignore if */ + if (isVue2) { + set(setupStore, key, actionValue); + } + else { + // @ts-expect-error + setupStore[key] = actionValue; + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + _hmrPayload.actions[key] = prop; + } + // list actions so they can be used in plugins + // @ts-expect-error + optionsForPlugin.actions[key] = prop; + } + else if ((process.env.NODE_ENV !== 'production')) { + // add getters for devtools + if (isComputed(prop)) { + _hmrPayload.getters[key] = isOptionsStore + ? // @ts-expect-error + options.getters[key] + : prop; + if (IS_CLIENT) { + const getters = setupStore._getters || + // @ts-expect-error: same + (setupStore._getters = markRaw([])); + getters.push(key); + } + } + } + } + // add the state, getters, and action properties + /* istanbul ignore if */ + if (isVue2) { + Object.keys(setupStore).forEach((key) => { + set(store, key, setupStore[key]); + }); + } + else { + assign(store, setupStore); + // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object. + // Make `storeToRefs()` work with `reactive()` #799 + assign(toRaw(store), setupStore); + } + // use this instead of a computed with setter to be able to create it anywhere + // without linking the computed lifespan to wherever the store is first + // created. + Object.defineProperty(store, '$state', { + get: () => ((process.env.NODE_ENV !== 'production') && hot ? hotState.value : pinia.state.value[$id]), + set: (state) => { + /* istanbul ignore if */ + if ((process.env.NODE_ENV !== 'production') && hot) { + throw new Error('cannot set hotState'); + } + $patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, state); + }); + }, + }); + // add the hotUpdate before plugins to allow them to override it + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + store._hotUpdate = markRaw((newStore) => { + store._hotUpdating = true; + newStore._hmrPayload.state.forEach((stateKey) => { + if (stateKey in store.$state) { + const newStateTarget = newStore.$state[stateKey]; + const oldStateSource = store.$state[stateKey]; + if (typeof newStateTarget === 'object' && + isPlainObject(newStateTarget) && + isPlainObject(oldStateSource)) { + patchObject(newStateTarget, oldStateSource); + } + else { + // transfer the ref + newStore.$state[stateKey] = oldStateSource; + } + } + // patch direct access properties to allow store.stateProperty to work as + // store.$state.stateProperty + set(store, stateKey, toRef(newStore.$state, stateKey)); + }); + // remove deleted state properties + Object.keys(store.$state).forEach((stateKey) => { + if (!(stateKey in newStore.$state)) { + del(store, stateKey); + } + }); + // avoid devtools logging this as a mutation + isListening = false; + isSyncListening = false; + pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState'); + isSyncListening = true; + nextTick().then(() => { + isListening = true; + }); + for (const actionName in newStore._hmrPayload.actions) { + const actionFn = newStore[actionName]; + set(store, actionName, action(actionFn, actionName)); + } + // TODO: does this work in both setup and option store? + for (const getterName in newStore._hmrPayload.getters) { + const getter = newStore._hmrPayload.getters[getterName]; + const getterValue = isOptionsStore + ? // special handling of options api + computed(() => { + setActivePinia(pinia); + return getter.call(store, store); + }) + : getter; + set(store, getterName, getterValue); + } + // remove deleted getters + Object.keys(store._hmrPayload.getters).forEach((key) => { + if (!(key in newStore._hmrPayload.getters)) { + del(store, key); + } + }); + // remove old actions + Object.keys(store._hmrPayload.actions).forEach((key) => { + if (!(key in newStore._hmrPayload.actions)) { + del(store, key); + } + }); + // update the values used in devtools and to allow deleting new properties later on + store._hmrPayload = newStore._hmrPayload; + store._getters = newStore._getters; + store._hotUpdating = false; + }); + } + if ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + const nonEnumerable = { + writable: true, + configurable: true, + // avoid warning on devtools trying to display this property + enumerable: false, + }; + ['_p', '_hmrPayload', '_getters', '_customProperties'].forEach((p) => { + Object.defineProperty(store, p, assign({ value: store[p] }, nonEnumerable)); + }); + } + /* istanbul ignore if */ + if (isVue2) { + // mark the store as ready before plugins + store._r = true; + } + // apply all plugins + pinia._p.forEach((extender) => { + /* istanbul ignore else */ + if ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + const extensions = scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + })); + Object.keys(extensions || {}).forEach((key) => store._customProperties.add(key)); + assign(store, extensions); + } + else { + assign(store, scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + }))); + } + }); + if ((process.env.NODE_ENV !== 'production') && + store.$state && + typeof store.$state === 'object' && + typeof store.$state.constructor === 'function' && + !store.$state.constructor.toString().includes('[native code]')) { + console.warn(`[🍍]: The "state" must be a plain object. It cannot be\n` + + `\tstate: () => new MyClass()\n` + + `Found in store "${store.$id}".`); + } + // only apply hydrate to option stores with an initial state in pinia + if (initialState && + isOptionsStore && + options.hydrate) { + options.hydrate(store.$state, initialState); + } + isListening = true; + isSyncListening = true; + return store; +} +// improves tree shaking +/*#__NO_SIDE_EFFECTS__*/ +function defineStore( +// TODO: add proper types from above +idOrOptions, setup, setupOptions) { + let id; + let options; + const isSetupStore = typeof setup === 'function'; + if (typeof idOrOptions === 'string') { + id = idOrOptions; + // the option store setup will contain the actual options in this case + options = isSetupStore ? setupOptions : setup; + } + else { + options = idOrOptions; + id = idOrOptions.id; + if ((process.env.NODE_ENV !== 'production') && typeof id !== 'string') { + throw new Error(`[🍍]: "defineStore()" must be passed a store id as its first argument.`); + } + } + function useStore(pinia, hot) { + const hasContext = hasInjectionContext(); + pinia = + // in test mode, ignore the argument provided as we can always retrieve a + // pinia instance with getActivePinia() + ((process.env.NODE_ENV === 'test') && activePinia && activePinia._testing ? null : pinia) || + (hasContext ? inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + if ((process.env.NODE_ENV !== 'production') && !activePinia) { + throw new Error(`[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?\n` + + `See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.\n` + + `This will fail in production.`); + } + pinia = activePinia; + if (!pinia._s.has(id)) { + // creating the store registers it in `pinia._s` + if (isSetupStore) { + createSetupStore(id, setup, options, pinia); + } + else { + createOptionsStore(id, options, pinia); + } + /* istanbul ignore else */ + if ((process.env.NODE_ENV !== 'production')) { + // @ts-expect-error: not the right inferred type + useStore._pinia = pinia; + } + } + const store = pinia._s.get(id); + if ((process.env.NODE_ENV !== 'production') && hot) { + const hotId = '__hot:' + id; + const newStore = isSetupStore + ? createSetupStore(hotId, setup, options, pinia, true) + : createOptionsStore(hotId, assign({}, options), pinia, true); + hot._hotUpdate(newStore); + // cleanup the state properties and the store from the cache + delete pinia.state.value[hotId]; + pinia._s.delete(hotId); + } + if ((process.env.NODE_ENV !== 'production') && IS_CLIENT) { + const currentInstance = getCurrentInstance(); + // save stores in instances to access them devtools + if (currentInstance && + currentInstance.proxy && + // avoid adding stores that are just built for hot module replacement + !hot) { + const vm = currentInstance.proxy; + const cache = '_pStores' in vm ? vm._pStores : (vm._pStores = {}); + cache[id] = store; + } + } + // StoreGeneric cannot be casted towards Store + return store; + } + useStore.$id = id; + return useStore; +} + +let mapStoreSuffix = 'Store'; +/** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ +function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS +) { + mapStoreSuffix = suffix; +} +/** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ +function mapStores(...stores) { + if ((process.env.NODE_ENV !== 'production') && Array.isArray(stores[0])) { + console.warn(`[🍍]: Directly pass all stores to "mapStores()" without putting them in an array:\n` + + `Replace\n` + + `\tmapStores([useAuthStore, useCartStore])\n` + + `with\n` + + `\tmapStores(useAuthStore, useCartStore)\n` + + `This will fail in production if not fixed.`); + stores = stores[0]; + } + return stores.reduce((reduced, useStore) => { + // @ts-expect-error: $id is added by defineStore + reduced[useStore.$id + mapStoreSuffix] = function () { + return useStore(this.$pinia); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + reduced[key] = function () { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function () { + const store = useStore(this.$pinia); + const storeKey = keysOrMapper[key]; + // for some reason TS is unable to infer the type of storeKey to be a + // function + return typeof storeKey === 'function' + ? storeKey.call(this, store) + : // @ts-expect-error: FIXME: should work? + store[storeKey]; + }; + return reduced; + }, {}); +} +/** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ +const mapGetters = mapState; +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapActions(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key](...args); + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]](...args); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapWritableState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[key] = value); + }, + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[keysOrMapper[key]] = value); + }, + }; + return reduced; + }, {}); +} + +/** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ +function storeToRefs(store) { + // See https://github.com/vuejs/pinia/issues/852 + // It's easier to just use toRefs() even if it includes more stuff + if (isVue2) { + // @ts-expect-error: toRefs include methods and others + return toRefs(store); + } + else { + store = toRaw(store); + const refs = {}; + for (const key in store) { + const value = store[key]; + if (isRef(value) || isReactive(value)) { + // @ts-expect-error: the key is state or getter + refs[key] = + // --- + toRef(store, key); + } + } + return refs; + } +} + +/** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ +const PiniaVuePlugin = function (_Vue) { + // Equivalent of + // app.config.globalProperties.$pinia = pinia + _Vue.mixin({ + beforeCreate() { + const options = this.$options; + if (options.pinia) { + const pinia = options.pinia; + // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31 + /* istanbul ignore else */ + if (!this._provided) { + const provideCache = {}; + Object.defineProperty(this, '_provided', { + get: () => provideCache, + set: (v) => Object.assign(provideCache, v), + }); + } + this._provided[piniaSymbol] = pinia; + // propagate the pinia instance in an SSR friendly way + // avoid adding it to nuxt twice + /* istanbul ignore else */ + if (!this.$pinia) { + this.$pinia = pinia; + } + pinia._a = this; + if (IS_CLIENT) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + } + if ((((process.env.NODE_ENV !== 'production') || (typeof __VUE_PROD_DEVTOOLS__ !== 'undefined' && __VUE_PROD_DEVTOOLS__)) && !(process.env.NODE_ENV === 'test')) && IS_CLIENT) { + registerPiniaDevtools(pinia._a, pinia); + } + } + else if (!this.$pinia && options.parent && options.parent.$pinia) { + this.$pinia = options.parent.$pinia; + } + }, + destroyed() { + delete this._pStores; + }, + }); +}; + +export { MutationType, PiniaVuePlugin, acceptHMRUpdate, createPinia, defineStore, disposePinia, getActivePinia, mapActions, mapGetters, mapState, mapStores, mapWritableState, setActivePinia, setMapStoreSuffix, skipHydrate, storeToRefs }; diff --git a/node_modules/pinia/dist/pinia.prod.cjs b/node_modules/pinia/dist/pinia.prod.cjs new file mode 100644 index 0000000..bf5e7e8 --- /dev/null +++ b/node_modules/pinia/dist/pinia.prod.cjs @@ -0,0 +1,844 @@ +/*! + * pinia v2.2.4 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */ +'use strict'; + +var vueDemi = require('vue-demi'); + +/** + * setActivePinia must be called to handle SSR at the top of functions like + * `fetch`, `setup`, `serverPrefetch` and others + */ +let activePinia; +/** + * Sets or unsets the active pinia. Used in SSR and internally when calling + * actions and getters + * + * @param pinia - Pinia instance + */ +// @ts-expect-error: cannot constrain the type of the return +const setActivePinia = (pinia) => (activePinia = pinia); +/** + * Get the currently active pinia if there is any. + */ +const getActivePinia = () => (vueDemi.hasInjectionContext() && vueDemi.inject(piniaSymbol)) || activePinia; +const piniaSymbol = (/* istanbul ignore next */ Symbol()); + +function isPlainObject( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +o) { + return (o && + typeof o === 'object' && + Object.prototype.toString.call(o) === '[object Object]' && + typeof o.toJSON !== 'function'); +} +// type DeepReadonly = { readonly [P in keyof T]: DeepReadonly } +// TODO: can we change these to numbers? +/** + * Possible types for SubscriptionCallback + */ +exports.MutationType = void 0; +(function (MutationType) { + /** + * Direct mutation of the state: + * + * - `store.name = 'new name'` + * - `store.$state.name = 'new name'` + * - `store.list.push('new item')` + */ + MutationType["direct"] = "direct"; + /** + * Mutated the state with `$patch` and an object + * + * - `store.$patch({ name: 'newName' })` + */ + MutationType["patchObject"] = "patch object"; + /** + * Mutated the state with `$patch` and a function + * + * - `store.$patch(state => state.name = 'newName')` + */ + MutationType["patchFunction"] = "patch function"; + // maybe reset? for $state = {} and $reset +})(exports.MutationType || (exports.MutationType = {})); + +const IS_CLIENT = typeof window !== 'undefined'; + +/** + * Creates a Pinia instance to be used by the application + */ +function createPinia() { + const scope = vueDemi.effectScope(true); + // NOTE: here we could check the window object for a state and directly set it + // if there is anything like it with Vue 3 SSR + const state = scope.run(() => vueDemi.ref({})); + let _p = []; + // plugins added before calling app.use(pinia) + let toBeInstalled = []; + const pinia = vueDemi.markRaw({ + install(app) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + if (!vueDemi.isVue2) { + pinia._a = app; + app.provide(piniaSymbol, pinia); + app.config.globalProperties.$pinia = pinia; + toBeInstalled.forEach((plugin) => _p.push(plugin)); + toBeInstalled = []; + } + }, + use(plugin) { + if (!this._a && !vueDemi.isVue2) { + toBeInstalled.push(plugin); + } + else { + _p.push(plugin); + } + return this; + }, + _p, + // it's actually undefined here + // @ts-expect-error + _a: null, + _e: scope, + _s: new Map(), + state, + }); + return pinia; +} +/** + * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly + * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances. + * Once disposed, the pinia instance cannot be used anymore. + * + * @param pinia - pinia instance + */ +function disposePinia(pinia) { + pinia._e.stop(); + pinia._s.clear(); + pinia._p.splice(0); + pinia.state.value = {}; + // @ts-expect-error: non valid + pinia._a = null; +} + +/** + * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications. + * + * @example + * ```js + * const useUser = defineStore(...) + * if (import.meta.hot) { + * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot)) + * } + * ``` + * + * @param initialUseStore - return of the defineStore to hot update + * @param hot - `import.meta.hot` + */ +function acceptHMRUpdate(initialUseStore, hot) { + // strip as much as possible from iife.prod + { + return () => { }; + } +} + +const noop = () => { }; +function addSubscription(subscriptions, callback, detached, onCleanup = noop) { + subscriptions.push(callback); + const removeSubscription = () => { + const idx = subscriptions.indexOf(callback); + if (idx > -1) { + subscriptions.splice(idx, 1); + onCleanup(); + } + }; + if (!detached && vueDemi.getCurrentScope()) { + vueDemi.onScopeDispose(removeSubscription); + } + return removeSubscription; +} +function triggerSubscriptions(subscriptions, ...args) { + subscriptions.slice().forEach((callback) => { + callback(...args); + }); +} + +const fallbackRunWithContext = (fn) => fn(); +/** + * Marks a function as an action for `$onAction` + * @internal + */ +const ACTION_MARKER = Symbol(); +/** + * Action name symbol. Allows to add a name to an action after defining it + * @internal + */ +const ACTION_NAME = Symbol(); +function mergeReactiveObjects(target, patchToApply) { + // Handle Map instances + if (target instanceof Map && patchToApply instanceof Map) { + patchToApply.forEach((value, key) => target.set(key, value)); + } + else if (target instanceof Set && patchToApply instanceof Set) { + // Handle Set instances + patchToApply.forEach(target.add, target); + } + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + if (!patchToApply.hasOwnProperty(key)) + continue; + const subPatch = patchToApply[key]; + const targetValue = target[key]; + if (isPlainObject(targetValue) && + isPlainObject(subPatch) && + target.hasOwnProperty(key) && + !vueDemi.isRef(subPatch) && + !vueDemi.isReactive(subPatch)) { + // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might + // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that + // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`. + target[key] = mergeReactiveObjects(targetValue, subPatch); + } + else { + // @ts-expect-error: subPatch is a valid value + target[key] = subPatch; + } + } + return target; +} +const skipHydrateSymbol = /* istanbul ignore next */ Symbol(); +const skipHydrateMap = /*#__PURE__*/ new WeakMap(); +/** + * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a + * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store. + * + * @param obj - target object + * @returns obj + */ +function skipHydrate(obj) { + return vueDemi.isVue2 + ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work... + /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj + : Object.defineProperty(obj, skipHydrateSymbol, {}); +} +/** + * Returns whether a value should be hydrated + * + * @param obj - target variable + * @returns true if `obj` should be hydrated + */ +function shouldHydrate(obj) { + return vueDemi.isVue2 + ? /* istanbul ignore next */ !skipHydrateMap.has(obj) + : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol); +} +const { assign } = Object; +function isComputed(o) { + return !!(vueDemi.isRef(o) && o.effect); +} +function createOptionsStore(id, options, pinia, hot) { + const { state, actions, getters } = options; + const initialState = pinia.state.value[id]; + let store; + function setup() { + if (!initialState && (!false)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, id, state ? state() : {}); + } + else { + pinia.state.value[id] = state ? state() : {}; + } + } + // avoid creating a state in pinia.state.value + const localState = vueDemi.toRefs(pinia.state.value[id]); + return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { + computedGetters[name] = vueDemi.markRaw(vueDemi.computed(() => { + setActivePinia(pinia); + // it was created just before + const store = pinia._s.get(id); + // allow cross using stores + /* istanbul ignore if */ + if (vueDemi.isVue2 && !store._r) + return; + // @ts-expect-error + // return getters![name].call(context, context) + // TODO: avoid reading the getter while assigning with a global variable + return getters[name].call(store, store); + })); + return computedGetters; + }, {})); + } + store = createSetupStore(id, setup, options, pinia, hot, true); + return store; +} +function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { + let scope; + const optionsForPlugin = assign({ actions: {} }, options); + // watcher options for $subscribe + const $subscribeOptions = { deep: true }; + // internal state + let isListening; // set to true at the end + let isSyncListening; // set to true at the end + let subscriptions = []; + let actionSubscriptions = []; + let debuggerEvents; + const initialState = pinia.state.value[$id]; + // avoid setting the state for option stores if it is set + // by the setup + if (!isOptionsStore && !initialState && (!false)) { + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value, $id, {}); + } + else { + pinia.state.value[$id] = {}; + } + } + vueDemi.ref({}); + // avoid triggering too many listeners + // https://github.com/vuejs/pinia/issues/1129 + let activeListener; + function $patch(partialStateOrMutator) { + let subscriptionMutation; + isListening = isSyncListening = false; + if (typeof partialStateOrMutator === 'function') { + partialStateOrMutator(pinia.state.value[$id]); + subscriptionMutation = { + type: exports.MutationType.patchFunction, + storeId: $id, + events: debuggerEvents, + }; + } + else { + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); + subscriptionMutation = { + type: exports.MutationType.patchObject, + payload: partialStateOrMutator, + storeId: $id, + events: debuggerEvents, + }; + } + const myListenerId = (activeListener = Symbol()); + vueDemi.nextTick().then(() => { + if (activeListener === myListenerId) { + isListening = true; + } + }); + isSyncListening = true; + // because we paused the watcher, we need to manually call the subscriptions + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); + } + const $reset = isOptionsStore + ? function $reset() { + const { state } = options; + const newState = state ? state() : {}; + // we use a patch to group all changes into one single subscription + this.$patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, newState); + }); + } + : /* istanbul ignore next */ + noop; + function $dispose() { + scope.stop(); + subscriptions = []; + actionSubscriptions = []; + pinia._s.delete($id); + } + /** + * Helper that wraps function so it can be tracked with $onAction + * @param fn - action to wrap + * @param name - name of the action + */ + const action = (fn, name = '') => { + if (ACTION_MARKER in fn) { + fn[ACTION_NAME] = name; + return fn; + } + const wrappedAction = function () { + setActivePinia(pinia); + const args = Array.from(arguments); + const afterCallbackList = []; + const onErrorCallbackList = []; + function after(callback) { + afterCallbackList.push(callback); + } + function onError(callback) { + onErrorCallbackList.push(callback); + } + // @ts-expect-error + triggerSubscriptions(actionSubscriptions, { + args, + name: wrappedAction[ACTION_NAME], + store, + after, + onError, + }); + let ret; + try { + ret = fn.apply(this && this.$id === $id ? this : store, args); + // handle sync errors + } + catch (error) { + triggerSubscriptions(onErrorCallbackList, error); + throw error; + } + if (ret instanceof Promise) { + return ret + .then((value) => { + triggerSubscriptions(afterCallbackList, value); + return value; + }) + .catch((error) => { + triggerSubscriptions(onErrorCallbackList, error); + return Promise.reject(error); + }); + } + // trigger after callbacks + triggerSubscriptions(afterCallbackList, ret); + return ret; + }; + wrappedAction[ACTION_MARKER] = true; + wrappedAction[ACTION_NAME] = name; // will be set later + // @ts-expect-error: we are intentionally limiting the returned type to just Fn + // because all the added properties are internals that are exposed through `$onAction()` only + return wrappedAction; + }; + const partialStore = { + _p: pinia, + // _s: scope, + $id, + $onAction: addSubscription.bind(null, actionSubscriptions), + $patch, + $reset, + $subscribe(callback, options = {}) { + const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher()); + const stopWatcher = scope.run(() => vueDemi.watch(() => pinia.state.value[$id], (state) => { + if (options.flush === 'sync' ? isSyncListening : isListening) { + callback({ + storeId: $id, + type: exports.MutationType.direct, + events: debuggerEvents, + }, state); + } + }, assign({}, $subscribeOptions, options))); + return removeSubscription; + }, + $dispose, + }; + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // start as non ready + partialStore._r = false; + } + const store = vueDemi.reactive(partialStore); + // store the partial store now so the setup of stores can instantiate each other before they are finished without + // creating infinite loops. + pinia._s.set($id, store); + const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext; + // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped + const setupStore = runWithContext(() => pinia._e.run(() => (scope = vueDemi.effectScope()).run(() => setup({ action })))); + // overwrite existing actions to support $onAction + for (const key in setupStore) { + const prop = setupStore[key]; + if ((vueDemi.isRef(prop) && !isComputed(prop)) || vueDemi.isReactive(prop)) { + // mark it as a piece of state to be serialized + if (!isOptionsStore) { + // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created + if (initialState && shouldHydrate(prop)) { + if (vueDemi.isRef(prop)) { + prop.value = initialState[key]; + } + else { + // probably a reactive object, lets recursively assign + // @ts-expect-error: prop is unknown + mergeReactiveObjects(prop, initialState[key]); + } + } + // transfer the ref to the pinia state to keep everything in sync + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(pinia.state.value[$id], key, prop); + } + else { + pinia.state.value[$id][key] = prop; + } + } + // action + } + else if (typeof prop === 'function') { + const actionValue = action(prop, key); + // this a hot module replacement store because the hotUpdate method needs + // to do it with the right context + /* istanbul ignore if */ + if (vueDemi.isVue2) { + vueDemi.set(setupStore, key, actionValue); + } + else { + // @ts-expect-error + setupStore[key] = actionValue; + } + // list actions so they can be used in plugins + // @ts-expect-error + optionsForPlugin.actions[key] = prop; + } + else ; + } + // add the state, getters, and action properties + /* istanbul ignore if */ + if (vueDemi.isVue2) { + Object.keys(setupStore).forEach((key) => { + vueDemi.set(store, key, setupStore[key]); + }); + } + else { + assign(store, setupStore); + // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object. + // Make `storeToRefs()` work with `reactive()` #799 + assign(vueDemi.toRaw(store), setupStore); + } + // use this instead of a computed with setter to be able to create it anywhere + // without linking the computed lifespan to wherever the store is first + // created. + Object.defineProperty(store, '$state', { + get: () => (pinia.state.value[$id]), + set: (state) => { + $patch(($state) => { + // @ts-expect-error: FIXME: shouldn't error? + assign($state, state); + }); + }, + }); + /* istanbul ignore if */ + if (vueDemi.isVue2) { + // mark the store as ready before plugins + store._r = true; + } + // apply all plugins + pinia._p.forEach((extender) => { + /* istanbul ignore else */ + { + assign(store, scope.run(() => extender({ + store: store, + app: pinia._a, + pinia, + options: optionsForPlugin, + }))); + } + }); + // only apply hydrate to option stores with an initial state in pinia + if (initialState && + isOptionsStore && + options.hydrate) { + options.hydrate(store.$state, initialState); + } + isListening = true; + isSyncListening = true; + return store; +} +// improves tree shaking +/*#__NO_SIDE_EFFECTS__*/ +function defineStore( +// TODO: add proper types from above +idOrOptions, setup, setupOptions) { + let id; + let options; + const isSetupStore = typeof setup === 'function'; + if (typeof idOrOptions === 'string') { + id = idOrOptions; + // the option store setup will contain the actual options in this case + options = isSetupStore ? setupOptions : setup; + } + else { + options = idOrOptions; + id = idOrOptions.id; + } + function useStore(pinia, hot) { + const hasContext = vueDemi.hasInjectionContext(); + pinia = + // in test mode, ignore the argument provided as we can always retrieve a + // pinia instance with getActivePinia() + ((process.env.NODE_ENV === 'test') && activePinia && activePinia._testing ? null : pinia) || + (hasContext ? vueDemi.inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + pinia = activePinia; + if (!pinia._s.has(id)) { + // creating the store registers it in `pinia._s` + if (isSetupStore) { + createSetupStore(id, setup, options, pinia); + } + else { + createOptionsStore(id, options, pinia); + } + } + const store = pinia._s.get(id); + // StoreGeneric cannot be casted towards Store + return store; + } + useStore.$id = id; + return useStore; +} + +let mapStoreSuffix = 'Store'; +/** + * Changes the suffix added by `mapStores()`. Can be set to an empty string. + * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization + * interface if you are using TypeScript. + * + * @param suffix - new suffix + */ +function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS +) { + mapStoreSuffix = suffix; +} +/** + * Allows using stores without the composition API (`setup()`) by generating an + * object to be spread in the `computed` field of a component. It accepts a list + * of store definitions. + * + * @example + * ```js + * export default { + * computed: { + * // other computed properties + * ...mapStores(useUserStore, useCartStore) + * }, + * + * created() { + * this.userStore // store with id "user" + * this.cartStore // store with id "cart" + * } + * } + * ``` + * + * @param stores - list of stores to map to an object + */ +function mapStores(...stores) { + return stores.reduce((reduced, useStore) => { + // @ts-expect-error: $id is added by defineStore + reduced[useStore.$id + mapStoreSuffix] = function () { + return useStore(this.$pinia); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + reduced[key] = function () { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function () { + const store = useStore(this.$pinia); + const storeKey = keysOrMapper[key]; + // for some reason TS is unable to infer the type of storeKey to be a + // function + return typeof storeKey === 'function' + ? storeKey.call(this, store) + : // @ts-expect-error: FIXME: should work? + store[storeKey]; + }; + return reduced; + }, {}); +} +/** + * Alias for `mapState()`. You should use `mapState()` instead. + * @deprecated use `mapState()` instead. + */ +const mapGetters = mapState; +/** + * Allows directly using actions from your store without using the composition + * API (`setup()`) by generating an object to be spread in the `methods` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapActions(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key](...args); + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-expect-error + reduced[key] = function (...args) { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]](...args); + }; + return reduced; + }, {}); +} +/** + * Allows using state and getters from one store without using the composition + * API (`setup()`) by generating an object to be spread in the `computed` field + * of a component. + * + * @param useStore - store to map from + * @param keysOrMapper - array or object + */ +function mapWritableState(useStore, keysOrMapper) { + return Array.isArray(keysOrMapper) + ? keysOrMapper.reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[key]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[key] = value); + }, + }; + return reduced; + }, {}) + : Object.keys(keysOrMapper).reduce((reduced, key) => { + // @ts-ignore + reduced[key] = { + get() { + // @ts-expect-error: FIXME: should work? + return useStore(this.$pinia)[keysOrMapper[key]]; + }, + set(value) { + // @ts-expect-error: FIXME: should work? + return (useStore(this.$pinia)[keysOrMapper[key]] = value); + }, + }; + return reduced; + }, {}); +} + +/** + * Creates an object of references with all the state, getters, and plugin-added + * state properties of the store. Similar to `toRefs()` but specifically + * designed for Pinia stores so methods and non reactive properties are + * completely ignored. + * + * @param store - store to extract the refs from + */ +function storeToRefs(store) { + // See https://github.com/vuejs/pinia/issues/852 + // It's easier to just use toRefs() even if it includes more stuff + if (vueDemi.isVue2) { + // @ts-expect-error: toRefs include methods and others + return vueDemi.toRefs(store); + } + else { + store = vueDemi.toRaw(store); + const refs = {}; + for (const key in store) { + const value = store[key]; + if (vueDemi.isRef(value) || vueDemi.isReactive(value)) { + // @ts-expect-error: the key is state or getter + refs[key] = + // --- + vueDemi.toRef(store, key); + } + } + return refs; + } +} + +/** + * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need + * this plugin if you are using Nuxt.js**. Use the `buildModule` instead: + * https://pinia.vuejs.org/ssr/nuxt.html. + * + * @example + * ```js + * import Vue from 'vue' + * import { PiniaVuePlugin, createPinia } from 'pinia' + * + * Vue.use(PiniaVuePlugin) + * const pinia = createPinia() + * + * new Vue({ + * el: '#app', + * // ... + * pinia, + * }) + * ``` + * + * @param _Vue - `Vue` imported from 'vue'. + */ +const PiniaVuePlugin = function (_Vue) { + // Equivalent of + // app.config.globalProperties.$pinia = pinia + _Vue.mixin({ + beforeCreate() { + const options = this.$options; + if (options.pinia) { + const pinia = options.pinia; + // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31 + /* istanbul ignore else */ + if (!this._provided) { + const provideCache = {}; + Object.defineProperty(this, '_provided', { + get: () => provideCache, + set: (v) => Object.assign(provideCache, v), + }); + } + this._provided[piniaSymbol] = pinia; + // propagate the pinia instance in an SSR friendly way + // avoid adding it to nuxt twice + /* istanbul ignore else */ + if (!this.$pinia) { + this.$pinia = pinia; + } + pinia._a = this; + if (IS_CLIENT) { + // this allows calling useStore() outside of a component setup after + // installing pinia's plugin + setActivePinia(pinia); + } + } + else if (!this.$pinia && options.parent && options.parent.$pinia) { + this.$pinia = options.parent.$pinia; + } + }, + destroyed() { + delete this._pStores; + }, + }); +}; + +exports.PiniaVuePlugin = PiniaVuePlugin; +exports.acceptHMRUpdate = acceptHMRUpdate; +exports.createPinia = createPinia; +exports.defineStore = defineStore; +exports.disposePinia = disposePinia; +exports.getActivePinia = getActivePinia; +exports.mapActions = mapActions; +exports.mapGetters = mapGetters; +exports.mapState = mapState; +exports.mapStores = mapStores; +exports.mapWritableState = mapWritableState; +exports.setActivePinia = setActivePinia; +exports.setMapStoreSuffix = setMapStoreSuffix; +exports.skipHydrate = skipHydrate; +exports.storeToRefs = storeToRefs; diff --git a/node_modules/pinia/index.cjs b/node_modules/pinia/index.cjs new file mode 100644 index 0000000..9fc1f5c --- /dev/null +++ b/node_modules/pinia/index.cjs @@ -0,0 +1,7 @@ +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./dist/pinia.prod.cjs') +} else { + module.exports = require('./dist/pinia.cjs') +} diff --git a/node_modules/pinia/index.js b/node_modules/pinia/index.js new file mode 100644 index 0000000..9fc1f5c --- /dev/null +++ b/node_modules/pinia/index.js @@ -0,0 +1,7 @@ +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./dist/pinia.prod.cjs') +} else { + module.exports = require('./dist/pinia.cjs') +} diff --git a/node_modules/pinia/package.json b/node_modules/pinia/package.json new file mode 100644 index 0000000..6eba2af --- /dev/null +++ b/node_modules/pinia/package.json @@ -0,0 +1,126 @@ +{ + "_from": "pinia", + "_id": "pinia@2.2.4", + "_inBundle": false, + "_integrity": "sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==", + "_location": "/pinia", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "pinia", + "name": "pinia", + "escapedName": "pinia", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.4.tgz", + "_shasum": "79b63b231a3da80968ab58f7721f575fe2c509ac", + "_spec": "pinia", + "_where": "D:\\work\\questionbankvue3", + "author": { + "name": "Eduardo San Martin Morote", + "email": "posva13@gmail.com" + }, + "bugs": { + "url": "https://github.com/vuejs/pinia/issues" + }, + "bundleDependencies": false, + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "deprecated": false, + "description": "Intuitive, type safe and flexible Store for Vue", + "devDependencies": { + "@microsoft/api-extractor": "7.47.9", + "@vue/test-utils": "^2.4.6" + }, + "exports": { + ".": { + "types": "./dist/pinia.d.ts", + "node": { + "import": { + "production": "./dist/pinia.prod.cjs", + "development": "./dist/pinia.mjs", + "default": "./dist/pinia.mjs" + }, + "require": { + "production": "./dist/pinia.prod.cjs", + "development": "./dist/pinia.cjs", + "default": "./index.js" + } + }, + "import": "./dist/pinia.mjs", + "require": "./index.js" + }, + "./package.json": "./package.json", + "./dist/*": "./dist/*" + }, + "files": [ + "dist/*.js", + "dist/*.mjs", + "dist/*.cjs", + "dist/pinia.d.ts", + "index.js", + "index.cjs", + "LICENSE", + "README.md" + ], + "funding": "https://github.com/sponsors/posva", + "homepage": "https://github.com/vuejs/pinia#readme", + "jsdelivr": "dist/pinia.iife.js", + "keywords": [ + "vue", + "vuex", + "store", + "pinia", + "piña", + "pigna", + "composition", + "api", + "setup", + "typed", + "typescript", + "ts", + "type", + "safe" + ], + "license": "MIT", + "main": "index.js", + "module": "dist/pinia.mjs", + "name": "pinia", + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "@vue/composition-api": { + "optional": true + } + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuejs/pinia.git" + }, + "scripts": { + "build": "rimraf dist && rollup -c ../../rollup.config.mjs --environment TARGET:pinia", + "build:dts": "api-extractor run --local --verbose && tail -n +3 ./src/globalExtensions.ts >> dist/pinia.d.ts", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l pinia -r 1", + "test": "pnpm run build && pnpm run build:dts && pnpm test:dts", + "test:dts": "tsc -p ./test-dts/tsconfig.json" + }, + "sideEffects": false, + "types": "dist/pinia.d.ts", + "unpkg": "dist/pinia.iife.js", + "version": "2.2.4" +} diff --git a/node_modules/vue-demi/LICENSE b/node_modules/vue-demi/LICENSE new file mode 100644 index 0000000..894ffaf --- /dev/null +++ b/node_modules/vue-demi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present, Anthony Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/vue-demi/README.md b/node_modules/vue-demi/README.md new file mode 100644 index 0000000..f941590 --- /dev/null +++ b/node_modules/vue-demi/README.md @@ -0,0 +1,229 @@ + + +

+Vue Demi (half in French) is a developing utility
allows you to write Universal Vue Libraries for Vue 2 & 3
+See more details in this blog post +

+ +
+ +
+ +## Strategies + +- `<=2.6`: exports from `vue` + `@vue/composition-api` with plugin auto installing. +- `2.7`: exports from `vue` (Composition API is built-in in Vue 2.7). +- `>=3.0`: exports from `vue`, with polyfill of Vue 2's `set` and `del` API. + +## Usage + +Install this as your plugin's dependency: + +```bash +npm i vue-demi +# or +yarn add vue-demi +# or +pnpm i vue-demi +``` + +Add `vue` and `@vue/composition-api` to your plugin's peer dependencies to specify what versions you support. + +```jsonc +{ + "dependencies": { + "vue-demi": "latest" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + }, + "devDependencies": { + "vue": "^3.0.0" // or "^2.6.0" base on your preferred working environment + }, +} +``` + +Import everything related to Vue from it, it will redirect to `vue@2` + `@vue/composition-api` or `vue@3` based on users' environments. + +```ts +import { ref, reactive, defineComponent } from 'vue-demi' +``` + +Publish your plugin and all is done! + +> When using with [Vite](https://vitejs.dev), you will need to opt-out the pre-bundling to get `vue-demi` work properly by +> ```js +> // vite.config.js +> export default defineConfig({ +> optimizeDeps: { +> exclude: ['vue-demi'] +> } +> }) +> ``` + +### Extra APIs + +`Vue Demi` provides extra APIs to help distinguish users' environments and to do some version-specific logic. + +### `isVue2` `isVue3` + +```ts +import { isVue2, isVue3 } from 'vue-demi' + +if (isVue2) { + // Vue 2 only +} else { + // Vue 3 only +} +``` + +### `Vue2` + +To avoid bringing in all the tree-shakable modules, we provide a `Vue2` export to support access to Vue 2's global API. (See [#41](https://github.com/vueuse/vue-demi/issues/41).) + +```ts +import { Vue2 } from 'vue-demi' + +if (Vue2) { + Vue2.config.ignoredElements.push('x-foo') +} +``` + +### `install()` + +Composition API in Vue 2 is provided as a plugin and needs to be installed on the Vue instance before using. Normally, `vue-demi` will try to install it automatically. For some usages where you might need to ensure the plugin gets installed correctly, the `install()` API is exposed to as a safe version of `Vue.use(CompositionAPI)`. `install()` in the Vue 3 environment will be an empty function (no-op). + +```ts +import { install } from 'vue-demi' + +install() +``` + +## CLI + +### Manually Switch Versions + +To explicitly switch the redirecting version, you can use these commands in your project's root. + +```bash +npx vue-demi-switch 2 +# or +npx vue-demi-switch 3 +``` + +### Package Aliasing + +If you would like to import `vue` under an alias, you can use the following command + +```bash +npx vue-demi-switch 2 vue2 +# or +npx vue-demi-switch 3 vue3 +``` + +Then `vue-demi` will redirect APIs from the alias name you specified, for example: + +```ts +import * as Vue from 'vue3' + +var isVue2 = false +var isVue3 = true +var Vue2 = undefined + +export * from 'vue3' +export { + Vue, + Vue2, + isVue2, + isVue3, +} +``` + +### Auto Fix + +If the `postinstall` hook doesn't get triggered or you have updated the Vue version, try to run the following command to resolve the redirecting. + +```bash +npx vue-demi-fix +``` + +### Isomorphic Testings + +You can support testing for both versions by adding npm alias in your dev dependencies. For example: + +```json +{ + "scripts": { + "test:2": "vue-demi-switch 2 vue2 && jest", + "test:3": "vue-demi-switch 3 && jest", + }, + "devDependencies": { + "vue": "^3.0.0", + "vue2": "npm:vue@2" + }, +} +``` + +or + +```json +{ + "scripts": { + "test:2": "vue-demi-switch 2 && jest", + "test:3": "vue-demi-switch 3 vue3 && jest", + }, + "devDependencies": { + "vue": "^2.6.0", + "vue3": "npm:vue@3" + }, +} +``` + +## Examples + +See [examples](./examples). + +## Who is using this? + +- [VueUse](https://github.com/vueuse/vueuse) - Collection of Composition API utils +- [@vue/apollo-composable](https://github.com/vuejs/vue-apollo/tree/v4/packages/vue-apollo-composable) - Apollo GraphQL functions for Vue Composition API +- [vuelidate](https://github.com/vuelidate/vuelidate) - Simple, lightweight model-based validation +- [vue-composition-test-utils](https://github.com/ariesjia/vue-composition-test-utils) - Simple vue composition api unit test utilities +- [vue-use-stripe](https://github.com/frandiox/vue-use-stripe) - Stripe Elements wrapper for Vue.js +- [@opd/g2plot-vue](https://github.com/open-data-plan/g2plot-vue) - G2plot for vue +- [vue-echarts](https://github.com/ecomfe/vue-echarts) - Vue.js component for Apache ECharts. +- [fluent-vue](https://github.com/Demivan/fluent-vue) - Vue.js integration for [Fluent.js](https://github.com/projectfluent/fluent.js) - JavaScript implementation of [Project Fluent](https://projectfluent.org) +- [vue-datatable-url-sync](https://github.com/socotecio/vue-datatable-url-sync) - Synchronize datatable options and filters with the url to keep user preference even after refresh or navigation +- [vue-insta-stories](https://github.com/UnevenSoftware/vue-insta-stories) - Instagram stories in your vue projects. +- [vue-tiny-validate](https://github.com/FrontLabsOfficial/vue-tiny-validate) - Tiny Vue Validate Composition +- [v-perfect-signature](https://github.com/wobsoriano/v-perfect-signature) - Pressure-sensitive signature drawing for Vue 2 and 3 +- [vue-winbox](https://github.com/wobsoriano/vue-winbox) - A wrapper component for WinBox.js that adds the ability to mount Vue components. +- [vue-word-highlighter](https://github.com/kawamataryo/vue-word-highlighter) - The word highlighter library for Vue 2 and Vue 3 +- [vue-chart-3](https://github.com/victorgarciaesgi/vue-chart-3) - Vue.js component for Chart.js +- [json-editor-vue](https://github.com/cloydlau/json-editor-vue) - JSON editor & viewer for Vue 2 and 3. +- [kidar-echarts](https://github.com/kidarjs/kidar-echarts) - A simpler echarts component for Vue 2 and 3. +- [vue3-sketch-ruler](https://github.com/kakajun/vue3-sketch-ruler) - The zoom operation used for page presentation for Vue 2 and 3( Replace render function with template ) +- [vue-rough-notation](https://github.com/Leecason/vue-rough-notation) - RoughNotation wrapper component for Vue 2 and 3. +- [vue-request](https://github.com/AttoJS/vue-request) - Vue composition API for data fetching, supports SWR, polling, error retry, cache request, pagination, etc. +- [vue3-lazyload](https://github.com/murongg/vue3-lazyload) - A vue3.x image lazyload plugin. +- [vue-codemirror6](https://github.com/logue/vue-codemirror6) - CodeMirror6 component for Vue2 and 3. +- [@tanstack/vue-query](https://github.com/TanStack/query) - TanStack Query for Vue. +> open a PR to add your library ;) + +## Underhood + +See [the blog post](https://antfu.me/posts/make-libraries-working-with-vue-2-and-3/#-introducing-vue-demi). + +## License + +MIT License © 2020 [Anthony Fu](https://github.com/antfu) diff --git a/node_modules/vue-demi/bin/vue-demi-fix.js b/node_modules/vue-demi/bin/vue-demi-fix.js new file mode 100644 index 0000000..684a621 --- /dev/null +++ b/node_modules/vue-demi/bin/vue-demi-fix.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +'use strict' +require('../scripts/postinstall') diff --git a/node_modules/vue-demi/bin/vue-demi-switch.js b/node_modules/vue-demi/bin/vue-demi-switch.js new file mode 100644 index 0000000..360eada --- /dev/null +++ b/node_modules/vue-demi/bin/vue-demi-switch.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +'use strict' +require('../scripts/switch-cli') diff --git a/node_modules/vue-demi/lib/index.cjs b/node_modules/vue-demi/lib/index.cjs new file mode 100644 index 0000000..8197f90 --- /dev/null +++ b/node_modules/vue-demi/lib/index.cjs @@ -0,0 +1,29 @@ +var Vue = require('vue') + +Object.keys(Vue).forEach(function(key) { + exports[key] = Vue[key] +}) + +exports.set = function(target, key, val) { + if (Array.isArray(target)) { + target.length = Math.max(target.length, key) + target.splice(key, 1, val) + return val + } + target[key] = val + return val +} + +exports.del = function(target, key) { + if (Array.isArray(target)) { + target.splice(key, 1) + return + } + delete target[key] +} + +exports.Vue = Vue +exports.Vue2 = undefined +exports.isVue2 = false +exports.isVue3 = true +exports.install = function(){} diff --git a/node_modules/vue-demi/lib/index.d.ts b/node_modules/vue-demi/lib/index.d.ts new file mode 100644 index 0000000..897b4c5 --- /dev/null +++ b/node_modules/vue-demi/lib/index.d.ts @@ -0,0 +1,22 @@ +import * as Vue from 'vue' +declare const isVue2: boolean +declare const isVue3: boolean +declare const Vue2: any +declare const install: (vue?: any) => void +/** + * @deprecated To avoid bringing in all the tree-shakable modules, this API has been deprecated. Use `Vue2` or named exports instead. + * Refer to https://github.com/vueuse/vue-demi/issues/41 + */ +declare const V: typeof Vue + +export function set(target: any, key: any, val: T): T +export function del(target: any, key: any): void + +export * from 'vue' +export { + V as Vue, + Vue2, + isVue2, + isVue3, + install, +} diff --git a/node_modules/vue-demi/lib/index.iife.js b/node_modules/vue-demi/lib/index.iife.js new file mode 100644 index 0000000..fd017b7 --- /dev/null +++ b/node_modules/vue-demi/lib/index.iife.js @@ -0,0 +1,126 @@ +var _VueDemiGlobal = typeof globalThis !== 'undefined' + ? globalThis + : typeof global !== 'undefined' + ? global + : typeof self !== 'undefined' + ? self + : this +var VueDemi = (function (VueDemi, Vue, VueCompositionAPI) { + if (VueDemi.install) { + return VueDemi + } + if (!Vue) { + console.error('[vue-demi] no Vue instance found, please be sure to import `vue` before `vue-demi`.') + return VueDemi + } + + // Vue 2.7 + if (Vue.version.slice(0, 4) === '2.7.') { + for (var key in Vue) { + VueDemi[key] = Vue[key] + } + VueDemi.isVue2 = true + VueDemi.isVue3 = false + VueDemi.install = function () {} + VueDemi.Vue = Vue + VueDemi.Vue2 = Vue + VueDemi.version = Vue.version + VueDemi.warn = Vue.util.warn + VueDemi.hasInjectionContext = function() { + return !!VueDemi.getCurrentInstance() + } + function createApp(rootComponent, rootProps) { + var vm + var provide = {} + var app = { + config: Vue.config, + use: Vue.use.bind(Vue), + mixin: Vue.mixin.bind(Vue), + component: Vue.component.bind(Vue), + provide: function (key, value) { + provide[key] = value + return this + }, + directive: function (name, dir) { + if (dir) { + Vue.directive(name, dir) + return app + } else { + return Vue.directive(name) + } + }, + mount: function (el, hydrating) { + if (!vm) { + vm = new Vue(Object.assign({ propsData: rootProps }, rootComponent, { provide: Object.assign(provide, rootComponent.provide) })) + vm.$mount(el, hydrating) + return vm + } else { + return vm + } + }, + unmount: function () { + if (vm) { + vm.$destroy() + vm = undefined + } + }, + } + return app + } + VueDemi.createApp = createApp + } + // Vue 2.6.x + else if (Vue.version.slice(0, 2) === '2.') { + if (VueCompositionAPI) { + for (var key in VueCompositionAPI) { + VueDemi[key] = VueCompositionAPI[key] + } + VueDemi.isVue2 = true + VueDemi.isVue3 = false + VueDemi.install = function () {} + VueDemi.Vue = Vue + VueDemi.Vue2 = Vue + VueDemi.version = Vue.version + VueDemi.hasInjectionContext = function() { + return !!VueDemi.getCurrentInstance() + } + } else { + console.error('[vue-demi] no VueCompositionAPI instance found, please be sure to import `@vue/composition-api` before `vue-demi`.') + } + } + // Vue 3 + else if (Vue.version.slice(0, 2) === '3.') { + for (var key in Vue) { + VueDemi[key] = Vue[key] + } + VueDemi.isVue2 = false + VueDemi.isVue3 = true + VueDemi.install = function () {} + VueDemi.Vue = Vue + VueDemi.Vue2 = undefined + VueDemi.version = Vue.version + VueDemi.set = function (target, key, val) { + if (Array.isArray(target)) { + target.length = Math.max(target.length, key) + target.splice(key, 1, val) + return val + } + target[key] = val + return val + } + VueDemi.del = function (target, key) { + if (Array.isArray(target)) { + target.splice(key, 1) + return + } + delete target[key] + } + } else { + console.error('[vue-demi] Vue version ' + Vue.version + ' is unsupported.') + } + return VueDemi +})( + (_VueDemiGlobal.VueDemi = _VueDemiGlobal.VueDemi || (typeof VueDemi !== 'undefined' ? VueDemi : {})), + _VueDemiGlobal.Vue || (typeof Vue !== 'undefined' ? Vue : undefined), + _VueDemiGlobal.VueCompositionAPI || (typeof VueCompositionAPI !== 'undefined' ? VueCompositionAPI : undefined) +); diff --git a/node_modules/vue-demi/lib/index.mjs b/node_modules/vue-demi/lib/index.mjs new file mode 100644 index 0000000..be3a96d --- /dev/null +++ b/node_modules/vue-demi/lib/index.mjs @@ -0,0 +1,34 @@ +import * as Vue from 'vue' + +var isVue2 = false +var isVue3 = true +var Vue2 = undefined + +function install() {} + +export function set(target, key, val) { + if (Array.isArray(target)) { + target.length = Math.max(target.length, key) + target.splice(key, 1, val) + return val + } + target[key] = val + return val +} + +export function del(target, key) { + if (Array.isArray(target)) { + target.splice(key, 1) + return + } + delete target[key] +} + +export * from 'vue' +export { + Vue, + Vue2, + isVue2, + isVue3, + install, +} diff --git a/node_modules/vue-demi/lib/v2.7/index.cjs b/node_modules/vue-demi/lib/v2.7/index.cjs new file mode 100644 index 0000000..8fa3b68 --- /dev/null +++ b/node_modules/vue-demi/lib/v2.7/index.cjs @@ -0,0 +1,60 @@ +var VueModule = require('vue') + +// get the real Vue https://github.com/vueuse/vue-demi/issues/192 +var Vue = VueModule.default || VueModule + +exports.Vue = Vue +exports.Vue2 = Vue +exports.isVue2 = true +exports.isVue3 = false +exports.install = function () {} +exports.warn = Vue.util.warn + +// createApp polyfill +exports.createApp = function (rootComponent, rootProps) { + var vm + var provide = {} + var app = { + config: Vue.config, + use: Vue.use.bind(Vue), + mixin: Vue.mixin.bind(Vue), + component: Vue.component.bind(Vue), + provide: function (key, value) { + provide[key] = value + return this + }, + directive: function (name, dir) { + if (dir) { + Vue.directive(name, dir) + return app + } else { + return Vue.directive(name) + } + }, + mount: function (el, hydrating) { + if (!vm) { + vm = new Vue(Object.assign({ propsData: rootProps }, rootComponent, { provide: Object.assign(provide, rootComponent.provide) })) + vm.$mount(el, hydrating) + return vm + } else { + return vm + } + }, + unmount: function () { + if (vm) { + vm.$destroy() + vm = undefined + } + }, + } + return app +} + +Object.keys(VueModule).forEach(function (key) { + exports[key] = VueModule[key] +}) + +// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance() +exports.hasInjectionContext = function() { + return !!VueModule.getCurrentInstance() +} \ No newline at end of file diff --git a/node_modules/vue-demi/lib/v2.7/index.d.ts b/node_modules/vue-demi/lib/v2.7/index.d.ts new file mode 100644 index 0000000..827c7b2 --- /dev/null +++ b/node_modules/vue-demi/lib/v2.7/index.d.ts @@ -0,0 +1,38 @@ +import Vue from 'vue' +import type { PluginFunction, PluginObject, VueConstructor, Directive, InjectionKey, Component } from 'vue' + +declare const isVue2: boolean +declare const isVue3: boolean +declare const Vue2: typeof Vue | undefined +declare const version: string +declare const install: (vue?: typeof Vue) => void +export declare function warn(msg: string, vm?: Component | null): void +/** + * @deprecated To avoid bringing in all the tree-shakable modules, this API has been deprecated. Use `Vue2` or named exports instead. + * Refer to https://github.com/vueuse/vue-demi/issues/41 + */ +declare const V: typeof Vue + +// accept no generic because Vue 3 doesn't accept any +// https://github.com/vuejs/vue-next/pull/2758/ +export declare type Plugin = PluginObject | PluginFunction +export type { VNode } from 'vue' +export * from 'vue' +export { V as Vue, Vue2, isVue2, isVue3, version, install } + +// #region createApp polyfill +export interface App { + config: VueConstructor['config'] + use: VueConstructor['use'] + mixin: VueConstructor['mixin'] + component: VueConstructor['component'] + directive(name: string): Directive | undefined + directive(name: string, directive: Directive): this + provide(key: InjectionKey | string, value: T): this + mount: Vue['$mount'] + unmount: Vue['$destroy'] +} +export declare function createApp(rootComponent: any, rootProps?: any): App +// #endregion + +export declare function hasInjectionContext(): boolean diff --git a/node_modules/vue-demi/lib/v2.7/index.mjs b/node_modules/vue-demi/lib/v2.7/index.mjs new file mode 100644 index 0000000..e575059 --- /dev/null +++ b/node_modules/vue-demi/lib/v2.7/index.mjs @@ -0,0 +1,80 @@ +import Vue from 'vue' +import { getCurrentInstance } from 'vue' + +var isVue2 = true +var isVue3 = false +var Vue2 = Vue +var warn = Vue.util.warn + +function install() {} + +// createApp polyfill +export function createApp(rootComponent, rootProps) { + var vm + var provide = {} + var app = { + config: Vue.config, + use: Vue.use.bind(Vue), + mixin: Vue.mixin.bind(Vue), + component: Vue.component.bind(Vue), + provide: function (key, value) { + provide[key] = value + return this + }, + directive: function (name, dir) { + if (dir) { + Vue.directive(name, dir) + return app + } else { + return Vue.directive(name) + } + }, + mount: function (el, hydrating) { + if (!vm) { + vm = new Vue(Object.assign({ propsData: rootProps }, rootComponent, { provide: Object.assign(provide, rootComponent.provide) })) + vm.$mount(el, hydrating) + return vm + } else { + return vm + } + }, + unmount: function () { + if (vm) { + vm.$destroy() + vm = undefined + } + }, + } + return app +} + +export { + Vue, + Vue2, + isVue2, + isVue3, + install, + warn +} + +// Vue 3 components mock +function createMockComponent(name) { + return { + setup() { + throw new Error('[vue-demi] ' + name + ' is not supported in Vue 2. It\'s provided to avoid compiler errors.') + } + } +} +export var Fragment = /*#__PURE__*/ createMockComponent('Fragment') +export var Transition = /*#__PURE__*/ createMockComponent('Transition') +export var TransitionGroup = /*#__PURE__*/ createMockComponent('TransitionGroup') +export var Teleport = /*#__PURE__*/ createMockComponent('Teleport') +export var Suspense = /*#__PURE__*/ createMockComponent('Suspense') +export var KeepAlive = /*#__PURE__*/ createMockComponent('KeepAlive') + +export * from 'vue' + +// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance() +export function hasInjectionContext() { + return !!getCurrentInstance() +} diff --git a/node_modules/vue-demi/lib/v2/index.cjs b/node_modules/vue-demi/lib/v2/index.cjs new file mode 100644 index 0000000..c3d298e --- /dev/null +++ b/node_modules/vue-demi/lib/v2/index.cjs @@ -0,0 +1,34 @@ +var Vue = require('vue') +var VueCompositionAPI = require('@vue/composition-api') + +function install(_vue) { + var vueLib = _vue || Vue + if (vueLib && 'default' in vueLib) { + vueLib = vueLib.default + } + + if (vueLib && !vueLib['__composition_api_installed__']) { + if (VueCompositionAPI && 'default' in VueCompositionAPI) + vueLib.use(VueCompositionAPI.default) + else if (VueCompositionAPI) + vueLib.use(VueCompositionAPI) + } +} + +install(Vue) + +Object.keys(VueCompositionAPI).forEach(function(key) { + exports[key] = VueCompositionAPI[key] +}) + +exports.Vue = Vue +exports.Vue2 = Vue +exports.isVue2 = true +exports.isVue3 = false +exports.install = install +exports.version = Vue.version + +// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance() +exports.hasInjectionContext = function () { + return !!VueCompositionAPI.getCurrentInstance() +} diff --git a/node_modules/vue-demi/lib/v2/index.d.ts b/node_modules/vue-demi/lib/v2/index.d.ts new file mode 100644 index 0000000..bbdcbc5 --- /dev/null +++ b/node_modules/vue-demi/lib/v2/index.d.ts @@ -0,0 +1,33 @@ +import Vue from 'vue' +import type { PluginFunction, PluginObject } from 'vue' +declare const isVue2: boolean +declare const isVue3: boolean +declare const Vue2: typeof Vue | undefined +declare const version: string +declare const install: (vue?: typeof Vue) => void +/** + * @deprecated To avoid bringing in all the tree-shakable modules, this API has been deprecated. Use `Vue2` or named exports instead. + * Refer to https://github.com/vueuse/vue-demi/issues/41 + */ +declare const V: typeof Vue + +/** + * DebuggerEvent is a Vue 3 development only feature. This type cannot exist in Vue 2. + */ +export declare type DebuggerEvent = never + +// accept no generic because Vue 3 doesn't accept any +// https://github.com/vuejs/vue-next/pull/2758/ +export declare type Plugin = PluginObject | PluginFunction +export type { VNode } from 'vue' +export * from '@vue/composition-api' +export { + V as Vue, + Vue2, + isVue2, + isVue3, + version, + install, +} + +export declare function hasInjectionContext(): boolean diff --git a/node_modules/vue-demi/lib/v2/index.mjs b/node_modules/vue-demi/lib/v2/index.mjs new file mode 100644 index 0000000..2c18122 --- /dev/null +++ b/node_modules/vue-demi/lib/v2/index.mjs @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueCompositionAPI, { getCurrentInstance } from '@vue/composition-api/dist/vue-composition-api.mjs' + +function install(_vue) { + _vue = _vue || Vue + if (_vue && !_vue['__composition_api_installed__']) + _vue.use(VueCompositionAPI) +} + +install(Vue) + +var isVue2 = true +var isVue3 = false +var Vue2 = Vue +var version = Vue.version + +/**VCA-EXPORTS**/ +export * from '@vue/composition-api/dist/vue-composition-api.mjs' +/**VCA-EXPORTS**/ + +export { + Vue, + Vue2, + isVue2, + isVue3, + version, + install, +} + + +// Vue 3 components mock +function createMockComponent(name) { + return { + setup() { + throw new Error('[vue-demi] ' + name + ' is not supported in Vue 2. It\'s provided to avoid compiler errors.') + } + } +} +export var Fragment = /*#__PURE__*/ createMockComponent('Fragment') +export var Transition = /*#__PURE__*/ createMockComponent('Transition') +export var TransitionGroup = /*#__PURE__*/ createMockComponent('TransitionGroup') +export var Teleport = /*#__PURE__*/ createMockComponent('Teleport') +export var Suspense = /*#__PURE__*/ createMockComponent('Suspense') +export var KeepAlive = /*#__PURE__*/ createMockComponent('KeepAlive') + +// Not implemented https://github.com/vuejs/core/pull/8111, falls back to getCurrentInstance() +export function hasInjectionContext() { + return !!getCurrentInstance() +} diff --git a/node_modules/vue-demi/lib/v3/index.cjs b/node_modules/vue-demi/lib/v3/index.cjs new file mode 100644 index 0000000..8197f90 --- /dev/null +++ b/node_modules/vue-demi/lib/v3/index.cjs @@ -0,0 +1,29 @@ +var Vue = require('vue') + +Object.keys(Vue).forEach(function(key) { + exports[key] = Vue[key] +}) + +exports.set = function(target, key, val) { + if (Array.isArray(target)) { + target.length = Math.max(target.length, key) + target.splice(key, 1, val) + return val + } + target[key] = val + return val +} + +exports.del = function(target, key) { + if (Array.isArray(target)) { + target.splice(key, 1) + return + } + delete target[key] +} + +exports.Vue = Vue +exports.Vue2 = undefined +exports.isVue2 = false +exports.isVue3 = true +exports.install = function(){} diff --git a/node_modules/vue-demi/lib/v3/index.d.ts b/node_modules/vue-demi/lib/v3/index.d.ts new file mode 100644 index 0000000..897b4c5 --- /dev/null +++ b/node_modules/vue-demi/lib/v3/index.d.ts @@ -0,0 +1,22 @@ +import * as Vue from 'vue' +declare const isVue2: boolean +declare const isVue3: boolean +declare const Vue2: any +declare const install: (vue?: any) => void +/** + * @deprecated To avoid bringing in all the tree-shakable modules, this API has been deprecated. Use `Vue2` or named exports instead. + * Refer to https://github.com/vueuse/vue-demi/issues/41 + */ +declare const V: typeof Vue + +export function set(target: any, key: any, val: T): T +export function del(target: any, key: any): void + +export * from 'vue' +export { + V as Vue, + Vue2, + isVue2, + isVue3, + install, +} diff --git a/node_modules/vue-demi/lib/v3/index.mjs b/node_modules/vue-demi/lib/v3/index.mjs new file mode 100644 index 0000000..be3a96d --- /dev/null +++ b/node_modules/vue-demi/lib/v3/index.mjs @@ -0,0 +1,34 @@ +import * as Vue from 'vue' + +var isVue2 = false +var isVue3 = true +var Vue2 = undefined + +function install() {} + +export function set(target, key, val) { + if (Array.isArray(target)) { + target.length = Math.max(target.length, key) + target.splice(key, 1, val) + return val + } + target[key] = val + return val +} + +export function del(target, key) { + if (Array.isArray(target)) { + target.splice(key, 1) + return + } + delete target[key] +} + +export * from 'vue' +export { + Vue, + Vue2, + isVue2, + isVue3, + install, +} diff --git a/node_modules/vue-demi/package.json b/node_modules/vue-demi/package.json new file mode 100644 index 0000000..684b91e --- /dev/null +++ b/node_modules/vue-demi/package.json @@ -0,0 +1,83 @@ +{ + "_from": "vue-demi@^0.14.10", + "_id": "vue-demi@0.14.10", + "_inBundle": false, + "_integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "_location": "/vue-demi", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "vue-demi@^0.14.10", + "name": "vue-demi", + "escapedName": "vue-demi", + "rawSpec": "^0.14.10", + "saveSpec": null, + "fetchSpec": "^0.14.10" + }, + "_requiredBy": [ + "/pinia" + ], + "_resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "_shasum": "afc78de3d6f9e11bf78c55e8510ee12814522f04", + "_spec": "vue-demi@^0.14.10", + "_where": "D:\\work\\questionbankvue3\\node_modules\\pinia", + "author": { + "name": "Anthony Fu", + "email": "anthonyfu117@hotmail.com" + }, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "bugs": { + "url": "https://github.com/antfu/vue-demi/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "


npm

", + "engines": { + "node": ">=12" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "require": "./lib/index.cjs", + "import": "./lib/index.mjs", + "browser": "./lib/index.mjs" + }, + "./*": "./*" + }, + "files": [ + "lib", + "bin", + "scripts" + ], + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/antfu/vue-demi#readme", + "jsdelivr": "lib/index.iife.js", + "license": "MIT", + "main": "lib/index.cjs", + "module": "lib/index.mjs", + "name": "vue-demi", + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + }, + "repository": { + "type": "git", + "url": "git+https://github.com/antfu/vue-demi.git" + }, + "scripts": { + "postinstall": "node -e \"try{require('./scripts/postinstall.js')}catch(e){}\"", + "release": "npx bumpp --tag --commit --push && npm publish" + }, + "types": "lib/index.d.ts", + "unpkg": "lib/index.iife.js", + "version": "0.14.10" +} diff --git a/node_modules/vue-demi/scripts/postinstall.js b/node_modules/vue-demi/scripts/postinstall.js new file mode 100644 index 0000000..7baf14e --- /dev/null +++ b/node_modules/vue-demi/scripts/postinstall.js @@ -0,0 +1,19 @@ +const { switchVersion, loadModule } = require('./utils') + +const Vue = loadModule('vue') + +if (!Vue || typeof Vue.version !== 'string') { + console.warn('[vue-demi] Vue is not found. Please run "npm install vue" to install.') +} +else if (Vue.version.startsWith('2.7.')) { + switchVersion(2.7) +} +else if (Vue.version.startsWith('2.')) { + switchVersion(2) +} +else if (Vue.version.startsWith('3.')) { + switchVersion(3) +} +else { + console.warn(`[vue-demi] Vue version v${Vue.version} is not supported.`) +} diff --git a/node_modules/vue-demi/scripts/switch-cli.js b/node_modules/vue-demi/scripts/switch-cli.js new file mode 100644 index 0000000..3c104ca --- /dev/null +++ b/node_modules/vue-demi/scripts/switch-cli.js @@ -0,0 +1,18 @@ +const { switchVersion } = require('./utils') + +const version = process.argv[2] +const vueEntry = process.argv[3] || 'vue' + +if (version === '2.7') { + switchVersion(2.7, vueEntry) + console.log(`[vue-demi] Switched for Vue 2.7 (entry: "${vueEntry}")`) +} else if (version === '2') { + switchVersion(2, vueEntry) + console.log(`[vue-demi] Switched for Vue 2 (entry: "${vueEntry}")`) +} else if (version === '3') { + switchVersion(3, vueEntry) + console.log(`[vue-demi] Switched for Vue 3 (entry: "${vueEntry}")`) +} else { + console.warn(`[vue-demi] expecting version "2" or "2.7" or "3" but got "${version}"`) + process.exit(1) +} diff --git a/node_modules/vue-demi/scripts/utils.js b/node_modules/vue-demi/scripts/utils.js new file mode 100644 index 0000000..eff99f0 --- /dev/null +++ b/node_modules/vue-demi/scripts/utils.js @@ -0,0 +1,62 @@ +const fs = require('fs') +const path = require('path') + +const dir = path.resolve(__dirname, '..', 'lib') + +function loadModule(name) { + try { + return require(name) + } catch (e) { + return undefined + } +} + +function copy(name, version, vue) { + vue = vue || 'vue' + const src = path.join(dir, `v${version}`, name) + const dest = path.join(dir, name) + let content = fs.readFileSync(src, 'utf-8') + content = content.replace(/'vue'/g, `'${vue}'`) + // unlink for pnpm, #92 + try { + fs.unlinkSync(dest) + } catch (error) { } + fs.writeFileSync(dest, content, 'utf-8') +} + +function updateVue2API() { + const ignoreList = ['version', 'default'] + const VCA = loadModule('@vue/composition-api') + if (!VCA) { + console.warn('[vue-demi] Composition API plugin is not found. Please run "npm install @vue/composition-api" to install.') + return + } + + const exports = Object.keys(VCA).filter(i => !ignoreList.includes(i)) + + const esmPath = path.join(dir, 'index.mjs') + let content = fs.readFileSync(esmPath, 'utf-8') + + content = content.replace( + /\/\*\*VCA-EXPORTS\*\*\/[\s\S]+\/\*\*VCA-EXPORTS\*\*\//m, +`/**VCA-EXPORTS**/ +export { ${exports.join(', ')} } from '@vue/composition-api/dist/vue-composition-api.mjs' +/**VCA-EXPORTS**/` + ) + + fs.writeFileSync(esmPath, content, 'utf-8') + +} + +function switchVersion(version, vue) { + copy('index.cjs', version, vue) + copy('index.mjs', version, vue) + copy('index.d.ts', version, vue) + + if (version === 2) + updateVue2API() +} + + +module.exports.loadModule = loadModule +module.exports.switchVersion = switchVersion diff --git a/package-lock.json b/package-lock.json index 29b32fe..6a44247 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,30 @@ { "name": "questionbankvue3", "version": "1.0.0", - "lockfileVersion": 3, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "questionbankvue3", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "js-md5": "^0.8.3" + "dependencies": { + "@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "js-md5": { + "version": "0.8.3" + }, + "pinia": { + "version": "2.2.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.4.tgz", + "integrity": "sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==", + "requires": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" } }, - "node_modules/js-md5": { - "version": "0.8.3", - "resolved": "https://registry.npmmirror.com/js-md5/-/js-md5-0.8.3.tgz", - "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==" + "vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==" } } } diff --git a/package.json b/package.json index ec6b7ff..c16c557 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "questionbankvue3", "version": "1.0.0", - "description": "", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -10,6 +9,13 @@ "author": "", "license": "ISC", "dependencies": { - "js-md5": "^0.8.3" - } + "js-md5": "^0.8.3", + "pinia": "^2.2.4" + }, + "devDependencies": {}, + "repository": { + "type": "git", + "url": "http://git.jaxc.cn/fangwenqiao/questionbankvue3.git" + }, + "description": "" } diff --git a/pages/exercises/brushQuestions/brushQuestions.vue b/pages/exercises/brushQuestions/brushQuestions.vue index 5c6d44a..51013df 100644 --- a/pages/exercises/brushQuestions/brushQuestions.vue +++ b/pages/exercises/brushQuestions/brushQuestions.vue @@ -16,40 +16,57 @@ {{ questionBank.title}} - - - - - {{item.key}} {{item.text}} + + + + + + {{item.key}} {{item.text}} + + + + + + + {{item.key}} {{item.text}} + + - - 正确答案是:B - 您的答案是:B + + 正确答案是 + {{ questionBank.answer=='false'?'错误':'正确' }} + {{ questionBank.answer }} + + 您的答案是 + {{ curOption.answer=='false'?'错误':'正确' }} + {{ curOption.answer }} + - 上一题 - 下一题 + + + - + 题目解析 - 解析解析解析解析解析解析解析解析解析解析解析解析 + {{ questionBank.resolving }} - 确定 + - 1 + {{ yesNum }} 答对 - 1 + {{ noNum }} 答错 - 1 + {{currentIndex}}/{{quesIdList.length}} 题目 @@ -58,7 +75,7 @@ 反馈 - + @@ -73,11 +90,37 @@ + + + 答题卡 + + + + {{ yesNum }} + 答对 + + + {{ noNum }} + 答错 + + + {{currentIndex}}/{{quesIdList.length}} + 题目 + + + + + + {{ index+1 }} + + + + @@ -176,39 +309,40 @@ height: 98rpx; background: #FFFFFF; border-top: 1rpx solid #F4F4F4; - .ul { + + } + .ul { + display: flex; + justify-content: space-between; + height: 100%; + align-items: center; + .li { + width: 16.6%; display: flex; - justify-content: space-between; - height: 100%; + flex-direction: column; align-items: center; - .li { - width: 16.6%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - .icon { - font-size: 30rpx; - height: 30rpx; - line-height: 30rpx; - image { - display: block; - margin-top: 4rpx; - width: 26rpx; - height: 26rpx; - } - } - .text { - font-weight: 500; - font-size: 24rpx; - color: #999999; - margin-top: 10rpx; + justify-content: center; + .icon { + font-size: 30rpx; + height: 30rpx; + line-height: 30rpx; + image { + display: block; + margin-top: 4rpx; + width: 26rpx; + height: 26rpx; } } + .text { + font-weight: 500; + font-size: 24rpx; + color: #999999; + margin-top: 10rpx; + } } } .content { - padding: 120rpx 0 0 0; + padding: 120rpx 0; min-height: 100vh; .u-nav-slot { width: 306rpx; @@ -231,7 +365,7 @@ } } .btn_row { - padding: 100rpx 0 50rpx 0; + padding: 60rpx 0 30rpx 0; .btn { width: 44%; height: 76rpx; @@ -241,6 +375,9 @@ text-align: center; font-size: 28rpx; color: $themC; + &.disable { + opacity: 0.4; + } &.bg { background: #3776FF; border-radius: 38rpx; @@ -290,17 +427,20 @@ } } - .answer { + .answerCss { height: 90rpx; background: #F4F4F4; - line-height: 90rpx; - padding: 0 30rpx; + padding: 30rpx; margin-top: 20rpx; + justify-content: space-around; .ans { - margin-right: 60rpx; + font-size: 30rpx; text { - color: red; + &.red { + color: red; + } } + } } @@ -331,4 +471,46 @@ } } } + +.popupCon { + width: 100%; + height: calc(100vh - 200rpx); + .h3 { + height: 88rpx; + border-bottom: 1px solid #F4F4F4; + line-height: 88rpx; + font-size: 30rpx; + padding: 0rpx 0 0 30rpx; + } + .ulRow { + height: 100rpx; + padding: 30rpx 0; + } + .ul2 { + display: flex; + flex-wrap: wrap; + padding: 30rpx 10rpx; + height: calc(100vh - 388rpx); + overflow-y: auto; + .li2 { + width: 16.6%; + margin-bottom: 20rpx; + .num { + width: 100rpx; + height: 100rpx; + border-radius: 50%; + margin: auto; + background: #F6F7FA; + font-size: 32rpx; + line-height: 100rpx; + text-align: center; + &.active { + border: 1px solid #63C168; + background: rgba(99,193,104,0.1); + color: #63C168; + } + } + } + } +} \ No newline at end of file diff --git a/pages/index/index.vue b/pages/index/index.vue index 3cc8fff..345f22c 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -22,6 +22,11 @@

+ +
+
npm +