You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

805 lines
27 KiB

2 months ago
  1. /*!
  2. * pinia v2.1.7
  3. * (c) 2023 Eduardo San Martin Morote
  4. * @license MIT
  5. */
  6. 'use strict';
  7. var vueDemi = require('vue-demi');
  8. /**
  9. * setActivePinia must be called to handle SSR at the top of functions like
  10. * `fetch`, `setup`, `serverPrefetch` and others
  11. */
  12. let activePinia;
  13. /**
  14. * Sets or unsets the active pinia. Used in SSR and internally when calling
  15. * actions and getters
  16. *
  17. * @param pinia - Pinia instance
  18. */
  19. // @ts-expect-error: cannot constrain the type of the return
  20. const setActivePinia = (pinia) => (activePinia = pinia);
  21. /**
  22. * Get the currently active pinia if there is any.
  23. */
  24. const getActivePinia = () => (vueDemi.hasInjectionContext() && vueDemi.inject(piniaSymbol)) || activePinia;
  25. const piniaSymbol = (/* istanbul ignore next */ Symbol());
  26. function isPlainObject(
  27. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  28. o) {
  29. return (o &&
  30. typeof o === 'object' &&
  31. Object.prototype.toString.call(o) === '[object Object]' &&
  32. typeof o.toJSON !== 'function');
  33. }
  34. // type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }
  35. // TODO: can we change these to numbers?
  36. /**
  37. * Possible types for SubscriptionCallback
  38. */
  39. exports.MutationType = void 0;
  40. (function (MutationType) {
  41. /**
  42. * Direct mutation of the state:
  43. *
  44. * - `store.name = 'new name'`
  45. * - `store.$state.name = 'new name'`
  46. * - `store.list.push('new item')`
  47. */
  48. MutationType["direct"] = "direct";
  49. /**
  50. * Mutated the state with `$patch` and an object
  51. *
  52. * - `store.$patch({ name: 'newName' })`
  53. */
  54. MutationType["patchObject"] = "patch object";
  55. /**
  56. * Mutated the state with `$patch` and a function
  57. *
  58. * - `store.$patch(state => state.name = 'newName')`
  59. */
  60. MutationType["patchFunction"] = "patch function";
  61. // maybe reset? for $state = {} and $reset
  62. })(exports.MutationType || (exports.MutationType = {}));
  63. const IS_CLIENT = typeof window !== 'undefined';
  64. /**
  65. * Creates a Pinia instance to be used by the application
  66. */
  67. function createPinia() {
  68. const scope = vueDemi.effectScope(true);
  69. // NOTE: here we could check the window object for a state and directly set it
  70. // if there is anything like it with Vue 3 SSR
  71. const state = scope.run(() => vueDemi.ref({}));
  72. let _p = [];
  73. // plugins added before calling app.use(pinia)
  74. let toBeInstalled = [];
  75. const pinia = vueDemi.markRaw({
  76. install(app) {
  77. // this allows calling useStore() outside of a component setup after
  78. // installing pinia's plugin
  79. setActivePinia(pinia);
  80. if (!vueDemi.isVue2) {
  81. pinia._a = app;
  82. app.provide(piniaSymbol, pinia);
  83. app.config.globalProperties.$pinia = pinia;
  84. toBeInstalled.forEach((plugin) => _p.push(plugin));
  85. toBeInstalled = [];
  86. }
  87. },
  88. use(plugin) {
  89. if (!this._a && !vueDemi.isVue2) {
  90. toBeInstalled.push(plugin);
  91. }
  92. else {
  93. _p.push(plugin);
  94. }
  95. return this;
  96. },
  97. _p,
  98. // it's actually undefined here
  99. // @ts-expect-error
  100. _a: null,
  101. _e: scope,
  102. _s: new Map(),
  103. state,
  104. });
  105. return pinia;
  106. }
  107. /**
  108. * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications.
  109. *
  110. * @example
  111. * ```js
  112. * const useUser = defineStore(...)
  113. * if (import.meta.hot) {
  114. * import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot))
  115. * }
  116. * ```
  117. *
  118. * @param initialUseStore - return of the defineStore to hot update
  119. * @param hot - `import.meta.hot`
  120. */
  121. function acceptHMRUpdate(initialUseStore, hot) {
  122. // strip as much as possible from iife.prod
  123. {
  124. return () => { };
  125. }
  126. }
  127. const noop = () => { };
  128. function addSubscription(subscriptions, callback, detached, onCleanup = noop) {
  129. subscriptions.push(callback);
  130. const removeSubscription = () => {
  131. const idx = subscriptions.indexOf(callback);
  132. if (idx > -1) {
  133. subscriptions.splice(idx, 1);
  134. onCleanup();
  135. }
  136. };
  137. if (!detached && vueDemi.getCurrentScope()) {
  138. vueDemi.onScopeDispose(removeSubscription);
  139. }
  140. return removeSubscription;
  141. }
  142. function triggerSubscriptions(subscriptions, ...args) {
  143. subscriptions.slice().forEach((callback) => {
  144. callback(...args);
  145. });
  146. }
  147. const fallbackRunWithContext = (fn) => fn();
  148. function mergeReactiveObjects(target, patchToApply) {
  149. // Handle Map instances
  150. if (target instanceof Map && patchToApply instanceof Map) {
  151. patchToApply.forEach((value, key) => target.set(key, value));
  152. }
  153. // Handle Set instances
  154. if (target instanceof Set && patchToApply instanceof Set) {
  155. patchToApply.forEach(target.add, target);
  156. }
  157. // no need to go through symbols because they cannot be serialized anyway
  158. for (const key in patchToApply) {
  159. if (!patchToApply.hasOwnProperty(key))
  160. continue;
  161. const subPatch = patchToApply[key];
  162. const targetValue = target[key];
  163. if (isPlainObject(targetValue) &&
  164. isPlainObject(subPatch) &&
  165. target.hasOwnProperty(key) &&
  166. !vueDemi.isRef(subPatch) &&
  167. !vueDemi.isReactive(subPatch)) {
  168. // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might
  169. // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that
  170. // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`.
  171. target[key] = mergeReactiveObjects(targetValue, subPatch);
  172. }
  173. else {
  174. // @ts-expect-error: subPatch is a valid value
  175. target[key] = subPatch;
  176. }
  177. }
  178. return target;
  179. }
  180. const skipHydrateSymbol = /* istanbul ignore next */ Symbol();
  181. const skipHydrateMap = /*#__PURE__*/ new WeakMap();
  182. /**
  183. * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a
  184. * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store.
  185. *
  186. * @param obj - target object
  187. * @returns obj
  188. */
  189. function skipHydrate(obj) {
  190. return vueDemi.isVue2
  191. ? // in @vue/composition-api, the refs are sealed so defineProperty doesn't work...
  192. /* istanbul ignore next */ skipHydrateMap.set(obj, 1) && obj
  193. : Object.defineProperty(obj, skipHydrateSymbol, {});
  194. }
  195. /**
  196. * Returns whether a value should be hydrated
  197. *
  198. * @param obj - target variable
  199. * @returns true if `obj` should be hydrated
  200. */
  201. function shouldHydrate(obj) {
  202. return vueDemi.isVue2
  203. ? /* istanbul ignore next */ !skipHydrateMap.has(obj)
  204. : !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol);
  205. }
  206. const { assign } = Object;
  207. function isComputed(o) {
  208. return !!(vueDemi.isRef(o) && o.effect);
  209. }
  210. function createOptionsStore(id, options, pinia, hot) {
  211. const { state, actions, getters } = options;
  212. const initialState = pinia.state.value[id];
  213. let store;
  214. function setup() {
  215. if (!initialState && (!false )) {
  216. /* istanbul ignore if */
  217. if (vueDemi.isVue2) {
  218. vueDemi.set(pinia.state.value, id, state ? state() : {});
  219. }
  220. else {
  221. pinia.state.value[id] = state ? state() : {};
  222. }
  223. }
  224. // avoid creating a state in pinia.state.value
  225. const localState = vueDemi.toRefs(pinia.state.value[id]);
  226. return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => {
  227. computedGetters[name] = vueDemi.markRaw(vueDemi.computed(() => {
  228. setActivePinia(pinia);
  229. // it was created just before
  230. const store = pinia._s.get(id);
  231. // allow cross using stores
  232. /* istanbul ignore next */
  233. if (vueDemi.isVue2 && !store._r)
  234. return;
  235. // @ts-expect-error
  236. // return getters![name].call(context, context)
  237. // TODO: avoid reading the getter while assigning with a global variable
  238. return getters[name].call(store, store);
  239. }));
  240. return computedGetters;
  241. }, {}));
  242. }
  243. store = createSetupStore(id, setup, options, pinia, hot, true);
  244. return store;
  245. }
  246. function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) {
  247. let scope;
  248. const optionsForPlugin = assign({ actions: {} }, options);
  249. // watcher options for $subscribe
  250. const $subscribeOptions = {
  251. deep: true,
  252. // flush: 'post',
  253. };
  254. // internal state
  255. let isListening; // set to true at the end
  256. let isSyncListening; // set to true at the end
  257. let subscriptions = [];
  258. let actionSubscriptions = [];
  259. let debuggerEvents;
  260. const initialState = pinia.state.value[$id];
  261. // avoid setting the state for option stores if it is set
  262. // by the setup
  263. if (!isOptionsStore && !initialState && (!false )) {
  264. /* istanbul ignore if */
  265. if (vueDemi.isVue2) {
  266. vueDemi.set(pinia.state.value, $id, {});
  267. }
  268. else {
  269. pinia.state.value[$id] = {};
  270. }
  271. }
  272. vueDemi.ref({});
  273. // avoid triggering too many listeners
  274. // https://github.com/vuejs/pinia/issues/1129
  275. let activeListener;
  276. function $patch(partialStateOrMutator) {
  277. let subscriptionMutation;
  278. isListening = isSyncListening = false;
  279. if (typeof partialStateOrMutator === 'function') {
  280. partialStateOrMutator(pinia.state.value[$id]);
  281. subscriptionMutation = {
  282. type: exports.MutationType.patchFunction,
  283. storeId: $id,
  284. events: debuggerEvents,
  285. };
  286. }
  287. else {
  288. mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator);
  289. subscriptionMutation = {
  290. type: exports.MutationType.patchObject,
  291. payload: partialStateOrMutator,
  292. storeId: $id,
  293. events: debuggerEvents,
  294. };
  295. }
  296. const myListenerId = (activeListener = Symbol());
  297. vueDemi.nextTick().then(() => {
  298. if (activeListener === myListenerId) {
  299. isListening = true;
  300. }
  301. });
  302. isSyncListening = true;
  303. // because we paused the watcher, we need to manually call the subscriptions
  304. triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]);
  305. }
  306. const $reset = isOptionsStore
  307. ? function $reset() {
  308. const { state } = options;
  309. const newState = state ? state() : {};
  310. // we use a patch to group all changes into one single subscription
  311. this.$patch(($state) => {
  312. assign($state, newState);
  313. });
  314. }
  315. : /* istanbul ignore next */
  316. noop;
  317. function $dispose() {
  318. scope.stop();
  319. subscriptions = [];
  320. actionSubscriptions = [];
  321. pinia._s.delete($id);
  322. }
  323. /**
  324. * Wraps an action to handle subscriptions.
  325. *
  326. * @param name - name of the action
  327. * @param action - action to wrap
  328. * @returns a wrapped action to handle subscriptions
  329. */
  330. function wrapAction(name, action) {
  331. return function () {
  332. setActivePinia(pinia);
  333. const args = Array.from(arguments);
  334. const afterCallbackList = [];
  335. const onErrorCallbackList = [];
  336. function after(callback) {
  337. afterCallbackList.push(callback);
  338. }
  339. function onError(callback) {
  340. onErrorCallbackList.push(callback);
  341. }
  342. // @ts-expect-error
  343. triggerSubscriptions(actionSubscriptions, {
  344. args,
  345. name,
  346. store,
  347. after,
  348. onError,
  349. });
  350. let ret;
  351. try {
  352. ret = action.apply(this && this.$id === $id ? this : store, args);
  353. // handle sync errors
  354. }
  355. catch (error) {
  356. triggerSubscriptions(onErrorCallbackList, error);
  357. throw error;
  358. }
  359. if (ret instanceof Promise) {
  360. return ret
  361. .then((value) => {
  362. triggerSubscriptions(afterCallbackList, value);
  363. return value;
  364. })
  365. .catch((error) => {
  366. triggerSubscriptions(onErrorCallbackList, error);
  367. return Promise.reject(error);
  368. });
  369. }
  370. // trigger after callbacks
  371. triggerSubscriptions(afterCallbackList, ret);
  372. return ret;
  373. };
  374. }
  375. const partialStore = {
  376. _p: pinia,
  377. // _s: scope,
  378. $id,
  379. $onAction: addSubscription.bind(null, actionSubscriptions),
  380. $patch,
  381. $reset,
  382. $subscribe(callback, options = {}) {
  383. const removeSubscription = addSubscription(subscriptions, callback, options.detached, () => stopWatcher());
  384. const stopWatcher = scope.run(() => vueDemi.watch(() => pinia.state.value[$id], (state) => {
  385. if (options.flush === 'sync' ? isSyncListening : isListening) {
  386. callback({
  387. storeId: $id,
  388. type: exports.MutationType.direct,
  389. events: debuggerEvents,
  390. }, state);
  391. }
  392. }, assign({}, $subscribeOptions, options)));
  393. return removeSubscription;
  394. },
  395. $dispose,
  396. };
  397. /* istanbul ignore if */
  398. if (vueDemi.isVue2) {
  399. // start as non ready
  400. partialStore._r = false;
  401. }
  402. const store = vueDemi.reactive(partialStore);
  403. // store the partial store now so the setup of stores can instantiate each other before they are finished without
  404. // creating infinite loops.
  405. pinia._s.set($id, store);
  406. const runWithContext = (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext;
  407. // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped
  408. const setupStore = runWithContext(() => pinia._e.run(() => (scope = vueDemi.effectScope()).run(setup)));
  409. // overwrite existing actions to support $onAction
  410. for (const key in setupStore) {
  411. const prop = setupStore[key];
  412. if ((vueDemi.isRef(prop) && !isComputed(prop)) || vueDemi.isReactive(prop)) {
  413. // mark it as a piece of state to be serialized
  414. if (!isOptionsStore) {
  415. // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created
  416. if (initialState && shouldHydrate(prop)) {
  417. if (vueDemi.isRef(prop)) {
  418. prop.value = initialState[key];
  419. }
  420. else {
  421. // probably a reactive object, lets recursively assign
  422. // @ts-expect-error: prop is unknown
  423. mergeReactiveObjects(prop, initialState[key]);
  424. }
  425. }
  426. // transfer the ref to the pinia state to keep everything in sync
  427. /* istanbul ignore if */
  428. if (vueDemi.isVue2) {
  429. vueDemi.set(pinia.state.value[$id], key, prop);
  430. }
  431. else {
  432. pinia.state.value[$id][key] = prop;
  433. }
  434. }
  435. // action
  436. }
  437. else if (typeof prop === 'function') {
  438. // @ts-expect-error: we are overriding the function we avoid wrapping if
  439. const actionValue = wrapAction(key, prop);
  440. // this a hot module replacement store because the hotUpdate method needs
  441. // to do it with the right context
  442. /* istanbul ignore if */
  443. if (vueDemi.isVue2) {
  444. vueDemi.set(setupStore, key, actionValue);
  445. }
  446. else {
  447. // @ts-expect-error
  448. setupStore[key] = actionValue;
  449. }
  450. // list actions so they can be used in plugins
  451. // @ts-expect-error
  452. optionsForPlugin.actions[key] = prop;
  453. }
  454. else ;
  455. }
  456. // add the state, getters, and action properties
  457. /* istanbul ignore if */
  458. if (vueDemi.isVue2) {
  459. Object.keys(setupStore).forEach((key) => {
  460. vueDemi.set(store, key, setupStore[key]);
  461. });
  462. }
  463. else {
  464. assign(store, setupStore);
  465. // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object.
  466. // Make `storeToRefs()` work with `reactive()` #799
  467. assign(vueDemi.toRaw(store), setupStore);
  468. }
  469. // use this instead of a computed with setter to be able to create it anywhere
  470. // without linking the computed lifespan to wherever the store is first
  471. // created.
  472. Object.defineProperty(store, '$state', {
  473. get: () => (pinia.state.value[$id]),
  474. set: (state) => {
  475. $patch(($state) => {
  476. assign($state, state);
  477. });
  478. },
  479. });
  480. /* istanbul ignore if */
  481. if (vueDemi.isVue2) {
  482. // mark the store as ready before plugins
  483. store._r = true;
  484. }
  485. // apply all plugins
  486. pinia._p.forEach((extender) => {
  487. /* istanbul ignore else */
  488. {
  489. assign(store, scope.run(() => extender({
  490. store,
  491. app: pinia._a,
  492. pinia,
  493. options: optionsForPlugin,
  494. })));
  495. }
  496. });
  497. // only apply hydrate to option stores with an initial state in pinia
  498. if (initialState &&
  499. isOptionsStore &&
  500. options.hydrate) {
  501. options.hydrate(store.$state, initialState);
  502. }
  503. isListening = true;
  504. isSyncListening = true;
  505. return store;
  506. }
  507. function defineStore(
  508. // TODO: add proper types from above
  509. idOrOptions, setup, setupOptions) {
  510. let id;
  511. let options;
  512. const isSetupStore = typeof setup === 'function';
  513. if (typeof idOrOptions === 'string') {
  514. id = idOrOptions;
  515. // the option store setup will contain the actual options in this case
  516. options = isSetupStore ? setupOptions : setup;
  517. }
  518. else {
  519. options = idOrOptions;
  520. id = idOrOptions.id;
  521. }
  522. function useStore(pinia, hot) {
  523. const hasContext = vueDemi.hasInjectionContext();
  524. pinia =
  525. // in test mode, ignore the argument provided as we can always retrieve a
  526. // pinia instance with getActivePinia()
  527. ((process.env.NODE_ENV === 'test') && activePinia && activePinia._testing ? null : pinia) ||
  528. (hasContext ? vueDemi.inject(piniaSymbol, null) : null);
  529. if (pinia)
  530. setActivePinia(pinia);
  531. pinia = activePinia;
  532. if (!pinia._s.has(id)) {
  533. // creating the store registers it in `pinia._s`
  534. if (isSetupStore) {
  535. createSetupStore(id, setup, options, pinia);
  536. }
  537. else {
  538. createOptionsStore(id, options, pinia);
  539. }
  540. }
  541. const store = pinia._s.get(id);
  542. // StoreGeneric cannot be casted towards Store
  543. return store;
  544. }
  545. useStore.$id = id;
  546. return useStore;
  547. }
  548. let mapStoreSuffix = 'Store';
  549. /**
  550. * Changes the suffix added by `mapStores()`. Can be set to an empty string.
  551. * Defaults to `"Store"`. Make sure to extend the MapStoresCustomization
  552. * interface if you are using TypeScript.
  553. *
  554. * @param suffix - new suffix
  555. */
  556. function setMapStoreSuffix(suffix // could be 'Store' but that would be annoying for JS
  557. ) {
  558. mapStoreSuffix = suffix;
  559. }
  560. /**
  561. * Allows using stores without the composition API (`setup()`) by generating an
  562. * object to be spread in the `computed` field of a component. It accepts a list
  563. * of store definitions.
  564. *
  565. * @example
  566. * ```js
  567. * export default {
  568. * computed: {
  569. * // other computed properties
  570. * ...mapStores(useUserStore, useCartStore)
  571. * },
  572. *
  573. * created() {
  574. * this.userStore // store with id "user"
  575. * this.cartStore // store with id "cart"
  576. * }
  577. * }
  578. * ```
  579. *
  580. * @param stores - list of stores to map to an object
  581. */
  582. function mapStores(...stores) {
  583. return stores.reduce((reduced, useStore) => {
  584. // @ts-expect-error: $id is added by defineStore
  585. reduced[useStore.$id + mapStoreSuffix] = function () {
  586. return useStore(this.$pinia);
  587. };
  588. return reduced;
  589. }, {});
  590. }
  591. /**
  592. * Allows using state and getters from one store without using the composition
  593. * API (`setup()`) by generating an object to be spread in the `computed` field
  594. * of a component.
  595. *
  596. * @param useStore - store to map from
  597. * @param keysOrMapper - array or object
  598. */
  599. function mapState(useStore, keysOrMapper) {
  600. return Array.isArray(keysOrMapper)
  601. ? keysOrMapper.reduce((reduced, key) => {
  602. reduced[key] = function () {
  603. return useStore(this.$pinia)[key];
  604. };
  605. return reduced;
  606. }, {})
  607. : Object.keys(keysOrMapper).reduce((reduced, key) => {
  608. // @ts-expect-error
  609. reduced[key] = function () {
  610. const store = useStore(this.$pinia);
  611. const storeKey = keysOrMapper[key];
  612. // for some reason TS is unable to infer the type of storeKey to be a
  613. // function
  614. return typeof storeKey === 'function'
  615. ? storeKey.call(this, store)
  616. : store[storeKey];
  617. };
  618. return reduced;
  619. }, {});
  620. }
  621. /**
  622. * Alias for `mapState()`. You should use `mapState()` instead.
  623. * @deprecated use `mapState()` instead.
  624. */
  625. const mapGetters = mapState;
  626. /**
  627. * Allows directly using actions from your store without using the composition
  628. * API (`setup()`) by generating an object to be spread in the `methods` field
  629. * of a component.
  630. *
  631. * @param useStore - store to map from
  632. * @param keysOrMapper - array or object
  633. */
  634. function mapActions(useStore, keysOrMapper) {
  635. return Array.isArray(keysOrMapper)
  636. ? keysOrMapper.reduce((reduced, key) => {
  637. // @ts-expect-error
  638. reduced[key] = function (...args) {
  639. return useStore(this.$pinia)[key](...args);
  640. };
  641. return reduced;
  642. }, {})
  643. : Object.keys(keysOrMapper).reduce((reduced, key) => {
  644. // @ts-expect-error
  645. reduced[key] = function (...args) {
  646. return useStore(this.$pinia)[keysOrMapper[key]](...args);
  647. };
  648. return reduced;
  649. }, {});
  650. }
  651. /**
  652. * Allows using state and getters from one store without using the composition
  653. * API (`setup()`) by generating an object to be spread in the `computed` field
  654. * of a component.
  655. *
  656. * @param useStore - store to map from
  657. * @param keysOrMapper - array or object
  658. */
  659. function mapWritableState(useStore, keysOrMapper) {
  660. return Array.isArray(keysOrMapper)
  661. ? keysOrMapper.reduce((reduced, key) => {
  662. // @ts-ignore
  663. reduced[key] = {
  664. get() {
  665. return useStore(this.$pinia)[key];
  666. },
  667. set(value) {
  668. // it's easier to type it here as any
  669. return (useStore(this.$pinia)[key] = value);
  670. },
  671. };
  672. return reduced;
  673. }, {})
  674. : Object.keys(keysOrMapper).reduce((reduced, key) => {
  675. // @ts-ignore
  676. reduced[key] = {
  677. get() {
  678. return useStore(this.$pinia)[keysOrMapper[key]];
  679. },
  680. set(value) {
  681. // it's easier to type it here as any
  682. return (useStore(this.$pinia)[keysOrMapper[key]] = value);
  683. },
  684. };
  685. return reduced;
  686. }, {});
  687. }
  688. /**
  689. * Creates an object of references with all the state, getters, and plugin-added
  690. * state properties of the store. Similar to `toRefs()` but specifically
  691. * designed for Pinia stores so methods and non reactive properties are
  692. * completely ignored.
  693. *
  694. * @param store - store to extract the refs from
  695. */
  696. function storeToRefs(store) {
  697. // See https://github.com/vuejs/pinia/issues/852
  698. // It's easier to just use toRefs() even if it includes more stuff
  699. if (vueDemi.isVue2) {
  700. // @ts-expect-error: toRefs include methods and others
  701. return vueDemi.toRefs(store);
  702. }
  703. else {
  704. store = vueDemi.toRaw(store);
  705. const refs = {};
  706. for (const key in store) {
  707. const value = store[key];
  708. if (vueDemi.isRef(value) || vueDemi.isReactive(value)) {
  709. // @ts-expect-error: the key is state or getter
  710. refs[key] =
  711. // ---
  712. vueDemi.toRef(store, key);
  713. }
  714. }
  715. return refs;
  716. }
  717. }
  718. /**
  719. * Vue 2 Plugin that must be installed for pinia to work. Note **you don't need
  720. * this plugin if you are using Nuxt.js**. Use the `buildModule` instead:
  721. * https://pinia.vuejs.org/ssr/nuxt.html.
  722. *
  723. * @example
  724. * ```js
  725. * import Vue from 'vue'
  726. * import { PiniaVuePlugin, createPinia } from 'pinia'
  727. *
  728. * Vue.use(PiniaVuePlugin)
  729. * const pinia = createPinia()
  730. *
  731. * new Vue({
  732. * el: '#app',
  733. * // ...
  734. * pinia,
  735. * })
  736. * ```
  737. *
  738. * @param _Vue - `Vue` imported from 'vue'.
  739. */
  740. const PiniaVuePlugin = function (_Vue) {
  741. // Equivalent of
  742. // app.config.globalProperties.$pinia = pinia
  743. _Vue.mixin({
  744. beforeCreate() {
  745. const options = this.$options;
  746. if (options.pinia) {
  747. const pinia = options.pinia;
  748. // HACK: taken from provide(): https://github.com/vuejs/composition-api/blob/main/src/apis/inject.ts#L31
  749. /* istanbul ignore else */
  750. if (!this._provided) {
  751. const provideCache = {};
  752. Object.defineProperty(this, '_provided', {
  753. get: () => provideCache,
  754. set: (v) => Object.assign(provideCache, v),
  755. });
  756. }
  757. this._provided[piniaSymbol] = pinia;
  758. // propagate the pinia instance in an SSR friendly way
  759. // avoid adding it to nuxt twice
  760. /* istanbul ignore else */
  761. if (!this.$pinia) {
  762. this.$pinia = pinia;
  763. }
  764. pinia._a = this;
  765. if (IS_CLIENT) {
  766. // this allows calling useStore() outside of a component setup after
  767. // installing pinia's plugin
  768. setActivePinia(pinia);
  769. }
  770. }
  771. else if (!this.$pinia && options.parent && options.parent.$pinia) {
  772. this.$pinia = options.parent.$pinia;
  773. }
  774. },
  775. destroyed() {
  776. delete this._pStores;
  777. },
  778. });
  779. };
  780. exports.PiniaVuePlugin = PiniaVuePlugin;
  781. exports.acceptHMRUpdate = acceptHMRUpdate;
  782. exports.createPinia = createPinia;
  783. exports.defineStore = defineStore;
  784. exports.getActivePinia = getActivePinia;
  785. exports.mapActions = mapActions;
  786. exports.mapGetters = mapGetters;
  787. exports.mapState = mapState;
  788. exports.mapStores = mapStores;
  789. exports.mapWritableState = mapWritableState;
  790. exports.setActivePinia = setActivePinia;
  791. exports.setMapStoreSuffix = setMapStoreSuffix;
  792. exports.skipHydrate = skipHydrate;
  793. exports.storeToRefs = storeToRefs;