@atoapayments/pinia-store-lifecycle-manager 0.0.1
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/README.md +320 -0
- package/dist/index.cjs +242 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +215 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# Pinia Store Lifecycle Manager
|
|
2
|
+
|
|
3
|
+
A robust Pinia plugin designed to manage store lifecycles seamlessly, including state resets and refreshes. Enhance your Vue applications with streamlined state management tailored for dynamic development needs.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Installation](#installation)
|
|
7
|
+
- [Usage](#usage)
|
|
8
|
+
- [Basic Setup](#basic-setup)
|
|
9
|
+
- [Advanced Configuration](#advanced-configuration)
|
|
10
|
+
- [Features](#features)
|
|
11
|
+
- [Use Cases](#use-cases)
|
|
12
|
+
- [API Reference](#api-reference)
|
|
13
|
+
- [Why Choose Pinia Store Lifecycle Manager](#why-choose-pinia-store-lifecycle-manager)
|
|
14
|
+
- [License](#license)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
You can install the Pinia Store Lifecycle Manager using npm or Yarn:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @atoapayments/pinia-store-lifecycle-manager
|
|
22
|
+
# or
|
|
23
|
+
yarn add @atoapayments/pinia-store-lifecycle-manager
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Basic Setup
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
//vue
|
|
32
|
+
import { createPinia } from "pinia";
|
|
33
|
+
import { PiniaStoreLifecycleManager } from "pinia-store-lifecycle-manager";
|
|
34
|
+
|
|
35
|
+
const pinia = createPinia();
|
|
36
|
+
pinia.use((context) => {
|
|
37
|
+
PiniaStoreLifecycleManager(context, (lifecycleEvents) => {
|
|
38
|
+
// Register your lifecycle event handlers here
|
|
39
|
+
// For example:
|
|
40
|
+
// lifecycleEvents.reset();
|
|
41
|
+
// lifecycleEvents.reconfigure();
|
|
42
|
+
// lifecycleEvents.refresh({ mode: 'full' });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### Nuxt
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// \plugin\pinia-store-lifecycle-manager.ts
|
|
51
|
+
import type { Pinia } from "pinia";
|
|
52
|
+
import { PiniaStoreLifecycleManager } from "pinia-store-lifecycle-manager";
|
|
53
|
+
|
|
54
|
+
export default defineNuxtPlugin(({ $pinia }) => {
|
|
55
|
+
($pinia as Pinia).use((pluginContext) => {
|
|
56
|
+
return PiniaStoreLifecycleManager(
|
|
57
|
+
pluginContext,
|
|
58
|
+
(lifecycleEvents) => {
|
|
59
|
+
// Use event listeners of your choice
|
|
60
|
+
const { $listen } = useNuxtApp();
|
|
61
|
+
|
|
62
|
+
// Register event listeners
|
|
63
|
+
$listen("user:logOut", () => {
|
|
64
|
+
lifecycleEvents.reset();
|
|
65
|
+
});
|
|
66
|
+
$listen("user:switchProfile", () => {
|
|
67
|
+
// Reset the store and perform a full refresh when switching profile
|
|
68
|
+
lifecycleEvents.reset();
|
|
69
|
+
lifecycleEvents.refresh({
|
|
70
|
+
mode: "full",
|
|
71
|
+
includeResetOnlyActions: true,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
$listen("user:anyEvent", () => {
|
|
75
|
+
// Reconfigure the store and perform a partial refresh
|
|
76
|
+
lifecycleEvents.reconfigure();
|
|
77
|
+
lifecycleEvents.refresh({ mode: "partial" });
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
enableDebugLogs: __DEV__,
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Advanced Configuration
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { createPinia } from "pinia";
|
|
93
|
+
import { PiniaStoreLifecycleManager } from "pinia-store-lifecycle-manager";
|
|
94
|
+
|
|
95
|
+
const pinia = createPinia();
|
|
96
|
+
pinia.use((context) => {
|
|
97
|
+
PiniaStoreLifecycleManager(
|
|
98
|
+
context,
|
|
99
|
+
(lifecycleEvents) => {
|
|
100
|
+
// Define custom lifecycle event handlers
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
enableDebugLogs: true,
|
|
104
|
+
enableSSR: false,
|
|
105
|
+
disableAutoRegister: false,
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Features
|
|
112
|
+
|
|
113
|
+
- **State Reset:** Easily reset the store to its initial state.
|
|
114
|
+
- **Reconfiguration:** Update multiple store properties simultaneously.
|
|
115
|
+
- **Data Refresh:** Refresh store data by invoking specified actions.
|
|
116
|
+
- **Debug Logging:** Enable detailed logging for lifecycle events and actions.
|
|
117
|
+
- **SSR Support:** Compatible with server-side rendering setups.
|
|
118
|
+
|
|
119
|
+
## Use Cases
|
|
120
|
+
|
|
121
|
+
- **User Authentication:** Reset store states upon user logout.
|
|
122
|
+
- **Dynamic Configuration:** Reconfigure store settings based on different environments.
|
|
123
|
+
- **Real-time Data Management:** Refresh store data in response to real-time events.
|
|
124
|
+
- **State Management Optimization:** Streamline complex state transitions.
|
|
125
|
+
|
|
126
|
+
## API Reference
|
|
127
|
+
|
|
128
|
+
### Lifecycle Options
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
interface DefineStoreOptionsBase<S extends StateTree, Store> {
|
|
132
|
+
lifecycleOptions?: {
|
|
133
|
+
clean?: CleanOptions<Store> | CleanFunction<Store> | (() => CleanOptions<Store> | void);
|
|
134
|
+
reConfigure?: ReconfigureOptions<Store> | ReconfigureFunction<Store> | (() => ReconfigureOptions<Store> | void);
|
|
135
|
+
refresh?: RefreshOptions<Store> | RefreshFunction<Store> | (() => RefreshOptions<Store> | void);
|
|
136
|
+
disableListener?: boolean;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Clean and Reconfigure
|
|
142
|
+
|
|
143
|
+
Both `clean` and `reConfigure` can be defined as an object, a function that takes the store as an argument, or a function that takes no arguments:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// As an object
|
|
147
|
+
clean: {
|
|
148
|
+
user: null,
|
|
149
|
+
token: '',
|
|
150
|
+
isAuthenticated: false
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// As a function with store argument
|
|
154
|
+
clean: (store) => ({
|
|
155
|
+
user: null,
|
|
156
|
+
token: '',
|
|
157
|
+
isAuthenticated: false
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// As a function without arguments
|
|
161
|
+
clean: () => ({
|
|
162
|
+
user: null,
|
|
163
|
+
token: '',
|
|
164
|
+
isAuthenticated: false
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// Reconfigure examples (similar pattern)
|
|
168
|
+
reConfigure: {
|
|
169
|
+
theme: 'dark',
|
|
170
|
+
language: 'en'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
reConfigure: (store) => ({
|
|
174
|
+
theme: 'dark',
|
|
175
|
+
language: 'en'
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
reConfigure: () => ({
|
|
179
|
+
theme: 'dark',
|
|
180
|
+
language: 'en'
|
|
181
|
+
})
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Refresh
|
|
185
|
+
|
|
186
|
+
The `refresh` option can be defined as an object, a function that takes the store as an argument, or a function that takes no arguments:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// As an object
|
|
190
|
+
refresh: {
|
|
191
|
+
fetchUserProfile: { params: [], resetOnly: false },
|
|
192
|
+
fetchUserSettings: { params: [], resetOnly: true }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// As a function with store argument
|
|
196
|
+
refresh: (store) => ({
|
|
197
|
+
fetchUserProfile: { params: [] },
|
|
198
|
+
fetchUserSettings: { params: [], resetOnly: true }
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// As a function without arguments
|
|
202
|
+
refresh: () => ({
|
|
203
|
+
fetchUserProfile: { params: [] },
|
|
204
|
+
fetchUserSettings: { params: [], resetOnly: true }
|
|
205
|
+
})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Refresh Options
|
|
209
|
+
|
|
210
|
+
When calling the `refresh` function, you can specify options:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
type PiniaStoreLifecycleManagerRefreshOptions = {
|
|
214
|
+
mode: "full" | "partial";
|
|
215
|
+
includeResetOnlyActions?: boolean;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Use 'full' mode when you need to refresh all data, including reset-only actions.
|
|
220
|
+
* This is useful for major state changes, like switching user accounts.
|
|
221
|
+
*
|
|
222
|
+
* Use 'partial' mode for routine updates or when switching to a related context
|
|
223
|
+
* that doesn't require a full data refresh.
|
|
224
|
+
*/
|
|
225
|
+
lifecycleEvents.refresh({ mode: 'full', includeResetOnlyActions: true });
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### Property Exclusions
|
|
229
|
+
|
|
230
|
+
The `clean`, `reConfigure`, and `refresh` options automatically exclude certain properties to ensure safe state management:
|
|
231
|
+
|
|
232
|
+
- Pinia's internal properties (starting with `$`)
|
|
233
|
+
- Conventionally private properties (starting with `_`)
|
|
234
|
+
- Non-writable computed properties
|
|
235
|
+
- State properties defined in the store
|
|
236
|
+
|
|
237
|
+
This ensures that only appropriate properties are modified during lifecycle operations.
|
|
238
|
+
|
|
239
|
+
#### Clean and Reconfigure
|
|
240
|
+
|
|
241
|
+
Both `clean` and `reConfigure` can be defined as either an object or a function:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// As an object
|
|
245
|
+
clean: {
|
|
246
|
+
user: null,
|
|
247
|
+
token: '',
|
|
248
|
+
isAuthenticated: false
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// As a function
|
|
252
|
+
clean: (store) => ({
|
|
253
|
+
previousUserId: store.user?.id,
|
|
254
|
+
user: null,
|
|
255
|
+
token: '',
|
|
256
|
+
isAuthenticated: false
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// Reconfigure example
|
|
260
|
+
reConfigure: () => ({
|
|
261
|
+
theme: 'dark',
|
|
262
|
+
language: 'en',
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### Refresh
|
|
267
|
+
|
|
268
|
+
The `refresh` option can be defined as either an object or a function:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// As an object
|
|
272
|
+
refresh: {
|
|
273
|
+
fetchUserProfile: { params: ['userId'] },
|
|
274
|
+
fetchUserSettings: { params: [], resetOnly: true }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// As a function
|
|
278
|
+
refresh: (store) => ({
|
|
279
|
+
fetchUserProfile: { params: [store.userId] },
|
|
280
|
+
fetchUserSettings: { params: [], resetOnly: true }
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Property Inclusions and Exclusions
|
|
285
|
+
|
|
286
|
+
The `clean` and `reConfigure` options include:
|
|
287
|
+
- Data variables (including list types)
|
|
288
|
+
- Writable computed properties
|
|
289
|
+
|
|
290
|
+
They automatically exclude:
|
|
291
|
+
- Pinia's internal properties (starting with `$`)
|
|
292
|
+
- Conventionally private properties (starting with `_`)
|
|
293
|
+
- Non-writable computed properties
|
|
294
|
+
- Function properties
|
|
295
|
+
|
|
296
|
+
This ensures that only appropriate properties are modified during lifecycle operations while including all relevant data types.
|
|
297
|
+
|
|
298
|
+
#### Readonly Properties
|
|
299
|
+
|
|
300
|
+
The `clean` and `reConfigure` operations automatically skip readonly properties to prevent errors:
|
|
301
|
+
|
|
302
|
+
- If a property is readonly or a non-writable computed property, it will not be modified during reset or reconfigure operations.
|
|
303
|
+
- When debug logs are enabled, a warning with a 🚫 emoji will be issued for each skipped non-writable property, reminding developers to avoid including such properties in clean or reConfigure options.
|
|
304
|
+
|
|
305
|
+
This ensures that only modifiable properties are affected by lifecycle operations, maintaining the integrity of readonly state and computed properties.
|
|
306
|
+
|
|
307
|
+
> **Open Issue / Upcoming Enhancement:**
|
|
308
|
+
> We are aware that non-writable properties can currently be included in `clean` and `reConfigure` options, which is not ideal. We plan to enhance the type system to prevent this in a future update. For now, we have a runtime safety check that skips these properties and issues a warning when encountered. Please avoid including non-writable properties in your `clean` and `reConfigure` options to ensure smooth operation and prepare for future updates.
|
|
309
|
+
|
|
310
|
+
## Why Choose Pinia Store Lifecycle Manager
|
|
311
|
+
|
|
312
|
+
- **Seamless Integration:** Easily integrates with existing Pinia stores.
|
|
313
|
+
- **Highly Customizable:** Offers flexible configuration options.
|
|
314
|
+
- **Performance Optimized:** Ensures minimal overhead in state management operations.
|
|
315
|
+
- **Comprehensive Logging:** Facilitates debugging through optional debug logs.
|
|
316
|
+
- **Active Maintenance:** Regular updates and community support.
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
PiniaStoreLifecycleManager: () => PiniaStoreLifecycleManager
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/utils.ts
|
|
28
|
+
function debugLog(storeId, message, data) {
|
|
29
|
+
console.log(`\u{1F504} [${storeId}] ${message}`, data ? data : "");
|
|
30
|
+
}
|
|
31
|
+
function devWarnNonWritable(storeId, propertyName) {
|
|
32
|
+
console.warn(
|
|
33
|
+
`\u{1F504} \u{1F6AB} [${storeId}] Skipped non-writable property '${propertyName}'. Avoid including non-writable computed properties in clean or reConfigure options.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function devError(storeId, message, error) {
|
|
37
|
+
console.error(
|
|
38
|
+
`\u{1F504} [PiniaStoreLifecycleManager] (${storeId}) ${message}`,
|
|
39
|
+
error
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/plugin.ts
|
|
44
|
+
var import_vue = require("vue");
|
|
45
|
+
var PiniaStoreLifecycleManager = ({ options, store }, lifecycleEventHandler, pluginOptions) => {
|
|
46
|
+
if ((typeof window === "undefined" || typeof document === "undefined") && !pluginOptions?.enableSSR) {
|
|
47
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
48
|
+
debugLog(
|
|
49
|
+
store.$id,
|
|
50
|
+
"PiniaStoreLifecycleManager is not running on the client side."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!store.$state.hasOwnProperty("hasStoreLifecycleManagerListener") && (!pluginOptions?.disableAutoRegister && options.lifecycleOptions?.disableListener !== true || pluginOptions?.disableAutoRegister && options.lifecycleOptions?.disableListener === false)) {
|
|
56
|
+
const _hasStoreLifecycleManagerListener = (0, import_vue.ref)(true);
|
|
57
|
+
store.$state._hasStoreLifecycleManagerListener = _hasStoreLifecycleManagerListener;
|
|
58
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
59
|
+
debugLog(store.$id, "Attaching PiniaStoreLifecycleManager listener");
|
|
60
|
+
}
|
|
61
|
+
lifecycleEventHandler({
|
|
62
|
+
reset: resetState,
|
|
63
|
+
reconfigure: reconfigureState,
|
|
64
|
+
refresh: refreshState
|
|
65
|
+
});
|
|
66
|
+
store._hasStoreLifecycleManagerListener = (0, import_vue.toRef)(
|
|
67
|
+
store.$state,
|
|
68
|
+
"_hasStoreLifecycleManagerListener"
|
|
69
|
+
);
|
|
70
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
71
|
+
debugLog(
|
|
72
|
+
store.$id,
|
|
73
|
+
"PiniaStoreLifecycleManager listener attached successfully"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
78
|
+
debugLog(
|
|
79
|
+
store.$id,
|
|
80
|
+
"Skipping PiniaStoreLifecycleManager listener attachment",
|
|
81
|
+
{
|
|
82
|
+
reason: store.$state.hasStoreLifecycleManagerListener ? "Already attached" : "Disabled by configuration"
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function resetState() {
|
|
88
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
89
|
+
debugLog(
|
|
90
|
+
store.$id,
|
|
91
|
+
"Initiating reset state operation",
|
|
92
|
+
options.lifecycleOptions?.clean
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (!options.lifecycleOptions?.clean) {
|
|
96
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
97
|
+
debugLog(
|
|
98
|
+
store.$id,
|
|
99
|
+
"Reset state operation skipped: No clean options defined"
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const cleanOptions = typeof options.lifecycleOptions.clean === "function" ? options.lifecycleOptions.clean.length > 0 ? options.lifecycleOptions.clean(store) : options.lifecycleOptions.clean() : options.lifecycleOptions.clean;
|
|
105
|
+
if (cleanOptions) {
|
|
106
|
+
Object.keys(cleanOptions).forEach((key) => {
|
|
107
|
+
if ((0, import_vue.isReadonly)(store[key])) {
|
|
108
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
109
|
+
devWarnNonWritable(store.$id, key);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const oldValue = store[key];
|
|
114
|
+
try {
|
|
115
|
+
store[key] = cleanOptions[key];
|
|
116
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
117
|
+
debugLog(store.$id, `Reset state: Updated '${key}'`, {
|
|
118
|
+
oldValue,
|
|
119
|
+
newValue: store[key]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
devError(store.$id, `Failed to reset state for key '${key}'`, error);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
128
|
+
debugLog(store.$id, "Reset state operation completed");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function reconfigureState() {
|
|
132
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
133
|
+
debugLog(
|
|
134
|
+
store.$id,
|
|
135
|
+
"Initiating reconfigure state operation",
|
|
136
|
+
options.lifecycleOptions?.reConfigure
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (!options.lifecycleOptions?.reConfigure) {
|
|
140
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
141
|
+
debugLog(
|
|
142
|
+
store.$id,
|
|
143
|
+
"Reconfigure state operation skipped: No reconfigure options defined"
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const reconfigureOptions = typeof options.lifecycleOptions.reConfigure === "function" ? options.lifecycleOptions.reConfigure.length > 0 ? options.lifecycleOptions.reConfigure(store) : options.lifecycleOptions.reConfigure() : options.lifecycleOptions.reConfigure;
|
|
149
|
+
if (reconfigureOptions) {
|
|
150
|
+
Object.keys(reconfigureOptions).forEach((key) => {
|
|
151
|
+
if ((0, import_vue.isReadonly)(store[key])) {
|
|
152
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
153
|
+
devWarnNonWritable(store.$id, key);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const oldValue = store[key];
|
|
158
|
+
try {
|
|
159
|
+
store[key] = reconfigureOptions[key];
|
|
160
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
161
|
+
debugLog(store.$id, `Reconfigure state: Updated '${key}'`, {
|
|
162
|
+
oldValue,
|
|
163
|
+
newValue: store[key]
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
devError(
|
|
168
|
+
store.$id,
|
|
169
|
+
`Failed to reconfigure state for key '${key}'`,
|
|
170
|
+
error
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
176
|
+
debugLog(store.$id, "Reconfigure state operation completed");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function refreshState(refreshOptions) {
|
|
180
|
+
const { mode, includeResetOnlyActions = false } = refreshOptions;
|
|
181
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
182
|
+
debugLog(store.$id, "Initiating refresh action", {
|
|
183
|
+
mode,
|
|
184
|
+
includeResetOnlyActions,
|
|
185
|
+
refreshOptions: options.lifecycleOptions?.refresh
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (!options.lifecycleOptions?.refresh) {
|
|
189
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
190
|
+
debugLog(
|
|
191
|
+
store.$id,
|
|
192
|
+
"Refresh action skipped: No refresh options defined"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const refreshResult = typeof options.lifecycleOptions.refresh === "function" ? options.lifecycleOptions.refresh.length > 0 ? options.lifecycleOptions.refresh(store) : options.lifecycleOptions.refresh() : options.lifecycleOptions.refresh;
|
|
198
|
+
if (refreshResult && typeof refreshResult === "object") {
|
|
199
|
+
const filteredResult = Object.fromEntries(
|
|
200
|
+
Object.entries(refreshResult).filter(([key, value]) => {
|
|
201
|
+
const actionResetOnly = value?.resetOnly ?? false;
|
|
202
|
+
const shouldInclude = mode === "full" ? !includeResetOnlyActions || includeResetOnlyActions && actionResetOnly : !actionResetOnly;
|
|
203
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
204
|
+
debugLog(store.$id, `Filtering refresh action: ${key}`, {
|
|
205
|
+
shouldInclude,
|
|
206
|
+
mode,
|
|
207
|
+
includeResetOnlyActions,
|
|
208
|
+
actionResetOnly
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return shouldInclude;
|
|
212
|
+
})
|
|
213
|
+
);
|
|
214
|
+
Object.entries(filteredResult).forEach(([key, value]) => {
|
|
215
|
+
const actionKey = key;
|
|
216
|
+
const action = store[actionKey];
|
|
217
|
+
if (action) {
|
|
218
|
+
const params = value?.params ?? [];
|
|
219
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
220
|
+
debugLog(store.$id, `Executing refresh action: ${key}`, { params });
|
|
221
|
+
}
|
|
222
|
+
action(...params);
|
|
223
|
+
} else {
|
|
224
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
225
|
+
debugLog(store.$id, `Skipped non-existent refresh action: ${key}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
} else {
|
|
230
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
231
|
+
debugLog(store.$id, "Refresh action skipped: Invalid refresh result");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
235
|
+
debugLog(store.$id, "Refresh action completed");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
240
|
+
0 && (module.exports = {
|
|
241
|
+
PiniaStoreLifecycleManager
|
|
242
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { StoreState, StoreGetters, StoreActions, PiniaPluginContext } from 'pinia';
|
|
2
|
+
import { Ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a map of action parameters for a store.
|
|
6
|
+
* Each key is an action name, and the value contains the action's parameters and a flag indicating if it's reset-only.
|
|
7
|
+
*/
|
|
8
|
+
type ActionParamsMap<T> = {
|
|
9
|
+
[K in keyof T]: {
|
|
10
|
+
params: T[K] extends (...args: infer P) => unknown ? P : [];
|
|
11
|
+
resetOnly?: boolean;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Options for refreshing a Pinia store using the lifecycle manager.
|
|
16
|
+
*/
|
|
17
|
+
type PiniaStoreLifecycleManagerRefreshOptions = {
|
|
18
|
+
/** The refresh mode: 'full' refreshes all actions, 'partial' skips reset-only actions */
|
|
19
|
+
mode: "full" | "partial";
|
|
20
|
+
/** Whether to include reset-only actions in 'full' mode */
|
|
21
|
+
includeResetOnlyActions?: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Represents the options for cleaning a store's state.
|
|
25
|
+
* Omits the internal '_hasStoreLifecycleManagerListener' property.
|
|
26
|
+
*/
|
|
27
|
+
type CleanOptions<Store> = Partial<Omit<StoreState<Store> & StoreGetters<Store>, "_hasStoreLifecycleManagerListener">>;
|
|
28
|
+
/**
|
|
29
|
+
* Represents the options for reconfiguring a store's state.
|
|
30
|
+
* Omits the internal '_hasStoreLifecycleManagerListener' property.
|
|
31
|
+
*/
|
|
32
|
+
type ReconfigureOptions<Store> = Partial<Omit<StoreState<Store> & StoreGetters<Store>, "_hasStoreLifecycleManagerListener">>;
|
|
33
|
+
/**
|
|
34
|
+
* A function that takes a store and returns clean options or void.
|
|
35
|
+
*/
|
|
36
|
+
type CleanFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => CleanOptions<Store> | void;
|
|
37
|
+
/**
|
|
38
|
+
* A function that takes a store and returns reconfigure options or void.
|
|
39
|
+
*/
|
|
40
|
+
type ReconfigureFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => ReconfigureOptions<Store> | void;
|
|
41
|
+
/**
|
|
42
|
+
* Represents the options for refreshing a store's actions.
|
|
43
|
+
*/
|
|
44
|
+
type RefreshOptions<Store> = Partial<ActionParamsMap<StoreActions<Store>>>;
|
|
45
|
+
/**
|
|
46
|
+
* A function that takes a store and returns refresh options or void.
|
|
47
|
+
*/
|
|
48
|
+
type RefreshFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => RefreshOptions<Store> | void;
|
|
49
|
+
declare module "pinia" {
|
|
50
|
+
interface PiniaCustomProperties {
|
|
51
|
+
_hasStoreLifecycleManagerListener?: Ref<boolean>;
|
|
52
|
+
}
|
|
53
|
+
interface DefineStoreOptionsBase<S extends StateTree, Store> {
|
|
54
|
+
lifecycleOptions?: {
|
|
55
|
+
/**
|
|
56
|
+
* Defines properties to be reset during a hard-reset operation.
|
|
57
|
+
*/
|
|
58
|
+
clean?: CleanOptions<Store> | CleanFunction<Store> | (() => CleanOptions<Store> | void);
|
|
59
|
+
/**
|
|
60
|
+
* Defines properties to be reset during a soft-reset (reconfigure) operation.
|
|
61
|
+
*/
|
|
62
|
+
reConfigure?: ReconfigureOptions<Store> | ReconfigureFunction<Store> | (() => ReconfigureOptions<Store> | void);
|
|
63
|
+
/**
|
|
64
|
+
* Defines actions to be called during a refresh operation.
|
|
65
|
+
* @param v - The store instance with certain properties omitted.
|
|
66
|
+
* @returns Partial map of action parameters.
|
|
67
|
+
*/
|
|
68
|
+
refresh?: RefreshOptions<Store> | RefreshFunction<Store> | (() => RefreshOptions<Store> | void);
|
|
69
|
+
/**
|
|
70
|
+
* Controls the automatic registration of the listener.
|
|
71
|
+
* - For automatic registration: set to true to disable listener registration.
|
|
72
|
+
* - For manual registration: set to false to enable listener registration.
|
|
73
|
+
*/
|
|
74
|
+
disableListener?: boolean;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* PiniaStoreLifecycleManager: A Pinia plugin for managing store lifecycle, including state resets and refreshes.
|
|
81
|
+
*
|
|
82
|
+
* @param context - The Pinia plugin context.
|
|
83
|
+
* @param lifecycleEventHandler - The listener function to be called for various lifecycle events.
|
|
84
|
+
* @param pluginOptions - Configuration options for the plugin.
|
|
85
|
+
*/
|
|
86
|
+
declare const PiniaStoreLifecycleManager: ({ options, store }: PiniaPluginContext, lifecycleEventHandler: (events: {
|
|
87
|
+
/**
|
|
88
|
+
* Resets the store to its initial state.
|
|
89
|
+
* Use this when you need to completely clear the store, e.g., on user logout.
|
|
90
|
+
*/
|
|
91
|
+
reset: () => void;
|
|
92
|
+
/**
|
|
93
|
+
* Reconfigures the store with new values.
|
|
94
|
+
* Use this when you need to update multiple store properties at once,
|
|
95
|
+
* e.g., when switching to a different configuration or context.
|
|
96
|
+
*/
|
|
97
|
+
reconfigure: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Refreshes the store data by calling specified actions.
|
|
100
|
+
* @param options RefreshOptions to control the refresh behavior.
|
|
101
|
+
*
|
|
102
|
+
* Use 'full' mode when you need to refresh all data, including reset-only actions.
|
|
103
|
+
* This is useful for major state changes, like switching user accounts.
|
|
104
|
+
*
|
|
105
|
+
* Use 'partial' mode for routine updates or when switching to a related context
|
|
106
|
+
* that doesn't require a full data refresh.
|
|
107
|
+
*/
|
|
108
|
+
refresh: (options: PiniaStoreLifecycleManagerRefreshOptions) => void;
|
|
109
|
+
}) => void, pluginOptions?: {
|
|
110
|
+
/**
|
|
111
|
+
* Disables the automatic registration of lifecycle listeners.
|
|
112
|
+
* Set to true to manually manage lifecycle listener registration.
|
|
113
|
+
*/
|
|
114
|
+
disableAutoRegister?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Enables debug logging for the PiniaStoreLifecycleManager.
|
|
117
|
+
* Set to true to output debug information to the console.
|
|
118
|
+
*/
|
|
119
|
+
enableDebugLogs?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Enables server-side rendering support for the PiniaStoreLifecycleManager.
|
|
122
|
+
* Set to true if the application uses SSR to ensure proper lifecycle management.
|
|
123
|
+
*
|
|
124
|
+
* **Note:** Only enable if you are sure that your listener works perfectly in SSR,
|
|
125
|
+
* as this plugin only assigns the listener once either on the server or on the client.
|
|
126
|
+
*/
|
|
127
|
+
enableSSR?: boolean;
|
|
128
|
+
}) => void;
|
|
129
|
+
|
|
130
|
+
export { type ActionParamsMap, type CleanFunction, type CleanOptions, PiniaStoreLifecycleManager, type PiniaStoreLifecycleManagerRefreshOptions, type ReconfigureFunction, type ReconfigureOptions, type RefreshFunction, type RefreshOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { StoreState, StoreGetters, StoreActions, PiniaPluginContext } from 'pinia';
|
|
2
|
+
import { Ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a map of action parameters for a store.
|
|
6
|
+
* Each key is an action name, and the value contains the action's parameters and a flag indicating if it's reset-only.
|
|
7
|
+
*/
|
|
8
|
+
type ActionParamsMap<T> = {
|
|
9
|
+
[K in keyof T]: {
|
|
10
|
+
params: T[K] extends (...args: infer P) => unknown ? P : [];
|
|
11
|
+
resetOnly?: boolean;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Options for refreshing a Pinia store using the lifecycle manager.
|
|
16
|
+
*/
|
|
17
|
+
type PiniaStoreLifecycleManagerRefreshOptions = {
|
|
18
|
+
/** The refresh mode: 'full' refreshes all actions, 'partial' skips reset-only actions */
|
|
19
|
+
mode: "full" | "partial";
|
|
20
|
+
/** Whether to include reset-only actions in 'full' mode */
|
|
21
|
+
includeResetOnlyActions?: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Represents the options for cleaning a store's state.
|
|
25
|
+
* Omits the internal '_hasStoreLifecycleManagerListener' property.
|
|
26
|
+
*/
|
|
27
|
+
type CleanOptions<Store> = Partial<Omit<StoreState<Store> & StoreGetters<Store>, "_hasStoreLifecycleManagerListener">>;
|
|
28
|
+
/**
|
|
29
|
+
* Represents the options for reconfiguring a store's state.
|
|
30
|
+
* Omits the internal '_hasStoreLifecycleManagerListener' property.
|
|
31
|
+
*/
|
|
32
|
+
type ReconfigureOptions<Store> = Partial<Omit<StoreState<Store> & StoreGetters<Store>, "_hasStoreLifecycleManagerListener">>;
|
|
33
|
+
/**
|
|
34
|
+
* A function that takes a store and returns clean options or void.
|
|
35
|
+
*/
|
|
36
|
+
type CleanFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => CleanOptions<Store> | void;
|
|
37
|
+
/**
|
|
38
|
+
* A function that takes a store and returns reconfigure options or void.
|
|
39
|
+
*/
|
|
40
|
+
type ReconfigureFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => ReconfigureOptions<Store> | void;
|
|
41
|
+
/**
|
|
42
|
+
* Represents the options for refreshing a store's actions.
|
|
43
|
+
*/
|
|
44
|
+
type RefreshOptions<Store> = Partial<ActionParamsMap<StoreActions<Store>>>;
|
|
45
|
+
/**
|
|
46
|
+
* A function that takes a store and returns refresh options or void.
|
|
47
|
+
*/
|
|
48
|
+
type RefreshFunction<Store> = (store: Omit<Store, `$${string}` | `_customProperties` | "_hasStoreLifecycleManagerListener">) => RefreshOptions<Store> | void;
|
|
49
|
+
declare module "pinia" {
|
|
50
|
+
interface PiniaCustomProperties {
|
|
51
|
+
_hasStoreLifecycleManagerListener?: Ref<boolean>;
|
|
52
|
+
}
|
|
53
|
+
interface DefineStoreOptionsBase<S extends StateTree, Store> {
|
|
54
|
+
lifecycleOptions?: {
|
|
55
|
+
/**
|
|
56
|
+
* Defines properties to be reset during a hard-reset operation.
|
|
57
|
+
*/
|
|
58
|
+
clean?: CleanOptions<Store> | CleanFunction<Store> | (() => CleanOptions<Store> | void);
|
|
59
|
+
/**
|
|
60
|
+
* Defines properties to be reset during a soft-reset (reconfigure) operation.
|
|
61
|
+
*/
|
|
62
|
+
reConfigure?: ReconfigureOptions<Store> | ReconfigureFunction<Store> | (() => ReconfigureOptions<Store> | void);
|
|
63
|
+
/**
|
|
64
|
+
* Defines actions to be called during a refresh operation.
|
|
65
|
+
* @param v - The store instance with certain properties omitted.
|
|
66
|
+
* @returns Partial map of action parameters.
|
|
67
|
+
*/
|
|
68
|
+
refresh?: RefreshOptions<Store> | RefreshFunction<Store> | (() => RefreshOptions<Store> | void);
|
|
69
|
+
/**
|
|
70
|
+
* Controls the automatic registration of the listener.
|
|
71
|
+
* - For automatic registration: set to true to disable listener registration.
|
|
72
|
+
* - For manual registration: set to false to enable listener registration.
|
|
73
|
+
*/
|
|
74
|
+
disableListener?: boolean;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* PiniaStoreLifecycleManager: A Pinia plugin for managing store lifecycle, including state resets and refreshes.
|
|
81
|
+
*
|
|
82
|
+
* @param context - The Pinia plugin context.
|
|
83
|
+
* @param lifecycleEventHandler - The listener function to be called for various lifecycle events.
|
|
84
|
+
* @param pluginOptions - Configuration options for the plugin.
|
|
85
|
+
*/
|
|
86
|
+
declare const PiniaStoreLifecycleManager: ({ options, store }: PiniaPluginContext, lifecycleEventHandler: (events: {
|
|
87
|
+
/**
|
|
88
|
+
* Resets the store to its initial state.
|
|
89
|
+
* Use this when you need to completely clear the store, e.g., on user logout.
|
|
90
|
+
*/
|
|
91
|
+
reset: () => void;
|
|
92
|
+
/**
|
|
93
|
+
* Reconfigures the store with new values.
|
|
94
|
+
* Use this when you need to update multiple store properties at once,
|
|
95
|
+
* e.g., when switching to a different configuration or context.
|
|
96
|
+
*/
|
|
97
|
+
reconfigure: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Refreshes the store data by calling specified actions.
|
|
100
|
+
* @param options RefreshOptions to control the refresh behavior.
|
|
101
|
+
*
|
|
102
|
+
* Use 'full' mode when you need to refresh all data, including reset-only actions.
|
|
103
|
+
* This is useful for major state changes, like switching user accounts.
|
|
104
|
+
*
|
|
105
|
+
* Use 'partial' mode for routine updates or when switching to a related context
|
|
106
|
+
* that doesn't require a full data refresh.
|
|
107
|
+
*/
|
|
108
|
+
refresh: (options: PiniaStoreLifecycleManagerRefreshOptions) => void;
|
|
109
|
+
}) => void, pluginOptions?: {
|
|
110
|
+
/**
|
|
111
|
+
* Disables the automatic registration of lifecycle listeners.
|
|
112
|
+
* Set to true to manually manage lifecycle listener registration.
|
|
113
|
+
*/
|
|
114
|
+
disableAutoRegister?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Enables debug logging for the PiniaStoreLifecycleManager.
|
|
117
|
+
* Set to true to output debug information to the console.
|
|
118
|
+
*/
|
|
119
|
+
enableDebugLogs?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Enables server-side rendering support for the PiniaStoreLifecycleManager.
|
|
122
|
+
* Set to true if the application uses SSR to ensure proper lifecycle management.
|
|
123
|
+
*
|
|
124
|
+
* **Note:** Only enable if you are sure that your listener works perfectly in SSR,
|
|
125
|
+
* as this plugin only assigns the listener once either on the server or on the client.
|
|
126
|
+
*/
|
|
127
|
+
enableSSR?: boolean;
|
|
128
|
+
}) => void;
|
|
129
|
+
|
|
130
|
+
export { type ActionParamsMap, type CleanFunction, type CleanOptions, PiniaStoreLifecycleManager, type PiniaStoreLifecycleManagerRefreshOptions, type ReconfigureFunction, type ReconfigureOptions, type RefreshFunction, type RefreshOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
function debugLog(storeId, message, data) {
|
|
3
|
+
console.log(`\u{1F504} [${storeId}] ${message}`, data ? data : "");
|
|
4
|
+
}
|
|
5
|
+
function devWarnNonWritable(storeId, propertyName) {
|
|
6
|
+
console.warn(
|
|
7
|
+
`\u{1F504} \u{1F6AB} [${storeId}] Skipped non-writable property '${propertyName}'. Avoid including non-writable computed properties in clean or reConfigure options.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
function devError(storeId, message, error) {
|
|
11
|
+
console.error(
|
|
12
|
+
`\u{1F504} [PiniaStoreLifecycleManager] (${storeId}) ${message}`,
|
|
13
|
+
error
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/plugin.ts
|
|
18
|
+
import { ref, toRef, isReadonly } from "vue";
|
|
19
|
+
var PiniaStoreLifecycleManager = ({ options, store }, lifecycleEventHandler, pluginOptions) => {
|
|
20
|
+
if ((typeof window === "undefined" || typeof document === "undefined") && !pluginOptions?.enableSSR) {
|
|
21
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
22
|
+
debugLog(
|
|
23
|
+
store.$id,
|
|
24
|
+
"PiniaStoreLifecycleManager is not running on the client side."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!store.$state.hasOwnProperty("hasStoreLifecycleManagerListener") && (!pluginOptions?.disableAutoRegister && options.lifecycleOptions?.disableListener !== true || pluginOptions?.disableAutoRegister && options.lifecycleOptions?.disableListener === false)) {
|
|
30
|
+
const _hasStoreLifecycleManagerListener = ref(true);
|
|
31
|
+
store.$state._hasStoreLifecycleManagerListener = _hasStoreLifecycleManagerListener;
|
|
32
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
33
|
+
debugLog(store.$id, "Attaching PiniaStoreLifecycleManager listener");
|
|
34
|
+
}
|
|
35
|
+
lifecycleEventHandler({
|
|
36
|
+
reset: resetState,
|
|
37
|
+
reconfigure: reconfigureState,
|
|
38
|
+
refresh: refreshState
|
|
39
|
+
});
|
|
40
|
+
store._hasStoreLifecycleManagerListener = toRef(
|
|
41
|
+
store.$state,
|
|
42
|
+
"_hasStoreLifecycleManagerListener"
|
|
43
|
+
);
|
|
44
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
45
|
+
debugLog(
|
|
46
|
+
store.$id,
|
|
47
|
+
"PiniaStoreLifecycleManager listener attached successfully"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
52
|
+
debugLog(
|
|
53
|
+
store.$id,
|
|
54
|
+
"Skipping PiniaStoreLifecycleManager listener attachment",
|
|
55
|
+
{
|
|
56
|
+
reason: store.$state.hasStoreLifecycleManagerListener ? "Already attached" : "Disabled by configuration"
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resetState() {
|
|
62
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
63
|
+
debugLog(
|
|
64
|
+
store.$id,
|
|
65
|
+
"Initiating reset state operation",
|
|
66
|
+
options.lifecycleOptions?.clean
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (!options.lifecycleOptions?.clean) {
|
|
70
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
71
|
+
debugLog(
|
|
72
|
+
store.$id,
|
|
73
|
+
"Reset state operation skipped: No clean options defined"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const cleanOptions = typeof options.lifecycleOptions.clean === "function" ? options.lifecycleOptions.clean.length > 0 ? options.lifecycleOptions.clean(store) : options.lifecycleOptions.clean() : options.lifecycleOptions.clean;
|
|
79
|
+
if (cleanOptions) {
|
|
80
|
+
Object.keys(cleanOptions).forEach((key) => {
|
|
81
|
+
if (isReadonly(store[key])) {
|
|
82
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
83
|
+
devWarnNonWritable(store.$id, key);
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const oldValue = store[key];
|
|
88
|
+
try {
|
|
89
|
+
store[key] = cleanOptions[key];
|
|
90
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
91
|
+
debugLog(store.$id, `Reset state: Updated '${key}'`, {
|
|
92
|
+
oldValue,
|
|
93
|
+
newValue: store[key]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
devError(store.$id, `Failed to reset state for key '${key}'`, error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
102
|
+
debugLog(store.$id, "Reset state operation completed");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function reconfigureState() {
|
|
106
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
107
|
+
debugLog(
|
|
108
|
+
store.$id,
|
|
109
|
+
"Initiating reconfigure state operation",
|
|
110
|
+
options.lifecycleOptions?.reConfigure
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (!options.lifecycleOptions?.reConfigure) {
|
|
114
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
115
|
+
debugLog(
|
|
116
|
+
store.$id,
|
|
117
|
+
"Reconfigure state operation skipped: No reconfigure options defined"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const reconfigureOptions = typeof options.lifecycleOptions.reConfigure === "function" ? options.lifecycleOptions.reConfigure.length > 0 ? options.lifecycleOptions.reConfigure(store) : options.lifecycleOptions.reConfigure() : options.lifecycleOptions.reConfigure;
|
|
123
|
+
if (reconfigureOptions) {
|
|
124
|
+
Object.keys(reconfigureOptions).forEach((key) => {
|
|
125
|
+
if (isReadonly(store[key])) {
|
|
126
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
127
|
+
devWarnNonWritable(store.$id, key);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const oldValue = store[key];
|
|
132
|
+
try {
|
|
133
|
+
store[key] = reconfigureOptions[key];
|
|
134
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
135
|
+
debugLog(store.$id, `Reconfigure state: Updated '${key}'`, {
|
|
136
|
+
oldValue,
|
|
137
|
+
newValue: store[key]
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
devError(
|
|
142
|
+
store.$id,
|
|
143
|
+
`Failed to reconfigure state for key '${key}'`,
|
|
144
|
+
error
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
150
|
+
debugLog(store.$id, "Reconfigure state operation completed");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function refreshState(refreshOptions) {
|
|
154
|
+
const { mode, includeResetOnlyActions = false } = refreshOptions;
|
|
155
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
156
|
+
debugLog(store.$id, "Initiating refresh action", {
|
|
157
|
+
mode,
|
|
158
|
+
includeResetOnlyActions,
|
|
159
|
+
refreshOptions: options.lifecycleOptions?.refresh
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (!options.lifecycleOptions?.refresh) {
|
|
163
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
164
|
+
debugLog(
|
|
165
|
+
store.$id,
|
|
166
|
+
"Refresh action skipped: No refresh options defined"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const refreshResult = typeof options.lifecycleOptions.refresh === "function" ? options.lifecycleOptions.refresh.length > 0 ? options.lifecycleOptions.refresh(store) : options.lifecycleOptions.refresh() : options.lifecycleOptions.refresh;
|
|
172
|
+
if (refreshResult && typeof refreshResult === "object") {
|
|
173
|
+
const filteredResult = Object.fromEntries(
|
|
174
|
+
Object.entries(refreshResult).filter(([key, value]) => {
|
|
175
|
+
const actionResetOnly = value?.resetOnly ?? false;
|
|
176
|
+
const shouldInclude = mode === "full" ? !includeResetOnlyActions || includeResetOnlyActions && actionResetOnly : !actionResetOnly;
|
|
177
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
178
|
+
debugLog(store.$id, `Filtering refresh action: ${key}`, {
|
|
179
|
+
shouldInclude,
|
|
180
|
+
mode,
|
|
181
|
+
includeResetOnlyActions,
|
|
182
|
+
actionResetOnly
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return shouldInclude;
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
Object.entries(filteredResult).forEach(([key, value]) => {
|
|
189
|
+
const actionKey = key;
|
|
190
|
+
const action = store[actionKey];
|
|
191
|
+
if (action) {
|
|
192
|
+
const params = value?.params ?? [];
|
|
193
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
194
|
+
debugLog(store.$id, `Executing refresh action: ${key}`, { params });
|
|
195
|
+
}
|
|
196
|
+
action(...params);
|
|
197
|
+
} else {
|
|
198
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
199
|
+
debugLog(store.$id, `Skipped non-existent refresh action: ${key}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
205
|
+
debugLog(store.$id, "Refresh action skipped: Invalid refresh result");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (pluginOptions?.enableDebugLogs) {
|
|
209
|
+
debugLog(store.$id, "Refresh action completed");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
export {
|
|
214
|
+
PiniaStoreLifecycleManager
|
|
215
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atoapayments/pinia-store-lifecycle-manager",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A Pinia plugin for managing store lifecycle, including state resets and refreshes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
13
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
14
|
+
"lint": "eslint src/**/*.ts",
|
|
15
|
+
"clean": "rm -rf dist",
|
|
16
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"pinia",
|
|
20
|
+
"vue",
|
|
21
|
+
"store",
|
|
22
|
+
"lifecycle",
|
|
23
|
+
"management"
|
|
24
|
+
],
|
|
25
|
+
"author": "Tushar gupta",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.7.4",
|
|
29
|
+
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
|
30
|
+
"@typescript-eslint/parser": "^8.7.0",
|
|
31
|
+
"eslint": "^9.11.1",
|
|
32
|
+
"pinia": "^2.2.2",
|
|
33
|
+
"tsup": "^8.3.0",
|
|
34
|
+
"typescript": "^5.6.2",
|
|
35
|
+
"vue": "^3.5.9"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"pinia": "^2.0.0",
|
|
39
|
+
"vue": "^3.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"pinia": {
|
|
43
|
+
"optional": false
|
|
44
|
+
},
|
|
45
|
+
"vue": {
|
|
46
|
+
"optional": false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|