iten-devtools 0.4.1 → 0.5.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/README.md +71 -2
- package/dist/index.cjs +89 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.mjs +88 -0
- package/package.json +35 -5
- package/src/index.ts +152 -0
- package/index.js +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# iten-devtools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Adapter-neutral devtools helpers for `iten`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package does not render a UI. It subscribes to any `ItenCore`-like router and keeps a bounded event log that custom panels, tests, browser extensions, or embedded app debug views can render.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install iten-devtools
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createItenCore, createRoute, defineRoutes, route } from 'iten-core'
|
|
17
|
+
import { createItenDevtools } from 'iten-devtools'
|
|
18
|
+
|
|
19
|
+
const routes = defineRoutes({
|
|
20
|
+
home: route(),
|
|
21
|
+
detail: route<{ id: string }>(),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const toRoute = createRoute(routes)
|
|
25
|
+
|
|
26
|
+
const router = createItenCore({
|
|
27
|
+
initial: toRoute({ name: 'home' }),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const devtools = createItenDevtools({ router })
|
|
31
|
+
|
|
32
|
+
await router.navigate({ target: toRoute({ name: 'detail', params: { id: '42' } }) })
|
|
33
|
+
|
|
34
|
+
devtools.getSnapshot().events
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Snapshot Shape
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
type DevtoolsSnapshot = {
|
|
41
|
+
state: RouterState
|
|
42
|
+
events: DevtoolsEvent[]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Each event includes:
|
|
47
|
+
|
|
48
|
+
- `type`: `init`, `route`, `pending`, `error`, `idle`, or `history`
|
|
49
|
+
- `timestamp`
|
|
50
|
+
- `route`
|
|
51
|
+
- `pendingRoute`
|
|
52
|
+
- `error`
|
|
53
|
+
- `canGoBack`
|
|
54
|
+
- `historyLength`
|
|
55
|
+
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const devtools = createItenDevtools({
|
|
60
|
+
router,
|
|
61
|
+
maxEvents: 100,
|
|
62
|
+
now: Date.now,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const stop = devtools.subscribe({
|
|
66
|
+
listener: (snapshot) => {
|
|
67
|
+
console.log(snapshot.events)
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
devtools.clear()
|
|
72
|
+
stop()
|
|
73
|
+
devtools.dispose()
|
|
74
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
const DEFAULT_MAX_EVENTS = 100;
|
|
4
|
+
function sameRoute({ left, right }) {
|
|
5
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
6
|
+
}
|
|
7
|
+
function classifyEvent({ previous, next }) {
|
|
8
|
+
if (!previous) return "init";
|
|
9
|
+
if (next.error && next.error !== previous.error) return "error";
|
|
10
|
+
if (!sameRoute({
|
|
11
|
+
left: previous.route,
|
|
12
|
+
right: next.route
|
|
13
|
+
})) return "route";
|
|
14
|
+
if (!sameRoute({
|
|
15
|
+
left: previous.pendingRoute,
|
|
16
|
+
right: next.pendingRoute
|
|
17
|
+
})) return "pending";
|
|
18
|
+
if (previous.isNavigating !== next.isNavigating) return next.isNavigating ? "pending" : "idle";
|
|
19
|
+
if (previous.history.length !== next.history.length) return "history";
|
|
20
|
+
return "idle";
|
|
21
|
+
}
|
|
22
|
+
function createItenDevtools({ router, maxEvents = DEFAULT_MAX_EVENTS, now = Date.now }) {
|
|
23
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
24
|
+
const events = [];
|
|
25
|
+
let currentState = router.getState();
|
|
26
|
+
let previousState = null;
|
|
27
|
+
let nextId = 1;
|
|
28
|
+
let disposed = false;
|
|
29
|
+
function emit({ state }) {
|
|
30
|
+
if (disposed) return;
|
|
31
|
+
currentState = state;
|
|
32
|
+
const event = {
|
|
33
|
+
id: nextId,
|
|
34
|
+
type: classifyEvent({
|
|
35
|
+
previous: previousState,
|
|
36
|
+
next: state
|
|
37
|
+
}),
|
|
38
|
+
timestamp: now(),
|
|
39
|
+
route: state.route,
|
|
40
|
+
pendingRoute: state.pendingRoute,
|
|
41
|
+
error: state.error?.error ?? null,
|
|
42
|
+
canGoBack: state.canGoBack,
|
|
43
|
+
historyLength: state.history.length
|
|
44
|
+
};
|
|
45
|
+
nextId += 1;
|
|
46
|
+
previousState = state;
|
|
47
|
+
events.push(event);
|
|
48
|
+
if (events.length > maxEvents) events.splice(0, events.length - maxEvents);
|
|
49
|
+
notify();
|
|
50
|
+
}
|
|
51
|
+
function getSnapshot() {
|
|
52
|
+
return {
|
|
53
|
+
state: currentState,
|
|
54
|
+
events: [...events]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function notify() {
|
|
58
|
+
const snapshot = getSnapshot();
|
|
59
|
+
for (const listener of listeners) listener(snapshot);
|
|
60
|
+
}
|
|
61
|
+
const stopRouter = router.subscribe({ listener: (state) => {
|
|
62
|
+
emit({ state });
|
|
63
|
+
} });
|
|
64
|
+
emit({ state: currentState });
|
|
65
|
+
function subscribe({ listener }) {
|
|
66
|
+
listeners.add(listener);
|
|
67
|
+
return () => {
|
|
68
|
+
listeners.delete(listener);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function clear() {
|
|
72
|
+
events.splice(0, events.length);
|
|
73
|
+
notify();
|
|
74
|
+
}
|
|
75
|
+
function dispose() {
|
|
76
|
+
disposed = true;
|
|
77
|
+
stopRouter();
|
|
78
|
+
listeners.clear();
|
|
79
|
+
events.splice(0, events.length);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
getSnapshot,
|
|
83
|
+
subscribe,
|
|
84
|
+
clear,
|
|
85
|
+
dispose
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
exports.createItenDevtools = createItenDevtools;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RouteMapDef, RouteUnion, RouterState } from "iten-core";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type DevtoolsRouter<M extends RouteMapDef> = {
|
|
5
|
+
getState: () => RouterState<M>;
|
|
6
|
+
subscribe: (input: {
|
|
7
|
+
listener: (state: RouterState<M>) => void;
|
|
8
|
+
}) => () => void;
|
|
9
|
+
};
|
|
10
|
+
type DevtoolsEventType = "init" | "route" | "pending" | "error" | "idle" | "history";
|
|
11
|
+
type DevtoolsEvent<M extends RouteMapDef> = {
|
|
12
|
+
id: number;
|
|
13
|
+
type: DevtoolsEventType;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
route: RouteUnion<M> | null;
|
|
16
|
+
pendingRoute: RouteUnion<M> | null;
|
|
17
|
+
error: unknown;
|
|
18
|
+
canGoBack: boolean;
|
|
19
|
+
historyLength: number;
|
|
20
|
+
};
|
|
21
|
+
type DevtoolsSnapshot<M extends RouteMapDef> = {
|
|
22
|
+
state: RouterState<M>;
|
|
23
|
+
events: DevtoolsEvent<M>[];
|
|
24
|
+
};
|
|
25
|
+
type CreateDevtoolsInput<M extends RouteMapDef> = {
|
|
26
|
+
router: DevtoolsRouter<M>;
|
|
27
|
+
maxEvents?: number | undefined;
|
|
28
|
+
now?: (() => number) | undefined;
|
|
29
|
+
};
|
|
30
|
+
type ItenDevtools<M extends RouteMapDef> = {
|
|
31
|
+
getSnapshot: () => DevtoolsSnapshot<M>;
|
|
32
|
+
subscribe: (input: {
|
|
33
|
+
listener: (snapshot: DevtoolsSnapshot<M>) => void;
|
|
34
|
+
}) => () => void;
|
|
35
|
+
clear: () => void;
|
|
36
|
+
dispose: () => void;
|
|
37
|
+
};
|
|
38
|
+
declare function createItenDevtools<M extends RouteMapDef>({
|
|
39
|
+
router,
|
|
40
|
+
maxEvents,
|
|
41
|
+
now
|
|
42
|
+
}: CreateDevtoolsInput<M>): ItenDevtools<M>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { CreateDevtoolsInput, DevtoolsEvent, DevtoolsEventType, DevtoolsRouter, DevtoolsSnapshot, ItenDevtools, createItenDevtools };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RouteMapDef, RouteUnion, RouterState } from "iten-core";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type DevtoolsRouter<M extends RouteMapDef> = {
|
|
5
|
+
getState: () => RouterState<M>;
|
|
6
|
+
subscribe: (input: {
|
|
7
|
+
listener: (state: RouterState<M>) => void;
|
|
8
|
+
}) => () => void;
|
|
9
|
+
};
|
|
10
|
+
type DevtoolsEventType = "init" | "route" | "pending" | "error" | "idle" | "history";
|
|
11
|
+
type DevtoolsEvent<M extends RouteMapDef> = {
|
|
12
|
+
id: number;
|
|
13
|
+
type: DevtoolsEventType;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
route: RouteUnion<M> | null;
|
|
16
|
+
pendingRoute: RouteUnion<M> | null;
|
|
17
|
+
error: unknown;
|
|
18
|
+
canGoBack: boolean;
|
|
19
|
+
historyLength: number;
|
|
20
|
+
};
|
|
21
|
+
type DevtoolsSnapshot<M extends RouteMapDef> = {
|
|
22
|
+
state: RouterState<M>;
|
|
23
|
+
events: DevtoolsEvent<M>[];
|
|
24
|
+
};
|
|
25
|
+
type CreateDevtoolsInput<M extends RouteMapDef> = {
|
|
26
|
+
router: DevtoolsRouter<M>;
|
|
27
|
+
maxEvents?: number | undefined;
|
|
28
|
+
now?: (() => number) | undefined;
|
|
29
|
+
};
|
|
30
|
+
type ItenDevtools<M extends RouteMapDef> = {
|
|
31
|
+
getSnapshot: () => DevtoolsSnapshot<M>;
|
|
32
|
+
subscribe: (input: {
|
|
33
|
+
listener: (snapshot: DevtoolsSnapshot<M>) => void;
|
|
34
|
+
}) => () => void;
|
|
35
|
+
clear: () => void;
|
|
36
|
+
dispose: () => void;
|
|
37
|
+
};
|
|
38
|
+
declare function createItenDevtools<M extends RouteMapDef>({
|
|
39
|
+
router,
|
|
40
|
+
maxEvents,
|
|
41
|
+
now
|
|
42
|
+
}: CreateDevtoolsInput<M>): ItenDevtools<M>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { CreateDevtoolsInput, DevtoolsEvent, DevtoolsEventType, DevtoolsRouter, DevtoolsSnapshot, ItenDevtools, createItenDevtools };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
const DEFAULT_MAX_EVENTS = 100;
|
|
3
|
+
function sameRoute({ left, right }) {
|
|
4
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
5
|
+
}
|
|
6
|
+
function classifyEvent({ previous, next }) {
|
|
7
|
+
if (!previous) return "init";
|
|
8
|
+
if (next.error && next.error !== previous.error) return "error";
|
|
9
|
+
if (!sameRoute({
|
|
10
|
+
left: previous.route,
|
|
11
|
+
right: next.route
|
|
12
|
+
})) return "route";
|
|
13
|
+
if (!sameRoute({
|
|
14
|
+
left: previous.pendingRoute,
|
|
15
|
+
right: next.pendingRoute
|
|
16
|
+
})) return "pending";
|
|
17
|
+
if (previous.isNavigating !== next.isNavigating) return next.isNavigating ? "pending" : "idle";
|
|
18
|
+
if (previous.history.length !== next.history.length) return "history";
|
|
19
|
+
return "idle";
|
|
20
|
+
}
|
|
21
|
+
function createItenDevtools({ router, maxEvents = DEFAULT_MAX_EVENTS, now = Date.now }) {
|
|
22
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
23
|
+
const events = [];
|
|
24
|
+
let currentState = router.getState();
|
|
25
|
+
let previousState = null;
|
|
26
|
+
let nextId = 1;
|
|
27
|
+
let disposed = false;
|
|
28
|
+
function emit({ state }) {
|
|
29
|
+
if (disposed) return;
|
|
30
|
+
currentState = state;
|
|
31
|
+
const event = {
|
|
32
|
+
id: nextId,
|
|
33
|
+
type: classifyEvent({
|
|
34
|
+
previous: previousState,
|
|
35
|
+
next: state
|
|
36
|
+
}),
|
|
37
|
+
timestamp: now(),
|
|
38
|
+
route: state.route,
|
|
39
|
+
pendingRoute: state.pendingRoute,
|
|
40
|
+
error: state.error?.error ?? null,
|
|
41
|
+
canGoBack: state.canGoBack,
|
|
42
|
+
historyLength: state.history.length
|
|
43
|
+
};
|
|
44
|
+
nextId += 1;
|
|
45
|
+
previousState = state;
|
|
46
|
+
events.push(event);
|
|
47
|
+
if (events.length > maxEvents) events.splice(0, events.length - maxEvents);
|
|
48
|
+
notify();
|
|
49
|
+
}
|
|
50
|
+
function getSnapshot() {
|
|
51
|
+
return {
|
|
52
|
+
state: currentState,
|
|
53
|
+
events: [...events]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function notify() {
|
|
57
|
+
const snapshot = getSnapshot();
|
|
58
|
+
for (const listener of listeners) listener(snapshot);
|
|
59
|
+
}
|
|
60
|
+
const stopRouter = router.subscribe({ listener: (state) => {
|
|
61
|
+
emit({ state });
|
|
62
|
+
} });
|
|
63
|
+
emit({ state: currentState });
|
|
64
|
+
function subscribe({ listener }) {
|
|
65
|
+
listeners.add(listener);
|
|
66
|
+
return () => {
|
|
67
|
+
listeners.delete(listener);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function clear() {
|
|
71
|
+
events.splice(0, events.length);
|
|
72
|
+
notify();
|
|
73
|
+
}
|
|
74
|
+
function dispose() {
|
|
75
|
+
disposed = true;
|
|
76
|
+
stopRouter();
|
|
77
|
+
listeners.clear();
|
|
78
|
+
events.splice(0, events.length);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
getSnapshot,
|
|
82
|
+
subscribe,
|
|
83
|
+
clear,
|
|
84
|
+
dispose
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { createItenDevtools };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iten-devtools",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Devtools helpers for iten — inspect ultralight in-memory router state",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"devtools",
|
|
7
7
|
"router",
|
|
@@ -19,13 +19,43 @@
|
|
|
19
19
|
"url": "https://github.com/andrew-bierman/iten/issues"
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/andrew-bierman/iten#readme",
|
|
22
|
-
"
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"bun": "./src/index.ts",
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"default": "./dist/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"main": "./dist/index.cjs",
|
|
38
|
+
"module": "./dist/index.mjs",
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
23
40
|
"files": [
|
|
24
|
-
"
|
|
25
|
-
"
|
|
41
|
+
"dist",
|
|
42
|
+
"src"
|
|
26
43
|
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"typecheck:types": "bun run build && tsc -p tsconfig.type-tests.json",
|
|
48
|
+
"test": "bun test ./test"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"iten-core": "^0.5.0"
|
|
52
|
+
},
|
|
27
53
|
"publishConfig": {
|
|
28
54
|
"access": "public",
|
|
29
55
|
"registry": "https://registry.npmjs.org/"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"tsdown": "0.21.9",
|
|
59
|
+
"typescript": "^5.7.0"
|
|
30
60
|
}
|
|
31
61
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { RouteMapDef, RouterState, RouteUnion } from 'iten-core'
|
|
2
|
+
|
|
3
|
+
export type DevtoolsRouter<M extends RouteMapDef> = {
|
|
4
|
+
getState: () => RouterState<M>
|
|
5
|
+
subscribe: (input: { listener: (state: RouterState<M>) => void }) => () => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type DevtoolsEventType = 'init' | 'route' | 'pending' | 'error' | 'idle' | 'history'
|
|
9
|
+
|
|
10
|
+
export type DevtoolsEvent<M extends RouteMapDef> = {
|
|
11
|
+
id: number
|
|
12
|
+
type: DevtoolsEventType
|
|
13
|
+
timestamp: number
|
|
14
|
+
route: RouteUnion<M> | null
|
|
15
|
+
pendingRoute: RouteUnion<M> | null
|
|
16
|
+
error: unknown
|
|
17
|
+
canGoBack: boolean
|
|
18
|
+
historyLength: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type DevtoolsSnapshot<M extends RouteMapDef> = {
|
|
22
|
+
state: RouterState<M>
|
|
23
|
+
events: DevtoolsEvent<M>[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type CreateDevtoolsInput<M extends RouteMapDef> = {
|
|
27
|
+
router: DevtoolsRouter<M>
|
|
28
|
+
maxEvents?: number | undefined
|
|
29
|
+
now?: (() => number) | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ItenDevtools<M extends RouteMapDef> = {
|
|
33
|
+
getSnapshot: () => DevtoolsSnapshot<M>
|
|
34
|
+
subscribe: (input: { listener: (snapshot: DevtoolsSnapshot<M>) => void }) => () => void
|
|
35
|
+
clear: () => void
|
|
36
|
+
dispose: () => void
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_MAX_EVENTS = 100
|
|
40
|
+
|
|
41
|
+
function sameRoute<M extends RouteMapDef>({
|
|
42
|
+
left,
|
|
43
|
+
right,
|
|
44
|
+
}: {
|
|
45
|
+
left: RouteUnion<M> | null
|
|
46
|
+
right: RouteUnion<M> | null
|
|
47
|
+
}): boolean {
|
|
48
|
+
return JSON.stringify(left) === JSON.stringify(right)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function classifyEvent<M extends RouteMapDef>({
|
|
52
|
+
previous,
|
|
53
|
+
next,
|
|
54
|
+
}: {
|
|
55
|
+
previous: RouterState<M> | null
|
|
56
|
+
next: RouterState<M>
|
|
57
|
+
}): DevtoolsEventType {
|
|
58
|
+
if (!previous) return 'init'
|
|
59
|
+
if (next.error && next.error !== previous.error) return 'error'
|
|
60
|
+
if (!sameRoute({ left: previous.route, right: next.route })) return 'route'
|
|
61
|
+
if (!sameRoute({ left: previous.pendingRoute, right: next.pendingRoute })) return 'pending'
|
|
62
|
+
if (previous.isNavigating !== next.isNavigating) return next.isNavigating ? 'pending' : 'idle'
|
|
63
|
+
if (previous.history.length !== next.history.length) return 'history'
|
|
64
|
+
return 'idle'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createItenDevtools<M extends RouteMapDef>({
|
|
68
|
+
router,
|
|
69
|
+
maxEvents = DEFAULT_MAX_EVENTS,
|
|
70
|
+
now = Date.now,
|
|
71
|
+
}: CreateDevtoolsInput<M>): ItenDevtools<M> {
|
|
72
|
+
const listeners = new Set<(snapshot: DevtoolsSnapshot<M>) => void>()
|
|
73
|
+
const events: DevtoolsEvent<M>[] = []
|
|
74
|
+
let currentState = router.getState()
|
|
75
|
+
let previousState: RouterState<M> | null = null
|
|
76
|
+
let nextId = 1
|
|
77
|
+
let disposed = false
|
|
78
|
+
|
|
79
|
+
function emit({ state }: { state: RouterState<M> }): void {
|
|
80
|
+
if (disposed) return
|
|
81
|
+
currentState = state
|
|
82
|
+
const event: DevtoolsEvent<M> = {
|
|
83
|
+
id: nextId,
|
|
84
|
+
type: classifyEvent({ previous: previousState, next: state }),
|
|
85
|
+
timestamp: now(),
|
|
86
|
+
route: state.route,
|
|
87
|
+
pendingRoute: state.pendingRoute,
|
|
88
|
+
error: state.error?.error ?? null,
|
|
89
|
+
canGoBack: state.canGoBack,
|
|
90
|
+
historyLength: state.history.length,
|
|
91
|
+
}
|
|
92
|
+
nextId += 1
|
|
93
|
+
previousState = state
|
|
94
|
+
events.push(event)
|
|
95
|
+
if (events.length > maxEvents) {
|
|
96
|
+
events.splice(0, events.length - maxEvents)
|
|
97
|
+
}
|
|
98
|
+
notify()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getSnapshot(): DevtoolsSnapshot<M> {
|
|
102
|
+
return {
|
|
103
|
+
state: currentState,
|
|
104
|
+
events: [...events],
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function notify(): void {
|
|
109
|
+
const snapshot = getSnapshot()
|
|
110
|
+
for (const listener of listeners) {
|
|
111
|
+
listener(snapshot)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const stopRouter = router.subscribe({
|
|
116
|
+
listener: (state) => {
|
|
117
|
+
emit({ state })
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
emit({ state: currentState })
|
|
122
|
+
|
|
123
|
+
function subscribe({
|
|
124
|
+
listener,
|
|
125
|
+
}: {
|
|
126
|
+
listener: (snapshot: DevtoolsSnapshot<M>) => void
|
|
127
|
+
}): () => void {
|
|
128
|
+
listeners.add(listener)
|
|
129
|
+
return () => {
|
|
130
|
+
listeners.delete(listener)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function clear(): void {
|
|
135
|
+
events.splice(0, events.length)
|
|
136
|
+
notify()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function dispose(): void {
|
|
140
|
+
disposed = true
|
|
141
|
+
stopRouter()
|
|
142
|
+
listeners.clear()
|
|
143
|
+
events.splice(0, events.length)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
getSnapshot,
|
|
148
|
+
subscribe,
|
|
149
|
+
clear,
|
|
150
|
+
dispose,
|
|
151
|
+
}
|
|
152
|
+
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
throw new Error('iten-devtools is reserved for upcoming iten developer tools.')
|