@real-router/logger-plugin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Oleg Ivanov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # @real-router/logger-plugin
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
5
+
6
+ A plugin for logging router events to the console. Provides transition timing display, parameter diff tracking, Performance API integration, and log grouping.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @real-router/logger-plugin
12
+ # or
13
+ pnpm add @real-router/logger-plugin
14
+ # or
15
+ yarn add @real-router/logger-plugin
16
+ # or
17
+ bun add @real-router/logger-plugin
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { createRouter } from "@real-router/core";
24
+ import { loggerPlugin } from "@real-router/logger-plugin";
25
+
26
+ const router = createRouter(routes);
27
+
28
+ // Use with default settings
29
+ router.usePlugin(loggerPlugin);
30
+
31
+ router.start();
32
+ ```
33
+
34
+ **Console output:**
35
+
36
+ ```
37
+ Router started
38
+ ▼ Router transition
39
+ Transition: home → users (1.23ms)
40
+ Transition success (1.23ms)
41
+ ```
42
+
43
+ **Fast transitions (<0.1ms) display in microseconds:**
44
+
45
+ ```
46
+ Transition success (27.29μs)
47
+ ```
48
+
49
+ ## API
50
+
51
+ ### `loggerPlugin`
52
+
53
+ Ready-to-use plugin instance with default settings.
54
+
55
+ ```typescript
56
+ import { loggerPlugin } from "@real-router/logger-plugin";
57
+
58
+ router.usePlugin(loggerPlugin);
59
+ ```
60
+
61
+ ### `loggerPluginFactory()`
62
+
63
+ Factory for creating a new plugin instance.
64
+
65
+ ```typescript
66
+ import { loggerPluginFactory } from "@real-router/logger-plugin";
67
+
68
+ router.usePlugin(loggerPluginFactory());
69
+ ```
70
+
71
+ ## Default Configuration
72
+
73
+ The plugin uses the following default configuration:
74
+
75
+ ```typescript
76
+ interface LoggerPluginConfig {
77
+ level: "all" | "transitions" | "errors" | "none"; // default: "all"
78
+ showTiming: boolean; // default: true
79
+ showParamsDiff: boolean; // default: true
80
+ usePerformanceMarks: boolean; // default: false
81
+ context: string; // default: "real-router-logger-plugin"
82
+ }
83
+ ```
84
+
85
+ ### `level`
86
+
87
+ Event logging level.
88
+
89
+ - `'all'` **(default)** - logs all events (router start/stop + transitions)
90
+ - `'transitions'` - only transition events (start/success/cancel/error)
91
+ - `'errors'` - only transition errors
92
+ - `'none'` - disables all logs
93
+
94
+ ### `showTiming`
95
+
96
+ Display transition execution time with adaptive units.
97
+
98
+ - `true` **(default)** - show timing (μs for fast transitions <0.1ms, ms otherwise)
99
+ - `false` - hide timing
100
+
101
+ **Output examples:**
102
+
103
+ ```
104
+ Transition success (15ms) // normal timing
105
+ Transition success (27.29μs) // fast transitions
106
+ ```
107
+
108
+ ### `showParamsDiff`
109
+
110
+ Show differences in route parameters when navigating within the same route.
111
+
112
+ - `true` **(default)** - show changed, added, and removed parameters
113
+ - `false` - don't show parameter changes
114
+
115
+ **Example output:**
116
+
117
+ ```
118
+ ▼ Router transition
119
+ Transition: users.view → users.view
120
+ Changed: { id: "123" → "456" }, Added: {"sort":"name"}
121
+ Transition success (2.15ms)
122
+ ```
123
+
124
+ **Diff types displayed:**
125
+
126
+ - **Changed** - parameters with different values
127
+ - **Added** - new parameters in target state
128
+ - **Removed** - parameters present in source but not in target
129
+
130
+ **When diff is shown:**
131
+
132
+ - ✅ Only when navigating within the same route (e.g., `users.view` → `users.view`)
133
+ - ✅ Only when parameters actually changed
134
+ - ❌ Not shown when navigating between different routes
135
+ - ❌ Not shown when parameters are identical
136
+
137
+ ### `context`
138
+
139
+ Context name for logs. Useful when working with multiple routers.
140
+
141
+ - **Default:** `'logger-plugin'`
142
+
143
+ **Example output:**
144
+
145
+ ```
146
+ [logger-plugin] Transition: dashboard → users
147
+ ```
148
+
149
+ ## Logged Events
150
+
151
+ ### Router Lifecycle
152
+
153
+ **`onStart`** - called when router starts
154
+
155
+ ```
156
+ Router started
157
+ ```
158
+
159
+ **`onStop`** - called when router stops
160
+
161
+ ```
162
+ Router stopped
163
+ ```
164
+
165
+ ### Transition Events
166
+
167
+ **`onTransitionStart`** - transition begins
168
+
169
+ ```
170
+ ▼ Router transition
171
+ Transition: home → users
172
+ ```
173
+
174
+ **`onTransitionSuccess`** - transition completed successfully
175
+
176
+ ```
177
+ Transition success (24ms)
178
+ ```
179
+
180
+ **`onTransitionCancel`** - transition cancelled
181
+
182
+ ```
183
+ Transition cancelled (12ms)
184
+ ```
185
+
186
+ **`onTransitionError`** - transition error
187
+
188
+ ```
189
+ Transition error: ROUTE_NOT_FOUND (8ms)
190
+ ```
191
+
192
+ ## Log Grouping
193
+
194
+ Transition events are automatically grouped in the console for better readability:
195
+
196
+ ```
197
+ ▼ Router transition
198
+ Transition: users → users.view
199
+ [middleware logs...]
200
+ [guard logs...]
201
+ Transition success (45ms)
202
+ ```
203
+
204
+ This helps organize logs when working with complex transitions, middleware, and guards.
205
+
206
+ ## TypeScript
207
+
208
+ The plugin is fully typed:
209
+
210
+ ```typescript
211
+ import {
212
+ loggerPlugin,
213
+ loggerPluginFactory,
214
+ type LoggerPluginConfig,
215
+ type LogLevel,
216
+ } from "@real-router/logger-plugin";
217
+ ```
218
+
219
+ ## Related Packages
220
+
221
+ - [@real-router/core](https://www.npmjs.com/package/@real-router/core) — Core router
222
+ - [@real-router/react](https://www.npmjs.com/package/@real-router/react) — React integration
223
+ - [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) — Browser history
224
+
225
+ ## License
226
+
227
+ MIT © [Oleg Ivanov](https://github.com/greydragon888)
@@ -0,0 +1,71 @@
1
+ import { PluginFactory } from '@real-router/core';
2
+
3
+ /**
4
+ * Creates a logger plugin for real-router.
5
+ *
6
+ * Configuration is managed through the logger singleton.
7
+ *
8
+ * @returns Plugin factory function for real-router
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { loggerPluginFactory } from "@real-router/logger-plugin";
13
+ *
14
+ * router.usePlugin(loggerPluginFactory());
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // Use with default configuration
20
+ * router.usePlugin(loggerPluginFactory());
21
+ * ```
22
+ */
23
+ declare function loggerPluginFactory(): PluginFactory;
24
+ /**
25
+ * Default logger plugin instance with standard configuration.
26
+ * Provided for backward compatibility with existing code.
27
+ *
28
+ * @example
29
+ * // Use default configuration
30
+ * router.usePlugin(loggerPlugin);
31
+ */
32
+ declare const loggerPlugin: PluginFactory;
33
+
34
+ /**
35
+ * Logging level for router events.
36
+ * Controls which events are logged to the console.
37
+ */
38
+ type LogLevel = "all" | "transitions" | "errors" | "none";
39
+ /**
40
+ * Configuration options for the logger plugin.
41
+ */
42
+ interface LoggerPluginConfig {
43
+ /**
44
+ * Logging level - controls what router events to log.
45
+ *
46
+ * - 'all': Log all router events (default)
47
+ * - 'transitions': Log only transition-related events
48
+ * - 'errors': Log only transition errors
49
+ * - 'none': Disable all logging
50
+ *
51
+ * @default 'all'
52
+ */
53
+ level?: LogLevel;
54
+ /**
55
+ * Show diff of changed route parameters between transitions.
56
+ * Only applies when navigating within the same route.
57
+ * Helps identify which parameters changed during navigation.
58
+ *
59
+ * @default false
60
+ */
61
+ showParamsDiff?: boolean;
62
+ /**
63
+ * Custom context name for logger.
64
+ * Useful when running multiple routers.
65
+ *
66
+ * @default 'logger-plugin'
67
+ */
68
+ context?: string;
69
+ }
70
+
71
+ export { type LogLevel, type LoggerPluginConfig, loggerPlugin, loggerPluginFactory };
@@ -0,0 +1 @@
1
+ var o={context:"logger-plugin"},n=o=>o?.name??"(none)";function e(){return()=>{const e=o,t=(o=>{let n=!1;return{open(e){o&&!n&&(console.group(e),n=!0)},close(){o&&n&&(console.groupEnd(),n=!1)},isOpen:()=>n}})("undefined"!=typeof console&&"function"==typeof console.group&&"function"==typeof console.groupEnd);return{onStart(){console.log(`[${e.context}] Router started`)},onStop(){t.close(),console.log(`[${e.context}] Router stopped`)},onTransitionStart(o,r){t.open("Router transition");const s=n(r),c=n(o);console.log(`[${e.context}] Transition: ${s} → ${c}`,{from:r,to:o}),((o,n)=>{if(!n)return;if(o.name!==n.name)return;const t=((o,n)=>{const e={},t={},r={};let s=!1;for(const t in o)t in n?o[t]!==n[t]&&(e[t]={from:o[t],to:n[t]},s=!0):(r[t]=o[t],s=!0);for(const e in n)e in o||(t[e]=n[e],s=!0);return s?{changed:e,added:t,removed:r}:null})(n.params,o.params);t&&((o,n)=>{const e=[],t=Object.entries(o.changed);if(t.length>0){const o=[];for(const[n,{from:e,to:r}]of t)o.push(`${n}: ${JSON.stringify(e)} → ${JSON.stringify(r)}`);e.push(`Changed: { ${o.join(", ")} }`)}Object.entries(o.added).length>0&&e.push(`Added: ${JSON.stringify(o.added)}`),Object.entries(o.removed).length>0&&e.push(`Removed: ${JSON.stringify(o.removed)}`),console.log(`[${n}] ${e.join(", ")}`)})(t,e.context)})(o,r)},onTransitionSuccess(o,n){console.log(`[${e.context}] Transition success`,{to:o,from:n}),t.close()},onTransitionCancel(o,n){console.warn(`[${e.context}] Transition cancelled`,{to:o,from:n}),t.close()},onTransitionError(o,n,r){console.error(`[${e.context}] Transition error: ${r.code}`,{error:r,stack:r.stack,to:o,from:n}),t.close()},teardown(){t.close()}}}}var t=e();exports.loggerPlugin=t,exports.loggerPluginFactory=e;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/internal/console-groups.ts","../../src/internal/formatting.ts","../../src/internal/params-diff.ts","../../src/plugin.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// packages/logger-plugin/modules/constants.ts\n\nimport type { LoggerPluginConfig } from \"./types\";\n\nexport const DEFAULT_CONFIG: Required<LoggerPluginConfig> = {\n level: \"all\",\n showParamsDiff: true,\n context: \"logger-plugin\",\n};\n","// packages/logger-plugin/modules/internal/console-groups.ts\n\n/**\n * Checks if console.group is supported in the current environment.\n */\nexport const supportsConsoleGroups = (): boolean => {\n return (\n typeof console !== \"undefined\" &&\n typeof console.group === \"function\" &&\n typeof console.groupEnd === \"function\"\n );\n};\n\n/**\n * Manager for handling console groups\n */\ninterface GroupManager {\n /**\n * Opens a group if it's not already open\n */\n open: (label: string) => void;\n /**\n * Closes a group if it's open\n */\n close: () => void;\n /**\n * Checks if a group is currently open\n */\n isOpen: () => boolean;\n}\n\n/**\n * Creates a manager for handling console groups.\n * Prevents duplicate group opening.\n *\n * @param enabled - Whether groups are supported in the environment\n * @returns Object with open and close methods\n */\nexport const createGroupManager = (enabled: boolean): GroupManager => {\n let isOpened = false;\n\n return {\n /**\n * Opens a group if it's not already open.\n */\n open(label: string): void {\n if (!enabled || isOpened) {\n return;\n }\n\n console.group(label);\n isOpened = true;\n },\n\n /**\n * Closes a group if it's open.\n */\n close(): void {\n if (!enabled || !isOpened) {\n return;\n }\n\n console.groupEnd();\n isOpened = false;\n },\n\n /**\n * Checks if a group is currently open.\n */\n isOpen(): boolean {\n return isOpened;\n },\n };\n};\n","// packages/logger-plugin/modules/internal/formatting.ts\n\nimport type { State } from \"@real-router/core\";\n\n/**\n * Formats route name for logging output.\n * Handles undefined/null.\n */\nexport const formatRouteName = (state?: State): string => {\n return state?.name ?? \"(none)\";\n};\n","// packages/logger-plugin/modules/internal/params-diff.ts\n\nimport type { Params } from \"@real-router/core\";\n\nexport interface ParamsDiff {\n changed: Record<string, { from: unknown; to: unknown }>;\n added: Record<string, unknown>;\n removed: Record<string, unknown>;\n}\n\n/**\n * Calculates differences between two parameter objects.\n * Performs only shallow comparison.\n *\n * @param fromParams - Previous parameters\n * @param toParams - New parameters\n * @returns Object with differences or null if there are no changes\n */\nexport const getParamsDiff = (\n fromParams: Params,\n toParams: Params,\n): ParamsDiff | null => {\n const changed: ParamsDiff[\"changed\"] = {};\n const added: ParamsDiff[\"added\"] = {};\n const removed: ParamsDiff[\"removed\"] = {};\n\n // Track if any changes found to avoid iterating through objects at the end.\n // This is a performance optimization: instead of calling Object.keys().length\n // three times to check if objects are empty, we set this flag when we find\n // any change and check it once at the end.\n let hasChanges = false;\n\n // Find changed and removed\n for (const key in fromParams) {\n if (!(key in toParams)) {\n removed[key] = fromParams[key];\n hasChanges = true;\n } else if (fromParams[key] !== toParams[key]) {\n changed[key] = { from: fromParams[key], to: toParams[key] };\n hasChanges = true;\n }\n }\n\n // Find added\n for (const key in toParams) {\n if (!(key in fromParams)) {\n added[key] = toParams[key];\n hasChanges = true;\n }\n }\n\n // Return null if there are no changes\n if (!hasChanges) {\n return null;\n }\n\n return { changed, added, removed };\n};\n\n/**\n * Formats and logs parameter differences.\n *\n * @param diff - Object with differences\n * @param context - Context for logger\n */\nexport const logParamsDiff = (diff: ParamsDiff, context: string): void => {\n const parts: string[] = [];\n\n // Cache entries to avoid double iteration\n const changedEntries = Object.entries(diff.changed);\n\n if (changedEntries.length > 0) {\n const items: string[] = [];\n\n for (const [key, { from, to }] of changedEntries) {\n items.push(`${key}: ${JSON.stringify(from)} → ${JSON.stringify(to)}`);\n }\n\n parts.push(`Changed: { ${items.join(\", \")} }`);\n }\n\n const addedEntries = Object.entries(diff.added);\n\n if (addedEntries.length > 0) {\n parts.push(`Added: ${JSON.stringify(diff.added)}`);\n }\n\n const removedEntries = Object.entries(diff.removed);\n\n if (removedEntries.length > 0) {\n parts.push(`Removed: ${JSON.stringify(diff.removed)}`);\n }\n\n console.log(`[${context}] ${parts.join(\", \")}`);\n};\n","// packages/logger-plugin/modules/plugin.ts\n\nimport { DEFAULT_CONFIG } from \"./constants\";\nimport {\n createGroupManager,\n supportsConsoleGroups,\n} from \"./internal/console-groups\";\nimport { formatRouteName } from \"./internal/formatting\";\nimport { getParamsDiff, logParamsDiff } from \"./internal/params-diff\";\n\nimport type { PluginFactory, RouterError, State } from \"@real-router/core\";\n\n/**\n * Creates a logger plugin for real-router.\n *\n * Configuration is managed through the logger singleton.\n *\n * @returns Plugin factory function for real-router\n *\n * @example\n * ```ts\n * import { loggerPluginFactory } from \"@real-router/logger-plugin\";\n *\n * router.usePlugin(loggerPluginFactory());\n * ```\n *\n * @example\n * ```ts\n * // Use with default configuration\n * router.usePlugin(loggerPluginFactory());\n * ```\n */\nexport function loggerPluginFactory(): PluginFactory {\n // eslint-disable-next-line unicorn/consistent-function-scoping -- factory pattern: closure captures config, groups\n return () => {\n const config = DEFAULT_CONFIG;\n\n // Create helper managers\n const groups = createGroupManager(supportsConsoleGroups());\n\n /**\n * Logs parameter differences when navigating within the same route.\n */\n const logParamsIfNeeded = (toState: State, fromState?: State): void => {\n if (!fromState) {\n return;\n }\n\n // Show diff only for the same route\n if (toState.name !== fromState.name) {\n return;\n }\n\n const diff = getParamsDiff(fromState.params, toState.params);\n\n if (diff) {\n logParamsDiff(diff, config.context);\n }\n };\n\n return {\n onStart() {\n console.log(`[${config.context}] Router started`);\n },\n\n onStop() {\n groups.close();\n console.log(`[${config.context}] Router stopped`);\n },\n\n onTransitionStart(toState: State, fromState?: State) {\n groups.open(\"Router transition\");\n\n const fromRoute = formatRouteName(fromState);\n const toRoute = formatRouteName(toState);\n\n console.log(\n `[${config.context}] Transition: ${fromRoute} → ${toRoute}`,\n {\n from: fromState,\n to: toState,\n },\n );\n\n logParamsIfNeeded(toState, fromState);\n },\n\n onTransitionSuccess(toState: State, fromState?: State) {\n console.log(`[${config.context}] Transition success`, {\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n onTransitionCancel(toState: State, fromState?: State) {\n console.warn(`[${config.context}] Transition cancelled`, {\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n onTransitionError(\n toState: State | undefined,\n fromState: State | undefined,\n err: RouterError,\n ) {\n console.error(`[${config.context}] Transition error: ${err.code}`, {\n error: err,\n stack: err.stack,\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n teardown() {\n groups.close();\n },\n };\n };\n}\n\n/**\n * Default logger plugin instance with standard configuration.\n * Provided for backward compatibility with existing code.\n *\n * @example\n * // Use default configuration\n * router.usePlugin(loggerPlugin);\n */\nexport const loggerPlugin = loggerPluginFactory();\n"]}
@@ -0,0 +1 @@
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":233,"imports":[{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/internal/console-groups.ts":{"bytes":1490,"imports":[{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/internal/formatting.ts":{"bytes":281,"imports":[{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/internal/params-diff.ts":{"bytes":2619,"imports":[{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":3413,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/internal/console-groups.ts","kind":"import-statement","original":"./internal/console-groups"},{"path":"src/internal/formatting.ts","kind":"import-statement","original":"./internal/formatting"},{"path":"src/internal/params-diff.ts","kind":"import-statement","original":"./internal/params-diff"},{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":266,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"/Users/olegivanov/WebstormProjects/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":11720},"dist/cjs/index.js":{"imports":[],"exports":["loggerPlugin","loggerPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/constants.ts":{"bytesInOutput":93},"src/internal/console-groups.ts":{"bytesInOutput":727},"src/internal/formatting.ts":{"bytesInOutput":72},"src/internal/params-diff.ts":{"bytesInOutput":1361},"src/plugin.ts":{"bytesInOutput":1902},"src/index.ts":{"bytesInOutput":0}},"bytes":4341}}}
@@ -0,0 +1,71 @@
1
+ import { PluginFactory } from '@real-router/core';
2
+
3
+ /**
4
+ * Creates a logger plugin for real-router.
5
+ *
6
+ * Configuration is managed through the logger singleton.
7
+ *
8
+ * @returns Plugin factory function for real-router
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { loggerPluginFactory } from "@real-router/logger-plugin";
13
+ *
14
+ * router.usePlugin(loggerPluginFactory());
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // Use with default configuration
20
+ * router.usePlugin(loggerPluginFactory());
21
+ * ```
22
+ */
23
+ declare function loggerPluginFactory(): PluginFactory;
24
+ /**
25
+ * Default logger plugin instance with standard configuration.
26
+ * Provided for backward compatibility with existing code.
27
+ *
28
+ * @example
29
+ * // Use default configuration
30
+ * router.usePlugin(loggerPlugin);
31
+ */
32
+ declare const loggerPlugin: PluginFactory;
33
+
34
+ /**
35
+ * Logging level for router events.
36
+ * Controls which events are logged to the console.
37
+ */
38
+ type LogLevel = "all" | "transitions" | "errors" | "none";
39
+ /**
40
+ * Configuration options for the logger plugin.
41
+ */
42
+ interface LoggerPluginConfig {
43
+ /**
44
+ * Logging level - controls what router events to log.
45
+ *
46
+ * - 'all': Log all router events (default)
47
+ * - 'transitions': Log only transition-related events
48
+ * - 'errors': Log only transition errors
49
+ * - 'none': Disable all logging
50
+ *
51
+ * @default 'all'
52
+ */
53
+ level?: LogLevel;
54
+ /**
55
+ * Show diff of changed route parameters between transitions.
56
+ * Only applies when navigating within the same route.
57
+ * Helps identify which parameters changed during navigation.
58
+ *
59
+ * @default false
60
+ */
61
+ showParamsDiff?: boolean;
62
+ /**
63
+ * Custom context name for logger.
64
+ * Useful when running multiple routers.
65
+ *
66
+ * @default 'logger-plugin'
67
+ */
68
+ context?: string;
69
+ }
70
+
71
+ export { type LogLevel, type LoggerPluginConfig, loggerPlugin, loggerPluginFactory };
@@ -0,0 +1 @@
1
+ var o={context:"logger-plugin"},n=o=>o?.name??"(none)";function e(){return()=>{const e=o,t=(o=>{let n=!1;return{open(e){o&&!n&&(console.group(e),n=!0)},close(){o&&n&&(console.groupEnd(),n=!1)},isOpen:()=>n}})("undefined"!=typeof console&&"function"==typeof console.group&&"function"==typeof console.groupEnd);return{onStart(){console.log(`[${e.context}] Router started`)},onStop(){t.close(),console.log(`[${e.context}] Router stopped`)},onTransitionStart(o,r){t.open("Router transition");const s=n(r),c=n(o);console.log(`[${e.context}] Transition: ${s} → ${c}`,{from:r,to:o}),((o,n)=>{if(!n)return;if(o.name!==n.name)return;const t=((o,n)=>{const e={},t={},r={};let s=!1;for(const t in o)t in n?o[t]!==n[t]&&(e[t]={from:o[t],to:n[t]},s=!0):(r[t]=o[t],s=!0);for(const e in n)e in o||(t[e]=n[e],s=!0);return s?{changed:e,added:t,removed:r}:null})(n.params,o.params);t&&((o,n)=>{const e=[],t=Object.entries(o.changed);if(t.length>0){const o=[];for(const[n,{from:e,to:r}]of t)o.push(`${n}: ${JSON.stringify(e)} → ${JSON.stringify(r)}`);e.push(`Changed: { ${o.join(", ")} }`)}Object.entries(o.added).length>0&&e.push(`Added: ${JSON.stringify(o.added)}`),Object.entries(o.removed).length>0&&e.push(`Removed: ${JSON.stringify(o.removed)}`),console.log(`[${n}] ${e.join(", ")}`)})(t,e.context)})(o,r)},onTransitionSuccess(o,n){console.log(`[${e.context}] Transition success`,{to:o,from:n}),t.close()},onTransitionCancel(o,n){console.warn(`[${e.context}] Transition cancelled`,{to:o,from:n}),t.close()},onTransitionError(o,n,r){console.error(`[${e.context}] Transition error: ${r.code}`,{error:r,stack:r.stack,to:o,from:n}),t.close()},teardown(){t.close()}}}}var t=e();export{t as loggerPlugin,e as loggerPluginFactory};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/internal/console-groups.ts","../../src/internal/formatting.ts","../../src/internal/params-diff.ts","../../src/plugin.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/logger-plugin/modules/constants.ts\n\nimport type { LoggerPluginConfig } from \"./types\";\n\nexport const DEFAULT_CONFIG: Required<LoggerPluginConfig> = {\n level: \"all\",\n showParamsDiff: true,\n context: \"logger-plugin\",\n};\n","// packages/logger-plugin/modules/internal/console-groups.ts\n\n/**\n * Checks if console.group is supported in the current environment.\n */\nexport const supportsConsoleGroups = (): boolean => {\n return (\n typeof console !== \"undefined\" &&\n typeof console.group === \"function\" &&\n typeof console.groupEnd === \"function\"\n );\n};\n\n/**\n * Manager for handling console groups\n */\ninterface GroupManager {\n /**\n * Opens a group if it's not already open\n */\n open: (label: string) => void;\n /**\n * Closes a group if it's open\n */\n close: () => void;\n /**\n * Checks if a group is currently open\n */\n isOpen: () => boolean;\n}\n\n/**\n * Creates a manager for handling console groups.\n * Prevents duplicate group opening.\n *\n * @param enabled - Whether groups are supported in the environment\n * @returns Object with open and close methods\n */\nexport const createGroupManager = (enabled: boolean): GroupManager => {\n let isOpened = false;\n\n return {\n /**\n * Opens a group if it's not already open.\n */\n open(label: string): void {\n if (!enabled || isOpened) {\n return;\n }\n\n console.group(label);\n isOpened = true;\n },\n\n /**\n * Closes a group if it's open.\n */\n close(): void {\n if (!enabled || !isOpened) {\n return;\n }\n\n console.groupEnd();\n isOpened = false;\n },\n\n /**\n * Checks if a group is currently open.\n */\n isOpen(): boolean {\n return isOpened;\n },\n };\n};\n","// packages/logger-plugin/modules/internal/formatting.ts\n\nimport type { State } from \"@real-router/core\";\n\n/**\n * Formats route name for logging output.\n * Handles undefined/null.\n */\nexport const formatRouteName = (state?: State): string => {\n return state?.name ?? \"(none)\";\n};\n","// packages/logger-plugin/modules/internal/params-diff.ts\n\nimport type { Params } from \"@real-router/core\";\n\nexport interface ParamsDiff {\n changed: Record<string, { from: unknown; to: unknown }>;\n added: Record<string, unknown>;\n removed: Record<string, unknown>;\n}\n\n/**\n * Calculates differences between two parameter objects.\n * Performs only shallow comparison.\n *\n * @param fromParams - Previous parameters\n * @param toParams - New parameters\n * @returns Object with differences or null if there are no changes\n */\nexport const getParamsDiff = (\n fromParams: Params,\n toParams: Params,\n): ParamsDiff | null => {\n const changed: ParamsDiff[\"changed\"] = {};\n const added: ParamsDiff[\"added\"] = {};\n const removed: ParamsDiff[\"removed\"] = {};\n\n // Track if any changes found to avoid iterating through objects at the end.\n // This is a performance optimization: instead of calling Object.keys().length\n // three times to check if objects are empty, we set this flag when we find\n // any change and check it once at the end.\n let hasChanges = false;\n\n // Find changed and removed\n for (const key in fromParams) {\n if (!(key in toParams)) {\n removed[key] = fromParams[key];\n hasChanges = true;\n } else if (fromParams[key] !== toParams[key]) {\n changed[key] = { from: fromParams[key], to: toParams[key] };\n hasChanges = true;\n }\n }\n\n // Find added\n for (const key in toParams) {\n if (!(key in fromParams)) {\n added[key] = toParams[key];\n hasChanges = true;\n }\n }\n\n // Return null if there are no changes\n if (!hasChanges) {\n return null;\n }\n\n return { changed, added, removed };\n};\n\n/**\n * Formats and logs parameter differences.\n *\n * @param diff - Object with differences\n * @param context - Context for logger\n */\nexport const logParamsDiff = (diff: ParamsDiff, context: string): void => {\n const parts: string[] = [];\n\n // Cache entries to avoid double iteration\n const changedEntries = Object.entries(diff.changed);\n\n if (changedEntries.length > 0) {\n const items: string[] = [];\n\n for (const [key, { from, to }] of changedEntries) {\n items.push(`${key}: ${JSON.stringify(from)} → ${JSON.stringify(to)}`);\n }\n\n parts.push(`Changed: { ${items.join(\", \")} }`);\n }\n\n const addedEntries = Object.entries(diff.added);\n\n if (addedEntries.length > 0) {\n parts.push(`Added: ${JSON.stringify(diff.added)}`);\n }\n\n const removedEntries = Object.entries(diff.removed);\n\n if (removedEntries.length > 0) {\n parts.push(`Removed: ${JSON.stringify(diff.removed)}`);\n }\n\n console.log(`[${context}] ${parts.join(\", \")}`);\n};\n","// packages/logger-plugin/modules/plugin.ts\n\nimport { DEFAULT_CONFIG } from \"./constants\";\nimport {\n createGroupManager,\n supportsConsoleGroups,\n} from \"./internal/console-groups\";\nimport { formatRouteName } from \"./internal/formatting\";\nimport { getParamsDiff, logParamsDiff } from \"./internal/params-diff\";\n\nimport type { PluginFactory, RouterError, State } from \"@real-router/core\";\n\n/**\n * Creates a logger plugin for real-router.\n *\n * Configuration is managed through the logger singleton.\n *\n * @returns Plugin factory function for real-router\n *\n * @example\n * ```ts\n * import { loggerPluginFactory } from \"@real-router/logger-plugin\";\n *\n * router.usePlugin(loggerPluginFactory());\n * ```\n *\n * @example\n * ```ts\n * // Use with default configuration\n * router.usePlugin(loggerPluginFactory());\n * ```\n */\nexport function loggerPluginFactory(): PluginFactory {\n // eslint-disable-next-line unicorn/consistent-function-scoping -- factory pattern: closure captures config, groups\n return () => {\n const config = DEFAULT_CONFIG;\n\n // Create helper managers\n const groups = createGroupManager(supportsConsoleGroups());\n\n /**\n * Logs parameter differences when navigating within the same route.\n */\n const logParamsIfNeeded = (toState: State, fromState?: State): void => {\n if (!fromState) {\n return;\n }\n\n // Show diff only for the same route\n if (toState.name !== fromState.name) {\n return;\n }\n\n const diff = getParamsDiff(fromState.params, toState.params);\n\n if (diff) {\n logParamsDiff(diff, config.context);\n }\n };\n\n return {\n onStart() {\n console.log(`[${config.context}] Router started`);\n },\n\n onStop() {\n groups.close();\n console.log(`[${config.context}] Router stopped`);\n },\n\n onTransitionStart(toState: State, fromState?: State) {\n groups.open(\"Router transition\");\n\n const fromRoute = formatRouteName(fromState);\n const toRoute = formatRouteName(toState);\n\n console.log(\n `[${config.context}] Transition: ${fromRoute} → ${toRoute}`,\n {\n from: fromState,\n to: toState,\n },\n );\n\n logParamsIfNeeded(toState, fromState);\n },\n\n onTransitionSuccess(toState: State, fromState?: State) {\n console.log(`[${config.context}] Transition success`, {\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n onTransitionCancel(toState: State, fromState?: State) {\n console.warn(`[${config.context}] Transition cancelled`, {\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n onTransitionError(\n toState: State | undefined,\n fromState: State | undefined,\n err: RouterError,\n ) {\n console.error(`[${config.context}] Transition error: ${err.code}`, {\n error: err,\n stack: err.stack,\n to: toState,\n from: fromState,\n });\n\n groups.close();\n },\n\n teardown() {\n groups.close();\n },\n };\n };\n}\n\n/**\n * Default logger plugin instance with standard configuration.\n * Provided for backward compatibility with existing code.\n *\n * @example\n * // Use default configuration\n * router.usePlugin(loggerPlugin);\n */\nexport const loggerPlugin = loggerPluginFactory();\n"]}
@@ -0,0 +1 @@
1
+ {"inputs":{"src/constants.ts":{"bytes":233,"imports":[],"format":"esm"},"src/internal/console-groups.ts":{"bytes":1490,"imports":[],"format":"esm"},"src/internal/formatting.ts":{"bytes":281,"imports":[],"format":"esm"},"src/internal/params-diff.ts":{"bytes":2619,"imports":[],"format":"esm"},"src/plugin.ts":{"bytes":3413,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/internal/console-groups.ts","kind":"import-statement","original":"./internal/console-groups"},{"path":"src/internal/formatting.ts","kind":"import-statement","original":"./internal/formatting"},{"path":"src/internal/params-diff.ts","kind":"import-statement","original":"./internal/params-diff"}],"format":"esm"},"src/index.ts":{"bytes":266,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":11720},"dist/esm/index.mjs":{"imports":[],"exports":["loggerPlugin","loggerPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/constants.ts":{"bytesInOutput":93},"src/internal/console-groups.ts":{"bytesInOutput":727},"src/internal/formatting.ts":{"bytesInOutput":72},"src/internal/params-diff.ts":{"bytesInOutput":1361},"src/plugin.ts":{"bytesInOutput":1902},"src/index.ts":{"bytesInOutput":0}},"bytes":4341}}}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@real-router/logger-plugin",
3
+ "version": "0.0.1",
4
+ "type": "commonjs",
5
+ "description": "Development logging plugin with transition tracking and performance metrics",
6
+ "main": "./dist/cjs/index.js",
7
+ "module": "./dist/esm/index.mjs",
8
+ "types": "./dist/esm/index.d.mts",
9
+ "exports": {
10
+ ".": {
11
+ "types": {
12
+ "import": "./dist/esm/index.d.mts",
13
+ "require": "./dist/cjs/index.d.ts"
14
+ },
15
+ "import": "./dist/esm/index.mjs",
16
+ "require": "./dist/cjs/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/greydragon888/real-router.git"
25
+ },
26
+ "keywords": [
27
+ "real-router",
28
+ "logger",
29
+ "debugging",
30
+ "development",
31
+ "console",
32
+ "monitoring"
33
+ ],
34
+ "author": {
35
+ "name": "Oleg Ivanov",
36
+ "email": "greydragon888@gmail.com",
37
+ "url": "https://github.com/greydragon888"
38
+ },
39
+ "license": "MIT",
40
+ "bugs": {
41
+ "url": "https://github.com/greydragon888/real-router/issues"
42
+ },
43
+ "homepage": "https://github.com/greydragon888/real-router",
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "test": "vitest run",
47
+ "type-check": "tsc --noEmit",
48
+ "lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
49
+ "test:mutation": "stryker run",
50
+ "test:mutation:report": "open reports/mutation-report.html",
51
+ "lint:package": "publint",
52
+ "lint:types": "attw --pack ."
53
+ },
54
+ "sideEffects": false,
55
+ "dependencies": {
56
+ "@real-router/core": "workspace:^"
57
+ }
58
+ }