pinia-plugin-subscription 0.1.5 → 0.1.6-beta.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/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ 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
+ ## [0.1.6]
9
+
10
+ ### Added
11
+ - Added `execution` and `hydrationScheduler` support on `PluginSubscriber` / `PluginSubscriberInterface`.
12
+ - Added `createHydrationPlugin()` as the dedicated Nuxt / SSR hydration helper.
13
+ - Added `PluginSubscriptionOptions` for hydration-specific runtime overrides without changing the default `createPlugin()` API.
14
+ - Added optional `hydrate()` and `afterHydration()` lifecycle hooks to the subscriber and base `Store` contract for SSR-safe initialization logic.
15
+
16
+ ### Changed
17
+ - The execution policy now defaults to the subscriber itself, with framework-specific overrides available only through the hydration helper.
18
+ - `PluginSubscription` resolves hydration timing from the subscriber before falling back to the global runtime scheduler.
19
+ - The base `Store` class now exposes lifecycle stubs so browser-only bootstrap code can be triggered after the plugin is accepted for execution instead of during construction.
20
+
21
+ ### Fixed
22
+ - Avoided SSR hydration crashes by allowing client-only Pinia plugins to be skipped on the server or deferred on the client.
23
+ - Prevented browser-only initialization from running during server-side store creation by moving hydration bootstrap behind the subscriber lifecycle hooks.
24
+
25
+ ## [0.1.5]
26
+
27
+ Minor change
28
+
29
+
8
30
  ## [0.1.4]
9
31
 
10
32
  ### Added
@@ -15,9 +37,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
37
  ### Changed
16
38
  - `getDefineAStoreSetupContext()` now falls back to store ID lookup during plugin execution timing.
17
39
  - `defineAStoreSetup()` now stores setup contexts only for stores with `storeOptions.enhancedStore = true`.
40
+ - `defineAStoreSetup()` now removes ID-indexed setup contexts after weak-map registration and on store disposal.
41
+ - `defineAStoreSetup()` now logs setup-context map size transitions when `storeOptions.debug = true`.
18
42
  - Public exports now include `defineAStoreCtx`, `getEnhancedStore`, and `setEnhancedStore`.
19
43
  - GitHub Actions now publishes GitHub pre-releases to npm with the `beta` dist-tag.
20
44
 
45
+ ### Added
46
+ - Added a Vitest performance-oriented test for setup-context tracking behavior with and without `enhancedStore`.
47
+
21
48
  ### Deprecated
22
49
  - `ctx.extensions.extending` is deprecated. Use `ctx.extensions.enhancedStore`.
23
50
  - `getExtendingStore()` is deprecated. Use `getEnhancedStore()`.
