@typed/navigation 0.18.0 → 0.18.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/.nvmrc +1 -0
- package/biome.json +39 -0
- package/dist/Blocking.d.ts +23 -0
- package/dist/Blocking.js +41 -0
- package/dist/Blocking.js.map +1 -0
- package/dist/Destination.d.ts +11 -0
- package/dist/Destination.js +10 -0
- package/dist/Destination.js.map +1 -0
- package/dist/Error.d.ts +33 -0
- package/dist/Error.js +22 -0
- package/dist/Error.js.map +1 -0
- package/dist/Event.d.ts +45 -0
- package/dist/Event.js +17 -0
- package/dist/Event.js.map +1 -0
- package/dist/Forms.d.ts +79 -0
- package/dist/Forms.js +111 -0
- package/dist/Forms.js.map +1 -0
- package/dist/Handler.d.ts +6 -0
- package/dist/Handler.js +2 -0
- package/dist/Handler.js.map +1 -0
- package/dist/{dts/Layer.d.ts → Layer.d.ts} +11 -8
- package/dist/{esm/Layer.js → Layer.js} +2 -2
- package/dist/Layer.js.map +1 -0
- package/dist/NavigateOptions.d.ts +7 -0
- package/dist/NavigateOptions.js +7 -0
- package/dist/NavigateOptions.js.map +1 -0
- package/dist/Navigation.d.ts +79 -0
- package/dist/Navigation.js +49 -0
- package/dist/Navigation.js.map +1 -0
- package/dist/NavigationType.d.ts +3 -0
- package/dist/NavigationType.js +3 -0
- package/dist/NavigationType.js.map +1 -0
- package/dist/ProposedDestination.d.ts +13 -0
- package/dist/ProposedDestination.js +4 -0
- package/dist/ProposedDestination.js.map +1 -0
- package/dist/Url.d.ts +13 -0
- package/dist/Url.js +72 -0
- package/dist/Url.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/fromWindow.d.ts +4 -0
- package/dist/{esm/internal → internal}/fromWindow.js +125 -136
- package/dist/internal/fromWindow.js.map +1 -0
- package/dist/internal/memory.d.ts +6 -0
- package/dist/internal/memory.js +59 -0
- package/dist/internal/memory.js.map +1 -0
- package/dist/{dts/internal → internal}/shared.d.ts +44 -46
- package/dist/{esm/internal → internal}/shared.js +121 -168
- package/dist/internal/shared.js.map +1 -0
- package/package.json +35 -53
- package/readme.md +243 -0
- package/src/Blocking.ts +65 -65
- package/src/Destination.ts +14 -0
- package/src/Error.ts +28 -0
- package/src/Event.ts +26 -0
- package/src/Forms.ts +216 -0
- package/src/Handler.ts +16 -0
- package/src/Layer.ts +20 -9
- package/src/NavigateOptions.ts +9 -0
- package/src/Navigation.test.ts +697 -0
- package/src/Navigation.ts +135 -472
- package/src/NavigationType.ts +5 -0
- package/src/ProposedDestination.ts +8 -0
- package/src/Url.ts +106 -0
- package/src/index.ts +12 -17
- package/src/internal/fromWindow.ts +163 -234
- package/src/internal/memory.ts +62 -49
- package/src/internal/shared.ts +218 -377
- package/tsconfig.json +30 -0
- package/Blocking/package.json +0 -6
- package/LICENSE +0 -21
- package/Layer/package.json +0 -6
- package/Navigation/package.json +0 -6
- package/README.md +0 -5
- package/dist/cjs/Blocking.js +0 -58
- package/dist/cjs/Blocking.js.map +0 -1
- package/dist/cjs/Layer.js +0 -27
- package/dist/cjs/Layer.js.map +0 -1
- package/dist/cjs/Navigation.js +0 -278
- package/dist/cjs/Navigation.js.map +0 -1
- package/dist/cjs/index.js +0 -39
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/internal/fromWindow.js +0 -436
- package/dist/cjs/internal/fromWindow.js.map +0 -1
- package/dist/cjs/internal/memory.js +0 -72
- package/dist/cjs/internal/memory.js.map +0 -1
- package/dist/cjs/internal/shared.js +0 -525
- package/dist/cjs/internal/shared.js.map +0 -1
- package/dist/dts/Blocking.d.ts +0 -34
- package/dist/dts/Blocking.d.ts.map +0 -1
- package/dist/dts/Layer.d.ts.map +0 -1
- package/dist/dts/Navigation.d.ts +0 -451
- package/dist/dts/Navigation.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -17
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/internal/fromWindow.d.ts +0 -12
- package/dist/dts/internal/fromWindow.d.ts.map +0 -1
- package/dist/dts/internal/memory.d.ts +0 -6
- package/dist/dts/internal/memory.d.ts.map +0 -1
- package/dist/dts/internal/shared.d.ts.map +0 -1
- package/dist/esm/Blocking.js +0 -46
- package/dist/esm/Blocking.js.map +0 -1
- package/dist/esm/Layer.js.map +0 -1
- package/dist/esm/Navigation.js +0 -238
- package/dist/esm/Navigation.js.map +0 -1
- package/dist/esm/index.js +0 -17
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal/fromWindow.js.map +0 -1
- package/dist/esm/internal/memory.js +0 -56
- package/dist/esm/internal/memory.js.map +0 -1
- package/dist/esm/internal/shared.js.map +0 -1
- package/dist/esm/package.json +0 -4
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as
|
|
3
|
-
import {
|
|
4
|
-
import * as
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import * as
|
|
8
|
-
import
|
|
9
|
-
import * as Option from
|
|
10
|
-
import * as Runtime from
|
|
11
|
-
import * as Scope from
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import type
|
|
16
|
-
import
|
|
17
|
-
import type {
|
|
18
|
-
import { Navigation, NavigationError } from "../Navigation.js"
|
|
19
|
-
import type { ModelAndIntent, PatchedState } from "./shared.js"
|
|
1
|
+
import { GetRandomValues, Uuid4 } from '@typed/id'
|
|
2
|
+
import * as LazyRef from '@typed/lazy-ref'
|
|
3
|
+
import { Schema } from 'effect'
|
|
4
|
+
import * as Context from 'effect/Context'
|
|
5
|
+
import * as Effect from 'effect/Effect'
|
|
6
|
+
import * as Exit from 'effect/Exit'
|
|
7
|
+
import type * as Fiber from 'effect/Fiber'
|
|
8
|
+
import * as Layer from 'effect/Layer'
|
|
9
|
+
import * as Option from 'effect/Option'
|
|
10
|
+
import * as Runtime from 'effect/Runtime'
|
|
11
|
+
import * as Scope from 'effect/Scope'
|
|
12
|
+
import type { Destination } from '../Destination.js'
|
|
13
|
+
import { NavigationError } from '../Error.js'
|
|
14
|
+
import type { NavigationEvent, TransitionEvent } from '../Event.js'
|
|
15
|
+
import type { Commit } from '../Layer.js'
|
|
16
|
+
import { Navigation } from '../Navigation.js'
|
|
17
|
+
import type { ModelAndIntent, PatchedState } from './shared.js'
|
|
20
18
|
import {
|
|
21
19
|
getOriginalState,
|
|
22
20
|
getUrl,
|
|
@@ -24,169 +22,130 @@ import {
|
|
|
24
22
|
makeDestination,
|
|
25
23
|
makeHandlersState,
|
|
26
24
|
NavigationState,
|
|
27
|
-
setupFromModelAndIntent
|
|
28
|
-
} from
|
|
29
|
-
|
|
30
|
-
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
|
31
|
-
type NativeNavigation = import("@virtualstate/navigation").Navigation
|
|
32
|
-
type NativeEntry = import("@virtualstate/navigation").NavigationHistoryEntry
|
|
33
|
-
type NativeEvent = import("@virtualstate/navigation").NavigationEventMap["navigate"]
|
|
34
|
-
/* eslint-enable @typescript-eslint/consistent-type-imports */
|
|
35
|
-
|
|
36
|
-
declare global {
|
|
37
|
-
export interface Window {
|
|
38
|
-
navigation?: NativeNavigation
|
|
39
|
-
}
|
|
40
|
-
}
|
|
25
|
+
setupFromModelAndIntent,
|
|
26
|
+
} from './shared.js'
|
|
41
27
|
|
|
42
|
-
export const fromWindow: Layer.Layer<Navigation, never,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
export const fromWindow: (window: Window) => Layer.Layer<Navigation, never, GetRandomValues> = (
|
|
29
|
+
window: Window,
|
|
30
|
+
) =>
|
|
31
|
+
Layer.scoped(
|
|
32
|
+
Navigation,
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
const getRandomValues = yield* GetRandomValues
|
|
46
35
|
const { run, runPromise } = yield* scopedRuntime<never>()
|
|
47
36
|
const hasNativeNavigation = !!window.navigation
|
|
37
|
+
const base = getBaseHref(window)
|
|
48
38
|
const modelAndIntent = yield* hasNativeNavigation
|
|
49
|
-
? setupWithNavigation(window.navigation
|
|
50
|
-
: setupWithHistory(window, (event) => run(handleHistoryEvent(event)))
|
|
39
|
+
? setupWithNavigation(window.navigation, runPromise)
|
|
40
|
+
: setupWithHistory(window, base, (event) => run(handleHistoryEvent(event)))
|
|
51
41
|
|
|
52
42
|
const navigation = setupFromModelAndIntent(
|
|
53
43
|
modelAndIntent,
|
|
54
44
|
window.location.origin,
|
|
55
|
-
|
|
45
|
+
base,
|
|
56
46
|
getRandomValues,
|
|
57
|
-
hasNativeNavigation
|
|
58
|
-
? () => getNavigationState(window.navigation!)
|
|
59
|
-
: undefined
|
|
47
|
+
hasNativeNavigation ? () => getNavigationState(window.navigation) : undefined,
|
|
60
48
|
)
|
|
61
49
|
|
|
62
50
|
return navigation
|
|
63
51
|
|
|
64
52
|
function handleHistoryEvent(event: HistoryEvent) {
|
|
65
|
-
return Effect.gen(function*() {
|
|
66
|
-
if (event._tag ===
|
|
67
|
-
return yield* navigation.navigate(
|
|
68
|
-
|
|
69
|
-
{},
|
|
70
|
-
event.skipCommit
|
|
71
|
-
)
|
|
72
|
-
} else if (event._tag === "ReplaceState") {
|
|
53
|
+
return Effect.gen(function* () {
|
|
54
|
+
if (event._tag === 'PushState') {
|
|
55
|
+
return yield* navigation.navigate(event.url, {}, event.skipCommit)
|
|
56
|
+
} else if (event._tag === 'ReplaceState') {
|
|
73
57
|
if (Option.isSome(event.url)) {
|
|
74
58
|
return yield* navigation.navigate(
|
|
75
59
|
event.url.value,
|
|
76
|
-
{ history:
|
|
77
|
-
event.skipCommit
|
|
60
|
+
{ history: 'replace', state: event.state },
|
|
61
|
+
event.skipCommit,
|
|
78
62
|
)
|
|
79
63
|
} else {
|
|
80
64
|
return yield* navigation.updateCurrentEntry(event)
|
|
81
65
|
}
|
|
82
|
-
} else if (event._tag ===
|
|
66
|
+
} else if (event._tag === 'Traverse') {
|
|
83
67
|
const { entries, index } = yield* modelAndIntent.state
|
|
84
|
-
const toIndex = Math.min(
|
|
85
|
-
Math.max(0, index + event.delta),
|
|
86
|
-
entries.length - 1
|
|
87
|
-
)
|
|
68
|
+
const toIndex = Math.min(Math.max(0, index + event.delta), entries.length - 1)
|
|
88
69
|
const to = entries[toIndex]
|
|
89
70
|
|
|
90
|
-
const result = yield* navigation.traverseTo(
|
|
91
|
-
to.key,
|
|
92
|
-
{},
|
|
93
|
-
event.skipCommit
|
|
94
|
-
)
|
|
71
|
+
const result = yield* navigation.traverseTo(to.key, {}, event.skipCommit)
|
|
95
72
|
|
|
96
73
|
return result
|
|
97
74
|
} else {
|
|
98
75
|
yield* navigation.traverseTo(event.key, {}, event.skipCommit)
|
|
99
76
|
return yield* navigation.updateCurrentEntry({
|
|
100
|
-
state: event.state
|
|
77
|
+
state: event.state,
|
|
101
78
|
})
|
|
102
79
|
}
|
|
103
80
|
})
|
|
104
81
|
}
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
)
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
108
84
|
|
|
109
85
|
function getBaseHref(window: Window) {
|
|
110
|
-
const base = window.document.querySelector(
|
|
111
|
-
return base ? base.href :
|
|
86
|
+
const base = window.document.querySelector('base')
|
|
87
|
+
return base ? base.href : '/'
|
|
112
88
|
}
|
|
113
89
|
|
|
114
|
-
const getNavigationState = (navigation:
|
|
90
|
+
const getNavigationState = (navigation: globalThis.Navigation): NavigationState => {
|
|
115
91
|
const entries = navigation.entries().map(nativeEntryToDestination)
|
|
116
|
-
|
|
92
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
93
|
+
const { index } = navigation.currentEntry!
|
|
117
94
|
|
|
118
95
|
return {
|
|
119
96
|
entries,
|
|
120
97
|
index,
|
|
121
|
-
transition: Option.none<
|
|
98
|
+
transition: Option.none<TransitionEvent>(),
|
|
122
99
|
}
|
|
123
100
|
}
|
|
124
101
|
|
|
125
102
|
function setupWithNavigation(
|
|
126
|
-
navigation:
|
|
127
|
-
runPromise: <E, A>(effect: Effect.Effect<A, E, Scope.Scope>) => Promise<A
|
|
103
|
+
navigation: globalThis.Navigation,
|
|
104
|
+
runPromise: <E, A>(effect: Effect.Effect<A, E, Scope.Scope>) => Promise<A>,
|
|
128
105
|
): Effect.Effect<ModelAndIntent, never, Scope.Scope | GetRandomValues> {
|
|
129
|
-
return Effect.gen(function*() {
|
|
130
|
-
const state = yield*
|
|
106
|
+
return Effect.gen(function* () {
|
|
107
|
+
const state = yield* LazyRef.fromEffect(
|
|
131
108
|
Effect.sync((): NavigationState => getNavigationState(navigation)),
|
|
132
109
|
{
|
|
133
|
-
eq:
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
}
|
|
110
|
+
eq: Schema.equivalence(Schema.typeSchema(NavigationState)),
|
|
111
|
+
},
|
|
137
112
|
)
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
(s) => s.index < s.entries.length - 1
|
|
142
|
-
)
|
|
143
|
-
const { beforeHandlers, formDataHandlers, handlers } = yield* makeHandlersState()
|
|
144
|
-
const commit: Commit = (to: Destination, event: BeforeNavigationEvent) =>
|
|
145
|
-
Effect.gen(function*(_) {
|
|
113
|
+
const { beforeHandlers, handlers } = yield* makeHandlersState
|
|
114
|
+
const commit: Commit = (to: Destination, event: TransitionEvent) =>
|
|
115
|
+
Effect.gen(function* () {
|
|
146
116
|
const { key, state, url } = to
|
|
147
117
|
const { info, type } = event
|
|
148
118
|
|
|
149
|
-
if (type ===
|
|
150
|
-
yield*
|
|
151
|
-
|
|
152
|
-
()
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}).committed
|
|
158
|
-
),
|
|
159
|
-
Effect.catchAllDefect((error) => Effect.fail(new NavigationError({ error })))
|
|
160
|
-
)
|
|
161
|
-
} else if (event.type === "reload") {
|
|
162
|
-
yield* _(
|
|
163
|
-
Effect.promise(() => navigation.reload({ state, info }).committed),
|
|
164
|
-
Effect.catchAllDefect((error) => Effect.fail(new NavigationError({ error })))
|
|
119
|
+
if (type === 'push' || type === 'replace') {
|
|
120
|
+
yield* Effect.promise(
|
|
121
|
+
() =>
|
|
122
|
+
navigation.navigate(url.toString(), {
|
|
123
|
+
history: type,
|
|
124
|
+
state,
|
|
125
|
+
info,
|
|
126
|
+
}).committed,
|
|
165
127
|
)
|
|
128
|
+
} else if (event.type === 'reload') {
|
|
129
|
+
yield* Effect.promise(() => navigation.reload({ state, info }).committed)
|
|
166
130
|
} else {
|
|
167
|
-
yield*
|
|
168
|
-
Effect.promise(
|
|
169
|
-
() => navigation.traverseTo(key, { info }).committed
|
|
170
|
-
),
|
|
171
|
-
Effect.catchAllDefect((error) => Effect.fail(new NavigationError({ error })))
|
|
172
|
-
)
|
|
131
|
+
yield* Effect.promise(() => navigation.traverseTo(key, { info }).committed)
|
|
173
132
|
}
|
|
174
|
-
})
|
|
133
|
+
}).pipe(Effect.catchAllDefect((cause) => new NavigationError({ cause })))
|
|
175
134
|
|
|
176
|
-
const runHandlers = (native:
|
|
177
|
-
Effect.gen(function*() {
|
|
135
|
+
const runHandlers = (native: globalThis.NavigationEventMap['navigate']) =>
|
|
136
|
+
Effect.gen(function* () {
|
|
178
137
|
const eventHandlers = yield* handlers
|
|
179
138
|
const matches: Array<Effect.Effect<unknown>> = []
|
|
180
|
-
|
|
181
139
|
const event: NavigationEvent = {
|
|
182
140
|
type: native.navigationType,
|
|
183
|
-
|
|
184
|
-
|
|
141
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
142
|
+
destination: nativeEntryToDestination(navigation.currentEntry!),
|
|
143
|
+
info: native.info,
|
|
185
144
|
}
|
|
186
145
|
|
|
187
146
|
for (const [handler, ctx] of eventHandlers) {
|
|
188
147
|
const match = yield* Effect.provide(handler(event), ctx)
|
|
189
|
-
if (Option.isSome(match)) {
|
|
148
|
+
if (match !== undefined && Option.isSome(match)) {
|
|
190
149
|
matches.push(Effect.provide(match.value, ctx))
|
|
191
150
|
}
|
|
192
151
|
}
|
|
@@ -196,39 +155,40 @@ function setupWithNavigation(
|
|
|
196
155
|
}
|
|
197
156
|
})
|
|
198
157
|
|
|
199
|
-
navigation.addEventListener(
|
|
158
|
+
navigation.addEventListener('navigate', (ev) => {
|
|
200
159
|
if (shouldNotIntercept(ev)) return
|
|
201
160
|
|
|
202
161
|
ev.intercept({
|
|
203
|
-
handler: () => runPromise(runHandlers(ev))
|
|
162
|
+
handler: () => runPromise(runHandlers(ev)),
|
|
204
163
|
})
|
|
205
164
|
})
|
|
206
165
|
|
|
207
166
|
return {
|
|
208
167
|
state,
|
|
209
|
-
canGoBack,
|
|
210
|
-
canGoForward,
|
|
211
168
|
beforeHandlers,
|
|
212
169
|
handlers,
|
|
213
|
-
|
|
214
|
-
commit
|
|
170
|
+
commit,
|
|
215
171
|
} as const
|
|
216
172
|
})
|
|
217
173
|
}
|
|
218
174
|
|
|
219
175
|
function nativeEntryToDestination(
|
|
220
|
-
entry: Pick<
|
|
176
|
+
entry: Pick<
|
|
177
|
+
globalThis.NavigationHistoryEntry,
|
|
178
|
+
'id' | 'key' | 'url' | 'getState' | 'sameDocument'
|
|
179
|
+
>,
|
|
221
180
|
): Destination {
|
|
222
181
|
return {
|
|
223
|
-
id:
|
|
224
|
-
key:
|
|
182
|
+
id: Uuid4.make(entry.id),
|
|
183
|
+
key: Uuid4.make(entry.key),
|
|
184
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
225
185
|
url: new URL(entry.url!),
|
|
226
186
|
state: entry.getState(),
|
|
227
|
-
sameDocument: entry.sameDocument
|
|
187
|
+
sameDocument: entry.sameDocument,
|
|
228
188
|
}
|
|
229
189
|
}
|
|
230
190
|
|
|
231
|
-
function shouldNotIntercept(navigationEvent:
|
|
191
|
+
function shouldNotIntercept(navigationEvent: globalThis.NavigationEventMap['navigate']): boolean {
|
|
232
192
|
return (
|
|
233
193
|
!navigationEvent.canIntercept ||
|
|
234
194
|
// If this is just a hashChange,
|
|
@@ -245,69 +205,54 @@ function shouldNotIntercept(navigationEvent: NativeEvent): boolean {
|
|
|
245
205
|
|
|
246
206
|
function setupWithHistory(
|
|
247
207
|
window: Window,
|
|
248
|
-
|
|
208
|
+
base: string,
|
|
209
|
+
onEvent: (event: HistoryEvent) => void,
|
|
249
210
|
): Effect.Effect<ModelAndIntent, never, GetRandomValues | Scope.Scope> {
|
|
250
|
-
return Effect.gen(function*() {
|
|
211
|
+
return Effect.gen(function* () {
|
|
251
212
|
const { location } = window
|
|
252
|
-
const {
|
|
253
|
-
getHistoryState,
|
|
254
|
-
original: history,
|
|
255
|
-
unpatch
|
|
256
|
-
} = patchHistory(window, onEvent)
|
|
213
|
+
const { getHistoryState, original: history, unpatch } = patchHistory(window, onEvent, base)
|
|
257
214
|
|
|
258
215
|
yield* Effect.addFinalizer(() => unpatch)
|
|
259
216
|
|
|
260
|
-
const state = yield*
|
|
217
|
+
const state = yield* LazyRef.fromEffect(
|
|
261
218
|
Effect.suspend(() =>
|
|
262
219
|
Effect.map(
|
|
263
|
-
makeDestination(
|
|
264
|
-
new URL(location.href),
|
|
265
|
-
getHistoryState(),
|
|
266
|
-
location.origin
|
|
267
|
-
),
|
|
220
|
+
makeDestination(new URL(location.href), getHistoryState(), location.origin),
|
|
268
221
|
(destination): NavigationState => ({
|
|
269
222
|
entries: [destination],
|
|
270
223
|
index: 0,
|
|
271
|
-
transition: Option.none()
|
|
272
|
-
})
|
|
273
|
-
)
|
|
224
|
+
transition: Option.none(),
|
|
225
|
+
}),
|
|
226
|
+
),
|
|
274
227
|
),
|
|
275
|
-
{ eq:
|
|
276
|
-
)
|
|
277
|
-
const canGoBack = RefSubject.map(state, (s) => s.index > 0)
|
|
278
|
-
const canGoForward = RefSubject.map(
|
|
279
|
-
state,
|
|
280
|
-
(s) => s.index < s.entries.length - 1
|
|
228
|
+
{ eq: Schema.equivalence(Schema.typeSchema(NavigationState)) },
|
|
281
229
|
)
|
|
282
|
-
const { beforeHandlers,
|
|
283
|
-
const commit: Commit = (
|
|
284
|
-
{ id, key, state, url }: Destination,
|
|
285
|
-
event: BeforeNavigationEvent
|
|
286
|
-
) =>
|
|
230
|
+
const { beforeHandlers, handlers } = yield* makeHandlersState
|
|
231
|
+
const commit: Commit = ({ id, key, state, url }: Destination, event: TransitionEvent) =>
|
|
287
232
|
Effect.sync(() => {
|
|
288
233
|
const { type } = event
|
|
289
234
|
|
|
290
|
-
if (type ===
|
|
235
|
+
if (type === 'push') {
|
|
291
236
|
history.pushState(
|
|
292
237
|
{
|
|
293
238
|
__typed__navigation__id__: id,
|
|
294
239
|
__typed__navigation__key__: key,
|
|
295
|
-
__typed__navigation__state__: state
|
|
240
|
+
__typed__navigation__state__: state,
|
|
296
241
|
},
|
|
297
|
-
|
|
298
|
-
url
|
|
242
|
+
'',
|
|
243
|
+
url,
|
|
299
244
|
)
|
|
300
|
-
} else if (type ===
|
|
245
|
+
} else if (type === 'replace') {
|
|
301
246
|
history.replaceState(
|
|
302
247
|
{
|
|
303
248
|
__typed__navigation__id__: id,
|
|
304
249
|
__typed__navigation__key__: key,
|
|
305
|
-
__typed__navigation__state__: state
|
|
250
|
+
__typed__navigation__state__: state,
|
|
306
251
|
},
|
|
307
|
-
|
|
308
|
-
url
|
|
252
|
+
'',
|
|
253
|
+
url,
|
|
309
254
|
)
|
|
310
|
-
} else if (event.type ===
|
|
255
|
+
} else if (event.type === 'reload') {
|
|
311
256
|
location.reload()
|
|
312
257
|
} else {
|
|
313
258
|
history.go(event.delta)
|
|
@@ -316,63 +261,57 @@ function setupWithHistory(
|
|
|
316
261
|
{
|
|
317
262
|
__typed__navigation__id__: id,
|
|
318
263
|
__typed__navigation__key__: key,
|
|
319
|
-
__typed__navigation__state__: state
|
|
264
|
+
__typed__navigation__state__: state,
|
|
320
265
|
},
|
|
321
|
-
|
|
322
|
-
window.location.href
|
|
266
|
+
'',
|
|
267
|
+
window.location.href,
|
|
323
268
|
)
|
|
324
269
|
}
|
|
325
270
|
})
|
|
326
271
|
|
|
327
272
|
return {
|
|
328
273
|
state,
|
|
329
|
-
canGoBack,
|
|
330
|
-
canGoForward,
|
|
331
274
|
beforeHandlers,
|
|
332
275
|
handlers,
|
|
333
|
-
|
|
334
|
-
commit
|
|
276
|
+
commit,
|
|
335
277
|
} satisfies ModelAndIntent
|
|
336
278
|
})
|
|
337
279
|
}
|
|
338
280
|
|
|
339
|
-
type HistoryEvent =
|
|
340
|
-
| PushStateEvent
|
|
341
|
-
| ReplaceStateEvent
|
|
342
|
-
| TraverseEvent
|
|
343
|
-
| TraverseToEvent
|
|
281
|
+
type HistoryEvent = PushStateEvent | ReplaceStateEvent | TraverseEvent | TraverseToEvent
|
|
344
282
|
|
|
345
283
|
type PushStateEvent = {
|
|
346
|
-
_tag:
|
|
284
|
+
_tag: 'PushState'
|
|
347
285
|
state: unknown
|
|
348
286
|
url: URL
|
|
349
287
|
skipCommit: boolean
|
|
350
288
|
}
|
|
351
289
|
type ReplaceStateEvent = {
|
|
352
|
-
_tag:
|
|
290
|
+
_tag: 'ReplaceState'
|
|
353
291
|
state: unknown
|
|
354
292
|
url: Option.Option<URL>
|
|
355
293
|
skipCommit: boolean
|
|
356
294
|
}
|
|
357
|
-
type TraverseEvent = { _tag:
|
|
295
|
+
type TraverseEvent = { _tag: 'Traverse'; delta: number; skipCommit: boolean }
|
|
358
296
|
type TraverseToEvent = {
|
|
359
|
-
_tag:
|
|
360
|
-
key:
|
|
297
|
+
_tag: 'TraverseTo'
|
|
298
|
+
key: Uuid4
|
|
361
299
|
state: unknown
|
|
362
300
|
skipCommit: boolean
|
|
363
301
|
}
|
|
364
302
|
|
|
365
|
-
function patchHistory(window: Window, onEvent: (event: HistoryEvent) => void) {
|
|
303
|
+
function patchHistory(window: Window, onEvent: (event: HistoryEvent) => void, base: string) {
|
|
366
304
|
const { history, location } = window
|
|
367
|
-
const stateDescriptor =
|
|
368
|
-
Object.getOwnPropertyDescriptor(history,
|
|
305
|
+
const stateDescriptor =
|
|
306
|
+
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(history), 'state') ||
|
|
307
|
+
Object.getOwnPropertyDescriptor(history, 'state')
|
|
369
308
|
|
|
370
309
|
const methods = {
|
|
371
310
|
pushState: history.pushState.bind(history),
|
|
372
311
|
replaceState: history.replaceState.bind(history),
|
|
373
312
|
go: history.go.bind(history),
|
|
374
313
|
back: history.back.bind(history),
|
|
375
|
-
forward: history.forward.bind(history)
|
|
314
|
+
forward: history.forward.bind(history),
|
|
376
315
|
}
|
|
377
316
|
const getStateDescriptor = stateDescriptor?.get?.bind(history)
|
|
378
317
|
|
|
@@ -397,76 +336,76 @@ function patchHistory(window: Window, onEvent: (event: HistoryEvent) => void) {
|
|
|
397
336
|
},
|
|
398
337
|
replaceState(data, _, url) {
|
|
399
338
|
return methods.replaceState(data, _, url?.toString())
|
|
400
|
-
}
|
|
339
|
+
},
|
|
401
340
|
}
|
|
402
341
|
|
|
403
342
|
history.pushState = (state, _, url) => {
|
|
404
343
|
if (url) {
|
|
405
344
|
onEvent({
|
|
406
|
-
_tag:
|
|
345
|
+
_tag: 'PushState',
|
|
407
346
|
state,
|
|
408
|
-
url: getUrl(location.origin, url),
|
|
409
|
-
skipCommit: false
|
|
347
|
+
url: getUrl(location.origin, url, base),
|
|
348
|
+
skipCommit: false,
|
|
410
349
|
})
|
|
411
350
|
} else {
|
|
412
351
|
onEvent({
|
|
413
|
-
_tag:
|
|
352
|
+
_tag: 'ReplaceState',
|
|
414
353
|
state,
|
|
415
354
|
url: Option.none(),
|
|
416
|
-
skipCommit: false
|
|
355
|
+
skipCommit: false,
|
|
417
356
|
})
|
|
418
357
|
}
|
|
419
358
|
}
|
|
420
359
|
history.replaceState = (state, _, url) => {
|
|
421
360
|
onEvent({
|
|
422
|
-
_tag:
|
|
361
|
+
_tag: 'ReplaceState',
|
|
423
362
|
state,
|
|
424
|
-
url: url ? Option.some(getUrl(location.origin, url)) : Option.none(),
|
|
425
|
-
skipCommit: false
|
|
363
|
+
url: url ? Option.some(getUrl(location.origin, url, base)) : Option.none(),
|
|
364
|
+
skipCommit: false,
|
|
426
365
|
})
|
|
427
366
|
}
|
|
428
367
|
history.go = (delta) => {
|
|
429
368
|
if (delta && delta !== 0) {
|
|
430
|
-
onEvent({ _tag:
|
|
369
|
+
onEvent({ _tag: 'Traverse', delta, skipCommit: false })
|
|
431
370
|
}
|
|
432
371
|
}
|
|
433
372
|
history.back = () => {
|
|
434
|
-
onEvent({ _tag:
|
|
373
|
+
onEvent({ _tag: 'Traverse', delta: -1, skipCommit: false })
|
|
435
374
|
}
|
|
436
375
|
history.forward = () => {
|
|
437
|
-
onEvent({ _tag:
|
|
376
|
+
onEvent({ _tag: 'Traverse', delta: 1, skipCommit: false })
|
|
438
377
|
}
|
|
439
378
|
|
|
440
379
|
const onHashChange = (ev: HashChangeEvent) => {
|
|
441
380
|
onEvent({
|
|
442
|
-
_tag:
|
|
381
|
+
_tag: 'ReplaceState',
|
|
443
382
|
state: history.state,
|
|
444
383
|
url: Option.some(new URL(ev.newURL)),
|
|
445
|
-
skipCommit: false
|
|
384
|
+
skipCommit: false,
|
|
446
385
|
})
|
|
447
386
|
}
|
|
448
387
|
|
|
449
|
-
window.addEventListener(
|
|
388
|
+
window.addEventListener('hashchange', onHashChange, { capture: true })
|
|
450
389
|
|
|
451
390
|
const onPopState = (ev: PopStateEvent) => {
|
|
452
391
|
if (isPatchedState(ev.state)) {
|
|
453
392
|
onEvent({
|
|
454
|
-
_tag:
|
|
393
|
+
_tag: 'TraverseTo',
|
|
455
394
|
key: ev.state.__typed__navigation__key__,
|
|
456
395
|
state: ev.state.__typed__navigation__state__,
|
|
457
|
-
skipCommit: true
|
|
396
|
+
skipCommit: true,
|
|
458
397
|
})
|
|
459
398
|
} else {
|
|
460
399
|
onEvent({
|
|
461
|
-
_tag:
|
|
400
|
+
_tag: 'ReplaceState',
|
|
462
401
|
state: ev.state,
|
|
463
402
|
url: Option.some(new URL(location.href)),
|
|
464
|
-
skipCommit: true
|
|
403
|
+
skipCommit: true,
|
|
465
404
|
})
|
|
466
405
|
}
|
|
467
406
|
}
|
|
468
407
|
|
|
469
|
-
window.addEventListener(
|
|
408
|
+
window.addEventListener('popstate', onPopState, { capture: true })
|
|
470
409
|
|
|
471
410
|
const unpatch = Effect.sync(() => {
|
|
472
411
|
history.pushState = original.pushState
|
|
@@ -477,28 +416,28 @@ function patchHistory(window: Window, onEvent: (event: HistoryEvent) => void) {
|
|
|
477
416
|
|
|
478
417
|
if (stateDescriptor) {
|
|
479
418
|
try {
|
|
480
|
-
Object.defineProperty(history,
|
|
419
|
+
Object.defineProperty(history, 'state', stateDescriptor)
|
|
481
420
|
} catch {
|
|
482
421
|
// We tried, but it didn't work
|
|
483
422
|
}
|
|
484
423
|
}
|
|
485
424
|
|
|
486
|
-
window.removeEventListener(
|
|
487
|
-
window.removeEventListener(
|
|
425
|
+
window.removeEventListener('hashchange', onHashChange)
|
|
426
|
+
window.removeEventListener('popstate', onPopState)
|
|
488
427
|
})
|
|
489
428
|
|
|
490
|
-
Object.defineProperty(history,
|
|
429
|
+
Object.defineProperty(history, 'state', {
|
|
491
430
|
get() {
|
|
492
|
-
console.log("here")
|
|
493
431
|
return getOriginalState(getStateDescriptor?.() ?? history.state)
|
|
494
432
|
},
|
|
495
433
|
set(value) {
|
|
496
|
-
const { __typed__navigation__id__, __typed__navigation__key__ } =
|
|
434
|
+
const { __typed__navigation__id__, __typed__navigation__key__ } =
|
|
435
|
+
getStateDescriptor?.() ?? original.state
|
|
497
436
|
|
|
498
437
|
if (isPatchedState(value)) {
|
|
499
438
|
// The setter is not actually modifying the history.state
|
|
500
439
|
// We need to call the original replaceState to update the actual state
|
|
501
|
-
original.replaceState.call(history, value,
|
|
440
|
+
original.replaceState.call(history, value, '', location.href)
|
|
502
441
|
} else {
|
|
503
442
|
// The setter is not actually modifying the history.state
|
|
504
443
|
// We need to call the original replaceState to update the actual state
|
|
@@ -507,54 +446,44 @@ function patchHistory(window: Window, onEvent: (event: HistoryEvent) => void) {
|
|
|
507
446
|
{
|
|
508
447
|
__typed__navigation__id__,
|
|
509
448
|
__typed__navigation__key__,
|
|
510
|
-
__typed__navigation__state__: value
|
|
449
|
+
__typed__navigation__state__: value,
|
|
511
450
|
} satisfies PatchedState,
|
|
512
|
-
|
|
513
|
-
location.href
|
|
451
|
+
'',
|
|
452
|
+
location.href,
|
|
514
453
|
)
|
|
515
454
|
}
|
|
516
455
|
|
|
517
456
|
return value
|
|
518
|
-
}
|
|
457
|
+
},
|
|
519
458
|
})
|
|
520
459
|
|
|
521
460
|
return {
|
|
522
461
|
getHistoryState,
|
|
523
462
|
original,
|
|
524
463
|
patched: history,
|
|
525
|
-
unpatch
|
|
464
|
+
unpatch,
|
|
526
465
|
} as const
|
|
527
466
|
}
|
|
528
467
|
|
|
529
468
|
type ScopedRuntime<R> = {
|
|
530
469
|
readonly runtime: Runtime.Runtime<R | Scope.Scope>
|
|
531
470
|
readonly scope: Scope.Scope
|
|
532
|
-
readonly run: <E, A>(
|
|
533
|
-
|
|
534
|
-
) => Fiber.RuntimeFiber<A, E>
|
|
535
|
-
readonly runPromise: <E, A>(
|
|
536
|
-
effect: Effect.Effect<A, E, R | Scope.Scope>
|
|
537
|
-
) => Promise<A>
|
|
471
|
+
readonly run: <E, A>(effect: Effect.Effect<A, E, R | Scope.Scope>) => Fiber.RuntimeFiber<A, E>
|
|
472
|
+
readonly runPromise: <E, A>(effect: Effect.Effect<A, E, R | Scope.Scope>) => Promise<A>
|
|
538
473
|
}
|
|
539
474
|
|
|
540
|
-
function scopedRuntime<R>(): Effect.Effect<
|
|
541
|
-
ScopedRuntime<R>,
|
|
542
|
-
never,
|
|
543
|
-
R | Scope.Scope
|
|
544
|
-
> {
|
|
475
|
+
function scopedRuntime<R>(): Effect.Effect<ScopedRuntime<R>, never, R | Scope.Scope> {
|
|
545
476
|
return Effect.map(Effect.runtime<R | Scope.Scope>(), (runtime) => {
|
|
546
477
|
const scope = Context.get(runtime.context, Scope.Scope)
|
|
547
478
|
const runFork = Runtime.runFork(runtime)
|
|
548
|
-
const runPromise = <E, A>(
|
|
549
|
-
effect: Effect.Effect<A, E, R | Scope.Scope>
|
|
550
|
-
): Promise<A> =>
|
|
479
|
+
const runPromise = <E, A>(effect: Effect.Effect<A, E, R | Scope.Scope>): Promise<A> =>
|
|
551
480
|
new Promise((resolve, reject) => {
|
|
552
481
|
const fiber = runFork(effect, { scope })
|
|
553
482
|
fiber.addObserver(
|
|
554
483
|
Exit.match({
|
|
555
484
|
onFailure: (cause) => reject(Runtime.makeFiberFailure(cause)),
|
|
556
|
-
onSuccess: resolve
|
|
557
|
-
})
|
|
485
|
+
onSuccess: resolve,
|
|
486
|
+
}),
|
|
558
487
|
)
|
|
559
488
|
})
|
|
560
489
|
|
|
@@ -562,7 +491,7 @@ function scopedRuntime<R>(): Effect.Effect<
|
|
|
562
491
|
runtime,
|
|
563
492
|
scope: Context.unsafeGet(runtime.context, Scope.Scope),
|
|
564
493
|
run: (eff) => runFork(eff, { scope }),
|
|
565
|
-
runPromise
|
|
494
|
+
runPromise,
|
|
566
495
|
} as const
|
|
567
496
|
})
|
|
568
497
|
}
|