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.

844 lines
29 KiB

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