pinia-plugin-subscription 1.0.0-beta.5 → 1.0.0-beta.6
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 +16 -1
- package/README.md +7 -225
- package/SECURITY.md +6 -6
- package/dist/chunks/constants-CBDmzOr_.js +274 -0
- package/dist/chunks/constants-CBDmzOr_.js.map +1 -0
- package/dist/core/PluginSubscription.d.ts +2 -11
- package/dist/core/Store.d.ts +2 -16
- package/dist/factories/trace-registry.d.ts +2 -12
- package/dist/helpers/index.d.ts +2 -1
- package/dist/helpers.js +275 -181
- package/dist/helpers.js.map +1 -1
- package/dist/pinia-plugin-subscription.js +218 -198
- package/dist/pinia-plugin-subscription.js.map +1 -1
- package/dist/system/TraceEmitter.d.ts +25 -0
- package/dist/system/Tracer.d.ts +3 -0
- package/dist/system/log.d.ts +2 -7
- package/dist/types/index.d.ts +1 -1
- package/dist/types/plugin.d.ts +1 -1
- package/dist/types/trace.d.ts +37 -12
- package/dist/utils/constants.d.ts +3 -0
- package/dist/utils/environment.d.ts +7 -0
- package/dist/utils/redact.d.ts +15 -0
- package/docs/authoring-plugins.md +115 -4
- package/docs/tracing.md +147 -0
- package/docs/using-plugins.md +4 -0
- package/package.json +2 -3
- package/dist/chunks/constants-CReRDbPp.js +0 -261
- package/dist/chunks/constants-CReRDbPp.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
|
+
- Added a dedicated tracing guide, `docs/tracing.md`; the README now only presents tracing and links to it
|
|
12
|
+
- Moved the API reference, import subpaths, development, `Store` summary and testing sections from `README.md` to `docs/authoring-plugins.md`
|
|
13
|
+
- `createTracerRegistry({ redact })`: opt-in redaction of event payloads before they reach listeners. `true` masks a default list of sensitive keys (`password`, `token`, `secret`, `authorization`, API/private keys, `credential`, `cookie`, `cipher`...); an object adds custom `keys`, a `replacement`, or drops the defaults with `includeDefaults: false`. It works on a copy, covers arrays/`Map`/`Set`, handles circular references, and replaces the payload by `__traceRedactionError` if redaction fails. `defaultRedactedKeys` and `TraceRedactOptions` are exported
|
|
14
|
+
- `createTracerRegistry({ enabled })`: tracing is now disabled by default in production builds (`process.env.NODE_ENV === 'production'`); `addListener()` becomes a no-op unless `enabled: true` is passed. `enabled: false` forces it off in any environment. Error reporting through `console.error` is unchanged
|
|
11
15
|
- Added `createTracerRegistry()` to create isolated trace listeners and tracers for each application or Pinia instance
|
|
12
16
|
- Added `createTracerRegistry()` to group matching trace events in the console
|
|
13
17
|
- Added the `Tracer` class and registry methods `addTraceListener()`, `clearTraceListeners()` and `hasTraceListeners()`
|
|
@@ -32,9 +36,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
32
36
|
- Error events are delivered to matching listeners and reported with `console.error` when none match
|
|
33
37
|
- Store trace labels can include the store class name by default, and callers can override the event namespace
|
|
34
38
|
- Excluded `scripts/**` from coverage thresholds, as build tooling is not shipped to consumers
|
|
39
|
+
- `Store` and `PluginSubscription` now share the abstract `TraceEmitter` class for `trace()`, `traceError()` and class-name label formatting, removing duplicated code
|
|
40
|
+
- `Tracer` listener matching no longer allocates intermediate arrays beyond the matched set, and duplicated unhandled-error reporting was factored out
|
|
41
|
+
- The `pluginsubscription` namespace prefix is now a shared constant (`pluginSubscriptionNamespace`) instead of a hard-coded string
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
- Trace groups opened by `onAction`, `$subscribe` and `$reset` are now always closed (also when the action fails or a callback throws), so grouped events no longer stay buffered forever
|
|
45
|
+
- Listener definitions passed to `addListener()` are no longer mutated when their `filter` is an array of criteria
|
|
46
|
+
- `TracerHandler` and `TraceRegistry` types now match the implementation (`createTracer(source, options?)`, `void` group callbacks, `addListener()` with options, `tracerHandler`); `createTracerRegistry()` is explicitly typed as `TraceRegistry`
|
|
47
|
+
- `CustomConsole.rebuildArgs()` no longer returns `undefined` for unsupported argument types
|
|
48
|
+
- `clearTraceListeners()` now empties the shared listener list in place, so tracers created before the call stop delivering to cleared listeners and see listeners registered afterwards
|
|
35
49
|
|
|
36
50
|
### Removed
|
|
37
|
-
-
|
|
51
|
+
- Removed the internal `PluginConsole` singleton and the dead commented code of `system/log.ts`; `CustomConsole` stays exported from `helpers` for plugins not yet migrated to the tracer
|
|
52
|
+
- **Breaking:** removed `debugLog()` and `logError()` from `Store` and `PluginSubscription` — use `trace()` and `traceError()` instead (see the migration snippet in `docs/tracing.md`)
|
|
38
53
|
- **Breaking:** removed the obsolete `debug` and custom-console arguments from `createPlugin()`, `PluginSubscription`, `PluginSubscriber`, `Store`, and `Store.customizeStore()`
|
|
39
54
|
- **Breaking:** removed the `debug` argument from subscriber `invoke()`, `hydrate()`, and `afterHydration()` hooks; tracing is now configured exclusively through listeners
|
|
40
55
|
- **Breaking:** removed the legacy `debug` and `console` accessors from `Store` and `PluginSubscription`
|
package/README.md
CHANGED
|
@@ -34,7 +34,8 @@ The package serves two audiences:
|
|
|
34
34
|
| You want to… | Guide |
|
|
35
35
|
| --- | --- |
|
|
36
36
|
| 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) |
|
|
37
|
-
| Build your own pinia plugin on top of `pinia-plugin-subscription` | [Plugin authoring guide](docs/authoring-plugins.md) |
|
|
37
|
+
| Build your own pinia plugin on top of `pinia-plugin-subscription` (API reference, `Store` class, import subpaths, development and testing) | [Plugin authoring guide](docs/authoring-plugins.md) |
|
|
38
|
+
| Debug stores and plugins with structured trace events | [Tracing](docs/tracing.md) |
|
|
38
39
|
|
|
39
40
|
## Installation
|
|
40
41
|
|
|
@@ -60,8 +61,6 @@ app.use(pinia)
|
|
|
60
61
|
app.mount('#app')
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
### Import subpaths
|
|
64
|
-
|
|
65
64
|
## Usage — Examples
|
|
66
65
|
|
|
67
66
|
**1) Subscriber using a `Store` subclass:**
|
|
@@ -117,238 +116,21 @@ export const myStoreSubscriber: PluginSubscriberInterface = {
|
|
|
117
116
|
- **SSR and hydration control:** Configure whether each subscribed plugin runs on the server, the client, or only after client hydration.
|
|
118
117
|
- **Lifecycle safety:** Internal hydration ordering is now guarded so `hydrate()` completes before `afterHydration()` and disposed stores do not retain stale subscriber metadata.
|
|
119
118
|
|
|
120
|
-
## API Reference
|
|
121
|
-
|
|
122
|
-
### `createPlugin(subscribers: PluginSubscriber[], options?: PluginSubscriptionOptions): PiniaPlugin`
|
|
123
|
-
|
|
124
|
-
Creates and returns a Pinia plugin from the provided `subscribers`. Each subscriber is invoked when a store is registered. The optional `options` configure execution, hydration and tracing.
|
|
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
|
-
|
|
173
|
-
For helper-only imports, the package exposes an additive subpath kept separate from the default library API:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
import { PluginSubscriber, Store } from 'pinia-plugin-subscription/helpers'
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
For type-only imports, prefer the dedicated public types entry:
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
import type { PluginSubscriptionOptions } from 'pinia-plugin-subscription/types'
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Development
|
|
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(label, payload?, scope?, namespace?)` — emits a structured trace event, scoped to the store by default.
|
|
207
|
-
- `traceError(label, error, payload?, scope?, namespace?)` — emits an error event, or reports it with `console.error` when no listener matches.
|
|
208
|
-
- Helpers: `stateHas()`, `storeHas()`, `getValue()`.
|
|
209
|
-
|
|
210
119
|
## Tracing
|
|
211
120
|
|
|
212
|
-
|
|
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
|
|
121
|
+
A built-in, opt-in debugging solution: plugins and stores emit structured trace events that you select with filters, optionally redact, and group in the console. It is disabled by default in production builds and costs nothing when unused.
|
|
223
122
|
|
|
224
123
|
```ts
|
|
225
124
|
import { createPlugin } from 'pinia-plugin-subscription'
|
|
226
125
|
import { createTracerRegistry } from 'pinia-plugin-subscription/helpers'
|
|
227
126
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
{ target: 'scope', value: 'cart' },
|
|
231
|
-
{ target: 'namespace', comparator: 'startsWith', value: 'pluginsubscription:subscription:' }
|
|
232
|
-
])
|
|
233
|
-
|
|
234
|
-
pinia.use(createPlugin(subscribers, {
|
|
235
|
-
tracerHandler: traceGroupsRegistry.tracerHandler
|
|
236
|
-
}))
|
|
237
|
-
|
|
238
|
-
remove() // unsubscribe
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Criteria are evaluated against event metadata, before the payload is resolved.
|
|
242
|
-
By default, all criteria must match and events are grouped by `scope`. Supported
|
|
243
|
-
comparators are `equals` (the default), `contains`, `startsWith` and `endsWith`;
|
|
244
|
-
set `filterMode` to `some` to match any criterion or `groupBy` to group by a
|
|
245
|
-
different event metadata field:
|
|
246
|
-
|
|
247
|
-
```ts
|
|
248
|
-
traceGroupsRegistry.addListener([
|
|
249
|
-
{ target: 'scope', value: 'cart' },
|
|
250
|
-
{ target: 'namespace', comparator: 'startsWith', value: 'pluginsubscription:subscription:' }
|
|
251
|
-
], { filterMode: 'every', groupBy: 'scope' })
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
The registry uses the console's grouped output for matching events.
|
|
255
|
-
|
|
256
|
-
### Event shape
|
|
257
|
-
|
|
258
|
-
| Field | Description |
|
|
259
|
-
| --- | --- |
|
|
260
|
-
| `namespace` | Structured identifier, derived from the label by default |
|
|
261
|
-
| `label` | Human-readable event label |
|
|
262
|
-
| `source` | Source passed to `createTracer`, usually `pinia-plugin-subscription` |
|
|
263
|
-
| `scope` | Instance identifier, usually `store.$id` |
|
|
264
|
-
| `payload` | Event data, resolved only when consumed |
|
|
265
|
-
| `level` | `debug` or `error` |
|
|
266
|
-
| `timestamp` | `Date.now()` at emission |
|
|
267
|
-
| `error` | Present on `error` events |
|
|
268
|
-
|
|
269
|
-
By default, `Store` prefixes its class name to labels and derived namespaces.
|
|
270
|
-
Library events use `PluginSubscription` as their prefix (for example,
|
|
271
|
-
`pluginsubscription:store:mutation` and
|
|
272
|
-
`pluginsubscription:subscription:delivery`). Set
|
|
273
|
-
`classNameAddedToTraceNamespace = false` on a `Store` subclass to omit its
|
|
274
|
-
class prefix, or provide an explicit namespace as the fourth argument to
|
|
275
|
-
`trace()` / `traceError()`.
|
|
276
|
-
|
|
277
|
-
### Emitting your own events
|
|
278
|
-
|
|
279
|
-
```ts
|
|
280
|
-
class CartStore extends Store {
|
|
281
|
-
hydrate() {
|
|
282
|
-
this.trace('cart:hydrate', () => ({ items: this.state.items }))
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
`Store.trace(label, payload?, scope?, namespace?)` and
|
|
288
|
-
`Store.traceError(label, error, payload?, scope?, namespace?)` emit the events.
|
|
289
|
-
The store class name is included in the label by default; set
|
|
290
|
-
`classNameAddedToTraceNamespace = false` to omit it. Unless an explicit
|
|
291
|
-
namespace is provided, the normalized label is used as the namespace.
|
|
292
|
-
|
|
293
|
-
Always prefer the **function form** for `payload`: it is only invoked once a
|
|
294
|
-
listener matches, so unobserved tracing costs nothing.
|
|
295
|
-
|
|
296
|
-
### Grouped console output
|
|
297
|
-
|
|
298
|
-
Normal trace events do not write to a console. To opt into console output grouped
|
|
299
|
-
by store, configure the registry and pass its `tracerHandler` to the plugin:
|
|
300
|
-
|
|
301
|
-
```ts
|
|
302
|
-
import { createTracerRegistry } from 'pinia-plugin-subscription/helpers'
|
|
303
|
-
|
|
304
|
-
const traceGroupsRegistry = createTracerRegistry()
|
|
305
|
-
traceGroupsRegistry.addListener([{ target: 'scope', value: 'cart' }])
|
|
306
|
-
|
|
307
|
-
pinia.use(createPlugin(subscribers, {
|
|
308
|
-
tracerHandler: traceGroupsRegistry.tracerHandler
|
|
309
|
-
}))
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Groups are selected by event metadata and default to grouping by `scope`. Use
|
|
313
|
-
`addListener()`'s `groupBy` option to group by another metadata field.
|
|
314
|
-
|
|
315
|
-
### Error reporting
|
|
316
|
-
|
|
317
|
-
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.
|
|
318
|
-
|
|
319
|
-
### Migrating from `debugLog()` / `logError()`
|
|
320
|
-
|
|
321
|
-
These methods were removed in favour of structured events:
|
|
127
|
+
const registry = createTracerRegistry({ redact: true })
|
|
128
|
+
registry.addListener([{ target: 'scope', value: 'cart' }])
|
|
322
129
|
|
|
323
|
-
|
|
324
|
-
// before
|
|
325
|
-
this.debugLog(`$subscribe ${store.$id}`, { mutation, store })
|
|
326
|
-
this.logError('subscriptionDelivery()', error, store, options)
|
|
327
|
-
|
|
328
|
-
// after
|
|
329
|
-
this.trace('store:mutation', () => ({ mutation, store }))
|
|
330
|
-
this.traceError('subscription:delivery', error, () => ({ store, options }))
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
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.
|
|
334
|
-
|
|
335
|
-
## Testing
|
|
336
|
-
|
|
337
|
-
- `npm run dev` — runs the dedicated demo app in [`demo/`](demo/). The library build remains `npm run build` and is intentionally kept separate from the demo entry.
|
|
338
|
-
- `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/`.
|
|
339
|
-
- `npm run bundle:check` — measures the emitted bundle size.
|
|
340
|
-
|
|
341
|
-
### Dependency leak guard
|
|
342
|
-
|
|
343
|
-
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.
|
|
344
|
-
|
|
345
|
-
`npm run check:leaks` scans every built `.js` and `.d.ts` file and fails the build when a non-peer package appears:
|
|
346
|
-
|
|
347
|
-
```bash
|
|
348
|
-
npm run check:leaks
|
|
130
|
+
pinia.use(createPlugin(subscribers, { tracerHandler: registry.tracerHandler }))
|
|
349
131
|
```
|
|
350
132
|
|
|
351
|
-
|
|
133
|
+
See the [Tracing documentation](docs/tracing.md) for filtering, redaction, production safety, event shape and error reporting.
|
|
352
134
|
|
|
353
135
|
## License
|
|
354
136
|
|
package/SECURITY.md
CHANGED
|
@@ -38,22 +38,22 @@ When using pinia-plugin-subscription:
|
|
|
38
38
|
|
|
39
39
|
### Tracing and Production Logging
|
|
40
40
|
|
|
41
|
-
Normal trace events are opt-in: the plugin requires an explicitly configured `
|
|
41
|
+
Normal trace events are opt-in: the plugin requires an explicitly configured `tracerHandler` and matching listeners to deliver them. `createTracerRegistry()` ignores `addListener()` in production builds (`process.env.NODE_ENV === 'production'`) unless `createTracerRegistry({ enabled: true })` is passed. This guard relies on the consuming bundler replacing `process.env.NODE_ENV`; without it, tracing is considered enabled. Production tracing can be useful for incident diagnosis, but should only be enabled deliberately and with controlled access to its output.
|
|
42
42
|
|
|
43
|
-
Trace events are not
|
|
43
|
+
Trace events are not redacted by default. 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. `createTracerRegistry({ redact: true })` masks values stored under sensitive keys (passwords, tokens, secrets, authorization, API keys, cookies...) before any listener receives them, but it inspects keys only: it does not detect secrets under innocuous keys, and it does not redact `event.error`. 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
44
|
|
|
45
45
|
When configuring tracing:
|
|
46
46
|
|
|
47
|
-
1. **
|
|
48
|
-
2. **Minimize Logged Data:**
|
|
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.
|
|
47
|
+
1. **Keep Normal Tracing Disabled in Production:** This is the default of `createTracerRegistry()`. Only pass `enabled: true` when diagnostics are needed, and limit their duration and scope.
|
|
48
|
+
2. **Minimize Logged Data:** Enable `createTracerRegistry({ redact: true })` (extend it with your own `keys`) and select an explicit allowlist of fields 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. Use the `redact` option, and sanitize `event.error` and any remaining data in the handler before passing it to a logging destination.
|
|
50
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
51
|
|
|
52
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
53
|
|
|
54
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
55
|
|
|
56
|
-
See [Tracing](./
|
|
56
|
+
See [Tracing](./docs/tracing.md) for configuration and event details.
|
|
57
57
|
|
|
58
58
|
## Dependencies Security
|
|
59
59
|
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var f = (s, e, t) => e in s ? S(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
|
|
3
|
+
var n = (s, e, t) => f(s, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { isRef as g, ref as b, toRef as l } from "vue";
|
|
5
|
+
import { defineStore as d } from "pinia";
|
|
6
|
+
class P {
|
|
7
|
+
constructor(e, t) {
|
|
8
|
+
n(this, "_createInstance");
|
|
9
|
+
n(this, "_name");
|
|
10
|
+
n(this, "_pluginOptions");
|
|
11
|
+
n(this, "_storeInstance");
|
|
12
|
+
n(this, "_resetStoreCallback");
|
|
13
|
+
n(this, "_storeOnActionSubscription");
|
|
14
|
+
n(this, "_storeMutationSubscription");
|
|
15
|
+
n(this, "_subscriptions");
|
|
16
|
+
n(this, "pluginCreated");
|
|
17
|
+
n(this, "execution");
|
|
18
|
+
n(this, "hydrationScheduler");
|
|
19
|
+
this._name = e, this._createInstance = t;
|
|
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(e) {
|
|
31
|
+
this._pluginOptions = e;
|
|
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(e) {
|
|
46
|
+
this._subscriptions = e;
|
|
47
|
+
}
|
|
48
|
+
afterHydration(e) {
|
|
49
|
+
}
|
|
50
|
+
hydrate(e) {
|
|
51
|
+
}
|
|
52
|
+
invoke({ store: e, options: t }, r) {
|
|
53
|
+
return this._storeInstance = this._createInstance(e, {
|
|
54
|
+
...t,
|
|
55
|
+
...this.pluginOptions,
|
|
56
|
+
createTracer: r ? () => r?.(this.name) : void 0
|
|
57
|
+
}), this._storeInstance ? (this._subscriptions = this._storeInstance.getSubscriptions(), this._storeMutationSubscription = this._storeInstance.storeSubscribe, this._storeOnActionSubscription = this._storeInstance.onAction, this.pluginCreated && this.pluginCreated(e), !0) : !1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class O {
|
|
61
|
+
constructor() {
|
|
62
|
+
n(this, "_tracer");
|
|
63
|
+
n(this, "_traceOptions", {
|
|
64
|
+
addClassNameToLabel: !0
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
get classNameAddedToTraceNamespace() {
|
|
68
|
+
return this._traceOptions.addClassNameToLabel === !0;
|
|
69
|
+
}
|
|
70
|
+
set classNameAddedToTraceNamespace(e) {
|
|
71
|
+
this._traceOptions.addClassNameToLabel = e;
|
|
72
|
+
}
|
|
73
|
+
get tracer() {
|
|
74
|
+
return this._tracer;
|
|
75
|
+
}
|
|
76
|
+
trace(e, t, r, i) {
|
|
77
|
+
const o = this.tracer;
|
|
78
|
+
o && (e = this.traceLabelFormatter(e), o.debug(e, i ?? e.toLowerCase(), r, t));
|
|
79
|
+
}
|
|
80
|
+
traceError(e, t, r, i, o) {
|
|
81
|
+
const a = this.tracer;
|
|
82
|
+
a ? (e = this.traceLabelFormatter(e), a.error(e, o ?? e.toLowerCase(), t, i, r)) : console.error(t);
|
|
83
|
+
}
|
|
84
|
+
traceLabelFormatter(e) {
|
|
85
|
+
return this.classNameAddedToTraceNamespace ? `${this._className}:${e}` : e;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const h = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new WeakSet();
|
|
89
|
+
function y(s, e) {
|
|
90
|
+
if (p.has(s) || typeof s.$dispose != "function")
|
|
91
|
+
return;
|
|
92
|
+
const t = s.$dispose;
|
|
93
|
+
s.$dispose = (...r) => (h.delete(s), c.delete(e), t.apply(s, r)), p.add(s);
|
|
94
|
+
}
|
|
95
|
+
function j(s, e, t) {
|
|
96
|
+
const r = t ? { storeOptions: t } : {};
|
|
97
|
+
return typeof e == "function" ? _(
|
|
98
|
+
s,
|
|
99
|
+
e,
|
|
100
|
+
r
|
|
101
|
+
) : m(
|
|
102
|
+
s,
|
|
103
|
+
e,
|
|
104
|
+
r
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function q(s, e, t) {
|
|
108
|
+
const r = t ? { storeOptions: { ...t, enhancedStore: !0 } } : { storeOptions: { enhancedStore: !0 } };
|
|
109
|
+
return _(
|
|
110
|
+
s,
|
|
111
|
+
e,
|
|
112
|
+
r
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function _(s, e, t) {
|
|
116
|
+
const r = {
|
|
117
|
+
id: s,
|
|
118
|
+
extensions: {}
|
|
119
|
+
}, i = t?.storeOptions?.enhancedStore === !0, o = d(
|
|
120
|
+
s,
|
|
121
|
+
() => e(r),
|
|
122
|
+
t
|
|
123
|
+
);
|
|
124
|
+
return Object.assign(((...a) => {
|
|
125
|
+
i && c.set(s, r);
|
|
126
|
+
const u = o(...a);
|
|
127
|
+
return i && (h.set(u, r), c.delete(s), y(u, s)), u;
|
|
128
|
+
}), o);
|
|
129
|
+
}
|
|
130
|
+
function m(s, e, t) {
|
|
131
|
+
return t && (e = { ...e, ...t ?? {} }), d(s, e);
|
|
132
|
+
}
|
|
133
|
+
const A = /* @__PURE__ */ new Set(["_", "$"]);
|
|
134
|
+
function C(s) {
|
|
135
|
+
return A.has(s[0]);
|
|
136
|
+
}
|
|
137
|
+
function x(s) {
|
|
138
|
+
const e = s;
|
|
139
|
+
return h.get(s) ?? (typeof e?.$id == "string" ? c.get(e.$id) : void 0);
|
|
140
|
+
}
|
|
141
|
+
function T(s) {
|
|
142
|
+
const e = s.extensions.enhancedStore ?? s.extensions.extending;
|
|
143
|
+
if (!e)
|
|
144
|
+
throw new Error(`${s.id}Store - getEnhancedStore - Error: enhanced store is required`);
|
|
145
|
+
return e;
|
|
146
|
+
}
|
|
147
|
+
function $(s, e) {
|
|
148
|
+
s.extensions.enhancedStore = e, s.extensions.extending = e;
|
|
149
|
+
}
|
|
150
|
+
function F(s) {
|
|
151
|
+
return console.warn(`${s.id}Store - getExtendingStore is deprecated. Use getEnhancedStore instead.`), T(s);
|
|
152
|
+
}
|
|
153
|
+
function K(s, e) {
|
|
154
|
+
return `${s}-${e.$id}`;
|
|
155
|
+
}
|
|
156
|
+
function N(s) {
|
|
157
|
+
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");
|
|
158
|
+
}
|
|
159
|
+
class w extends O {
|
|
160
|
+
constructor(t, r) {
|
|
161
|
+
super();
|
|
162
|
+
n(this, "_className", "Store");
|
|
163
|
+
n(this, "_createTracer");
|
|
164
|
+
n(this, "_onAction");
|
|
165
|
+
n(this, "_options");
|
|
166
|
+
n(this, "_store");
|
|
167
|
+
n(this, "_storeSubscribe");
|
|
168
|
+
n(this, "_subscriptions", {});
|
|
169
|
+
if (this._options = r.storeOptions, this._store = t, this.initializeTracer(r), this.trace("instance:created", t.$id), this._options?.enhancedStore) {
|
|
170
|
+
const i = x(this.store);
|
|
171
|
+
i && ($(i, this.store), this.trace("setup:enhancedStore:success", this.store.$id));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
get onAction() {
|
|
175
|
+
if (this._onAction)
|
|
176
|
+
return () => ({
|
|
177
|
+
store: this.store,
|
|
178
|
+
callback: this._onAction
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
set onAction(t) {
|
|
182
|
+
this._onAction = t;
|
|
183
|
+
}
|
|
184
|
+
get options() {
|
|
185
|
+
return this._options;
|
|
186
|
+
}
|
|
187
|
+
get state() {
|
|
188
|
+
return this._store.$state;
|
|
189
|
+
}
|
|
190
|
+
set state(t) {
|
|
191
|
+
this._store.$state = t;
|
|
192
|
+
}
|
|
193
|
+
get store() {
|
|
194
|
+
return this._store;
|
|
195
|
+
}
|
|
196
|
+
get storeSubscribe() {
|
|
197
|
+
if (this._storeSubscribe)
|
|
198
|
+
return () => ({
|
|
199
|
+
store: this.store,
|
|
200
|
+
callback: this._storeSubscribe
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
set storeSubscribe(t) {
|
|
204
|
+
this._storeSubscribe = t;
|
|
205
|
+
}
|
|
206
|
+
get tracer() {
|
|
207
|
+
return !this._tracer && this._createTracer && (this._tracer = this._createTracer(), this._tracer.scope = this.store.$id), this._tracer;
|
|
208
|
+
}
|
|
209
|
+
addToState(t, r) {
|
|
210
|
+
this.isOptionApi() || g(r) || (r = b(r)), this.state[t] = r, this.store[t] = l(this.state, t);
|
|
211
|
+
}
|
|
212
|
+
addSubscription(t, r, i) {
|
|
213
|
+
this._subscriptions[t] = { subscription: r, ...i ?? {} };
|
|
214
|
+
}
|
|
215
|
+
afterHydration() {
|
|
216
|
+
}
|
|
217
|
+
static customizeStore(t, r) {
|
|
218
|
+
if (r.storeOptions && this.hasRequiredKeys(r.storeOptions))
|
|
219
|
+
return new this(t, r);
|
|
220
|
+
}
|
|
221
|
+
getOption(t) {
|
|
222
|
+
return this.options && this.options[t];
|
|
223
|
+
}
|
|
224
|
+
getStatePropertyValue(t) {
|
|
225
|
+
return this.getValue(this.state[t]);
|
|
226
|
+
}
|
|
227
|
+
getSubscriptions() {
|
|
228
|
+
if (!N(this._subscriptions))
|
|
229
|
+
return this._subscriptions;
|
|
230
|
+
}
|
|
231
|
+
getValue(t) {
|
|
232
|
+
return t?.__v_isRef ? t.value : t;
|
|
233
|
+
}
|
|
234
|
+
hasDeniedFirstChar(t) {
|
|
235
|
+
return C(t);
|
|
236
|
+
}
|
|
237
|
+
static hasRequiredKeys(t) {
|
|
238
|
+
return this._requiredKeys !== void 0 && this._requiredKeys?.every((r) => !!t[r]);
|
|
239
|
+
}
|
|
240
|
+
hydrate() {
|
|
241
|
+
}
|
|
242
|
+
initializeTracer(t) {
|
|
243
|
+
this._createTracer = t?.createTracer;
|
|
244
|
+
}
|
|
245
|
+
isOptionApi() {
|
|
246
|
+
return this.store._isOptionsAPI;
|
|
247
|
+
}
|
|
248
|
+
stateHas(t) {
|
|
249
|
+
return Object.prototype.hasOwnProperty.call(this.state, t);
|
|
250
|
+
}
|
|
251
|
+
storeHas(t) {
|
|
252
|
+
return Object.prototype.hasOwnProperty.call(this.store, t);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
n(w, "_requiredKeys");
|
|
256
|
+
const M = "pinia-plugin-subscription", I = "PluginSubscription", R = I.toLowerCase();
|
|
257
|
+
export {
|
|
258
|
+
P,
|
|
259
|
+
w as S,
|
|
260
|
+
O as T,
|
|
261
|
+
R as a,
|
|
262
|
+
I as b,
|
|
263
|
+
q as c,
|
|
264
|
+
j as d,
|
|
265
|
+
x as e,
|
|
266
|
+
T as f,
|
|
267
|
+
K as g,
|
|
268
|
+
C as h,
|
|
269
|
+
N as i,
|
|
270
|
+
F as j,
|
|
271
|
+
M as p,
|
|
272
|
+
$ as s
|
|
273
|
+
};
|
|
274
|
+
//# sourceMappingURL=constants-CBDmzOr_.js.map
|