index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { merge as merge$1 } from 'lodash';
  2. /**
  3. * Created by championswimmer on 22/07/17.
  4. */
  5. let MockStorage;
  6. // @ts-ignore
  7. {
  8. MockStorage = class {
  9. get length() {
  10. return Object.keys(this).length;
  11. }
  12. key(index) {
  13. return Object.keys(this)[index];
  14. }
  15. setItem(key, data) {
  16. this[key] = data.toString();
  17. }
  18. getItem(key) {
  19. return this[key];
  20. }
  21. removeItem(key) {
  22. delete this[key];
  23. }
  24. clear() {
  25. for (let key of Object.keys(this)) {
  26. delete this[key];
  27. }
  28. }
  29. };
  30. }
  31. // tslint:disable: variable-name
  32. class SimplePromiseQueue {
  33. constructor() {
  34. this._queue = [];
  35. this._flushing = false;
  36. }
  37. enqueue(promise) {
  38. this._queue.push(promise);
  39. if (!this._flushing) {
  40. return this.flushQueue();
  41. }
  42. return Promise.resolve();
  43. }
  44. flushQueue() {
  45. this._flushing = true;
  46. const chain = () => {
  47. const nextTask = this._queue.shift();
  48. if (nextTask) {
  49. return nextTask.then(chain);
  50. }
  51. else {
  52. this._flushing = false;
  53. }
  54. };
  55. return Promise.resolve(chain());
  56. }
  57. }
  58. function merge(into, from) {
  59. return merge$1({}, into, from);
  60. }
  61. let FlattedJSON = JSON;
  62. /**
  63. * A class that implements the vuex persistence.
  64. * @type S type of the 'state' inside the store (default: any)
  65. */
  66. class VuexPersistence {
  67. /**
  68. * Create a {@link VuexPersistence} object.
  69. * Use the <code>plugin</code> function of this class as a
  70. * Vuex plugin.
  71. * @param {PersistOptions} options
  72. */
  73. constructor(options) {
  74. // tslint:disable-next-line:variable-name
  75. this._mutex = new SimplePromiseQueue();
  76. /**
  77. * Creates a subscriber on the store. automatically is used
  78. * when this is used a vuex plugin. Not for manual usage.
  79. * @param store
  80. */
  81. this.subscriber = (store) => (handler) => store.subscribe(handler);
  82. if (typeof options === 'undefined')
  83. options = {};
  84. this.key = ((options.key != null) ? options.key : 'vuex');
  85. this.subscribed = false;
  86. this.supportCircular = options.supportCircular || false;
  87. if (this.supportCircular) {
  88. FlattedJSON = require('flatted');
  89. }
  90. let localStorageLitmus = true;
  91. try {
  92. window.localStorage.getItem('');
  93. }
  94. catch (err) {
  95. localStorageLitmus = false;
  96. }
  97. this.storage = options.storage || localStorageLitmus && window.localStorage || MockStorage && new MockStorage();
  98. /**
  99. * How this works is -
  100. * 1. If there is options.reducer function, we use that, if not;
  101. * 2. We check options.modules;
  102. * 1. If there is no options.modules array, we use entire state in reducer
  103. * 2. Otherwise, we create a reducer that merges all those state modules that are
  104. * defined in the options.modules[] array
  105. * @type {((state: S) => {}) | ((state: S) => S) | ((state: any) => {})}
  106. */
  107. this.reducer = ((options.reducer != null)
  108. ? options.reducer
  109. : ((options.modules == null)
  110. ? ((state) => state)
  111. : ((state) => options.modules.reduce((a, i) => merge(a, { [i]: state[i] }), { /* start empty accumulator*/}))));
  112. this.filter = options.filter || ((mutation) => true);
  113. this.strictMode = options.strictMode || false;
  114. this.RESTORE_MUTATION = function RESTORE_MUTATION(state, savedState) {
  115. const mergedState = merge(state, savedState || {});
  116. for (const propertyName of Object.keys(mergedState)) {
  117. this._vm.$set(state, propertyName, mergedState[propertyName]);
  118. }
  119. };
  120. this.asyncStorage = options.asyncStorage || false;
  121. if (this.asyncStorage) {
  122. /**
  123. * Async {@link #VuexPersistence.restoreState} implementation
  124. * @type {((key: string, storage?: Storage) =>
  125. * (Promise<S> | S)) | ((key: string, storage: AsyncStorage) => Promise<any>)}
  126. */
  127. this.restoreState = ((options.restoreState != null)
  128. ? options.restoreState
  129. : ((key, storage) => (storage).getItem(key)
  130. .then((value) => typeof value === 'string' // If string, parse, or else, just return
  131. ? (this.supportCircular
  132. ? FlattedJSON.parse(value || '{}')
  133. : JSON.parse(value || '{}'))
  134. : (value || {}))));
  135. /**
  136. * Async {@link #VuexPersistence.saveState} implementation
  137. * @type {((key: string, state: {}, storage?: Storage) =>
  138. * (Promise<void> | void)) | ((key: string, state: {}, storage?: Storage) => Promise<void>)}
  139. */
  140. this.saveState = ((options.saveState != null)
  141. ? options.saveState
  142. : ((key, state, storage) => (storage).setItem(key, // Second argument is state _object_ if asyc storage, stringified otherwise
  143. // do not stringify the state if the storage type is async
  144. (this.asyncStorage
  145. ? merge({}, state || {})
  146. : (this.supportCircular
  147. ? FlattedJSON.stringify(state)
  148. : JSON.stringify(state))))));
  149. /**
  150. * Async version of plugin
  151. * @param {Store<S>} store
  152. */
  153. this.plugin = (store) => {
  154. /**
  155. * For async stores, we're capturing the Promise returned
  156. * by the `restoreState()` function in a `restored` property
  157. * on the store itself. This would allow app developers to
  158. * determine when and if the store's state has indeed been
  159. * refreshed. This approach was suggested by GitHub user @hotdogee.
  160. * See https://github.com/championswimmer/vuex-persist/pull/118#issuecomment-500914963
  161. * @since 2.1.0
  162. */
  163. store.restored = (this.restoreState(this.key, this.storage)).then((savedState) => {
  164. /**
  165. * If in strict mode, do only via mutation
  166. */
  167. if (this.strictMode) {
  168. store.commit('RESTORE_MUTATION', savedState);
  169. }
  170. else {
  171. store.replaceState(merge(store.state, savedState || {}));
  172. }
  173. this.subscriber(store)((mutation, state) => {
  174. if (this.filter(mutation)) {
  175. this._mutex.enqueue(this.saveState(this.key, this.reducer(state), this.storage));
  176. }
  177. });
  178. this.subscribed = true;
  179. });
  180. };
  181. }
  182. else {
  183. /**
  184. * Sync {@link #VuexPersistence.restoreState} implementation
  185. * @type {((key: string, storage?: Storage) =>
  186. * (Promise<S> | S)) | ((key: string, storage: Storage) => (any | string | {}))}
  187. */
  188. this.restoreState = ((options.restoreState != null)
  189. ? options.restoreState
  190. : ((key, storage) => {
  191. const value = (storage).getItem(key);
  192. if (typeof value === 'string') { // If string, parse, or else, just return
  193. return (this.supportCircular
  194. ? FlattedJSON.parse(value || '{}')
  195. : JSON.parse(value || '{}'));
  196. }
  197. else {
  198. return (value || {});
  199. }
  200. }));
  201. /**
  202. * Sync {@link #VuexPersistence.saveState} implementation
  203. * @type {((key: string, state: {}, storage?: Storage) =>
  204. * (Promise<void> | void)) | ((key: string, state: {}, storage?: Storage) => Promise<void>)}
  205. */
  206. this.saveState = ((options.saveState != null)
  207. ? options.saveState
  208. : ((key, state, storage) => (storage).setItem(key, // Second argument is state _object_ if localforage, stringified otherwise
  209. (this.supportCircular
  210. ? FlattedJSON.stringify(state)
  211. : JSON.stringify(state)))));
  212. /**
  213. * Sync version of plugin
  214. * @param {Store<S>} store
  215. */
  216. this.plugin = (store) => {
  217. const savedState = this.restoreState(this.key, this.storage);
  218. if (this.strictMode) {
  219. store.commit('RESTORE_MUTATION', savedState);
  220. }
  221. else {
  222. store.replaceState(merge(store.state, savedState || {}));
  223. }
  224. this.subscriber(store)((mutation, state) => {
  225. if (this.filter(mutation)) {
  226. this.saveState(this.key, this.reducer(state), this.storage);
  227. }
  228. });
  229. this.subscribed = true;
  230. };
  231. }
  232. }
  233. }
  234. export default VuexPersistence;
  235. export { MockStorage, VuexPersistence };
  236. //# sourceMappingURL=index.js.map