pinia-plugin-subscription 0.1.7 → 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Added automatic state rollback on action failure via the `rollbackAfterFailure` store option: the declared state keys are snapshotted before the action runs and restored through `$patch` when the action throws (sync or async).
12
+ - Added `$rollbackAfterFailure(params, ...args)` to `PiniaCustomProperties`, available on every store registered by the plugin, to run an action with an explicit snapshot/rollback wrapper.
13
+ - Added `RollbackActionParams` and `RollbackAfterFailureParams` types to the public API.
14
+
15
+ ### Changed
16
+ - Action names starting with `_` or `$` are rejected by the rollback wrapper to protect internal store methods.
17
+ - Rollback tracking data (`rollbackAfterFailure` params and pending snapshots) is now cleaned up when a store is disposed, preventing memory leaks with dynamically created stores.
18
+
19
+ ## [0.1.8]
20
+
21
+ ### Added
22
+ - Added `onError` parameter to `storeOnActionSubscription` method
23
+ - Added `onError` property to `StoreOnActionCallbackParameters` interface
24
+
25
+ ### Changed
26
+ - Changing the directory structure
27
+
8
28
  ## [0.1.7]
9
29
 
10
30
  ### Changed
package/README.md CHANGED
@@ -6,7 +6,8 @@
6
6
  - a lightweight mechanism to declare "subscribers" that are invoked when stores are registered or updated by Pinia;
7
7
  - a `Store` base class (helper wrapper) to ease interacting with Pinia stores from subscribers or other plugin code;
8
8
  - an API to create a Pinia plugin from a list of subscribers;
9
- - the $reset method to all stores modified by the plugin.
9
+ - the $reset method to all stores modified by the plugin;
10
+ - automatic state rollback on action failure via the `rollbackAfterFailure` store option and the `$rollbackAfterFailure` method.
10
11
 
11
12
  The main goal is to offer a clear API for writing reusable Pinia plugins and to make it easy to extend stores from plugin code.
12
13
 
