pinia-plugin-subscription 0.2.1 → 1.0.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 +32 -1
- package/README.md +247 -3
- package/SECURITY.md +19 -1
- package/dist/chunks/constantes-BsD73jkk.js +245 -0
- package/dist/chunks/constantes-BsD73jkk.js.map +1 -0
- package/dist/core/PluginSubscriber.d.ts +36 -8
- package/dist/core/PluginSubscription.d.ts +20 -5
- package/dist/core/Store.d.ts +92 -11
- package/dist/factories/trace-registry.d.ts +6 -0
- package/dist/helpers/index.d.ts +5 -0
- package/dist/helpers.js +181 -5
- package/dist/helpers.js.map +1 -1
- package/dist/lib/main.d.ts +1 -3
- package/dist/pinia-plugin-subscription.js +207 -215
- package/dist/pinia-plugin-subscription.js.map +1 -1
- package/dist/plugins/createHydrationPlugin.d.ts +7 -0
- package/dist/plugins/createPlugin.d.ts +9 -2
- package/dist/system/Tracer.d.ts +20 -0
- package/dist/system/createConsoleTraceListener.d.ts +6 -0
- package/dist/types/index.d.ts +16 -5
- package/dist/types/plugin.d.ts +45 -6
- package/dist/types/store.d.ts +3 -5
- package/dist/types/trace.d.ts +80 -0
- package/docs/authoring-plugins.md +22 -23
- package/docs/using-plugins.md +56 -19
- package/package.json +3 -2
- package/dist/chunks/index-DKdl-fiq.js +0 -337
- package/dist/chunks/index-DKdl-fiq.js.map +0 -1
- package/dist/system/Debug.d.ts +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ 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 `createTracerRegistry()` to create isolated trace listeners and tracers for each application or Pinia instance
|
|
12
|
+
- Added the `Tracer` class and registry methods `addTraceListener()`, `clearTraceListeners()` and `hasTraceListeners()`
|
|
13
|
+
- Added `createConsoleTraceListener()` as an opt-in console sink for trace events
|
|
14
|
+
- Added `trace()` and `traceError()` to `Store` and `PluginSubscription`
|
|
15
|
+
- Added trace types: `TraceEvent`, `TraceEventMetadata`, `TraceFilter`, `TraceHandler`, `TraceLevel`, `TraceListener`, `TraceListenerRemover`, `TracePayload`, `TracerOptions`
|
|
16
|
+
- Added `check:leaks` script and `scripts/check-dist-leaks.mjs`, guarding published artifacts against non-peer dependency imports in `dist/` (`.js` and `.d.ts`)
|
|
17
|
+
- Added the leak guard to `npm run build` and to the CI workflow
|
|
18
|
+
- Added unit tests covering the tracing system and the leak detection logic
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Clarified that SSR tracing registries and plugin instances must be created per request, and added coverage for listener isolation between registries
|
|
22
|
+
- Replaced the `Debug` abstract class with composition: `Store` and `PluginSubscription` no longer extend it and now own a `Tracer`
|
|
23
|
+
- `createPlugin()` accepts `PluginSubscriptionOptions`, including a `createTracer` factory to connect a tracer registry to plugin and store trace events
|
|
24
|
+
- Trace errors with no matching listener, and failures in listener filters or handlers, are now reported with `console.error` instead of rethrowing the original error
|
|
25
|
+
- Deprecated `createHydrationPlugin()` in favor of `createPlugin()` with hydration options
|
|
26
|
+
- Trace payloads are now lazily resolved, so unobserved tracing no longer builds log arguments
|
|
27
|
+
- Internal log messages became structured namespaces (`plugin:invoke`, `store:mutation`, `subscription:delivery`, …) carrying `store.$id` as `scope`
|
|
28
|
+
- `Tracer` now delegates all output decisions to listeners and no longer owns a console or an enabled state
|
|
29
|
+
- Error events are delivered to matching listeners and reported with `console.error` when none match
|
|
30
|
+
- Excluded `scripts/**` from coverage thresholds, as build tooling is not shipped to consumers
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
- **Breaking:** removed `debugLog()` and `logError()` from `Store` and `PluginSubscription` — use `trace()` and `traceError()` instead (see the migration snippet in `README.md`)
|
|
34
|
+
- **Breaking:** removed the obsolete `debug` and custom-console arguments from `createPlugin()`, `PluginSubscription`, `PluginSubscriber`, `Store`, and `Store.customizeStore()`
|
|
35
|
+
- **Breaking:** removed the `debug` argument from subscriber `invoke()`, `hydrate()`, and `afterHydration()` hooks; tracing is now configured exclusively through listeners
|
|
36
|
+
- **Breaking:** removed the legacy `debug` and `console` accessors from `Store` and `PluginSubscription`
|
|
37
|
+
- Removed the internal `src/system/Debug.ts` abstract class (was never publicly exported)
|
|
38
|
+
- **Breaking:** replaced module-global trace-listener functions with methods on a `createTracerRegistry()` instance
|
|
39
|
+
|
|
8
40
|
## [0.2.1]
|
|
9
41
|
|
|
10
42
|
### Fixed
|
|
@@ -97,7 +129,6 @@ Minor change
|
|
|
97
129
|
- `getDefineAStoreSetupContext()` now falls back to store ID lookup during plugin execution timing.
|
|
98
130
|
- `defineAStoreSetup()` now stores setup contexts only for stores with `storeOptions.enhancedStore = true`.
|
|
99
131
|
- `defineAStoreSetup()` now removes ID-indexed setup contexts after weak-map registration and on store disposal.
|
|
100
|
-
- `defineAStoreSetup()` now logs setup-context map size transitions when `storeOptions.debug = true`.
|
|
101
132
|
- Public exports now include `defineAStoreCtx`, `getEnhancedStore`, and `setEnhancedStore`.
|
|
102
133
|
- GitHub Actions now publishes GitHub pre-releases to npm with the `beta` dist-tag.
|
|
103
134
|
|
package/README.md
CHANGED
|
@@ -47,15 +47,14 @@ Register the plugin in your `main.ts`:
|
|
|
47
47
|
```js
|
|
48
48
|
import { createApp } from 'vue'
|
|
49
49
|
import { createPinia } from 'pinia'
|
|
50
|
-
import { createPlugin
|
|
50
|
+
import { createPlugin } from 'pinia-plugin-subscription'
|
|
51
51
|
import { myStoreSubscriber } from './plugins/my-store'
|
|
52
52
|
import App from './App.vue'
|
|
53
53
|
|
|
54
54
|
const app = createApp(App)
|
|
55
55
|
const pinia = createPinia()
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
pinia.use(createPlugin([myStoreSubscriber], [PLUGIN_NAME]))
|
|
57
|
+
pinia.use(createPlugin([myStoreSubscriber]))
|
|
59
58
|
|
|
60
59
|
app.use(pinia)
|
|
61
60
|
app.mount('#app')
|
|
@@ -63,6 +62,114 @@ app.mount('#app')
|
|
|
63
62
|
|
|
64
63
|
### Import subpaths
|
|
65
64
|
|
|
65
|
+
## Usage — Examples
|
|
66
|
+
|
|
67
|
+
**1) Subscriber using a `Store` subclass:**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import PluginSubscriber from 'pinia-plugin-subscription'
|
|
71
|
+
import { Store } from 'pinia-plugin-subscription'
|
|
72
|
+
|
|
73
|
+
class MyPlugin extends Store {
|
|
74
|
+
protected override _className: string = 'MyPlugin'
|
|
75
|
+
protected static override _requiredKeys?: string[] | undefined = ['my-plugin-option']
|
|
76
|
+
|
|
77
|
+
constructor(store, options) {
|
|
78
|
+
super(store, options)
|
|
79
|
+
this.doSomething()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class MyPluginSubscriber extends PluginSubscriber<MyPlugin> {
|
|
84
|
+
constructor() {
|
|
85
|
+
super('my-plugin', MyPlugin.customizeStore.bind(MyPlugin))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const myStoreSubscriber = new MyPluginSubscriber()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**2) Simple subscriber implementing `PluginSubscriberInterface`:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import type { PluginSubscriberInterface } from 'pinia-plugin-subscription'
|
|
96
|
+
|
|
97
|
+
export const myStoreSubscriber: PluginSubscriberInterface = {
|
|
98
|
+
name: 'my-plugin',
|
|
99
|
+
|
|
100
|
+
invoke: (context) => {
|
|
101
|
+
// context contains `store`, `options`, `pinia`
|
|
102
|
+
console.log('store registered', context.store.$id)
|
|
103
|
+
|
|
104
|
+
return true
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
resetStoreCallback: (store) => {
|
|
108
|
+
console.log('store reset:', store.$id)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Advanced Features
|
|
114
|
+
|
|
115
|
+
- **Structured tracing:** Subscribe to typed trace events and filter them at runtime, per store or per namespace — see [Tracing](#tracing).
|
|
116
|
+
- **Reset callbacks:** Define `resetStoreCallback` to run custom logic when a store is reset.
|
|
117
|
+
- **SSR and hydration control:** Configure whether each subscribed plugin runs on the server, the client, or only after client hydration.
|
|
118
|
+
- **Lifecycle safety:** Internal hydration ordering is now guarded so `hydrate()` completes before `afterHydration()` and disposed stores do not retain stale subscriber metadata.
|
|
119
|
+
|
|
120
|
+
## API Reference
|
|
121
|
+
|
|
122
|
+
### `createPlugin(subscribers: PluginSubscriber[]): PiniaPlugin`
|
|
123
|
+
|
|
124
|
+
Creates and returns a Pinia plugin from the provided `subscribers`. Each subscriber is invoked when a store is registered.
|
|
125
|
+
|
|
126
|
+
### `createHydrationPlugin(subscribers: PluginSubscriber[], options?: PluginSubscriptionOptions): PiniaPlugin`
|
|
127
|
+
|
|
128
|
+
Deprecated. Use `createPlugin()` with hydration options instead. This helper remains available for compatibility.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { createPlugin } from 'pinia-plugin-subscription'
|
|
132
|
+
|
|
133
|
+
pinia.use(createPlugin([
|
|
134
|
+
persistedStateSubscriber
|
|
135
|
+
], {
|
|
136
|
+
hydrationScheduler: (run) => {
|
|
137
|
+
nextTick(() => run())
|
|
138
|
+
}
|
|
139
|
+
}))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### SSR / Nuxt hydration control
|
|
143
|
+
|
|
144
|
+
The source of truth for hydration policy stays on the subscriber itself. For example, a client-only plugin should declare its execution directly:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export const persistedStateSubscriber: PluginSubscriberInterface = {
|
|
148
|
+
name: 'persisted-state',
|
|
149
|
+
execution: {
|
|
150
|
+
environment: 'client',
|
|
151
|
+
hydration: 'defer',
|
|
152
|
+
},
|
|
153
|
+
hydrationScheduler: (run) => {
|
|
154
|
+
nextTick(() => run())
|
|
155
|
+
},
|
|
156
|
+
invoke: (context) => {
|
|
157
|
+
// ...
|
|
158
|
+
return true
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Available execution options:
|
|
164
|
+
|
|
165
|
+
- `environment: 'both' | 'client' | 'server'` — controls where the subscriber is allowed to run.
|
|
166
|
+
- `hydration: 'immediate' | 'defer'` — on the client, `defer` schedules execution after hydration.
|
|
167
|
+
- `hydrationScheduler` can be provided on a subscriber or through `createPlugin()` options to override execution timing when needed.
|
|
168
|
+
|
|
169
|
+
### SSR / Nuxt best practices
|
|
170
|
+
|
|
171
|
+
`pinia-plugin-subscription` is SSR-safe by design, but the runtime policy still needs to match the actual app lifecycle.
|
|
172
|
+
|
|
66
173
|
For helper-only imports, the package exposes an additive subpath kept separate from the default library API:
|
|
67
174
|
|
|
68
175
|
```ts
|
|
@@ -77,10 +184,147 @@ import type { PluginSubscriptionOptions } from 'pinia-plugin-subscription/types'
|
|
|
77
184
|
|
|
78
185
|
## Development
|
|
79
186
|
|
|
187
|
+
This example shows the recommended pattern for SSR / Nuxt-safe plugins:
|
|
188
|
+
|
|
189
|
+
- `execution.environment` restricts the subscriber to `'both'`, `'client'`, or `'server'`
|
|
190
|
+
- `execution.hydration` is `'immediate'` by default and can be set to `'defer'` when the plugin must wait for client hydration
|
|
191
|
+
- `hydrationScheduler` lets you override the runtime scheduling behavior, for example with Nuxt/Vue `nextTick()`
|
|
192
|
+
- `hydrate()` / `afterHydration()` are optional lifecycle hooks for browser-only initialization and post-hydration work; they help keep SSR-sensitive logic out of constructors
|
|
193
|
+
|
|
194
|
+
## The `Store` Class — Summary
|
|
195
|
+
|
|
196
|
+
The `Store` class (see [src/core/Store.ts](src/core/Store.ts)) is a wrapper around a `PiniaStore` providing:
|
|
197
|
+
|
|
198
|
+
- **Properties:** `options`, `state`, `store`.
|
|
199
|
+
- **Useful methods:**
|
|
200
|
+
- `addToState(name, value?)` — adds a property to store state and exposes it as a `Ref` when appropriate.
|
|
201
|
+
- `addSubscription(pluginName, subscription)` — registers a plugin-specific subscription function.
|
|
202
|
+
- `getSubscriptions()` — returns registered subscriptions.
|
|
203
|
+
- `storeSubscribe` (getter/setter) — factory for `store.$subscribe` ({ store, callback }).
|
|
204
|
+
- `onAction` (getter/setter) — factory for `onAction` ({ store, callback }).
|
|
205
|
+
- `static customizeStore(store, options)` — recommended factory for class instantiation.
|
|
206
|
+
- `trace(namespace, payload?)` — emits a structured trace event scoped to the store.
|
|
207
|
+
- `traceError(namespace, error, payload?)` — emits an error event, or reports it with `console.error` when no listener matches.
|
|
208
|
+
- Helpers: `stateHas()`, `storeHas()`, `getValue()`.
|
|
209
|
+
|
|
210
|
+
## Tracing
|
|
211
|
+
|
|
212
|
+
Tracing is **opt-in**, scoped to an explicit registry, and **allocation free when unused**. Create one registry per application or Pinia instance, then pass its `createTracer` factory to `createPlugin()` so plugin and store events share its listeners. For SSR, create a fresh registry, Pinia instance, and plugin instance for every request; do not keep them in module-level state or clear a shared registry at request completion, since requests may overlap. See the [SSR guide](./docs/using-plugins.md#ssr--nuxt) for an example.
|
|
213
|
+
|
|
214
|
+
### Production safety
|
|
215
|
+
|
|
216
|
+
Tracing is **not automatically blocked in production**. Leave normal tracing disabled by default using your application's environment configuration; enable production diagnostics only deliberately and with limited scope and duration.
|
|
217
|
+
|
|
218
|
+
Events are **not automatically redacted** and may include sensitive action arguments, store state, plugin options, or error details. Filters select events by metadata; they do not sanitize payloads. Before writing to a console or forwarding to a logging service, select an allowlist of fields and redact sensitive values rather than sending complete events.
|
|
219
|
+
|
|
220
|
+
**Disabling normal tracing does not disable error reporting.** Errors can still be sent to `console.error` without a tracer or when no listener matches. See [Error reporting](#error-reporting) and the [security guidance](./SECURITY.md#tracing-and-production-logging) for precautions, including log access and retention.
|
|
221
|
+
|
|
222
|
+
### Consuming events
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { createPlugin, createTracerRegistry } from 'pinia-plugin-subscription'
|
|
226
|
+
|
|
227
|
+
const traceRegistry = createTracerRegistry()
|
|
228
|
+
const remove = traceRegistry.addTraceListener({
|
|
229
|
+
filter: (event) => event.scope === 'cart' && event.namespace.startsWith('subscription:'),
|
|
230
|
+
handler: (event) => console.log(event.namespace, event.scope, event.payload)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
pinia.use(createPlugin(subscribers, {
|
|
234
|
+
createTracer: traceRegistry.createTracer
|
|
235
|
+
}))
|
|
236
|
+
|
|
237
|
+
remove() // unsubscribe
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`filter` is evaluated on every event, so you can narrow tracing to a single store without rebuilding. Registries do not share listeners with one another.
|
|
241
|
+
|
|
242
|
+
### Event shape
|
|
243
|
+
|
|
244
|
+
| Field | Description |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| `namespace` | Structured identifier, e.g. `subscription:delivery` |
|
|
247
|
+
| `source` | Emitting class name, e.g. `PluginSubscription` |
|
|
248
|
+
| `scope` | Instance identifier, usually `store.$id` |
|
|
249
|
+
| `payload` | Event data, resolved only when consumed |
|
|
250
|
+
| `level` | `debug` or `error` |
|
|
251
|
+
| `timestamp` | `Date.now()` at emission |
|
|
252
|
+
| `error` | Present on `error` events |
|
|
253
|
+
|
|
254
|
+
Namespaces emitted by the plugin: `plugin:invoke`, `store:action`, `store:mutation`, `store:reset`, `subscription:delivery`, `subscriber:execute`, `subscriber:hydrate`, `subscriber:afterHydration`.
|
|
255
|
+
|
|
256
|
+
### Emitting your own events
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
class CartStore extends Store {
|
|
260
|
+
hydrate() {
|
|
261
|
+
this.trace('cart:hydrate', () => ({ items: this.state.items }))
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Always prefer the **function form** for `payload`: it is only invoked once a listener matches, so unobserved tracing costs nothing.
|
|
267
|
+
|
|
268
|
+
### Console listener
|
|
269
|
+
|
|
270
|
+
Normal trace events do not write to a console. Console output for those events is an opt-in listener:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import {
|
|
274
|
+
createTracerRegistry,
|
|
275
|
+
createConsoleTraceListener
|
|
276
|
+
} from 'pinia-plugin-subscription'
|
|
277
|
+
|
|
278
|
+
const traceRegistry = createTracerRegistry()
|
|
279
|
+
const remove = traceRegistry.addTraceListener(createConsoleTraceListener())
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
By default, this listener uses the styled plugin console. Pass any object exposing `log` and `error` to use another target:
|
|
283
|
+
|
|
284
|
+
Any object exposing `log` and `error` satisfies the `Console` interface, so external logging libraries can be connected without this plugin depending on them:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const remove = traceRegistry.addTraceListener(createConsoleTraceListener(myLogger))
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Error reporting
|
|
291
|
+
|
|
292
|
+
An error is delivered to every matching listener. If no listener matches — including when listeners exist but all filters reject the event — it is reported with `console.error`; it is not rethrown. Errors thrown by filters or handlers are also reported and isolated so they do not interrupt the traced process or remaining listeners.
|
|
293
|
+
|
|
294
|
+
### Migrating from `debugLog()` / `logError()`
|
|
295
|
+
|
|
296
|
+
These methods were removed in favour of structured events:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
// before
|
|
300
|
+
this.debugLog(`$subscribe ${store.$id}`, { mutation, store })
|
|
301
|
+
this.logError('subscriptionDelivery()', error, store, options)
|
|
302
|
+
|
|
303
|
+
// after
|
|
304
|
+
this.trace('store:mutation', () => ({ mutation, store }))
|
|
305
|
+
this.traceError('subscription:delivery', error, () => ({ store, options }))
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The `namespace` replaces the formatted message, store trace events are scoped to `store.$id`, and the payload becomes a factory so it is only built when a listener consumes the event.
|
|
309
|
+
|
|
310
|
+
## Testing
|
|
311
|
+
|
|
80
312
|
- `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/).
|
|
81
313
|
- `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/`.
|
|
82
314
|
- `npm run bundle:check` — measures the emitted bundle size.
|
|
83
315
|
|
|
316
|
+
### Dependency leak guard
|
|
317
|
+
|
|
318
|
+
Published artifacts must only reference **peer dependencies** (`pinia`, `vue`) and Node builtins. A `devDependency` referenced from `src/` does not fail the local build, but it breaks consumers: Vite inlines runtime imports, while `tsc` keeps bare `import type` specifiers in the emitted `.d.ts`, which consumers cannot resolve.
|
|
319
|
+
|
|
320
|
+
`npm run check:leaks` scans every built `.js` and `.d.ts` file and fails the build when a non-peer package appears:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
npm run check:leaks
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
It runs automatically as part of `npm run build` and in CI. If it reports a leak, either remove the import from `src/`, or duplicate the required type into `src/types/`.
|
|
327
|
+
|
|
84
328
|
## License
|
|
85
329
|
|
|
86
330
|
MIT
|
package/SECURITY.md
CHANGED
|
@@ -35,7 +35,25 @@ When using pinia-plugin-subscription:
|
|
|
35
35
|
1. **Keep Dependencies Updated:** Regularly update Pinia, Vue, and other dependencies to the latest versions
|
|
36
36
|
2. **Review Plugin Code:** Always review subscriber code before using in production
|
|
37
37
|
3. **Validate Store Data:** Validate data before storing in Pinia stores
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
### Tracing and Production Logging
|
|
40
|
+
|
|
41
|
+
Normal trace events are opt-in: the plugin requires an explicitly configured `createTracer` factory and matching listeners to deliver them. The plugin does not automatically detect production environments or block tracing in production. Production tracing can be useful for incident diagnosis, but should only be enabled deliberately and with controlled access to its output.
|
|
42
|
+
|
|
43
|
+
Trace events are not automatically sanitized or redacted. Depending on the event, they may contain action arguments, store state, mutations, plugin options, subscriptions, or error details. These values can include application secrets or personal data. A listener can expose them through the browser console, server logs, or an external logging service. The absence of a production block is not, by itself, a vulnerability; the disclosure risk depends on the data emitted, the logging destination, and who can access it.
|
|
44
|
+
|
|
45
|
+
When configuring tracing:
|
|
46
|
+
|
|
47
|
+
1. **Disable Normal Tracing in Production by Default:** Use the application's environment configuration to omit `createTracer` or avoid registering normal trace listeners. Enable production diagnostics only when needed and limit their duration and scope.
|
|
48
|
+
2. **Minimize Logged Data:** Select an explicit allowlist of fields and redact sensitive values before writing or forwarding events. Avoid logging complete stores, action arguments, plugin options, or raw error objects.
|
|
49
|
+
3. **Do Not Treat Filters as Redaction:** Filters only select events using metadata; a matching handler receives the full event, including its resolved payload and error. Sanitize data in the handler before passing it to a logging destination.
|
|
50
|
+
4. **Protect Logging Destinations:** Review external logging services, restrict access to logs, and define appropriate retention periods. Do not assume browser console output or server logs are private.
|
|
51
|
+
|
|
52
|
+
**Error reporting is separate from normal tracing.** Without a tracer, plugin and store error reporting falls back to `console.error`. With a tracer, error events also fall back to `console.error` when no listener matches, including when all filters reject the event. Exceptions thrown by listener filters or handlers are reported to `console.error` as well. Removing listeners, clearing a registry, or omitting `createTracer` therefore does not guarantee that no errors will be logged.
|
|
53
|
+
|
|
54
|
+
For production, handle error events with a matching listener that reports only sanitized details, and ensure listener filters and handlers do not throw sensitive errors. Review the application's console and error logging policy as well; do not silently discard operational errors to prevent disclosure.
|
|
55
|
+
|
|
56
|
+
See [Tracing](./README.md#tracing) for configuration and event details.
|
|
39
57
|
|
|
40
58
|
## Dependencies Security
|
|
41
59
|
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var f = (e, t, r) => t in e ? S(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
|
+
var s = (e, t, r) => f(e, typeof t != "symbol" ? t + "" : t, r);
|
|
4
|
+
import { isRef as g, ref as b, toRef as l } from "vue";
|
|
5
|
+
import { defineStore as p } from "pinia";
|
|
6
|
+
class q {
|
|
7
|
+
constructor(t, r) {
|
|
8
|
+
s(this, "_createInstance");
|
|
9
|
+
s(this, "_name");
|
|
10
|
+
s(this, "_pluginOptions");
|
|
11
|
+
s(this, "_storeInstance");
|
|
12
|
+
s(this, "_resetStoreCallback");
|
|
13
|
+
s(this, "_storeOnActionSubscription");
|
|
14
|
+
s(this, "_storeMutationSubscription");
|
|
15
|
+
s(this, "_subscriptions");
|
|
16
|
+
s(this, "pluginCreated");
|
|
17
|
+
s(this, "execution");
|
|
18
|
+
s(this, "hydrationScheduler");
|
|
19
|
+
this._name = t, this._createInstance = r;
|
|
20
|
+
}
|
|
21
|
+
get name() {
|
|
22
|
+
return this._name;
|
|
23
|
+
}
|
|
24
|
+
get storeInstance() {
|
|
25
|
+
return this._storeInstance;
|
|
26
|
+
}
|
|
27
|
+
get pluginOptions() {
|
|
28
|
+
return this._pluginOptions ?? {};
|
|
29
|
+
}
|
|
30
|
+
set pluginOptions(t) {
|
|
31
|
+
this._pluginOptions = t;
|
|
32
|
+
}
|
|
33
|
+
get resetStoreCallback() {
|
|
34
|
+
return this._resetStoreCallback;
|
|
35
|
+
}
|
|
36
|
+
get storeOnActionSubscription() {
|
|
37
|
+
return this._storeOnActionSubscription;
|
|
38
|
+
}
|
|
39
|
+
get storeMutationSubscription() {
|
|
40
|
+
return this._storeMutationSubscription;
|
|
41
|
+
}
|
|
42
|
+
get subscriptions() {
|
|
43
|
+
return this._subscriptions;
|
|
44
|
+
}
|
|
45
|
+
set subscriptions(t) {
|
|
46
|
+
this._subscriptions = t;
|
|
47
|
+
}
|
|
48
|
+
afterHydration(t) {
|
|
49
|
+
}
|
|
50
|
+
hydrate(t) {
|
|
51
|
+
}
|
|
52
|
+
invoke({ store: t, options: r }, n) {
|
|
53
|
+
return this._storeInstance = this._createInstance(t, { ...r, ...this.pluginOptions, createTracer: n }), this._storeInstance ? (this._subscriptions = this._storeInstance.getSubscriptions(), this._storeMutationSubscription = this._storeInstance.storeSubscribe, this._storeOnActionSubscription = this._storeInstance.onAction, this.pluginCreated && this.pluginCreated(t), !0) : !1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const c = /* @__PURE__ */ new WeakMap(), i = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new WeakSet();
|
|
57
|
+
function O(e, t) {
|
|
58
|
+
if (h.has(e) || typeof e.$dispose != "function")
|
|
59
|
+
return;
|
|
60
|
+
const r = e.$dispose;
|
|
61
|
+
e.$dispose = (...n) => (c.delete(e), i.delete(t), r.apply(e, n)), h.add(e);
|
|
62
|
+
}
|
|
63
|
+
function K(e, t, r) {
|
|
64
|
+
const n = r ? { storeOptions: r } : {};
|
|
65
|
+
return typeof t == "function" ? _(
|
|
66
|
+
e,
|
|
67
|
+
t,
|
|
68
|
+
n
|
|
69
|
+
) : y(
|
|
70
|
+
e,
|
|
71
|
+
t,
|
|
72
|
+
n
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function M(e, t, r) {
|
|
76
|
+
const n = r ? { storeOptions: { ...r, enhancedStore: !0 } } : { storeOptions: { enhancedStore: !0 } };
|
|
77
|
+
return _(
|
|
78
|
+
e,
|
|
79
|
+
t,
|
|
80
|
+
n
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
function _(e, t, r) {
|
|
84
|
+
const n = {
|
|
85
|
+
id: e,
|
|
86
|
+
extensions: {}
|
|
87
|
+
}, a = r?.storeOptions?.enhancedStore === !0, u = p(
|
|
88
|
+
e,
|
|
89
|
+
() => t(n),
|
|
90
|
+
r
|
|
91
|
+
);
|
|
92
|
+
return Object.assign(((...d) => {
|
|
93
|
+
a && i.set(e, n);
|
|
94
|
+
const o = u(...d);
|
|
95
|
+
return a && (c.set(o, n), i.delete(e), O(o, e)), o;
|
|
96
|
+
}), u);
|
|
97
|
+
}
|
|
98
|
+
function y(e, t, r) {
|
|
99
|
+
return r && (t = { ...t, ...r ?? {} }), p(e, t);
|
|
100
|
+
}
|
|
101
|
+
const A = /* @__PURE__ */ new Set(["_", "$"]);
|
|
102
|
+
function x(e) {
|
|
103
|
+
return A.has(e[0]);
|
|
104
|
+
}
|
|
105
|
+
function C(e) {
|
|
106
|
+
const t = e;
|
|
107
|
+
return c.get(e) ?? (typeof t?.$id == "string" ? i.get(t.$id) : void 0);
|
|
108
|
+
}
|
|
109
|
+
function $(e) {
|
|
110
|
+
const t = e.extensions.enhancedStore ?? e.extensions.extending;
|
|
111
|
+
if (!t)
|
|
112
|
+
throw new Error(`${e.id}Store - getEnhancedStore - Error: enhanced store is required`);
|
|
113
|
+
return t;
|
|
114
|
+
}
|
|
115
|
+
function m(e, t) {
|
|
116
|
+
e.extensions.enhancedStore = t, e.extensions.extending = t;
|
|
117
|
+
}
|
|
118
|
+
function P(e) {
|
|
119
|
+
return console.warn(`${e.id}Store - getExtendingStore is deprecated. Use getEnhancedStore instead.`), $(e);
|
|
120
|
+
}
|
|
121
|
+
function R(e, t) {
|
|
122
|
+
return `${e}-${t.$id}`;
|
|
123
|
+
}
|
|
124
|
+
function I(e) {
|
|
125
|
+
return e ? Array.isArray(e) ? e.length === 0 : typeof e == "object" ? Object.keys(e).length === 0 : typeof e == "string" ? e.trim() === "" : !1 : !(typeof e == "boolean" || typeof e == "number");
|
|
126
|
+
}
|
|
127
|
+
class w {
|
|
128
|
+
constructor(t, r) {
|
|
129
|
+
s(this, "_className", "Store");
|
|
130
|
+
s(this, "_onAction");
|
|
131
|
+
s(this, "_options");
|
|
132
|
+
s(this, "_store");
|
|
133
|
+
s(this, "_subscriptions", {});
|
|
134
|
+
s(this, "_storeSubscribe");
|
|
135
|
+
s(this, "_tracer");
|
|
136
|
+
s(this, "_createTracer");
|
|
137
|
+
if (this._options = r.storeOptions, this._store = t, this.initializeTracer(r), this._options?.enhancedStore) {
|
|
138
|
+
const n = C(this.store);
|
|
139
|
+
n && m(n, this.store);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
get onAction() {
|
|
143
|
+
if (this._onAction)
|
|
144
|
+
return () => ({
|
|
145
|
+
store: this.store,
|
|
146
|
+
callback: this._onAction
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
set onAction(t) {
|
|
150
|
+
this._onAction = t;
|
|
151
|
+
}
|
|
152
|
+
get options() {
|
|
153
|
+
return this._options;
|
|
154
|
+
}
|
|
155
|
+
get state() {
|
|
156
|
+
return this._store.$state;
|
|
157
|
+
}
|
|
158
|
+
set state(t) {
|
|
159
|
+
this._store.$state = t;
|
|
160
|
+
}
|
|
161
|
+
get store() {
|
|
162
|
+
return this._store;
|
|
163
|
+
}
|
|
164
|
+
get storeSubscribe() {
|
|
165
|
+
if (this._storeSubscribe)
|
|
166
|
+
return () => ({
|
|
167
|
+
store: this.store,
|
|
168
|
+
callback: this._storeSubscribe
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
set storeSubscribe(t) {
|
|
172
|
+
this._storeSubscribe = t;
|
|
173
|
+
}
|
|
174
|
+
get tracer() {
|
|
175
|
+
return !this._tracer && this._createTracer && (this._tracer = this._createTracer(this._className), this._tracer.scope = this.store.$id), this._tracer;
|
|
176
|
+
}
|
|
177
|
+
addToState(t, r) {
|
|
178
|
+
this.isOptionApi() || g(r) || (r = b(r)), this.state[t] = r, this.store[t] = l(this.state, t);
|
|
179
|
+
}
|
|
180
|
+
addSubscription(t, r, n) {
|
|
181
|
+
this._subscriptions[t] = { subscription: r, ...n ?? {} };
|
|
182
|
+
}
|
|
183
|
+
afterHydration() {
|
|
184
|
+
}
|
|
185
|
+
static customizeStore(t, r) {
|
|
186
|
+
if (r.storeOptions && this.hasRequiredKeys(r.storeOptions))
|
|
187
|
+
return new this(t, r);
|
|
188
|
+
}
|
|
189
|
+
getOption(t) {
|
|
190
|
+
return this.options && this.options[t];
|
|
191
|
+
}
|
|
192
|
+
getStatePropertyValue(t) {
|
|
193
|
+
return this.getValue(this.state[t]);
|
|
194
|
+
}
|
|
195
|
+
getSubscriptions() {
|
|
196
|
+
if (!I(this._subscriptions))
|
|
197
|
+
return this._subscriptions;
|
|
198
|
+
}
|
|
199
|
+
getValue(t) {
|
|
200
|
+
return t?.__v_isRef ? t.value : t;
|
|
201
|
+
}
|
|
202
|
+
hasDeniedFirstChar(t) {
|
|
203
|
+
return x(t);
|
|
204
|
+
}
|
|
205
|
+
static hasRequiredKeys(t) {
|
|
206
|
+
return this._requiredKeys !== void 0 && this._requiredKeys?.every((r) => !!t[r]);
|
|
207
|
+
}
|
|
208
|
+
hydrate() {
|
|
209
|
+
}
|
|
210
|
+
initializeTracer(t) {
|
|
211
|
+
this._createTracer = t?.createTracer;
|
|
212
|
+
}
|
|
213
|
+
isOptionApi() {
|
|
214
|
+
return this.store._isOptionsAPI;
|
|
215
|
+
}
|
|
216
|
+
stateHas(t) {
|
|
217
|
+
return Object.prototype.hasOwnProperty.call(this.state, t);
|
|
218
|
+
}
|
|
219
|
+
storeHas(t) {
|
|
220
|
+
return Object.prototype.hasOwnProperty.call(this.store, t);
|
|
221
|
+
}
|
|
222
|
+
trace(t, r) {
|
|
223
|
+
this.tracer && this.tracer.debug(t, r);
|
|
224
|
+
}
|
|
225
|
+
traceError(t, r, n) {
|
|
226
|
+
this.tracer ? this.tracer.error(t, r, n) : console.error(r);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
s(w, "_requiredKeys");
|
|
230
|
+
const j = "pinia-plugin-subscription";
|
|
231
|
+
export {
|
|
232
|
+
q as P,
|
|
233
|
+
w as S,
|
|
234
|
+
M as a,
|
|
235
|
+
C as b,
|
|
236
|
+
$ as c,
|
|
237
|
+
K as d,
|
|
238
|
+
P as e,
|
|
239
|
+
R as g,
|
|
240
|
+
x as h,
|
|
241
|
+
I as i,
|
|
242
|
+
j as p,
|
|
243
|
+
m as s
|
|
244
|
+
};
|
|
245
|
+
//# sourceMappingURL=constantes-BsD73jkk.js.map
|