pinia-plugin-subscription 0.1.8 → 0.2.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 +30 -0
- package/README.md +47 -289
- package/dist/chunks/{index-B-zO0yrr.js → index-VL_PgzVK.js} +53 -54
- package/dist/chunks/index-VL_PgzVK.js.map +1 -0
- package/dist/core/PluginSubscriber.d.ts +1 -1
- package/dist/core/PluginSubscription.d.ts +39 -4
- package/dist/core/RollbackStateSnapshotsHandler.d.ts +65 -0
- package/dist/core/Store.d.ts +4 -4
- package/dist/factories/state-snapshot.d.ts +3 -0
- package/dist/helpers.js +2 -1
- package/dist/helpers.js.map +1 -0
- package/dist/lib/main.d.ts +2 -1
- package/dist/pinia-plugin-subscription.js +344 -199
- package/dist/pinia-plugin-subscription.js.map +1 -0
- package/dist/plugins/createPlugin.d.ts +9 -1
- package/dist/tests/defineAStore.test-d.d.ts +1 -0
- package/dist/tests/storeOptionsExtensions.test-d.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/plugin.d.ts +5 -2
- package/dist/types/store.d.ts +43 -2
- package/dist/utils/deep-clone.d.ts +7 -0
- package/dist/utils/store.d.ts +84 -8
- package/docs/authoring-plugins.md +408 -0
- package/docs/using-plugins.md +374 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ 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.2.0]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Added `StoreOptionsExtensions`, a centralized extension point for per-store plugin options: subscriber plugins can now type their own `storeOptions` keys through interface merging (`declare module 'pinia-plugin-subscription/types'`) instead of redeclaring pinia's `DefineStoreOptionsBase`, which would raise TS2717 type-conflict errors in consumer projects.
|
|
12
|
+
- Enabled Vitest typecheck tests (`*.test-d.ts`) so the `StoreOptionsExtensions` merging contract is validated at compile time alongside the runtime test suite.
|
|
13
|
+
- Added `docs/using-plugins.md`, a consumer-oriented guide: plugin registration, `defineAStore` / `defineAStoreCtx` reference (including TypeScript generics with JavaScript-first examples), `$reset`, state rollback, and SSR / Nuxt registration through `createHydrationPlugin`.
|
|
14
|
+
- Added `docs/authoring-plugins.md`, a plugin-author guide: `PluginSubscriberInterface` reference, the `PluginSubscriber` abstract class (including its `Instance` type parameter), the `Store` base class, execution policy & SSR best practices, and `StoreOptionsExtensions` augmentation.
|
|
15
|
+
- Documented the testing setup and the trust & supply-chain practices (GPG-signed commits, review-gated merges, npm provenance) in the README.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Restructured the documentation around the package's two audiences: the README is now a lightweight entry point that routes readers to the dedicated consumer and plugin-author guides.
|
|
19
|
+
- Added the `docs/` directory to the published package files so both guides ship with the npm tarball.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Aligned the `$rollbackAfterFailure` public type with its implementation: action arguments are now passed as a single array (`$rollbackAfterFailure(params, [arg1, arg2])`, forwarded via `apply()`), instead of the documented variadic form that threw `TypeError: CreateListFromArrayLike called on non-object` at runtime.
|
|
23
|
+
- Fixed store typings lost when defining stores with `defineAStore`, `defineAStoreCtx`, `defineAStoreSetup` or `defineAStoreOptionApi`: state, getters and actions are now inferred from the store definition (setup function return or options object), exactly like pinia's `defineStore`, so IDEs list store members and display action prototypes. Explicit store/state generics (e.g. `defineAStore<Sto, Sta>`) remain supported for plugin-augmented stores and now type the store returned by `useStore()` — they were previously intersected onto the store definition itself, which left the returned store loosely typed.
|
|
24
|
+
- Added the public `DefineAStoreDefinition<Sto, Sta>` type: the store definition returned when explicit store/state generics are provided. It extends pinia's `StoreDefinition` so it stays assignable wherever a standard store definition is expected.
|
|
25
|
+
- Widened the `getDefineAStoreSetupContext` parameter to accept any store object, so fully inferred stores (without index signatures) can be passed without a cast.
|
|
26
|
+
|
|
27
|
+
## [0.2.0-beta.0]
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- 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).
|
|
31
|
+
- Added `$rollbackAfterFailure(params, ...args)` to `PiniaCustomProperties`, available on every store registered by the plugin, to run an action with an explicit snapshot/rollback wrapper.
|
|
32
|
+
- Added `RollbackActionParams` and `RollbackAfterFailureParams` types to the public API.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- Action names starting with `_` or `$` are rejected by the rollback wrapper to protect internal store methods.
|
|
36
|
+
- Rollback tracking data (`rollbackAfterFailure` params and pending snapshots) is now cleaned up when a store is disposed, preventing memory leaks with dynamically created stores.
|
|
37
|
+
|
|
8
38
|
## [0.1.8]
|
|
9
39
|
|
|
10
40
|
### Added
|
package/README.md
CHANGED
|
@@ -1,325 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
# pinia-plugin-subscription
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pinia plugin for Vue.js that helps building Pinia plugins by centralizing subscriber registration and providing a `Store` base class for store helpers.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
This project provides:
|
|
6
|
+
- a lightweight mechanism to declare "subscribers" that are invoked when stores are registered or updated by Pinia;
|
|
7
|
+
- a `Store` base class (helper wrapper) to ease interacting with Pinia stores from subscribers or other plugin code;
|
|
8
|
+
- an API to create a Pinia plugin from a list of subscribers;
|
|
9
|
+
- the `$reset` method on 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
|
+
## Tested & maintained
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
The package is fully tested with [Vitest](https://vitest.dev/): runtime behavior, type-level contracts (`*.test-d.ts`) and performance regressions are covered, and coverage reports are available in `coverage/`. The full test suite and the build run on every push and pull request through [GitHub Actions](.github/workflows/ci.yml).
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
## Trust & supply chain
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
This package is **open source (MIT)** and developed in a [public repository](https://github.com/AlxBDo/pinia-plugin-subscription). You can read, audit, fork and self-host the code at any time — no vendor lock-in, no service-continuity risk.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
Development follows these practices:
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
- **No direct pushes to `main`** — every change lands through a reviewed pull request (branch protection enforced).
|
|
23
|
+
- **Commits are GPG-signed** and verified on GitHub.
|
|
24
|
+
- **Tests and build gate every merge** — a pull request cannot merge with a failing CI run.
|
|
25
|
+
- **npm releases ship with provenance** — each published package is built and signed on GitHub Actions and cryptographically linked to the exact commit and CI run that built it ([example transparency log entry](https://search.sigstore.dev/?logIndex=2811656622)), so you can verify what you install.
|
|
26
|
+
- **Security issues are handled privately** — see [SECURITY.md](SECURITY.md) for the reporting process.
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
## Documentation
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
The package serves two audiences:
|
|
31
|
+
|
|
32
|
+
| You want to… | Guide |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Use a compatible plugin (e.g. `pinia-plugin-extending-store`, `pinia-plugin-persist-state`) or this package's built-in store features (`defineAStore`, `$reset`, state rollback) in your app | [Using compatible plugins](docs/using-plugins.md) |
|
|
35
|
+
| Build your own pinia plugin on top of `pinia-plugin-subscription` | [Plugin authoring guide](docs/authoring-plugins.md) |
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install pinia pinia-plugin-subscription
|
|
29
41
|
```
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
Register the plugin in your `main.ts`:
|
|
44
|
+
|
|
45
|
+
```js
|
|
32
46
|
import { createApp } from 'vue'
|
|
33
47
|
import { createPinia } from 'pinia'
|
|
34
|
-
import { createPlugin, PLUGIN_NAME
|
|
35
|
-
import { myStoreSubscriber } from './
|
|
48
|
+
import { createPlugin, PLUGIN_NAME } from 'pinia-plugin-subscription'
|
|
49
|
+
import { myStoreSubscriber } from './plugins/my-store'
|
|
36
50
|
import App from './App.vue'
|
|
37
51
|
|
|
38
52
|
const app = createApp(App)
|
|
39
53
|
const pinia = createPinia()
|
|
40
54
|
|
|
41
|
-
// Register plugin (subscribers array, debug
|
|
42
|
-
pinia.use(createPlugin([myStoreSubscriber], [
|
|
55
|
+
// Register plugin (subscribers array, plugins to debug)
|
|
56
|
+
pinia.use(createPlugin([myStoreSubscriber], [PLUGIN_NAME]))
|
|
43
57
|
|
|
44
58
|
app.use(pinia)
|
|
45
59
|
app.mount('#app')
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
**1) Subscriber using a `Store` subclass:**
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import PluginSubscriber from 'pinia-plugin-subscription'
|
|
54
|
-
import { Store } from 'pinia-plugin-subscription'
|
|
55
|
-
|
|
56
|
-
class MyPlugin extends Store {
|
|
57
|
-
protected override _className: string = 'MyPlugin'
|
|
58
|
-
protected static override _requiredKeys?: string[] | undefined = ['my-plugin-option']
|
|
59
|
-
|
|
60
|
-
constructor(store, options, debug = false) {
|
|
61
|
-
super(store, options, debug)
|
|
62
|
-
this.doSomething()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
class MyPluginSubscriber extends PluginSubscriber<MyPlugin> {
|
|
67
|
-
constructor() {
|
|
68
|
-
super('my-plugin', MyPlugin.customizeStore.bind(MyPlugin))
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const myStoreSubscriber = new MyPluginSubscriber()
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**2) Simple subscriber implementing `PluginSubscriberInterface`:**
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import type { PluginSubscriberInterface } from 'pinia-plugin-subscription'
|
|
79
|
-
|
|
80
|
-
export const myStoreSubscriber: PluginSubscriberInterface = {
|
|
81
|
-
name: 'my-plugin',
|
|
82
|
-
|
|
83
|
-
invoke: (context, debug) => {
|
|
84
|
-
// context contains `store`, `options`, `pinia`
|
|
85
|
-
console.log('store registered', context.store.$id)
|
|
86
|
-
|
|
87
|
-
return true
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
resetStoreCallback: (store) => {
|
|
91
|
-
console.log('store reset:', store.$id)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Advanced Features
|
|
97
|
-
|
|
98
|
-
- **Debug mode:** Specifies the name of the plugin(s) to debug as the second argument to `createPlugin` to enable detailed logging.
|
|
99
|
-
- **Reset callbacks:** Define `resetStoreCallback` to run custom logic when a store is reset.
|
|
100
|
-
- **SSR and hydration control:** Configure whether each subscribed plugin runs on the server, the client, or only after client hydration.
|
|
101
|
-
- **Lifecycle safety:** Internal hydration ordering is now guarded so `hydrate()` completes before `afterHydration()` and disposed stores do not retain stale subscriber metadata.
|
|
102
|
-
|
|
103
|
-
## API Reference
|
|
104
|
-
|
|
105
|
-
### `createPlugin(subscribers: PluginSubscriber[], debug?: string[]): PiniaPlugin`
|
|
106
|
-
|
|
107
|
-
Creates and returns a Pinia plugin from the provided `subscribers`. Each subscriber is invoked when a store is registered.
|
|
108
|
-
|
|
109
|
-
### `createHydrationPlugin(subscribers: PluginSubscriber[], options?: PluginSubscriptionOptions): PiniaPlugin`
|
|
110
|
-
|
|
111
|
-
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.
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { createHydrationPlugin, PLUGIN_NAME as PPS } from 'pinia-plugin-subscription'
|
|
115
|
-
|
|
116
|
-
pinia.use(createHydrationPlugin([
|
|
117
|
-
persistedStateSubscriber
|
|
118
|
-
], {
|
|
119
|
-
debug: [PPS],
|
|
120
|
-
hydrationScheduler: (run) => {
|
|
121
|
-
nextTick(() => run())
|
|
122
|
-
}
|
|
123
|
-
}))
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### SSR / Nuxt hydration control
|
|
127
|
-
|
|
128
|
-
The source of truth for hydration policy stays on the subscriber itself. For example, a client-only plugin should declare its execution directly:
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
export const persistedStateSubscriber: PluginSubscriberInterface = {
|
|
132
|
-
name: 'persisted-state',
|
|
133
|
-
execution: {
|
|
134
|
-
environment: 'client',
|
|
135
|
-
hydration: 'defer',
|
|
136
|
-
},
|
|
137
|
-
hydrationScheduler: (run) => {
|
|
138
|
-
nextTick(() => run())
|
|
139
|
-
},
|
|
140
|
-
invoke: (context, debug) => {
|
|
141
|
-
// ...
|
|
142
|
-
return true
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Available execution options:
|
|
148
|
-
|
|
149
|
-
- `environment: 'both' | 'client' | 'server'` — controls where the subscriber is allowed to run.
|
|
150
|
-
- `hydration: 'immediate' | 'defer'` — on the client, `defer` schedules execution after hydration.
|
|
151
|
-
- `hydrationScheduler` can be provided either on a subscriber or via `createHydrationPlugin()` to override the execution timing when needed.
|
|
152
|
-
|
|
153
|
-
### SSR / Nuxt best practices
|
|
154
|
-
|
|
155
|
-
`pinia-plugin-subscription` is SSR-safe by design, but the runtime policy still needs to match the actual app lifecycle.
|
|
62
|
+
### Import subpaths
|
|
156
63
|
|
|
157
|
-
|
|
64
|
+
For helper-only imports, the package exposes an additive subpath kept separate from the default library API:
|
|
158
65
|
|
|
159
66
|
```ts
|
|
160
|
-
|
|
161
|
-
name: 'persisted-state',
|
|
162
|
-
execution: {
|
|
163
|
-
environment: 'client',
|
|
164
|
-
hydration: 'defer',
|
|
165
|
-
},
|
|
166
|
-
hydrationScheduler: (run) => nextTick(() => run()),
|
|
167
|
-
invoke: () => true,
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Use `runtimeEnvironment: 'server'` or `execution.environment: 'server'` when a subscriber is only valid during server-side rendering or when it should not touch browser-only objects.
|
|
172
|
-
|
|
173
|
-
```ts
|
|
174
|
-
pinia.use(createHydrationPlugin([serverOnlySubscriber], {
|
|
175
|
-
runtimeEnvironment: 'server'
|
|
176
|
-
}))
|
|
67
|
+
import { PluginSubscriber, Store } from 'pinia-plugin-subscription/helpers'
|
|
177
68
|
```
|
|
178
69
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
- do not access `window`, `document`, `localStorage`, or `matchMedia` at module scope or during store setup when the code may run on the server,
|
|
182
|
-
- keep browser-specific bootstrap in `hydrate()` or `afterHydration()`,
|
|
183
|
-
- prefer environment-aware guards such as `typeof window !== 'undefined'`,
|
|
184
|
-
- avoid doing network access or hydration reads before the client is ready unless the code is explicitly intended to run on both environments.
|
|
185
|
-
|
|
186
|
-
A safe pattern is:
|
|
70
|
+
For type-only imports, prefer the dedicated public types entry:
|
|
187
71
|
|
|
188
72
|
```ts
|
|
189
|
-
|
|
190
|
-
name: 'safe-browser-access',
|
|
191
|
-
execution: {
|
|
192
|
-
environment: 'client',
|
|
193
|
-
hydration: 'defer',
|
|
194
|
-
},
|
|
195
|
-
hydrate: (context) => {
|
|
196
|
-
// safe browser access here after hydration is allowed
|
|
197
|
-
const saved = localStorage.getItem('demo')
|
|
198
|
-
if (saved) {
|
|
199
|
-
context.store.$patch({ value: JSON.parse(saved) })
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
invoke: () => true,
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
This keeps the server render deterministic and prevents browser-only initialization from crashing Nuxt or SSR builds.
|
|
207
|
-
|
|
208
|
-
### `defineAStoreCtx(id, setup, options?)`
|
|
209
|
-
|
|
210
|
-
Use actions or access the state, added by one or more plugins, when defining the store.
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
import { defineAStoreCtx, getEnhancedStore } from 'pinia-plugin-subscription'
|
|
214
|
-
|
|
215
|
-
type EnhancedStore = { addItem: (value: unknown) => void }
|
|
216
|
-
|
|
217
|
-
export const useMyStore = defineAStoreCtx('myStore', (ctx) => {
|
|
218
|
-
const enhanced = getEnhancedStore<EnhancedStore>(ctx)
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
addViaEnhanced: (value: unknown) => enhanced.addItem(value)
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
`ctx.extensions` now supports:
|
|
227
|
-
- `enhancedStore`
|
|
228
|
-
|
|
229
|
-
The setup context is indexed internally only when `storeOptions.enhancedStore` is set to `true` (this is enabled automatically by `defineAStoreCtx`).
|
|
230
|
-
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.
|
|
231
|
-
When `storeOptions.debug` is `true`, setup-context map size transitions are logged for observability.
|
|
232
|
-
|
|
233
|
-
### `PluginSubscriberInterface`
|
|
234
|
-
|
|
235
|
-
An object with at least an `invoke(context: PiniaPluginContext, debug?: boolean)` method, plus optional properties:
|
|
236
|
-
|
|
237
|
-
- `execution?: { environment?: 'both' | 'client' | 'server'; hydration?: 'immediate' | 'defer' }`
|
|
238
|
-
- `hydrationScheduler?: (run: () => void) => void`
|
|
239
|
-
- `hydrate?: (context, debug) => void | Promise<void>` — optional hook for SSR-safe store initialization after the subscriber is accepted for execution
|
|
240
|
-
- `afterHydration?: (context, debug) => void | Promise<void>` — optional lifecycle hook for post-hydration work
|
|
241
|
-
- `resetStoreCallback?: (store?: any) => void`
|
|
242
|
-
- `storeOnActionSubscription?: { store, callback }` (getter)
|
|
243
|
-
- `storeMutationSubscription?: { store, callback }` (getter)
|
|
244
|
-
- `subscriptions?: Record<string, Function>` (plugin-specific subscription functions)
|
|
245
|
-
|
|
246
|
-
## The `PluginSubscriber` Abstract Class
|
|
247
|
-
|
|
248
|
-
The project provides an abstract `PluginSubscriber` implementation (see [src/core/PluginSubscriber.ts](src/core/PluginSubscriber.ts)) to simplify creating reusable subscribers.
|
|
249
|
-
|
|
250
|
-
**Typical usage:**
|
|
251
|
-
|
|
252
|
-
- The subscriber instantiates a `Store` (or subclass) via a factory (`MyStore.customizeStore`).
|
|
253
|
-
- The instance exposes:
|
|
254
|
-
- `subscriptions` (from `getSubscriptions()`)
|
|
255
|
-
- `storeMutationSubscription` (from `storeSubscribe`)
|
|
256
|
-
- `storeOnActionSubscription` (from `onAction`)
|
|
257
|
-
- optional `pluginCreated(store)` hook called after initialization
|
|
258
|
-
|
|
259
|
-
**Example:**
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
import { nextTick } from 'vue'
|
|
263
|
-
import PluginSubscriber from 'pinia-plugin-subscription'
|
|
264
|
-
import StoreExtension from './src/extending-pinia-store/core/StoreExtension'
|
|
265
|
-
import { addStore } from './src/extending-pinia-store/plugins/stores'
|
|
266
|
-
|
|
267
|
-
class ExtendingStoreSubscriber extends PluginSubscriber<StoreExtension> {
|
|
268
|
-
constructor() {
|
|
269
|
-
super('extendsPiniaStore', StoreExtension.customizeStore.bind(StoreExtension))
|
|
270
|
-
|
|
271
|
-
// Controls when this subscriber may run and whether it should wait for hydration on the client.
|
|
272
|
-
this.execution = {
|
|
273
|
-
environment: 'client',
|
|
274
|
-
hydration: 'defer',
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Optional framework-specific scheduler for deferred execution.
|
|
278
|
-
this.hydrationScheduler = (run) => {
|
|
279
|
-
nextTick(() => run())
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
this.pluginCreated = addStore
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
override hydrate() {
|
|
286
|
-
if (typeof window === 'undefined') {
|
|
287
|
-
return
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Safe browser-only initialization. This is only called when the policy allows it.
|
|
291
|
-
return this.storeInstance?.hydrate?.()
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export const extendingStoreSubscriber = new ExtendingStoreSubscriber()
|
|
73
|
+
import type { PluginSubscriptionOptions } from 'pinia-plugin-subscription/types'
|
|
296
74
|
```
|
|
297
75
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
- `execution.environment` restricts the subscriber to `'both'`, `'client'`, or `'server'`
|
|
301
|
-
- `execution.hydration` is `'immediate'` by default and can be set to `'defer'` when the plugin must wait for client hydration
|
|
302
|
-
- `hydrationScheduler` lets you override the runtime scheduling behavior, for example with Nuxt/Vue `nextTick()`
|
|
303
|
-
- `hydrate()` / `afterHydration()` are optional lifecycle hooks for browser-only initialization and post-hydration work; they help keep SSR-sensitive logic out of constructors
|
|
304
|
-
|
|
305
|
-
## The `Store` Class — Summary
|
|
306
|
-
|
|
307
|
-
The `Store` class (see [src/core/Store.ts](src/core/Store.ts)) is a wrapper around a `PiniaStore` providing:
|
|
308
|
-
|
|
309
|
-
- **Properties:** `debug`, `options`, `state`, `store`.
|
|
310
|
-
- **Useful methods:**
|
|
311
|
-
- `addToState(name, value?)` — adds a property to store state and exposes it as a `Ref` when appropriate.
|
|
312
|
-
- `addSubscription(pluginName, subscription)` — registers a plugin-specific subscription function.
|
|
313
|
-
- `getSubscriptions()` — returns registered subscriptions.
|
|
314
|
-
- `storeSubscribe` (getter/setter) — factory for `store.$subscribe` ({ store, callback }).
|
|
315
|
-
- `onAction` (getter/setter) — factory for `onAction` ({ store, callback }).
|
|
316
|
-
- `static customizeStore(store, options, debug?)` — recommended factory for class instantiation.
|
|
317
|
-
- `debugLog(message, args)` — conditional logging.
|
|
318
|
-
- Helpers: `stateHas()`, `storeHas()`, `getValue()`.
|
|
319
|
-
|
|
320
|
-
## Testing
|
|
76
|
+
## Development
|
|
321
77
|
|
|
322
|
-
|
|
78
|
+
- `npm run dev` — runs the dedicated demo app. The library build remains `npm run build` and is intentionally kept separate from the demo entry. The demo ships a complete plugin-authoring example in [`src/demo/extending-pinia-store/`](src/demo/extending-pinia-store/).
|
|
79
|
+
- `npm test` — runs the Vitest suite, including typecheck tests (`*.test-d.ts`) validating type-level contracts such as `StoreOptionsExtensions` merging. Coverage reports are available in `coverage/`.
|
|
80
|
+
- `npm run bundle:check` — measures the emitted bundle size.
|
|
323
81
|
|
|
324
82
|
## License
|
|
325
83
|
|
|
@@ -327,4 +85,4 @@ MIT
|
|
|
327
85
|
|
|
328
86
|
## Publishing
|
|
329
87
|
|
|
330
|
-
Releases are published to npm via GitHub Actions. GitHub releases marked as pre-releases are published with the `beta` dist-tag using `npm publish --tag beta`; stable releases use the default `latest` tag. The workflow uses npm provenance for better supply-chain security and requires a `NPM_TOKEN` secret in the repository settings.
|
|
88
|
+
Releases are published to npm via GitHub Actions. GitHub releases marked as pre-releases are published with the `beta` dist-tag using `npm publish --tag beta`; stable releases use the default `latest` tag. The workflow uses npm provenance for better supply-chain security and requires a `NPM_TOKEN` secret in the repository settings.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var f = Object.defineProperty;
|
|
2
|
-
var
|
|
3
|
-
var n = (s, t, e) =>
|
|
4
|
-
import { ref as
|
|
2
|
+
var S = (s, t, e) => t in s ? f(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
|
|
3
|
+
var n = (s, t, e) => S(s, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { ref as b, toRef as y } from "vue";
|
|
5
5
|
import { defineStore as g } from "pinia";
|
|
6
|
-
class
|
|
6
|
+
class H {
|
|
7
7
|
constructor(t, e, r) {
|
|
8
8
|
n(this, "_console");
|
|
9
9
|
n(this, "_createInstance");
|
|
@@ -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
|
}
|
|
@@ -98,19 +98,19 @@ function x(s, t, e) {
|
|
|
98
98
|
const r = s.$dispose;
|
|
99
99
|
s.$dispose = (...o) => (p.delete(s), c.delete(t), h(t, e, "dispose cleanup"), r.apply(s, o)), l.add(s);
|
|
100
100
|
}
|
|
101
|
-
function
|
|
101
|
+
function L(s, t, e) {
|
|
102
102
|
const r = e ? { storeOptions: e } : {};
|
|
103
103
|
return typeof t == "function" ? d(
|
|
104
104
|
s,
|
|
105
105
|
t,
|
|
106
106
|
r
|
|
107
|
-
) :
|
|
107
|
+
) : $(
|
|
108
108
|
s,
|
|
109
109
|
t,
|
|
110
110
|
r
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
|
-
function
|
|
113
|
+
function N(s, t, e) {
|
|
114
114
|
const r = e ? { storeOptions: { ...e, enhancedStore: !0 } } : { storeOptions: { enhancedStore: !0 } };
|
|
115
115
|
return d(
|
|
116
116
|
s,
|
|
@@ -122,36 +122,44 @@ function d(s, t, e) {
|
|
|
122
122
|
const r = {
|
|
123
123
|
id: s,
|
|
124
124
|
extensions: {}
|
|
125
|
-
}, o = e?.storeOptions?.enhancedStore === !0, i = g(
|
|
125
|
+
}, o = e?.storeOptions?.enhancedStore === !0, i = g(
|
|
126
|
+
s,
|
|
127
|
+
() => t(r),
|
|
128
|
+
e
|
|
129
|
+
);
|
|
126
130
|
return Object.assign(((...u) => {
|
|
127
131
|
o && (c.set(s, r), h(s, e, "registered context by id"));
|
|
128
132
|
const a = i(...u);
|
|
129
133
|
return o && (p.set(a, r), c.delete(s), h(s, e, "moved context to weak map"), x(a, s, e)), a;
|
|
130
134
|
}), i);
|
|
131
135
|
}
|
|
132
|
-
function
|
|
136
|
+
function $(s, t, e) {
|
|
133
137
|
return e && (t = { ...t, ...e ?? {} }), g(s, t);
|
|
134
138
|
}
|
|
135
|
-
const
|
|
139
|
+
const m = /* @__PURE__ */ new Set(["_", "$"]);
|
|
136
140
|
function I(s) {
|
|
137
|
-
return
|
|
141
|
+
return m.has(s[0]);
|
|
138
142
|
}
|
|
139
143
|
function w(s) {
|
|
140
|
-
|
|
144
|
+
const t = s;
|
|
145
|
+
return p.get(s) ?? (typeof t?.$id == "string" ? c.get(t.$id) : void 0);
|
|
141
146
|
}
|
|
142
|
-
function
|
|
147
|
+
function E(s) {
|
|
143
148
|
const t = s.extensions.enhancedStore ?? s.extensions.extending;
|
|
144
149
|
if (!t)
|
|
145
150
|
throw new Error(`${s.id}Store - getEnhancedStore - Error: enhanced store is required`);
|
|
146
151
|
return t;
|
|
147
152
|
}
|
|
148
|
-
function
|
|
153
|
+
function k(s, t) {
|
|
149
154
|
s.extensions.enhancedStore = t, s.extensions.extending = t;
|
|
150
155
|
}
|
|
151
156
|
function T(s) {
|
|
152
|
-
return
|
|
157
|
+
return console.warn(`${s.id}Store - getExtendingStore is deprecated. Use getEnhancedStore instead.`), E(s);
|
|
153
158
|
}
|
|
154
|
-
function
|
|
159
|
+
function W(s, t) {
|
|
160
|
+
return `${s}-${t.$id}`;
|
|
161
|
+
}
|
|
162
|
+
function M(s) {
|
|
155
163
|
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
164
|
}
|
|
157
165
|
class P extends C {
|
|
@@ -165,7 +173,7 @@ class P extends C {
|
|
|
165
173
|
n(this, "_storeSubscribe");
|
|
166
174
|
if (this._options = r.storeOptions, this._store = e, this._options?.enhancedStore) {
|
|
167
175
|
const u = w(this.store);
|
|
168
|
-
u &&
|
|
176
|
+
u && k(u, this.store);
|
|
169
177
|
}
|
|
170
178
|
}
|
|
171
179
|
get onAction() {
|
|
@@ -190,10 +198,6 @@ class P extends C {
|
|
|
190
198
|
get store() {
|
|
191
199
|
return this._store;
|
|
192
200
|
}
|
|
193
|
-
hydrate() {
|
|
194
|
-
}
|
|
195
|
-
afterHydration() {
|
|
196
|
-
}
|
|
197
201
|
get storeSubscribe() {
|
|
198
202
|
if (this._storeSubscribe)
|
|
199
203
|
return () => ({
|
|
@@ -204,34 +208,18 @@ class P extends C {
|
|
|
204
208
|
set storeSubscribe(e) {
|
|
205
209
|
this._storeSubscribe = e;
|
|
206
210
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Add property to state
|
|
209
|
-
* @param name
|
|
210
|
-
* @param value
|
|
211
|
-
*/
|
|
212
211
|
addToState(e, r) {
|
|
213
|
-
this.isOptionApi() || r?.value || (r =
|
|
212
|
+
this.isOptionApi() || r?.value || (r = b(r)), this.state[e] = r, this.store[e] = y(this.state, e);
|
|
214
213
|
}
|
|
215
214
|
addSubscription(e, r, o) {
|
|
216
215
|
this._subscriptions[e] = { subscription: r, ...o ?? {} };
|
|
217
216
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* @param store
|
|
221
|
-
* @param options
|
|
222
|
-
* @param debug
|
|
223
|
-
* @returns
|
|
224
|
-
*/
|
|
217
|
+
afterHydration() {
|
|
218
|
+
}
|
|
225
219
|
static customizeStore(e, r, o = !1, i) {
|
|
226
220
|
if (r.storeOptions && this.hasRequiredKeys(r.storeOptions))
|
|
227
221
|
return new this(e, r, o, i);
|
|
228
222
|
}
|
|
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
223
|
getOption(e) {
|
|
236
224
|
return this.options && this.options[e];
|
|
237
225
|
}
|
|
@@ -239,12 +227,20 @@ class P extends C {
|
|
|
239
227
|
return this.getValue(this.state[e]);
|
|
240
228
|
}
|
|
241
229
|
getSubscriptions() {
|
|
242
|
-
if (!
|
|
230
|
+
if (!M(this._subscriptions))
|
|
243
231
|
return this._subscriptions;
|
|
244
232
|
}
|
|
245
233
|
getValue(e) {
|
|
246
234
|
return e?.__v_isRef ? e.value : e;
|
|
247
235
|
}
|
|
236
|
+
hasDeniedFirstChar(e) {
|
|
237
|
+
return I(e);
|
|
238
|
+
}
|
|
239
|
+
static hasRequiredKeys(e) {
|
|
240
|
+
return this._requiredKeys !== void 0 && this._requiredKeys?.every((r) => !!e[r]);
|
|
241
|
+
}
|
|
242
|
+
hydrate() {
|
|
243
|
+
}
|
|
248
244
|
isOptionApi() {
|
|
249
245
|
return this.store._isOptionsAPI;
|
|
250
246
|
}
|
|
@@ -314,25 +310,28 @@ class D {
|
|
|
314
310
|
);
|
|
315
311
|
}
|
|
316
312
|
}
|
|
317
|
-
class
|
|
313
|
+
class K extends D {
|
|
318
314
|
constructor() {
|
|
319
315
|
super(...arguments);
|
|
320
316
|
n(this, "_pluginName", _);
|
|
321
317
|
}
|
|
322
318
|
}
|
|
323
|
-
const
|
|
319
|
+
const F = new K();
|
|
324
320
|
export {
|
|
325
321
|
D as C,
|
|
326
322
|
C as D,
|
|
327
|
-
|
|
323
|
+
F as P,
|
|
328
324
|
P as S,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
325
|
+
H as a,
|
|
326
|
+
N as b,
|
|
327
|
+
w as c,
|
|
328
|
+
L as d,
|
|
329
|
+
E as e,
|
|
330
|
+
T as f,
|
|
331
|
+
W as g,
|
|
332
|
+
I as h,
|
|
333
|
+
M as i,
|
|
336
334
|
_ as p,
|
|
337
|
-
|
|
335
|
+
k as s
|
|
338
336
|
};
|
|
337
|
+
//# sourceMappingURL=index-VL_PgzVK.js.map
|