@@ -205,6 +206,57 @@ const mySubscriber: PluginSubscriberInterface = {
205
206
 
206
207
  This keeps the server render deterministic and prevents browser-only initialization from crashing Nuxt or SSR builds.
207
208
 
209
+ ### State rollback on action failure
210
+
211
+ The plugin can automatically restore the previous state when an action throws. Declare the state keys to snapshot per action via the `rollbackAfterFailure` store option:
212
+
213
+ ```typescript
214
+ import { defineAStore } from 'pinia-plugin-subscription'
215
+
216
+ export const useMyStore = defineAStore('myStore', () => {
217
+ const count = ref(0)
218
+
219
+ function riskyAction() {
220
+ count.value += 1
221
+ throw new Error('Something went wrong')
222
+ }
223
+
224
+ return { count, riskyAction }
225
+ }, {
226
+ rollbackAfterFailure: {
227
+ // only the listed keys are snapshotted / restored (deep-cloned)
228
+ riskyAction: ['count']
229
+ }
230
+ })
231
+ ```
232
+
233
+ Before a configured action runs, the declared state keys are snapshotted. If the action throws (synchronously or by rejecting its returned promise), the snapshot is restored through `$patch`, so partial mutations are rolled back. The snapshot is always discarded once the action completes or fails.
234
+
235
+ Only the actions listed in `rollbackAfterFailure` are snapshotted, so unrelated actions pay no cloning cost. Use `'all'` instead of a key list to snapshot the full state — prefer explicit keys for large states.
236
+
237
+ Action names starting with `_` or `$` are rejected to protect internal store methods.
238
+
239
+ #### Manual rollback with `$rollbackAfterFailure`
240
+
241
+ Every store registered by the plugin also exposes a `$rollbackAfterFailure` method to run any action with an explicit snapshot/rollback wrapper, without declaring it in the store options:
242
+
243
+ ```typescript
244
+ const store = useMyStore()
245
+
246
+ try {
247
+ await store.$rollbackAfterFailure(
248
+ { action: 'riskyAction', stateKeys: ['count'] },
249
+ // ...args forwarded to the action
250
+ )
251
+ } catch (error) {
252
+ // the action failed and the state was restored
253
+ }
254
+ ```
255
+
256
+ - `params.action` — the name of the action to execute.
257
+ - `params.stateKeys` — optional; the state keys to snapshot, or `'all'` (default when omitted or empty).
258
+ - The original error is rethrown after the state is restored.
259
+
208
260
  ### `defineAStoreCtx(id, setup, options?)`
209
261
 
210
262
  Use actions or access the state, added by one or more plugins, when defining the store.
@@ -245,7 +297,7 @@ An object with at least an `invoke(context: PiniaPluginContext, debug?: boolean)
245
297
 
246
298
  ## The `PluginSubscriber` Abstract Class
247
299
 
248
- The project provides an abstract `PluginSubscriber` implementation (see [src/plugins/pluginSubscriber.ts](src/plugins/pluginSubscriber.ts)) to simplify creating reusable subscribers.
300
+ The project provides an abstract `PluginSubscriber` implementation (see [src/core/PluginSubscriber.ts](src/core/PluginSubscriber.ts)) to simplify creating reusable subscribers.
249
301
 
250
302
  **Typical usage:**
251
303
 
@@ -49,10 +49,10 @@ class L {
49
49
  set subscriptions(t) {
50
50
  this._subscriptions = t;
51
51
  }
52
- hydrate(t, e) {
53
- }
54
52
  afterHydration(t, e) {
55
53
  }
54
+ hydrate(t, e) {
55
+ }
56
56
  invoke({ store: t, options: e }, r) {
57
57
  return this._storeInstance = this._createInstance(t, { ...e, ...this.pluginOptions }, r, this.console), this._storeInstance ? (this._subscriptions = this._storeInstance.getSubscriptions(), this._storeMutationSubscription = this._storeInstance.storeSubscribe, this._storeOnActionSubscription = this._storeInstance.onAction, this.pluginCreated && this.pluginCreated(t), !0) : !1;
58
58
  }
@@ -104,7 +104,7 @@ function N(s, t, e) {
104
104
  s,
105
105
  t,
106
106
  r
107
- ) : m(
107
+ ) : $(
108
108
  s,
109
109
  t,
110
110
  r
@@ -129,12 +129,12 @@ function d(s, t, e) {
129
129
  return o && (p.set(a, r), c.delete(s), h(s, e, "moved context to weak map"), x(a, s, e)), a;
130
130
  }), i);
131
131
  }
132
- function m(s, t, e) {
132
+ function $(s, t, e) {
133
133
  return e && (t = { ...t, ...e ?? {} }), g(s, t);
134
134
  }
135
- const $ = /* @__PURE__ */ new Set(["_", "$"]);
135
+ const m = /* @__PURE__ */ new Set(["_", "$"]);
136
136
  function I(s) {
137
- return $.has(s[0]);
137
+ return m.has(s[0]);
138
138
  }
139
139
  function w(s) {
140
140
  return p.get(s) ?? (typeof s?.$id == "string" ? c.get(s.$id) : void 0);
@@ -151,6 +151,9 @@ function M(s, t) {
151
151
  function T(s) {
152
152
  return k(s);
153
153
  }
154
+ function W(s, t) {
155
+ return `${s}-${t.$id}`;
156
+ }
154
157
  function E(s) {
155
158
  return s ? Array.isArray(s) ? s.length === 0 : typeof s == "object" ? Object.keys(s).length === 0 : typeof s == "string" ? s.trim() === "" : !1 : !(typeof s == "boolean" || typeof s == "number");
156
159
  }
@@ -190,10 +193,6 @@ class P extends C {
190
193
  get store() {
191
194
  return this._store;
192
195
  }
193
- hydrate() {
194
- }
195
- afterHydration() {
196
- }
197
196
  get storeSubscribe() {
198
197
  if (this._storeSubscribe)
199
198
  return () => ({
@@ -215,6 +214,8 @@ class P extends C {
215
214
  addSubscription(e, r, o) {
216
215
  this._subscriptions[e] = { subscription: r, ...o ?? {} };
217
216
  }
217
+ afterHydration() {
218
+ }
218
219
  /**
219
220
  * Create and return a class instance
220
221
  * @param store
@@ -226,12 +227,6 @@ class P extends C {
226
227
  if (r.storeOptions && this.hasRequiredKeys(r.storeOptions))
227
228
  return new this(e, r, o, i);
228
229
  }
229
- hasDeniedFirstChar(e) {
230
- return I(e);
231
- }
232
- static hasRequiredKeys(e) {
233
- return this._requiredKeys !== void 0 && this._requiredKeys?.every((r) => !!e[r]);
234
- }
235
230
  getOption(e) {
236
231
  return this.options && this.options[e];
237
232
  }
@@ -245,6 +240,14 @@ class P extends C {
245
240
  getValue(e) {
246
241
  return e?.__v_isRef ? e.value : e;
247
242
  }
243
+ hasDeniedFirstChar(e) {
244
+ return I(e);
245
+ }
246
+ static hasRequiredKeys(e) {
247
+ return this._requiredKeys !== void 0 && this._requiredKeys?.every((r) => !!e[r]);
248
+ }
249
+ hydrate() {
250
+ }
248
251
  isOptionApi() {
249
252
  return this.store._isOptionsAPI;
250
253
  }
@@ -314,24 +317,26 @@ class D {
314
317
  );
315
318
  }
316
319
  }
317
- class j extends D {
320
+ class K extends D {
318
321
  constructor() {
319
322
  super(...arguments);
320
323
  n(this, "_pluginName", _);
321
324
  }
322
325
  }
323
- const W = new j();
326
+ const F = new K();
324
327
  export {
325
328
  D as C,
326
329
  C as D,
327
- W as P,
330
+ F as P,
328
331
  P as S,
329
332
  L as a,
330
333
  R as b,
331
- k as c,
334
+ w as c,
332
335
  N as d,
333
- T as e,
334
- w as g,
336
+ k as e,
337
+ T as f,
338
+ W as g,
339
+ I as h,
335
340
  E as i,
336
341
  _ as p,
337
342
  M as s
@@ -1,4 +1,4 @@
1
- import Store from "../core/Store";
1
+ import Store from "./Store";
2
2
  import type { PiniaPluginContext, Store as PiniaStore } from "pinia";
3
3
  import type { AnyObject } from "../types";
4
4
  import type { PluginExecutionOptions, PluginHydrationScheduler, PluginSubscriber as PluginSubscriberInterface, PluginSubscriptions, StoreMutationSubscription, StoreOnActionSubscription } from "../types/plugin";
@@ -28,8 +28,8 @@ export default abstract class PluginSubscriber<Instance extends Store> implement
28
28
  get subscriptions(): PluginSubscriptions | undefined;
29
29
  set subscriptions(subscriptions: PluginSubscriptions);
30
30
  constructor(pluginName: string, createInstanceFunction: CreateInstance, pluginConsole?: Console);
31
- hydrate(context: PiniaPluginContext, debug: boolean): void | Promise<void>;
32
31
  afterHydration(context: PiniaPluginContext, debug: boolean): void | Promise<void>;
32
+ hydrate(context: PiniaPluginContext, debug: boolean): void | Promise<void>;
33
33
  invoke({ store, options }: PiniaPluginContext, debug: boolean): boolean;
34
34
  }
35
35
  export {};
@@ -0,0 +1,75 @@
1
+ import Debug from "../system/Debug";
2
+ import type { PiniaPluginContext } from "pinia";
3
+ import type { PluginSubscriber, PluginSubscriptionOptions, PluginSubscriptions } from "../types/plugin";
4
+ export default class PluginSubscription extends Debug {
5
+ protected _className: string;
6
+ private _hydrationScheduler;
7
+ private _onActionSubscriptions;
8
+ private _onActionAfterSubscriptions;
9
+ private _onActionOnErrorSubscriptions;
10
+ private _options?;
11
+ private _pluginDebug?;
12
+ private _resetStoreCallback;
13
+ private _storeRollbackSnapshotsHandler;
14
+ private _subscribers;
15
+ private _subscribersDelivered;
16
+ private _subscribersScheduled;
17
+ private _subscriptions;
18
+ private _subscriptionsDelivered;
19
+ private _subscriptionsScheduled;
20
+ set subscribers(subscribers: PluginSubscriber[]);
21
+ get subscriptions(): PluginSubscriptions[] | undefined;
22
+ constructor(subscribers: PluginSubscriber[], debugOrOptions?: PluginSubscriptionOptions | string[], pluginOptions?: PluginSubscriptionOptions);
23
+ private addOnActionAfterCallback;
24
+ private addOnActionOnErrorCallback;
25
+ private addResetStoreCallback;
26
+ private clearOnActionCallbacksSubscriptions;
27
+ private clearStoreTracking;
28
+ /**
29
+ * Creates a callback collector for the specified action and store.
30
+ * @param actionName The name of the action to collect callbacks for.
31
+ * @param store The store instance associated with the action.
32
+ * @returns A function that collects callbacks for the specified action and store.
33
+ */
34
+ private createOnActionAfterCallbackCollector;
35
+ /**
36
+ * Creates a callback collector for the specified action and store that handles errors.
37
+ * @param actionName The name of the action to collect error callbacks for.
38
+ * @param store The store instance associated with the action.
39
+ * @returns A function that collects error callbacks for the specified action and store.
40
+ */
41
+ private createOnActionOnErrorCallbackCollector;
42
+ /**
43
+ * Rolls back the state snapshot for a given action if an error occurs, and removes the snapshot after the action completes or fails.
44
+ * @param actionName The name of the action being executed.
45
+ * @param store The Pinia store instance.
46
+ * @returns void
47
+ */
48
+ private createRollbackSnapshot;
49
+ private defineCurrentEnvironment;
50
+ private definePluginDebug;
51
+ private definePluginExecution;
52
+ private defineHydrationScheduler;
53
+ private defineSubscriberKey;
54
+ private defineSubscriptionKey;
55
+ private executeResetStoreCallbacks;
56
+ /**
57
+ * Executes the store's onAction subscription for the given plugin subscriber.
58
+ * @param store The Pinia store instance.
59
+ * @param subscriber The plugin subscriber.
60
+ * @returns void
61
+ */
62
+ private executeStoreOnActionSubscription;
63
+ private executeSubscriber;
64
+ private executeSubscriberSafely;
65
+ plugin({ store, options }: PiniaPluginContext): void;
66
+ private queueSubscriberExecution;
67
+ private registerStoreCleanup;
68
+ private rewriteResetStore;
69
+ private shouldInvokePlugin;
70
+ private shouldScheduleAfterHydration;
71
+ private storeOnActionSubscription;
72
+ private storeMutationSubscription;
73
+ private subscriptionDelivery;
74
+ private subscriptionDeliverySafely;
75
+ }
@@ -0,0 +1,64 @@
1
+ import type { PiniaPluginContext, Store } from 'pinia';
2
+ /**
3
+ * Handles rollback state snapshots for Pinia stores.
4
+ * This class provides methods to add rollback actions to stores,
5
+ * create rollback snapshots, and manage the state snapshots internally.
6
+ */
7
+ export default class RollbackStateSnapshotsHandler {
8
+ private _stateSnapshots;
9
+ private _storeHasRollbackSnapshots;
10
+ private _storeRollbackSnapshotsParams;
11
+ /**
12
+ * Adds a rollback action to the given store.
13
+ * This method extends the store with a $rollbackAfterFailure function that handles rollback logic for actions.
14
+ * @param {Store} store The store to which the rollback action will be added.
15
+ * @returns void
16
+ */
17
+ addRollbackAction(store: Store): void;
18
+ /**
19
+ * Creates a rollback snapshot for the specified action and store.
20
+ * This snapshot can be used to restore the store's state in case of an error during the action execution.
21
+ * @param {string} actionName The name of the action for which the rollback snapshot is created.
22
+ * @param {Store} store The Pinia store instance.
23
+ * @returns An object containing `after` and `onError` callbacks for handling the rollback, or `undefined` if no snapshot is created.
24
+ */
25
+ createOnActionCallbacks(actionName: string, store: Store): {
26
+ after: () => void;
27
+ onError: () => void;
28
+ } | undefined;
29
+ /**
30
+ * Initializes the rollback state snapshots handler from the given Pinia plugin context.
31
+ * This method extracts the rollback snapshot parameters from the store options and stores them internally.
32
+ * @param {PiniaPluginContext} param0 The Pinia plugin context containing the store and its options.
33
+ * @returns void
34
+ */
35
+ initFromPluginContext({ options, store }: PiniaPluginContext): void;
36
+ /**
37
+ * Clears all rollback tracking data for the given store.
38
+ * Must be called when the store is disposed to avoid memory leaks with dynamically created stores.
39
+ * @param {Store} store The disposed Pinia store instance.
40
+ * @returns void
41
+ */
42
+ clearStoreTracking(store: Store): void;
43
+ private patchStore;
44
+ /**
45
+ * Removes a state snapshot identified by the given snapshot key from the internal state snapshot tracking.
46
+ * @param snapshotKey The key of the state snapshot to be removed.
47
+ * @returns void
48
+ */
49
+ private removeStateSnapshot;
50
+ /**
51
+ * Saves the current state snapshot for a given store and action.
52
+ * @param store The Pinia store instance.
53
+ * @param actionName The name of the action being executed.
54
+ * @param stateKeys The state keys to include in the snapshot, or 'all' for the full state.
55
+ * @returns A unique symbol key representing the saved snapshot.
56
+ */
57
+ private saveStateSnapshot;
58
+ /**
59
+ * Checks if the given store has rollback snapshots.
60
+ * @param store The Pinia store instance.
61
+ * @returns True if the store has rollback snapshots, false otherwise.
62
+ */
63
+ storeHasRollbackSnapshots(store: Store): boolean;
64
+ }
@@ -18,8 +18,6 @@ export default class Store extends Debug {
18
18
  get state(): StateTree;
19
19
  set state(state: StateTree);
20
20
  get store(): AnyObject;
21
- hydrate(): void | Promise<void>;
22
- afterHydration(): void | Promise<void>;
23
21
  get storeSubscribe(): StoreMutationSubscription | undefined;
24
22
  set storeSubscribe(storeSubscribe: StoreMutationSubscriptionCallback);
25
23
  constructor(store: PiniaStore, options: AnyObject, debug?: boolean, customConsole?: Console);
@@ -33,6 +31,7 @@ export default class Store extends Debug {
33
31
  subscriptionOptions?: StoreOptions;
34
32
  stores?: PiniaStore[];
35
33
  }): void;
34
+ afterHydration(): void | Promise<void>;
36
35
  /**
37
36
  * Create and return a class instance
38
37
  * @param store
@@ -41,12 +40,13 @@ export default class Store extends Debug {
41
40
  * @returns
42
41
  */
43
42
  static customizeStore<Instance extends Store>(store: PiniaStore, options: AnyObject, debug?: boolean, customConsole?: Console): Instance | undefined;
44
- hasDeniedFirstChar(property: string): boolean;
45
- protected static hasRequiredKeys(options: AnyObject): boolean;
46
43
  getOption(optionName: keyof StoreOptions): import("../types").StoreOptionsPropertyValue;
47
44
  getStatePropertyValue(propertyName: string): any;
48
45
  getSubscriptions(): PluginSubscriptions | undefined;
49
46
  getValue(value: any): any;
47
+ protected hasDeniedFirstChar(property: string): boolean;
48
+ protected static hasRequiredKeys(options: AnyObject): boolean;
49
+ hydrate(): void | Promise<void>;
50
50
  isOptionApi(): boolean;
51
51
  stateHas(property: string): boolean;
52
52
  storeHas(property: string): boolean;
@@ -0,0 +1,3 @@
1
+ import type { StateTree } from 'pinia';
2
+ import type { CreateStateSnapshotKeys } from '../types/plugin';
3
+ export declare function createStateSnapshot(state: StateTree, keys?: CreateStateSnapshotKeys): StateTree;
@@ -1,4 +1,4 @@
1
- export { default as PluginSubscriber } from '../plugins/pluginSubscriber';
1
+ export { default as PluginSubscriber } from '../core/PluginSubscriber';
2
2
  export { default as Store } from '../core/Store';
3
3
  export { CustomConsole } from '../system/log';
4
4
  export { isEmpty } from '../utils/validation';
package/dist/helpers.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as a, a as r, S as e, i } from "./chunks/index-B-zO0yrr.js";
1
+ import { C as a, a as r, S as e, i } from "./chunks/index-D-rnwSJk.js";
2
2
  export {
3
3
  a as CustomConsole,
4
4
  r as PluginSubscriber,
@@ -1,10 +1,11 @@
1
- import PluginSubscriber from '../plugins/pluginSubscriber';
2
- import PluginSubscription from '../plugins/pluginSubscription';
1
+ import PluginSubscriber from '../core/PluginSubscriber';
2
+ import PluginSubscription from '../core/PluginSubscription';
3
3
  import Store from '../core/Store';
4
4
  export { createPlugin } from "../plugins/createPlugin";
5
5
  export { createHydrationPlugin } from "../plugins/createHydrationPlugin";
6
6
  export type { AnyObject, Console, CustomStore, DefineAStoreSetupContext, DefineAStoreSetupExtensions, EmptyExtensions, PluginExecutionEnvironment, PluginExecutionOptions, PluginHydrationScheduler, PluginHydrationTiming, PluginRuntimeEnvironment, PluginStoreOptions, PluginSubscriptionOptions, PluginSubscriberInterface, PluginSubscriptionDefinition, PluginSubscriptions, StatePropertyValue, StoreOptions, StoreOptionsPropertyValue, StoreMutationSubscription, StoreMutationSubscriptionCallback, StoreOnActionCallbackParameters, StoreOnActionSubscription, StoreOnActionSubscriptionCallback, NativePiniaSubscription, NativePiniaSubscriptionReturn, SearchCollectionCriteria, LogType, StyleDefinition, StyleDefinitionKeys, StyleDefinitions, ConsoleStyleDefinition, ConsoleStyleDefinitionKeys, ConsoleStyleDefinitions, PartialList, ListTypes, ListTypesMap, List } from "../types";
7
7
  export type { StoreMutationSubscriptionReturn, StoreOnActionSubscriptionReturn } from "../types/plugin";
8
+ export type { RollbackActionParams, RollbackAfterFailureParams } from "../types/store";
8
9
  export { defineAStore, defineAStoreCtx, getDefineAStoreSetupContext, getEnhancedStore, getExtendingStore, setEnhancedStore } from "../utils/store";
9
10
  export { CustomConsole } from "../system/log";
10
11
  export { isEmpty } from "../utils/validation";
@@ -1,288 +1,498 @@
1
- var S = Object.defineProperty;
2
- var y = (r, o, e) => o in r ? S(r, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[o] = e;
3
- var c = (r, o, e) => y(r, typeof o != "symbol" ? o + "" : o, e);
4
- import { D as g, i as l, P as _ } from "./chunks/index-B-zO0yrr.js";
5
- import { C as w, p as O, a as j, S as x, d as H, b as K, g as R, c as M, e as L, s as T } from "./chunks/index-B-zO0yrr.js";
6
- const f = "PluginSubscription", m = {
7
- environment: "both",
8
- hydration: "immediate"
9
- };
10
- function E(r) {
11
- if (typeof window < "u" && typeof window.requestAnimationFrame == "function") {
12
- window.requestAnimationFrame(() => globalThis.setTimeout(r, 0));
13
- return;
14
- }
15
- globalThis.setTimeout(r, 0);
16
- }
17
- function h(r) {
1
+ var g = Object.defineProperty;
2
+ var E = (n, s, t) => s in n ? g(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[s] = t;
3
+ var a = (n, s, t) => E(n, typeof s != "symbol" ? s + "" : s, t);
4
+ import { h as O, g as f, D as m, i as y, P as C } from "./chunks/index-D-rnwSJk.js";
5
+ import { C as T, p as W, a as L, S as I, d as q, b as G, c as U, e as z, f as B, s as J } from "./chunks/index-D-rnwSJk.js";
6
+ import { toRaw as _ } from "vue";
7
+ function u(n) {
18
8
  if (typeof structuredClone == "function")
19
9
  try {
20
- return structuredClone(r);
10
+ return structuredClone(n);
21
11
  } catch {
22
12
  }
23
- if (r === null || typeof r != "object")
24
- return r;
25
- if (r instanceof Map)
13
+ if (n === null || typeof n != "object")
14
+ return n;
15
+ if (n instanceof Map)
26
16
  return new Map(
27
- Array.from(r.entries(), ([e, i]) => [h(e), h(i)])
17
+ Array.from(n.entries(), ([t, e]) => [u(t), u(e)])
28
18
  );
29
- if (r instanceof Set)
30
- return new Set(Array.from(r.values(), (e) => h(e)));
31
- if (Array.isArray(r))
32
- return r.map((e) => h(e));
33
- if (Object.getPrototypeOf(r) === Object.prototype || Object.getPrototypeOf(r) === null)
19
+ if (n instanceof Set)
20
+ return new Set(Array.from(n.values(), (t) => u(t)));
21
+ if (Array.isArray(n))
22
+ return n.map((t) => u(t));
23
+ if (Object.getPrototypeOf(n) === Object.prototype || Object.getPrototypeOf(n) === null)
34
24
  return Object.fromEntries(
35
- Object.entries(r).map(([e, i]) => [e, h(i)])
25
+ Object.entries(n).map(([t, e]) => [t, u(e)])
26
+ );
27
+ const s = Object.create(Object.getPrototypeOf(n));
28
+ for (const [t, e] of Object.entries(n))
29
+ s[t] = u(e);
30
+ return s;
31
+ }
32
+ function $(n, s = "all") {
33
+ if (Array.isArray(s)) {
34
+ const t = {};
35
+ for (const e of s)
36
+ e in n && (t[e] = u(_(n[e])));
37
+ return t;
38
+ }
39
+ if (s !== "all")
40
+ throw new Error(`Invalid keys parameter: ${s}`);
41
+ return u(_(n));
42
+ }
43
+ class v {
44
+ constructor() {
45
+ a(this, "_stateSnapshots", /* @__PURE__ */ new Map());
46
+ a(this, "_storeHasRollbackSnapshots", /* @__PURE__ */ new Set());
47
+ a(this, "_storeRollbackSnapshotsParams", /* @__PURE__ */ new Map());
48
+ }
49
+ /**
50
+ * Adds a rollback action to the given store.
51
+ * This method extends the store with a $rollbackAfterFailure function that handles rollback logic for actions.
52
+ * @param {Store} store The store to which the rollback action will be added.
53
+ * @returns void
54
+ */
55
+ addRollbackAction(s) {
56
+ typeof s.$rollbackAfterFailure != "function" && (s.$rollbackAfterFailure = async ({ action: t, stateKeys: e }, ...i) => {
57
+ const r = s[t];
58
+ if (typeof r != "function" || O(t))
59
+ return;
60
+ const o = !e || e.length === 0 ? "all" : e, h = this.saveStateSnapshot(s, t, o);
61
+ try {
62
+ await r.apply(s, i);
63
+ } catch (c) {
64
+ throw this.patchStore(h, s), c;
65
+ } finally {
66
+ this.removeStateSnapshot(h);
67
+ }
68
+ });
69
+ }
70
+ /**
71
+ * Creates a rollback snapshot for the specified action and store.
72
+ * This snapshot can be used to restore the store's state in case of an error during the action execution.
73
+ * @param {string} actionName The name of the action for which the rollback snapshot is created.
74
+ * @param {Store} store The Pinia store instance.
75
+ * @returns An object containing `after` and `onError` callbacks for handling the rollback, or `undefined` if no snapshot is created.
76
+ */
77
+ createOnActionCallbacks(s, t) {
78
+ const e = this._storeRollbackSnapshotsParams.get(
79
+ f(s, t)
36
80
  );
37
- const o = Object.create(Object.getPrototypeOf(r));
38
- for (const [e, i] of Object.entries(r))
39
- o[e] = h(i);
40
- return o;
81
+ if (!e || e.length === 0)
82
+ return;
83
+ const i = this.saveStateSnapshot(t, s, e);
84
+ return {
85
+ after: () => {
86
+ this.removeStateSnapshot(i);
87
+ },
88
+ onError: () => {
89
+ this.patchStore(i, t), this.removeStateSnapshot(i);
90
+ }
91
+ };
92
+ }
93
+ /**
94
+ * Initializes the rollback state snapshots handler from the given Pinia plugin context.
95
+ * This method extracts the rollback snapshot parameters from the store options and stores them internally.
96
+ * @param {PiniaPluginContext} param0 The Pinia plugin context containing the store and its options.
97
+ * @returns void
98
+ */
99
+ initFromPluginContext({ options: s, store: t }) {
100
+ const e = s?.storeOptions?.rollbackAfterFailure;
101
+ e && !this._storeHasRollbackSnapshots.has(t.$id) && (Object.keys(e).forEach((i) => {
102
+ this._storeRollbackSnapshotsParams.set(
103
+ f(i, t),
104
+ e[i]
105
+ );
106
+ }), this._storeHasRollbackSnapshots.add(t.$id)), this.addRollbackAction(t);
107
+ }
108
+ /**
109
+ * Clears all rollback tracking data for the given store.
110
+ * Must be called when the store is disposed to avoid memory leaks with dynamically created stores.
111
+ * @param {Store} store The disposed Pinia store instance.
112
+ * @returns void
113
+ */
114
+ clearStoreTracking(s) {
115
+ const t = `-${s.$id}`;
116
+ for (const e of Array.from(this._storeRollbackSnapshotsParams.keys()))
117
+ e.endsWith(t) && this._storeRollbackSnapshotsParams.delete(e);
118
+ this._storeHasRollbackSnapshots.delete(s.$id);
119
+ for (const e of Array.from(this._stateSnapshots.keys()))
120
+ e.description?.startsWith(`${s.$id}:`) && this._stateSnapshots.delete(e);
121
+ }
122
+ patchStore(s, t) {
123
+ const e = this._stateSnapshots.get(s);
124
+ if (e)
125
+ try {
126
+ t.$patch(e);
127
+ } catch (i) {
128
+ console.error(`Failed to patch store with snapshot ${String(s)}`, i);
129
+ }
130
+ }
131
+ /**
132
+ * Removes a state snapshot identified by the given snapshot key from the internal state snapshot tracking.
133
+ * @param snapshotKey The key of the state snapshot to be removed.
134
+ * @returns void
135
+ */
136
+ removeStateSnapshot(s) {
137
+ s && this._stateSnapshots.delete(s);
138
+ }
139
+ /**
140
+ * Saves the current state snapshot for a given store and action.
141
+ * @param store The Pinia store instance.
142
+ * @param actionName The name of the action being executed.
143
+ * @param stateKeys The state keys to include in the snapshot, or 'all' for the full state.
144
+ * @returns A unique symbol key representing the saved snapshot.
145
+ */
146
+ saveStateSnapshot(s, t, e) {
147
+ const i = $(
148
+ s.$state,
149
+ e
150
+ ), r = /* @__PURE__ */ Symbol(`${s.$id}:${t}`);
151
+ return this._stateSnapshots.set(r, i), r;
152
+ }
153
+ /**
154
+ * Checks if the given store has rollback snapshots.
155
+ * @param store The Pinia store instance.
156
+ * @returns True if the store has rollback snapshots, false otherwise.
157
+ */
158
+ storeHasRollbackSnapshots(s) {
159
+ return this._storeHasRollbackSnapshots.has(s.$id);
160
+ }
161
+ }
162
+ const A = "PluginSubscription", P = {
163
+ environment: "both",
164
+ hydration: "immediate"
165
+ };
166
+ function R(n) {
167
+ if (typeof window < "u" && typeof window.requestAnimationFrame == "function") {
168
+ window.requestAnimationFrame(() => globalThis.setTimeout(n, 0));
169
+ return;
170
+ }
171
+ globalThis.setTimeout(n, 0);
41
172
  }
42
- function A(r) {
43
- return r !== void 0 && !Array.isArray(r);
173
+ function D(n) {
174
+ return n !== void 0 && !Array.isArray(n);
44
175
  }
45
- class p extends g {
46
- constructor(e, i, t) {
47
- const s = Array.isArray(i) ? i : void 0, n = A(i) ? i : t;
48
- super(Array.isArray(s) && !!s?.includes(f), _);
49
- c(this, "_className", f);
50
- c(this, "_hydrationScheduler");
51
- c(this, "_options");
52
- c(this, "_pluginDebug");
53
- c(this, "_resetStoreCallback", []);
54
- c(this, "_subscribers", []);
55
- c(this, "_subscribersDelivered", /* @__PURE__ */ new Set());
56
- c(this, "_subscribersScheduled", /* @__PURE__ */ new Set());
57
- c(this, "_subscriptions", []);
58
- c(this, "_subscriptionsDelivered", /* @__PURE__ */ new Set());
59
- c(this, "_subscriptionsScheduled", /* @__PURE__ */ new Set());
60
- this._hydrationScheduler = n?.hydrationScheduler ?? E, this._options = n, this._pluginDebug = Array.isArray(s) ? s : void 0, this._subscribers = e;
61
- }
62
- set subscribers(e) {
63
- this._subscribers = e;
176
+ class k extends m {
177
+ constructor(t, e, i) {
178
+ const r = Array.isArray(e) ? e : void 0, o = D(e) ? e : i;
179
+ super(Array.isArray(r) && !!r?.includes(A), C);
180
+ a(this, "_className", A);
181
+ a(this, "_hydrationScheduler");
182
+ a(this, "_onActionSubscriptions", []);
183
+ a(this, "_onActionAfterSubscriptions", {});
184
+ a(this, "_onActionOnErrorSubscriptions", {});
185
+ a(this, "_options");
186
+ a(this, "_pluginDebug");
187
+ a(this, "_resetStoreCallback", []);
188
+ a(this, "_storeRollbackSnapshotsHandler", new v());
189
+ a(this, "_subscribers", []);
190
+ a(this, "_subscribersDelivered", /* @__PURE__ */ new Set());
191
+ a(this, "_subscribersScheduled", /* @__PURE__ */ new Set());
192
+ a(this, "_subscriptions", []);
193
+ a(this, "_subscriptionsDelivered", /* @__PURE__ */ new Set());
194
+ a(this, "_subscriptionsScheduled", /* @__PURE__ */ new Set());
195
+ this._hydrationScheduler = o?.hydrationScheduler ?? R, this._options = o, this._pluginDebug = Array.isArray(r) ? r : void 0, this._subscribers = t;
196
+ }
197
+ set subscribers(t) {
198
+ this._subscribers = t;
64
199
  }
65
200
  get subscriptions() {
66
- if (!l(this._subscriptions))
201
+ if (!y(this._subscriptions))
67
202
  return this._subscriptions;
68
203
  }
69
- addResetStoreCallback(e) {
70
- this._resetStoreCallback.push(e);
204
+ addOnActionAfterCallback(t, e, i) {
205
+ const r = f(t, e);
206
+ this._onActionAfterSubscriptions[r] || (this._onActionAfterSubscriptions[r] = []), this._onActionAfterSubscriptions[r]?.push(i);
207
+ }
208
+ addOnActionOnErrorCallback(t, e, i) {
209
+ const r = f(t, e);
210
+ this._onActionOnErrorSubscriptions[r] || (this._onActionOnErrorSubscriptions[r] = []), this._onActionOnErrorSubscriptions[r]?.push(i);
211
+ }
212
+ addResetStoreCallback(t) {
213
+ this._resetStoreCallback.push(t);
214
+ }
215
+ clearOnActionCallbacksSubscriptions(t) {
216
+ delete this._onActionAfterSubscriptions[t], delete this._onActionOnErrorSubscriptions[t];
217
+ }
218
+ clearStoreTracking(t) {
219
+ const e = `-${t.$id}`;
220
+ for (const i of Array.from(this._subscribersDelivered))
221
+ i.endsWith(e) && this._subscribersDelivered.delete(i);
222
+ for (const i of Array.from(this._subscribersScheduled))
223
+ i.endsWith(e) && this._subscribersScheduled.delete(i);
224
+ for (const i of Array.from(this._subscriptionsDelivered))
225
+ i.endsWith(e) && this._subscriptionsDelivered.delete(i);
226
+ for (const i of Array.from(this._subscriptionsScheduled))
227
+ i.endsWith(e) && this._subscriptionsScheduled.delete(i);
228
+ for (const i of Object.keys(this._onActionAfterSubscriptions))
229
+ i.endsWith(e) && delete this._onActionAfterSubscriptions[i];
230
+ for (const i of Object.keys(this._onActionOnErrorSubscriptions))
231
+ i.endsWith(e) && delete this._onActionOnErrorSubscriptions[i];
232
+ }
233
+ /**
234
+ * Creates a callback collector for the specified action and store.
235
+ * @param actionName The name of the action to collect callbacks for.
236
+ * @param store The store instance associated with the action.
237
+ * @returns A function that collects callbacks for the specified action and store.
238
+ */
239
+ createOnActionAfterCallbackCollector(t, e) {
240
+ return (i) => {
241
+ this.addOnActionAfterCallback(t, e, i);
242
+ };
243
+ }
244
+ /**
245
+ * Creates a callback collector for the specified action and store that handles errors.
246
+ * @param actionName The name of the action to collect error callbacks for.
247
+ * @param store The store instance associated with the action.
248
+ * @returns A function that collects error callbacks for the specified action and store.
249
+ */
250
+ createOnActionOnErrorCallbackCollector(t, e) {
251
+ return (i) => {
252
+ this.addOnActionOnErrorCallback(t, e, i);
253
+ };
254
+ }
255
+ /**
256
+ * Rolls back the state snapshot for a given action if an error occurs, and removes the snapshot after the action completes or fails.
257
+ * @param actionName The name of the action being executed.
258
+ * @param store The Pinia store instance.
259
+ * @returns void
260
+ */
261
+ createRollbackSnapshot(t, e) {
262
+ const {
263
+ after: i,
264
+ onError: r
265
+ } = this._storeRollbackSnapshotsHandler.createOnActionCallbacks(
266
+ t,
267
+ e
268
+ ) ?? {};
269
+ !i || !r || (this.addOnActionAfterCallback(t, e, i), this.addOnActionOnErrorCallback(t, e, r));
71
270
  }
72
271
  defineCurrentEnvironment() {
73
272
  return this._options?.runtimeEnvironment ?? (typeof window > "u" ? "server" : "client");
74
273
  }
75
- definePluginDebug(e) {
76
- return !!this._pluginDebug?.includes(e.name);
274
+ definePluginDebug(t) {
275
+ return !!this._pluginDebug?.includes(t.name);
77
276
  }
78
- definePluginExecution(e) {
277
+ definePluginExecution(t) {
79
278
  return {
80
- ...m,
279
+ ...P,
81
280
  ...this._options?.execution ?? {},
82
- ...e.execution ?? {},
83
- ...this._options?.subscriberExecution?.[e.name] ?? {}
281
+ ...t.execution ?? {},
282
+ ...this._options?.subscriberExecution?.[t.name] ?? {}
84
283
  };
85
284
  }
86
- defineHydrationScheduler(e) {
87
- return e.hydrationScheduler ?? this._hydrationScheduler;
285
+ defineHydrationScheduler(t) {
286
+ return t.hydrationScheduler ?? this._hydrationScheduler;
88
287
  }
89
- defineSubscriberKey(e, i) {
90
- return `${e.name}-${i.$id}`;
288
+ defineSubscriberKey(t, e) {
289
+ return `${t.name}-${e.$id}`;
91
290
  }
92
- defineSubscriptionKey(e, i) {
93
- return `${e}-${i.$id}`;
291
+ defineSubscriptionKey(t, e) {
292
+ return `${t}-${e.$id}`;
94
293
  }
95
- executeResetStoreCallbacks(e) {
96
- this._resetStoreCallback.forEach((i) => i(e));
294
+ executeResetStoreCallbacks(t) {
295
+ this._resetStoreCallback.forEach((e) => e(t));
97
296
  }
98
- executeSubscriber(e, i) {
99
- const t = this.defineSubscriberKey(i, e.store), s = this.definePluginDebug(i);
100
- if (this._subscribersDelivered.has(t) || !i.invoke(
101
- e,
102
- s
297
+ /**
298
+ * Executes the store's onAction subscription for the given plugin subscriber.
299
+ * @param store The Pinia store instance.
300
+ * @param subscriber The plugin subscriber.
301
+ * @returns void
302
+ */
303
+ executeStoreOnActionSubscription(t) {
304
+ const e = this._onActionSubscriptions, i = this._storeRollbackSnapshotsHandler.storeHasRollbackSnapshots(t);
305
+ !e?.length && !i || t.$onAction(({ after: r, args: o, name: h, onError: c }) => {
306
+ const l = f(h, t);
307
+ this.debugLog(`storeOnActionSubscription ${l}`, { args: o, name: h, store: t }), i && this.createRollbackSnapshot(h, t), e?.length && e.forEach((b) => b({
308
+ after: this.createOnActionAfterCallbackCollector(h, t),
309
+ args: o,
310
+ name: h,
311
+ onError: this.createOnActionOnErrorCallbackCollector(h, t)
312
+ }));
313
+ const d = this._onActionAfterSubscriptions[l], S = this._onActionOnErrorSubscriptions[l];
314
+ this.clearOnActionCallbacksSubscriptions(l), d?.length && r((b) => {
315
+ d.forEach((p) => p(b));
316
+ }), S?.length && c((b) => {
317
+ S.forEach((p) => p(b));
318
+ });
319
+ });
320
+ }
321
+ executeSubscriber(t, e) {
322
+ const i = this.defineSubscriberKey(e, t.store), r = this.definePluginDebug(e);
323
+ if (this._subscribersDelivered.has(i) || !e.invoke(
324
+ t,
325
+ r
103
326
  ))
104
327
  return;
105
- this._subscribersDelivered.add(t);
106
- let n;
328
+ this._subscribersDelivered.add(i);
329
+ let o;
107
330
  try {
108
- n = i.hydrate?.(e, s);
109
- } catch (u) {
110
- this.logError(u, e.store, e.options);
331
+ o = e.hydrate?.(t, r);
332
+ } catch (c) {
333
+ this.logError(c, t.store, t.options);
111
334
  return;
112
335
  }
113
- const d = () => {
336
+ const h = () => {
114
337
  try {
115
- const u = i.afterHydration?.(e, s);
116
- u && typeof u.then == "function" && u.catch((a) => this.logError(a, e.store, e.options));
117
- } catch (u) {
118
- this.logError(u, e.store, e.options);
338
+ const c = e.afterHydration?.(t, r);
339
+ c && typeof c.then == "function" && c.catch((l) => this.logError(l, t.store, t.options));
340
+ } catch (c) {
341
+ this.logError(c, t.store, t.options);
119
342
  }
120
343
  };
121
- n && typeof n.then == "function" ? n.then(() => d()).catch((u) => this.logError(u, e.store, e.options)) : d(), i.subscriptions && this.subscriptionDelivery(e, i.subscriptions), i.storeMutationSubscription && this.storeMutationSubscription(i.storeMutationSubscription), i.storeOnActionSubscription && this.storeOnActionSubscription(i.storeOnActionSubscription), i.resetStoreCallback && this.addResetStoreCallback(i.resetStoreCallback);
344
+ o && typeof o.then == "function" ? o.then(() => h()).catch((c) => this.logError(c, t.store, t.options)) : h(), e.subscriptions && this.subscriptionDelivery(t, e.subscriptions), e.storeMutationSubscription && this.storeMutationSubscription(e.storeMutationSubscription), this.storeOnActionSubscription(e), e.resetStoreCallback && this.addResetStoreCallback(e.resetStoreCallback);
122
345
  }
123
- executeSubscriberSafely(e, i) {
346
+ executeSubscriberSafely(t, e) {
124
347
  try {
125
- this.executeSubscriber(e, i);
126
- } catch (t) {
127
- this.logError(t, e.store, e.options);
348
+ this.executeSubscriber(t, e);
349
+ } catch (i) {
350
+ this.logError(i, t.store, t.options);
128
351
  }
129
352
  }
130
- queueSubscriberExecution(e, i, t) {
131
- const s = this.defineHydrationScheduler(i);
132
- this._subscribersScheduled.add(t), s(() => {
133
- this._subscribersScheduled.delete(t), this.executeSubscriberSafely(e, i);
134
- });
135
- }
136
- shouldInvokePlugin(e) {
137
- const i = this.defineCurrentEnvironment();
138
- return e.environment === "both" || e.environment === i;
139
- }
140
- shouldScheduleAfterHydration(e) {
141
- return this.defineCurrentEnvironment() === "client" && e.hydration === "defer";
142
- }
143
- plugin({ store: e, options: i }) {
353
+ plugin({ store: t, options: e }) {
144
354
  if (this._subscribers.length) {
145
- this.debugLog(`plugin() - ${e.$id}`, [
355
+ this.debugLog(`plugin() - ${t.$id}`, [
146
356
  "subscriber:",
147
357
  this._subscribers,
148
358
  "store:",
149
- e,
359
+ t,
150
360
  "options:",
151
- i
361
+ e
152
362
  ]);
153
363
  try {
154
364
  this._subscribers.forEach(
155
- (t) => {
156
- const s = this.defineSubscriberKey(t, e), n = this.definePluginExecution(t);
157
- if (!(this._subscribersDelivered.has(s) || this._subscribersScheduled.has(s)) && this.shouldInvokePlugin(n)) {
158
- if (this.shouldScheduleAfterHydration(n)) {
159
- this.queueSubscriberExecution({ store: e, options: i }, t, s);
365
+ (i) => {
366
+ const r = this.defineSubscriberKey(i, t), o = this.definePluginExecution(i);
367
+ if (!(this._subscribersDelivered.has(r) || this._subscribersScheduled.has(r)) && this.shouldInvokePlugin(o)) {
368
+ if (this.shouldScheduleAfterHydration(o)) {
369
+ this.queueSubscriberExecution({ store: t, options: e }, i, r);
160
370
  return;
161
371
  }
162
- this.executeSubscriber({ store: e, options: i }, t);
372
+ this.executeSubscriber({ store: t, options: e }, i);
163
373
  }
164
374
  }
165
- ), this.rewriteResetStore({ store: e }, h(e.$state), Object.assign({}, e)), this.registerStoreCleanup(e);
166
- } catch (t) {
167
- this.logError(t, e, i);
375
+ ), this.rewriteResetStore({ store: t }, t.$state), this._storeRollbackSnapshotsHandler.initFromPluginContext({ store: t, options: e }), this.executeStoreOnActionSubscription(t), this.registerStoreCleanup(t);
376
+ } catch (i) {
377
+ this.logError(i, t, e);
168
378
  }
169
379
  }
170
380
  }
171
- rewriteResetStore({ store: e }, i, t) {
172
- const s = h(i);
173
- e.$reset = () => {
174
- this.debugLog("rewriteResetStore()", { initState: s, store: e, customStore: t }), this.executeResetStoreCallbacks(e), e.$patch(h(s));
175
- };
381
+ queueSubscriberExecution(t, e, i) {
382
+ const r = this.defineHydrationScheduler(e);
383
+ this._subscribersScheduled.add(i), r(() => {
384
+ this._subscribersScheduled.delete(i), this.executeSubscriberSafely(t, e);
385
+ });
176
386
  }
177
- registerStoreCleanup(e) {
178
- if (typeof e.$dispose != "function")
387
+ registerStoreCleanup(t) {
388
+ if (this._onActionSubscriptions = [], typeof t.$dispose != "function")
179
389
  return;
180
- const i = e.$dispose.bind(e), t = e;
181
- t.__piniaPluginSubscriptionDisposed || (t.__piniaPluginSubscriptionDisposed = !0, e.$dispose = () => (this.clearStoreTracking(e), i()));
182
- }
183
- clearStoreTracking(e) {
184
- const i = `-${e.$id}`;
185
- for (const t of Array.from(this._subscribersDelivered))
186
- t.endsWith(i) && this._subscribersDelivered.delete(t);
187
- for (const t of Array.from(this._subscribersScheduled))
188
- t.endsWith(i) && this._subscribersScheduled.delete(t);
189
- for (const t of Array.from(this._subscriptionsDelivered))
190
- t.endsWith(i) && this._subscriptionsDelivered.delete(t);
191
- for (const t of Array.from(this._subscriptionsScheduled))
192
- t.endsWith(i) && this._subscriptionsScheduled.delete(t);
193
- }
194
- storeOnActionSubscription(e) {
195
- const { store: i, callback: t } = e();
196
- i.$onAction(({ after: s, args: n, name: d }) => {
197
- this.debugLog(`storeOnActionSubscription ${i.$id}`, { after: s, args: n, name: d, store: i }), t({ after: s, args: n, name: d });
198
- });
390
+ const e = t.$dispose.bind(t), i = t;
391
+ i.__piniaPluginSubscriptionDisposed || (i.__piniaPluginSubscriptionDisposed = !0, t.$dispose = () => (this.clearStoreTracking(t), this._storeRollbackSnapshotsHandler.clearStoreTracking(t), e()));
392
+ }
393
+ rewriteResetStore({ store: t }, e) {
394
+ const i = u(e);
395
+ t.$reset = () => {
396
+ this.debugLog("rewriteResetStore()", { initState: i, store: t }), this.executeResetStoreCallbacks(t), t.$patch(u(i));
397
+ };
199
398
  }
200
- storeMutationSubscription(e) {
201
- const { store: i, callback: t } = e();
202
- i.$subscribe((s) => {
203
- this.debugLog(`$subscribe ${i.$id}`, { mutation: s, store: i }), t(s);
399
+ shouldInvokePlugin(t) {
400
+ const e = this.defineCurrentEnvironment();
401
+ return t.environment === "both" || t.environment === e;
402
+ }
403
+ shouldScheduleAfterHydration(t) {
404
+ return this.defineCurrentEnvironment() === "client" && t.hydration === "defer";
405
+ }
406
+ storeOnActionSubscription(t) {
407
+ const { store: e, callback: i } = t?.storeOnActionSubscription?.() ?? {};
408
+ !e || !i || this._onActionSubscriptions.push(i);
409
+ }
410
+ storeMutationSubscription(t) {
411
+ const { store: e, callback: i } = t();
412
+ e.$subscribe((r) => {
413
+ this.debugLog(`$subscribe ${e.$id}`, { mutation: r, store: e }), i(r);
204
414
  });
205
415
  }
206
- subscriptionDelivery(e, i) {
207
- Object.entries(i).forEach(([t, s]) => {
208
- const n = this.defineSubscriptionKey(t, e.store), d = this.definePluginExecution(s.subscription);
209
- if (!(this._subscriptionsDelivered.has(n) || this._subscriptionsScheduled.has(n)) && this.shouldInvokePlugin(d)) {
210
- if (this.shouldScheduleAfterHydration(d)) {
211
- const u = this.defineHydrationScheduler(s.subscription);
212
- this._subscriptionsScheduled.add(n), u(() => {
213
- this._subscriptionsScheduled.delete(n), this.subscriptionDeliverySafely(e, t, s);
416
+ subscriptionDelivery(t, e) {
417
+ Object.entries(e).forEach(([i, r]) => {
418
+ const o = this.defineSubscriptionKey(i, t.store), h = this.definePluginExecution(r.subscription);
419
+ if (!(this._subscriptionsDelivered.has(o) || this._subscriptionsScheduled.has(o)) && this.shouldInvokePlugin(h)) {
420
+ if (this.shouldScheduleAfterHydration(h)) {
421
+ const c = this.defineHydrationScheduler(r.subscription);
422
+ this._subscriptionsScheduled.add(o), c(() => {
423
+ this._subscriptionsScheduled.delete(o), this.subscriptionDeliverySafely(t, i, r);
214
424
  });
215
425
  return;
216
426
  }
217
- this.subscriptionDeliverySafely(e, t, s);
427
+ this.subscriptionDeliverySafely(t, i, r);
218
428
  }
219
429
  });
220
430
  }
221
- subscriptionDeliverySafely({ store: e, options: i }, t, s) {
431
+ subscriptionDeliverySafely({ store: t, options: e }, i, r) {
222
432
  try {
223
- if (this._subscriptionsDelivered.has(this.defineSubscriptionKey(t, e)))
433
+ if (this._subscriptionsDelivered.has(this.defineSubscriptionKey(i, t)))
224
434
  return;
225
- const { subscription: n, subscriptionOptions: d, stores: u } = s;
226
- this.debugLog(`subscriptionDelivery() - store: ${e.$id}`, [
435
+ const { subscription: o, subscriptionOptions: h, stores: c } = r;
436
+ this.debugLog(`subscriptionDelivery() - store: ${t.$id}`, [
227
437
  "pluginName:",
228
- t,
438
+ i,
229
439
  "subscription:",
230
- n,
440
+ o,
231
441
  "options:",
232
- s,
442
+ r,
233
443
  "stores:",
234
- u
444
+ c
235
445
  ]);
236
- const a = {
237
- storeOptions: { ...i?.storeOptions ?? {}, ...d }
446
+ const l = {
447
+ storeOptions: { ...e?.storeOptions ?? {}, ...h }
238
448
  };
239
- n.invoke(
240
- { store: e, options: { ...i, ...a } },
449
+ o.invoke(
450
+ { store: t, options: { ...e, ...l } },
241
451
  this.debug
242
- ), this._subscriptionsDelivered.add(this.defineSubscriptionKey(t, e)), l(u) || u?.forEach(
243
- (b) => n.invoke(
452
+ ), this._subscriptionsDelivered.add(this.defineSubscriptionKey(i, t)), y(c) || c?.forEach(
453
+ (d) => o.invoke(
244
454
  {
245
- store: b,
455
+ store: d,
246
456
  options: {
247
- ...i,
248
- ...a,
249
- ...b?.storeOptions ?? {}
457
+ ...e,
458
+ ...l,
459
+ ...d?.storeOptions ?? {}
250
460
  }
251
461
  },
252
462
  this.debug
253
463
  )
254
464
  );
255
- } catch (n) {
256
- this.logError("subscriptionDelivery()", n, e, i);
465
+ } catch (o) {
466
+ this.logError("subscriptionDelivery()", o, t, e);
257
467
  }
258
468
  }
259
469
  }
260
- function $(r, o) {
261
- const e = new p(r, o);
262
- return e.plugin.bind(e);
470
+ function j(n, s) {
471
+ const t = new k(n, s);
472
+ return t.plugin.bind(t);
263
473
  }
264
- function P(r, o) {
265
- const e = new p(r, o?.debug, {
266
- execution: o?.execution,
267
- hydrationScheduler: o?.hydrationScheduler,
268
- runtimeEnvironment: o?.runtimeEnvironment,
269
- subscriberExecution: o?.subscriberExecution
474
+ function K(n, s) {
475
+ const t = new k(n, s?.debug, {
476
+ execution: s?.execution,
477
+ hydrationScheduler: s?.hydrationScheduler,
478
+ runtimeEnvironment: s?.runtimeEnvironment,
479
+ subscriberExecution: s?.subscriberExecution
270
480
  });
271
- return e.plugin.bind(e);
481
+ return t.plugin.bind(t);
272
482
  }
273
483
  export {
274
- w as CustomConsole,
275
- O as PLUGIN_NAME,
276
- j as PluginSubscriber,
277
- p as PluginSubscription,
278
- x as Store,
279
- P as createHydrationPlugin,
280
- $ as createPlugin,
281
- H as defineAStore,
282
- K as defineAStoreCtx,
283
- R as getDefineAStoreSetupContext,
284
- M as getEnhancedStore,
285
- L as getExtendingStore,
286
- l as isEmpty,
287
- T as setEnhancedStore
484
+ T as CustomConsole,
485
+ W as PLUGIN_NAME,
486
+ L as PluginSubscriber,
487
+ k as PluginSubscription,
488
+ I as Store,
489
+ K as createHydrationPlugin,
490
+ j as createPlugin,
491
+ q as defineAStore,
492
+ G as defineAStoreCtx,
493
+ U as getDefineAStoreSetupContext,
494
+ z as getEnhancedStore,
495
+ B as getExtendingStore,
496
+ y as isEmpty,
497
+ J as setEnhancedStore
288
498
  };
@@ -1,9 +1,14 @@
1
1
  import type { PiniaPlugin } from "pinia";
2
2
  import type { PluginSubscriber } from "../types/plugin";
3
- import type { StoreOptions } from "../types/store";
3
+ import type { RollbackActionParams, StoreOptions } from "../types/store";
4
4
  export declare function createPlugin(subscribers: PluginSubscriber[], debug?: string[]): PiniaPlugin;
5
5
  declare module 'pinia' {
6
6
  interface PiniaCustomProperties {
7
+ /**
8
+ * Executes an action and restores the previous state snapshot if it fails.
9
+ * Added to every store when the plugin is active.
10
+ */
11
+ $rollbackAfterFailure: (params: RollbackActionParams, ...args: any[]) => Promise<void>;
7
12
  }
8
13
  interface DefineStoreOptionsBase<S, Store> {
9
14
  storeOptions?: StoreOptions;
@@ -1,6 +1,7 @@
1
1
  import type { PiniaPluginContext, StateTree, Store, SubscriptionCallbackMutation } from "pinia";
2
2
  import type { StoreOptions } from "./store";
3
3
  import type { Console, PluginSubscriberInterface } from ".";
4
+ export type CreateStateSnapshotKeys = (keyof StateTree)[] | 'all';
4
5
  export type PluginHydrationScheduler = (callback: () => void) => void;
5
6
  export type PluginHydrationTiming = 'defer' | 'immediate';
6
7
  export type PluginRuntimeEnvironment = 'client' | 'server';
@@ -33,10 +34,13 @@ export interface NativePiniaSubscriptionReturn<Callback> {
33
34
  store: Store;
34
35
  callback: Callback;
35
36
  }
37
+ export type StoreOnActionAfterCallbackParameter = (callback: (result?: unknown) => void) => void;
38
+ export type StoreOnActionOnErrorCallbackParameter = (callback: (error: unknown) => void) => void;
36
39
  export interface StoreOnActionCallbackParameters {
37
- after: Function;
40
+ after: StoreOnActionAfterCallbackParameter;
38
41
  args: any[] | object;
39
42
  name: string;
43
+ onError?: StoreOnActionOnErrorCallbackParameter;
40
44
  }
41
45
  export interface PluginSubscription {
42
46
  stores?: Store[];
@@ -1,4 +1,5 @@
1
1
  import type { AnyObject } from ".";
2
+ import type { CreateStateSnapshotKeys } from "./plugin";
2
3
  import type { PiniaCustomProperties, Store, StoreDefinition } from "pinia";
3
4
  import type { Ref } from "vue";
4
5
  export type CustomStore<TStore, TState> = Store & TStore & TState & PiniaCustomProperties & StoreDefinition;
@@ -20,8 +21,18 @@ export type OptionBaseProperty = string | number | boolean | null | undefined |
20
21
  export interface PluginStoreOptions {
21
22
  storeOptions: StoreOptions;
22
23
  }
24
+ export interface RollbackActionParams {
25
+ action: string;
26
+ stateKeys?: CreateStateSnapshotKeys;
27
+ }
28
+ export type RollbackAfterFailureParams = {
29
+ [key: string]: CreateStateSnapshotKeys;
30
+ };
23
31
  export type StatePropertyValue = StdStatePropertyValue | Ref<StdStatePropertyValue> | StdStatePropertyValue[] | Ref<StdStatePropertyValue[]> | Ref<StdStatePropertyValue>[];
24
32
  type StdStatePropertyValue = AnyObject | boolean | null | number | string | undefined;
25
- export type StoreOptions = Record<string, StoreOptionsPropertyValue>;
33
+ export interface StoreOptions {
34
+ [key: string]: StoreOptionsPropertyValue;
35
+ rollbackAfterFailure?: RollbackAfterFailureParams;
36
+ }
26
37
  export type StoreOptionsPropertyValue = OptionBaseProperty | OptionBaseProperty[] | ObjectBaseProperty | ObjectBaseProperty[];
27
38
  export {};
@@ -0,0 +1 @@
1
+ export declare function deepClone<T>(value: T): T;
@@ -1,4 +1,4 @@
1
- import type { DefineStoreOptions, StateTree, StoreDefinition } from "pinia";
1
+ import type { DefineStoreOptions, StateTree, Store, StoreDefinition } from "pinia";
2
2
  import type { AnyObject } from "../types";
3
3
  import type { DefineAStoreSetupContext, EmptyExtensions, PluginStoreOptions, StoreOptions } from "../types/store";
4
4
  export declare const itemState: {
@@ -28,4 +28,11 @@ export declare function setEnhancedStore<TEnhancedStore>(ctx: DefineAStoreSetupC
28
28
  * @deprecated Use getEnhancedStore instead.
29
29
  */
30
30
  export declare function getExtendingStore<TEnhancedStore>(ctx: DefineAStoreSetupContext<TEnhancedStore>): TEnhancedStore;
31
+ /**
32
+ * Get the storage key for a given action and store combination.
33
+ * @param actionName The name of the action.
34
+ * @param store The Pinia store instance.
35
+ * @returns The storage key for the action and store combination.
36
+ */
37
+ export declare function getActionStoreKey(actionName: string, store: Store): string;
31
38
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinia-plugin-subscription",
3
- "version": "0.1.7",
3
+ "version": "0.2.0-beta.0",
4
4
  "description": "Pinia plugin for Vue.js that helps building Pinia plugins by centralizing subscriber registration and providing a Store base class for store helpers.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,40 +0,0 @@
1
- import Debug from "../system/Debug";
2
- import type { PiniaPluginContext } from "pinia";
3
- import type { PluginSubscriber, PluginSubscriptionOptions, PluginSubscriptions } from "../types/plugin";
4
- export default class PluginSubscription extends Debug {
5
- protected _className: string;
6
- private _hydrationScheduler;
7
- private _options?;
8
- private _pluginDebug?;
9
- private _resetStoreCallback;
10
- private _subscribers;
11
- private _subscribersDelivered;
12
- private _subscribersScheduled;
13
- private _subscriptions;
14
- private _subscriptionsDelivered;
15
- private _subscriptionsScheduled;
16
- set subscribers(subscribers: PluginSubscriber[]);
17
- get subscriptions(): PluginSubscriptions[] | undefined;
18
- constructor(subscribers: PluginSubscriber[], debugOrOptions?: PluginSubscriptionOptions | string[], pluginOptions?: PluginSubscriptionOptions);
19
- private addResetStoreCallback;
20
- private defineCurrentEnvironment;
21
- private definePluginDebug;
22
- private definePluginExecution;
23
- private defineHydrationScheduler;
24
- private defineSubscriberKey;
25
- private defineSubscriptionKey;
26
- private executeResetStoreCallbacks;
27
- private executeSubscriber;
28
- private executeSubscriberSafely;
29
- private queueSubscriberExecution;
30
- private shouldInvokePlugin;
31
- private shouldScheduleAfterHydration;
32
- plugin({ store, options }: PiniaPluginContext): void;
33
- private rewriteResetStore;
34
- private registerStoreCleanup;
35
- private clearStoreTracking;
36
- private storeOnActionSubscription;
37
- private storeMutationSubscription;
38
- private subscriptionDelivery;
39
- private subscriptionDeliverySafely;
40
- }