package/README.md CHANGED
@@ -83,13 +83,58 @@ export const myStoreSubscriber: PluginSubscriberInterface = {
83
83
 
84
84
  - **Debug mode:** Specifies the name of the plugin(s) to debug as the second argument to `createPlugin` to enable detailed logging.
85
85
  - **Reset callbacks:** Define `resetStoreCallback` to run custom logic when a store is reset.
86
+ - **SSR and hydration control:** Configure whether each subscribed plugin runs on the server, the client, or only after client hydration.
86
87
 
87
88
  ## API Reference
88
89
 
89
- ### `createPlugin(subscribers: PluginSubscriber[], debug?: boolean): PiniaPlugin`
90
+ ### `createPlugin(subscribers: PluginSubscriber[], debug?: string[]): PiniaPlugin`
90
91
 
91
92
  Creates and returns a Pinia plugin from the provided `subscribers`. Each subscriber is invoked when a store is registered.
92
93
 
94
+ ### `createHydrationPlugin(subscribers: PluginSubscriber[], options?: PluginSubscriptionOptions): PiniaPlugin`
95
+
96
+ Creates a Pinia plugin dedicated to SSR / Nuxt hydration orchestration. This helper is useful when a store plugin needs a runtime-specific scheduler or environment override without forcing every app to re-declare the plugin’s execution policy.
97
+
98
+ ```typescript
99
+ import { createHydrationPlugin, PLUGIN_NAME as PPS } from 'pinia-plugin-subscription'
100
+
101
+ pinia.use(createHydrationPlugin([
102
+ persistedStateSubscriber
103
+ ], {
104
+ debug: [PPS],
105
+ hydrationScheduler: (run) => {
106
+ nextTick(() => run())
107
+ }
108
+ }))
109
+ ```
110
+
111
+ ### SSR / Nuxt hydration control
112
+
113
+ The source of truth for hydration policy stays on the subscriber itself. For example, a client-only plugin should declare its execution directly:
114
+
115
+ ```typescript
116
+ export const persistedStateSubscriber: PluginSubscriberInterface = {
117
+ name: 'persisted-state',
118
+ execution: {
119
+ environment: 'client',
120
+ hydration: 'defer',
121
+ },
122
+ hydrationScheduler: (run) => {
123
+ nextTick(() => run())
124
+ },
125
+ invoke: (context, debug) => {
126
+ // ...
127
+ return true
128
+ }
129
+ }
130
+ ```
131
+
132
+ Available execution options:
133
+
134
+ - `environment: 'both' | 'client' | 'server'` — controls where the subscriber is allowed to run.
135
+ - `hydration: 'immediate' | 'defer'` — on the client, `defer` schedules execution after hydration.
136
+ - `hydrationScheduler` can be provided either on a subscriber or via `createHydrationPlugin()` to override the execution timing when needed.
137
+
93
138
  ### `defineAStoreCtx(id, setup, options?)`
94
139
 
95
140
  Use actions or access the state, added by one or more plugins, when defining the store.
@@ -112,11 +157,17 @@ export const useMyStore = defineAStoreCtx('myStore', (ctx) => {
112
157
  - `enhancedStore`
113
158
 
114
159
  The setup context is indexed internally only when `storeOptions.enhancedStore` is set to `true` (this is enabled automatically by `defineAStoreCtx`).
160
+ The temporary lookup by store id is now cleaned as soon as the weak-map association is established (and on store dispose), which limits map growth over time.
161
+ When `storeOptions.debug` is `true`, setup-context map size transitions are logged for observability.
115
162
 
116
163
  ### `PluginSubscriberInterface`
117
164
 
118
165
  An object with at least an `invoke(context: PiniaPluginContext, debug?: boolean)` method, plus optional properties:
119
166
 
167
+ - `execution?: { environment?: 'both' | 'client' | 'server'; hydration?: 'immediate' | 'defer' }`
168
+ - `hydrationScheduler?: (run: () => void) => void`
169
+ - `hydrate?: (context, debug) => void | Promise<void>` — optional hook for SSR-safe store initialization after the subscriber is accepted for execution
170
+ - `afterHydration?: (context, debug) => void | Promise<void>` — optional lifecycle hook for post-hydration work
120
171
  - `resetStoreCallback?: (store?: any) => void`
121
172
  - `storeOnActionSubscription?: { store, callback }` (getter)
122
173
  - `storeMutationSubscription?: { store, callback }` (getter)
@@ -138,6 +189,7 @@ The project provides an abstract `PluginSubscriber` implementation (see [src/plu
138
189
  **Example:**
139
190
 
140
191
  ```typescript
192
+ import { nextTick } from 'vue'
141
193
  import PluginSubscriber from 'pinia-plugin-subscription'
142
194
  import StoreExtension from './src/extending-pinia-store/core/StoreExtension'
143
195
  import { addStore } from './src/extending-pinia-store/plugins/stores'
@@ -145,13 +197,41 @@ import { addStore } from './src/extending-pinia-store/plugins/stores'
145
197
  class ExtendingStoreSubscriber extends PluginSubscriber<StoreExtension> {
146
198
  constructor() {
147
199
  super('extendsPiniaStore', StoreExtension.customizeStore.bind(StoreExtension))
200
+
201
+ // Controls when this subscriber may run and whether it should wait for hydration on the client.
202
+ this.execution = {
203
+ environment: 'client',
204
+ hydration: 'defer',
205
+ }
206
+
207
+ // Optional framework-specific scheduler for deferred execution.
208
+ this.hydrationScheduler = (run) => {
209
+ nextTick(() => run())
210
+ }
211
+
148
212
  this.pluginCreated = addStore
149
213
  }
214
+
215
+ override hydrate() {
216
+ if (typeof window === 'undefined') {
217
+ return
218
+ }
219
+
220
+ // Safe browser-only initialization. This is only called when the policy allows it.
221
+ return this.storeInstance?.hydrate?.()
222
+ }
150
223
  }
151
224
 
152
225
  export const extendingStoreSubscriber = new ExtendingStoreSubscriber()
153
226
  ```
154
227
 
228
+ This example shows the recommended pattern for SSR / Nuxt-safe plugins:
229
+
230
+ - `execution.environment` restricts the subscriber to `'both'`, `'client'`, or `'server'`
231
+ - `execution.hydration` is `'immediate'` by default and can be set to `'defer'` when the plugin must wait for client hydration
232
+ - `hydrationScheduler` lets you override the runtime scheduling behavior, for example with Nuxt/Vue `nextTick()`
233
+ - `hydrate()` / `afterHydration()` are optional lifecycle hooks for browser-only initialization and post-hydration work; they help keep SSR-sensitive logic out of constructors
234
+
155
235
  ## The `Store` Class — Summary
156
236
 
157
237
  The `Store` class (see [src/core/Store.ts](src/core/Store.ts)) is a wrapper around a `PiniaStore` providing: