@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
package/src/internal/shared.ts
CHANGED
|
@@ -1,62 +1,84 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import * as
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
NavigationHandler,
|
|
26
|
-
ProposedDestination,
|
|
27
|
-
RedirectError
|
|
28
|
-
} from "../Navigation.js"
|
|
29
|
-
import { Destination, Transition } from "../Navigation.js"
|
|
1
|
+
import * as Headers from '@effect/platform/Headers'
|
|
2
|
+
import * as HttpClient from '@effect/platform/HttpClient'
|
|
3
|
+
import { GetRandomValues, makeUuid4, type Uuid4 } from '@typed/id'
|
|
4
|
+
import * as LazyRef from '@typed/lazy-ref'
|
|
5
|
+
import { Cause, Schema } from 'effect'
|
|
6
|
+
import type * as Context from 'effect/Context'
|
|
7
|
+
import * as Effect from 'effect/Effect'
|
|
8
|
+
import * as Either from 'effect/Either'
|
|
9
|
+
import * as Option from 'effect/Option'
|
|
10
|
+
import type * as Scope from 'effect/Scope'
|
|
11
|
+
import { Destination } from '../Destination.js'
|
|
12
|
+
import {
|
|
13
|
+
FormSubmitError,
|
|
14
|
+
type CancelNavigation,
|
|
15
|
+
type NavigationError,
|
|
16
|
+
type RedirectError,
|
|
17
|
+
} from '../Error.js'
|
|
18
|
+
import { TransitionEvent, type NavigationEvent } from '../Event.js'
|
|
19
|
+
import type { FormSubmit } from '../Forms.js'
|
|
20
|
+
import type { BeforeNavigationHandler, NavigationHandler } from '../Handler.js'
|
|
21
|
+
import type { Commit } from '../Layer.js'
|
|
22
|
+
import type { NavigateOptions } from '../NavigateOptions.js'
|
|
23
|
+
import type { Navigation } from '../Navigation.js'
|
|
24
|
+
import type { ProposedDestination } from '../ProposedDestination.js'
|
|
30
25
|
|
|
31
26
|
export type NavigationState = {
|
|
32
27
|
readonly entries: ReadonlyArray<Destination>
|
|
33
28
|
readonly index: number
|
|
34
|
-
readonly transition: Option.Option<
|
|
29
|
+
readonly transition: Option.Option<TransitionEvent>
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
export const NavigationState = Schema.Struct({
|
|
38
33
|
entries: Schema.Array(Destination),
|
|
39
34
|
index: Schema.Number,
|
|
40
|
-
transition: Schema.OptionFromNullishOr(
|
|
35
|
+
transition: Schema.OptionFromNullishOr(TransitionEvent, null),
|
|
41
36
|
})
|
|
42
37
|
|
|
43
|
-
export
|
|
44
|
-
|
|
38
|
+
export function getUrl(origin: string, urlOrPath: string | URL, base: string): URL {
|
|
39
|
+
if (typeof urlOrPath === 'string') {
|
|
40
|
+
const url = new URL(urlOrPath, origin)
|
|
41
|
+
|
|
42
|
+
// If the URL is relative, join it with the base
|
|
43
|
+
if (isRelativeUrl(urlOrPath)) {
|
|
44
|
+
url.pathname = joinPath(base, url.pathname)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return url
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return urlOrPath
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isRelativeUrl(url: string) {
|
|
54
|
+
if (url.includes('://')) return false
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function joinPath(a: string, b: string) {
|
|
59
|
+
const aEndsWithSlash = a[a.length - 1] === '/'
|
|
60
|
+
const bStartsWithSlash = b[0] === '/'
|
|
61
|
+
|
|
62
|
+
if (aEndsWithSlash && bStartsWithSlash) {
|
|
63
|
+
return a + b.slice(1)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!aEndsWithSlash && !bStartsWithSlash) {
|
|
67
|
+
return `${a}/${b}`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return a + b
|
|
45
71
|
}
|
|
46
72
|
|
|
47
73
|
export type ModelAndIntent = {
|
|
48
|
-
readonly state:
|
|
49
|
-
|
|
50
|
-
readonly
|
|
51
|
-
readonly beforeHandlers: RefSubject.RefSubject<
|
|
74
|
+
readonly state: LazyRef.LazyRef<NavigationState>
|
|
75
|
+
|
|
76
|
+
readonly beforeHandlers: LazyRef.LazyRef<
|
|
52
77
|
Set<readonly [BeforeNavigationHandler<any, any>, Context.Context<any>]>
|
|
53
78
|
>
|
|
54
|
-
readonly handlers: RefSubject.RefSubject<
|
|
55
|
-
Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>
|
|
56
|
-
>
|
|
57
79
|
|
|
58
|
-
readonly
|
|
59
|
-
Set<readonly [
|
|
80
|
+
readonly handlers: LazyRef.LazyRef<
|
|
81
|
+
Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>
|
|
60
82
|
>
|
|
61
83
|
|
|
62
84
|
readonly commit: Commit
|
|
@@ -68,37 +90,26 @@ export function setupFromModelAndIntent(
|
|
|
68
90
|
modelAndIntent: ModelAndIntent,
|
|
69
91
|
origin: string,
|
|
70
92
|
base: string,
|
|
71
|
-
getRandomValues: Context.
|
|
72
|
-
newNavigationState?: () => NavigationState
|
|
93
|
+
getRandomValues: Context.Tag.Service<typeof GetRandomValues>,
|
|
94
|
+
newNavigationState?: () => NavigationState,
|
|
73
95
|
) {
|
|
74
|
-
const {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const entries = RefSubject.map(state, (s) => s.entries)
|
|
84
|
-
const currentEntry = RefSubject.map(state, (s) => s.entries[s.index])
|
|
85
|
-
const transition = RefSubject.map(state, (s) => s.transition)
|
|
86
|
-
|
|
87
|
-
const runBeforeHandlers = (event: BeforeNavigationEvent) =>
|
|
88
|
-
Effect.gen(function*() {
|
|
96
|
+
const { beforeHandlers, commit, handlers, state } = modelAndIntent
|
|
97
|
+
const canGoBack = LazyRef.map(state, (s) => s.index > 0)
|
|
98
|
+
const canGoForward = LazyRef.map(state, (s) => s.index < s.entries.length - 1)
|
|
99
|
+
const entries = LazyRef.map(state, (s) => s.entries)
|
|
100
|
+
const currentEntry = LazyRef.map(state, (s) => s.entries[s.index])
|
|
101
|
+
const transition = LazyRef.map(state, (s) => s.transition)
|
|
102
|
+
|
|
103
|
+
const runBeforeHandlers = (event: TransitionEvent) =>
|
|
104
|
+
Effect.gen(function* () {
|
|
89
105
|
const handlers = yield* beforeHandlers
|
|
90
|
-
const matches: Array<
|
|
91
|
-
Effect.Effect<unknown, RedirectError | CancelNavigation>
|
|
92
|
-
> = []
|
|
106
|
+
const matches: Array<Effect.Effect<unknown, RedirectError | CancelNavigation>> = []
|
|
93
107
|
|
|
94
108
|
for (const [handler, ctx] of handlers) {
|
|
95
|
-
const exit = yield* handler(event).pipe(
|
|
96
|
-
Effect.provide(ctx),
|
|
97
|
-
Effect.either
|
|
98
|
-
)
|
|
109
|
+
const exit = yield* handler(event).pipe(Effect.provide(ctx), Effect.either)
|
|
99
110
|
if (Either.isRight(exit)) {
|
|
100
111
|
const match = exit.right
|
|
101
|
-
if (Option.isSome(match)) {
|
|
112
|
+
if (match !== undefined && Option.isSome(match)) {
|
|
102
113
|
matches.push(Effect.provide(match.value, ctx))
|
|
103
114
|
}
|
|
104
115
|
} else {
|
|
@@ -119,13 +130,13 @@ export function setupFromModelAndIntent(
|
|
|
119
130
|
})
|
|
120
131
|
|
|
121
132
|
const runHandlers = (event: NavigationEvent) =>
|
|
122
|
-
Effect.gen(function*() {
|
|
133
|
+
Effect.gen(function* () {
|
|
123
134
|
const eventHandlers = yield* handlers
|
|
124
135
|
const matches: Array<Effect.Effect<unknown>> = []
|
|
125
136
|
|
|
126
137
|
for (const [handler, ctx] of eventHandlers) {
|
|
127
138
|
const match = yield* Effect.provide(handler(event), ctx)
|
|
128
|
-
if (Option.isSome(match)) {
|
|
139
|
+
if (match !== undefined && Option.isSome(match)) {
|
|
129
140
|
matches.push(Effect.provide(match.value, ctx))
|
|
130
141
|
}
|
|
131
142
|
}
|
|
@@ -135,74 +146,17 @@ export function setupFromModelAndIntent(
|
|
|
135
146
|
}
|
|
136
147
|
})
|
|
137
148
|
|
|
138
|
-
const runFormDataHandlers = (
|
|
139
|
-
event: FormDataEvent
|
|
140
|
-
): Effect.Effect<
|
|
141
|
-
Either.Either<
|
|
142
|
-
Option.Option<HttpClientResponse.HttpClientResponse>,
|
|
143
|
-
RedirectError | CancelNavigation
|
|
144
|
-
>,
|
|
145
|
-
NavigationError | HttpClientError.HttpClientError,
|
|
146
|
-
Scope.Scope | HttpClient.HttpClient.Service
|
|
147
|
-
> =>
|
|
148
|
-
Effect.gen(function*() {
|
|
149
|
-
const handlers = yield* formDataHandlers
|
|
150
|
-
const matches: Array<
|
|
151
|
-
Effect.Effect<
|
|
152
|
-
Option.Option<HttpClientResponse.HttpClientResponse>,
|
|
153
|
-
RedirectError | CancelNavigation
|
|
154
|
-
>
|
|
155
|
-
> = []
|
|
156
|
-
|
|
157
|
-
for (const [handler, ctx] of handlers) {
|
|
158
|
-
const exit = yield* handler(event).pipe(
|
|
159
|
-
Effect.provide(ctx),
|
|
160
|
-
Effect.either
|
|
161
|
-
)
|
|
162
|
-
if (Either.isRight(exit)) {
|
|
163
|
-
const match = exit.right
|
|
164
|
-
if (Option.isSome(match)) {
|
|
165
|
-
matches.push(Effect.provide(match.value, ctx))
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
return Either.left(exit.left)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (matches.length > 0) {
|
|
173
|
-
for (const match of matches) {
|
|
174
|
-
const exit = yield* Effect.either(match)
|
|
175
|
-
if (Either.isLeft(exit)) {
|
|
176
|
-
return Either.left(exit.left)
|
|
177
|
-
} else if (Option.isSome(exit.right)) {
|
|
178
|
-
return Either.right(exit.right)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
// Only if there are 0 matches, we'll make a request to the server ourselves
|
|
183
|
-
const response = yield* makeFormDataRequest(
|
|
184
|
-
event,
|
|
185
|
-
Option.getOrElse(event.action, () => event.from.url.href)
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
return Either.right(Option.some(response))
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return Either.right(Option.none())
|
|
192
|
-
})
|
|
193
|
-
|
|
194
149
|
const runNavigationEvent = (
|
|
195
|
-
beforeEvent:
|
|
150
|
+
beforeEvent: TransitionEvent,
|
|
196
151
|
get: Effect.Effect<NavigationState>,
|
|
197
152
|
set: (a: NavigationState) => Effect.Effect<NavigationState>,
|
|
198
153
|
depth: number,
|
|
199
|
-
skipCommit
|
|
154
|
+
skipCommit = false,
|
|
200
155
|
): Effect.Effect<Destination, NavigationError> =>
|
|
201
|
-
Effect.gen(function*() {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
transition: Option.some(beforeEvent)
|
|
156
|
+
Effect.gen(function* () {
|
|
157
|
+
const current = yield* set({
|
|
158
|
+
...(yield* get),
|
|
159
|
+
transition: Option.some(beforeEvent),
|
|
206
160
|
})
|
|
207
161
|
|
|
208
162
|
if (!skipCommit) {
|
|
@@ -221,7 +175,7 @@ export function setupFromModelAndIntent(
|
|
|
221
175
|
yield* commit(to, beforeEvent)
|
|
222
176
|
}
|
|
223
177
|
|
|
224
|
-
if (newNavigationState) {
|
|
178
|
+
if (newNavigationState !== undefined) {
|
|
225
179
|
const { entries, index } = yield* set(newNavigationState())
|
|
226
180
|
|
|
227
181
|
return entries[index]
|
|
@@ -229,22 +183,22 @@ export function setupFromModelAndIntent(
|
|
|
229
183
|
const event: NavigationEvent = {
|
|
230
184
|
type: beforeEvent.type,
|
|
231
185
|
info: beforeEvent.info,
|
|
232
|
-
destination: to
|
|
186
|
+
destination: to,
|
|
233
187
|
}
|
|
234
188
|
|
|
235
|
-
if (beforeEvent.type ===
|
|
189
|
+
if (beforeEvent.type === 'push') {
|
|
236
190
|
const index = current.index + 1
|
|
237
191
|
const entries = current.entries.slice(0, index).concat([to])
|
|
238
192
|
|
|
239
193
|
yield* set({ entries, index, transition: Option.none() })
|
|
240
|
-
} else if (beforeEvent.type ===
|
|
194
|
+
} else if (beforeEvent.type === 'replace') {
|
|
241
195
|
const index = current.index
|
|
242
196
|
const before = current.entries.slice(0, index)
|
|
243
197
|
const after = current.entries.slice(index + 1)
|
|
244
198
|
const entries = [...before, to, ...after]
|
|
245
199
|
|
|
246
200
|
yield* set({ entries, index, transition: Option.none() })
|
|
247
|
-
} else if (beforeEvent.type ===
|
|
201
|
+
} else if (beforeEvent.type === 'reload') {
|
|
248
202
|
yield* set({ ...current, transition: Option.none() })
|
|
249
203
|
} else {
|
|
250
204
|
const { delta } = beforeEvent
|
|
@@ -253,7 +207,7 @@ export function setupFromModelAndIntent(
|
|
|
253
207
|
yield* set({
|
|
254
208
|
...current,
|
|
255
209
|
index: nextIndex,
|
|
256
|
-
transition: Option.none()
|
|
210
|
+
transition: Option.none(),
|
|
257
211
|
})
|
|
258
212
|
}
|
|
259
213
|
|
|
@@ -261,73 +215,66 @@ export function setupFromModelAndIntent(
|
|
|
261
215
|
}
|
|
262
216
|
|
|
263
217
|
return to
|
|
264
|
-
}).pipe(
|
|
218
|
+
}).pipe(Effect.provideService(GetRandomValues, getRandomValues))
|
|
265
219
|
|
|
266
220
|
const handleError = (
|
|
267
221
|
error: RedirectError | CancelNavigation,
|
|
268
222
|
get: Effect.Effect<NavigationState>,
|
|
269
223
|
set: (a: NavigationState) => Effect.Effect<NavigationState>,
|
|
270
|
-
depth: number
|
|
224
|
+
depth: number,
|
|
271
225
|
): Effect.Effect<Destination, NavigationError> =>
|
|
272
|
-
Effect.gen(function*() {
|
|
226
|
+
Effect.gen(function* () {
|
|
273
227
|
if (depth >= 25) {
|
|
274
|
-
return yield* Effect.dieMessage(
|
|
228
|
+
return yield* Effect.dieMessage('Redirect loop detected.')
|
|
275
229
|
}
|
|
276
230
|
|
|
277
231
|
const { entries, index } = yield* get
|
|
278
232
|
const from = entries[index]
|
|
279
233
|
|
|
280
|
-
if (error._tag ===
|
|
234
|
+
if (error._tag === 'CancelNavigation') {
|
|
281
235
|
yield* set({ entries, index, transition: Option.none() })
|
|
282
236
|
|
|
283
237
|
return from
|
|
284
238
|
} else {
|
|
285
|
-
const event = yield* makeRedirectEvent(origin, error, from)
|
|
239
|
+
const event = yield* makeRedirectEvent(origin, error, from, base)
|
|
286
240
|
|
|
287
241
|
return yield* runNavigationEvent(event, get, set, depth + 1)
|
|
288
242
|
}
|
|
289
|
-
}).pipe(
|
|
243
|
+
}).pipe(Effect.provideService(GetRandomValues, getRandomValues))
|
|
290
244
|
|
|
291
|
-
const navigate = (
|
|
292
|
-
pathOrUrl: string | URL,
|
|
293
|
-
options?: NavigateOptions,
|
|
294
|
-
skipCommit: boolean = false
|
|
295
|
-
) =>
|
|
245
|
+
const navigate = (pathOrUrl: string | URL, options?: NavigateOptions, skipCommit = false) =>
|
|
296
246
|
state.runUpdates(({ get, set }) =>
|
|
297
|
-
Effect.gen(function*() {
|
|
247
|
+
Effect.gen(function* () {
|
|
298
248
|
const state = yield* get
|
|
299
249
|
const from = state.entries[state.index]
|
|
300
|
-
const history = options?.history ??
|
|
250
|
+
const history = options?.history ?? 'auto'
|
|
301
251
|
const to = yield* makeOrUpdateDestination(
|
|
302
252
|
state,
|
|
303
|
-
getUrl(origin, pathOrUrl),
|
|
253
|
+
getUrl(origin, pathOrUrl, base),
|
|
304
254
|
options?.state,
|
|
305
|
-
origin
|
|
306
|
-
).pipe(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
: "push"
|
|
311
|
-
: history
|
|
312
|
-
const event: BeforeNavigationEvent = {
|
|
255
|
+
origin,
|
|
256
|
+
).pipe(Effect.provideService(GetRandomValues, getRandomValues))
|
|
257
|
+
|
|
258
|
+
const type = history === 'auto' ? (from.key === to.key ? 'replace' : 'push') : history
|
|
259
|
+
const event: TransitionEvent = {
|
|
313
260
|
type,
|
|
314
261
|
from,
|
|
315
262
|
to,
|
|
316
|
-
delta: type ===
|
|
317
|
-
info: options?.info
|
|
263
|
+
delta: type === 'replace' ? 0 : 1,
|
|
264
|
+
info: options?.info,
|
|
318
265
|
}
|
|
319
266
|
|
|
320
267
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit)
|
|
321
|
-
})
|
|
268
|
+
}),
|
|
322
269
|
)
|
|
323
270
|
|
|
324
271
|
const traverseTo = (
|
|
325
|
-
key: Destination[
|
|
272
|
+
key: Destination['key'],
|
|
326
273
|
options?: { readonly info?: unknown },
|
|
327
|
-
skipCommit
|
|
274
|
+
skipCommit = false,
|
|
328
275
|
) =>
|
|
329
276
|
state.runUpdates(({ get, set }) =>
|
|
330
|
-
Effect.gen(function*() {
|
|
277
|
+
Effect.gen(function* () {
|
|
331
278
|
const state = yield* get
|
|
332
279
|
const { entries, index } = state
|
|
333
280
|
const from = entries[index]
|
|
@@ -335,28 +282,23 @@ export function setupFromModelAndIntent(
|
|
|
335
282
|
|
|
336
283
|
if (nextIndex === -1) return from
|
|
337
284
|
|
|
338
|
-
const id = yield*
|
|
339
|
-
GetRandomValues.provide(getRandomValues)
|
|
340
|
-
)
|
|
285
|
+
const id = yield* makeUuid4.pipe(Effect.provideService(GetRandomValues, getRandomValues))
|
|
341
286
|
const to = { ...entries[nextIndex], id }
|
|
342
287
|
const delta = nextIndex - index
|
|
343
|
-
const event:
|
|
344
|
-
type:
|
|
288
|
+
const event: TransitionEvent = {
|
|
289
|
+
type: 'traverse',
|
|
345
290
|
from,
|
|
346
291
|
to,
|
|
347
292
|
delta,
|
|
348
|
-
info: options?.info
|
|
293
|
+
info: options?.info,
|
|
349
294
|
}
|
|
350
295
|
|
|
351
296
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit)
|
|
352
|
-
})
|
|
297
|
+
}),
|
|
353
298
|
)
|
|
354
299
|
|
|
355
|
-
const back = (
|
|
356
|
-
|
|
357
|
-
skipCommit: boolean = false
|
|
358
|
-
) =>
|
|
359
|
-
Effect.gen(function*() {
|
|
300
|
+
const back = (options?: { readonly info?: unknown }, skipCommit = false) =>
|
|
301
|
+
Effect.gen(function* () {
|
|
360
302
|
const { entries, index } = yield* state
|
|
361
303
|
if (index === 0) return entries[index]
|
|
362
304
|
const { key } = entries[index - 1]
|
|
@@ -364,11 +306,8 @@ export function setupFromModelAndIntent(
|
|
|
364
306
|
return yield* traverseTo(key, options, skipCommit)
|
|
365
307
|
})
|
|
366
308
|
|
|
367
|
-
const forward = (
|
|
368
|
-
|
|
369
|
-
skipCommit: boolean = false
|
|
370
|
-
) =>
|
|
371
|
-
Effect.gen(function*() {
|
|
309
|
+
const forward = (options?: { readonly info?: unknown }, skipCommit = false) =>
|
|
310
|
+
Effect.gen(function* () {
|
|
372
311
|
const { entries, index } = yield* state
|
|
373
312
|
if (index === entries.length - 1) return entries[index]
|
|
374
313
|
const { key } = entries[index + 1]
|
|
@@ -376,154 +315,95 @@ export function setupFromModelAndIntent(
|
|
|
376
315
|
return yield* traverseTo(key, options, skipCommit)
|
|
377
316
|
})
|
|
378
317
|
|
|
379
|
-
const reload = (
|
|
380
|
-
options?: { readonly info?: unknown },
|
|
381
|
-
skipCommit: boolean = false
|
|
382
|
-
) =>
|
|
318
|
+
const reload = (options?: { readonly info?: unknown }, skipCommit = false) =>
|
|
383
319
|
state.runUpdates(({ get, set }) =>
|
|
384
|
-
Effect.gen(function*() {
|
|
320
|
+
Effect.gen(function* () {
|
|
385
321
|
const { entries, index } = yield* state
|
|
386
322
|
const current = entries[index]
|
|
387
323
|
|
|
388
|
-
const event:
|
|
389
|
-
type:
|
|
324
|
+
const event: TransitionEvent = {
|
|
325
|
+
type: 'reload',
|
|
390
326
|
from: current,
|
|
391
327
|
to: current,
|
|
392
328
|
delta: 0,
|
|
393
|
-
info: options?.info
|
|
329
|
+
info: options?.info,
|
|
394
330
|
}
|
|
395
331
|
|
|
396
332
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit)
|
|
397
|
-
})
|
|
333
|
+
}),
|
|
398
334
|
)
|
|
399
335
|
|
|
400
336
|
const beforeNavigation = <R = never, R2 = never>(
|
|
401
|
-
handler: BeforeNavigationHandler<R, R2
|
|
337
|
+
handler: BeforeNavigationHandler<R, R2>,
|
|
402
338
|
): Effect.Effect<void, never, R | R2 | Scope.Scope> =>
|
|
403
339
|
Effect.contextWithEffect((ctx) => {
|
|
404
340
|
const entry = [handler, ctx] as const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
(
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
RefSubject.update(beforeHandlers, (handlers) => {
|
|
413
|
-
const updated = new Set(handlers)
|
|
414
|
-
updated.delete(entry)
|
|
415
|
-
return updated
|
|
416
|
-
})
|
|
417
|
-
)
|
|
341
|
+
const acquire = LazyRef.update(beforeHandlers, (handlers) => new Set([...handlers, entry]))
|
|
342
|
+
const release = Effect.ignoreLogged(
|
|
343
|
+
LazyRef.update(beforeHandlers, (handlers) => {
|
|
344
|
+
const updated = new Set(handlers)
|
|
345
|
+
updated.delete(entry)
|
|
346
|
+
return updated
|
|
347
|
+
}),
|
|
418
348
|
)
|
|
349
|
+
|
|
350
|
+
return Effect.acquireRelease(acquire, () => release)
|
|
419
351
|
})
|
|
420
352
|
|
|
421
353
|
const onNavigation = <R = never, R2 = never>(
|
|
422
|
-
handler: NavigationHandler<R, R2
|
|
354
|
+
handler: NavigationHandler<R, R2>,
|
|
423
355
|
): Effect.Effect<void, never, R | R2 | Scope.Scope> =>
|
|
424
356
|
Effect.contextWithEffect((ctx) => {
|
|
425
357
|
const entry = [handler, ctx] as const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
handlers
|
|
430
|
-
(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
RefSubject.update(handlers, (handlers) => {
|
|
434
|
-
const updated = new Set(handlers)
|
|
435
|
-
updated.delete(entry)
|
|
436
|
-
return updated
|
|
437
|
-
})
|
|
438
|
-
)
|
|
358
|
+
const acquire = LazyRef.update(handlers, (handlers) => new Set([...handlers, entry]))
|
|
359
|
+
const release = Effect.ignoreLogged(
|
|
360
|
+
LazyRef.update(handlers, (handlers) => {
|
|
361
|
+
const updated = new Set(handlers)
|
|
362
|
+
updated.delete(entry)
|
|
363
|
+
return updated
|
|
364
|
+
}),
|
|
439
365
|
)
|
|
366
|
+
|
|
367
|
+
return Effect.acquireRelease(acquire, () => release)
|
|
440
368
|
})
|
|
441
369
|
|
|
442
370
|
const updateCurrentEntry = (options: { readonly state: unknown }) =>
|
|
443
371
|
state.runUpdates(({ get, set }) =>
|
|
444
|
-
Effect.gen(function*() {
|
|
372
|
+
Effect.gen(function* () {
|
|
445
373
|
const { entries, index } = yield* get
|
|
446
374
|
const current = entries[index]
|
|
447
|
-
const event:
|
|
448
|
-
type:
|
|
375
|
+
const event: TransitionEvent = {
|
|
376
|
+
type: 'replace',
|
|
449
377
|
from: current,
|
|
450
378
|
to: { ...current, state: options.state },
|
|
451
379
|
delta: 0,
|
|
452
|
-
info: null
|
|
380
|
+
info: null,
|
|
453
381
|
}
|
|
454
382
|
|
|
455
383
|
return yield* runNavigationEvent(event, get, set, 0)
|
|
456
|
-
})
|
|
384
|
+
}),
|
|
457
385
|
)
|
|
458
386
|
|
|
459
|
-
const submit = (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
Effect.gen(function*() {
|
|
469
|
-
const { entries, index } = yield* get
|
|
470
|
-
const from = entries[index]
|
|
471
|
-
const event: FormDataEvent = {
|
|
472
|
-
from,
|
|
473
|
-
data,
|
|
474
|
-
name: Option.fromNullable(input?.name),
|
|
475
|
-
action: Option.fromNullable(input?.action),
|
|
476
|
-
method: Option.fromNullable(input?.method),
|
|
477
|
-
encoding: Option.fromNullable(input?.encoding)
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const either = yield* runFormDataHandlers(event)
|
|
481
|
-
|
|
482
|
-
if (Either.isLeft(either)) {
|
|
483
|
-
yield* handleError(either.left, get, set, 0)
|
|
484
|
-
return Option.none<HttpClientResponse.HttpClientResponse>()
|
|
485
|
-
} else {
|
|
486
|
-
if (Option.isNone(either.right)) {
|
|
487
|
-
return either.right
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const response = either.right.value
|
|
491
|
-
|
|
492
|
-
// If it is a redirect
|
|
493
|
-
if (REDIRECT_STATUS_CODES.has(response.status)) {
|
|
494
|
-
const location = Headers.get(response.headers, "location")
|
|
495
|
-
|
|
496
|
-
// And we have a location header
|
|
497
|
-
if (Option.isSome(location)) {
|
|
498
|
-
// Then we navigate to that location
|
|
499
|
-
yield* navigate(location.value, { history: "replace" })
|
|
500
|
-
}
|
|
501
|
-
}
|
|
387
|
+
const submit = (form: FormSubmit) =>
|
|
388
|
+
Effect.gen(function* () {
|
|
389
|
+
const current = yield* currentEntry
|
|
390
|
+
// Utilize the action URL if provided, otherwise use the current URL
|
|
391
|
+
const url = form.action ? getUrl(origin, form.action, base) : current.url
|
|
392
|
+
// Submit the HTTP request
|
|
393
|
+
const response = yield* HttpClient[form.method](url, form).pipe(
|
|
394
|
+
Effect.mapErrorCause((cause) => Cause.fail(new FormSubmitError({ cause }))),
|
|
395
|
+
)
|
|
502
396
|
|
|
503
|
-
|
|
397
|
+
// If the response is a redirect, navigate to the new URL
|
|
398
|
+
if (REDIRECT_STATUS_CODES.has(response.status)) {
|
|
399
|
+
// Get the location header
|
|
400
|
+
const location = Headers.get(response.headers, 'location')
|
|
401
|
+
if (Option.isSome(location)) {
|
|
402
|
+
return [yield* navigate(getUrl(origin, location.value, base), form), response] as const
|
|
504
403
|
}
|
|
505
|
-
}
|
|
506
|
-
)
|
|
507
|
-
|
|
508
|
-
const onFormData = <R = never, R2 = never>(
|
|
509
|
-
handler: FormDataHandler<R, R2>
|
|
510
|
-
): Effect.Effect<void, never, R | R2 | Scope.Scope> =>
|
|
511
|
-
Effect.contextWithEffect((ctx) => {
|
|
512
|
-
const entry = [handler, ctx] as const
|
|
404
|
+
}
|
|
513
405
|
|
|
514
|
-
return
|
|
515
|
-
RefSubject.update(
|
|
516
|
-
formDataHandlers,
|
|
517
|
-
(handlers) => new Set([...handlers, entry])
|
|
518
|
-
),
|
|
519
|
-
Effect.addFinalizer(() =>
|
|
520
|
-
RefSubject.update(formDataHandlers, (handlers) => {
|
|
521
|
-
const updated = new Set(handlers)
|
|
522
|
-
updated.delete(entry)
|
|
523
|
-
return updated
|
|
524
|
-
})
|
|
525
|
-
)
|
|
526
|
-
)
|
|
406
|
+
return [current, response] as const
|
|
527
407
|
})
|
|
528
408
|
|
|
529
409
|
const navigation = {
|
|
@@ -543,26 +423,26 @@ export function setupFromModelAndIntent(
|
|
|
543
423
|
traverseTo,
|
|
544
424
|
updateCurrentEntry,
|
|
545
425
|
submit,
|
|
546
|
-
onFormData
|
|
547
426
|
} satisfies Navigation
|
|
548
427
|
|
|
549
428
|
return navigation
|
|
550
429
|
}
|
|
551
430
|
|
|
552
|
-
|
|
431
|
+
function makeRedirectEvent(
|
|
553
432
|
origin: string,
|
|
554
433
|
redirect: RedirectError,
|
|
555
|
-
from: Destination
|
|
434
|
+
from: Destination,
|
|
435
|
+
base: string,
|
|
556
436
|
) {
|
|
557
|
-
return Effect.gen(function*() {
|
|
558
|
-
const url = getUrl(origin, redirect.path)
|
|
437
|
+
return Effect.gen(function* () {
|
|
438
|
+
const url = getUrl(origin, redirect.path, base)
|
|
559
439
|
const to = yield* makeDestination(url, redirect.options?.state, origin)
|
|
560
|
-
const event:
|
|
561
|
-
type:
|
|
440
|
+
const event: TransitionEvent = {
|
|
441
|
+
type: 'replace',
|
|
562
442
|
from,
|
|
563
443
|
to,
|
|
564
444
|
delta: 0,
|
|
565
|
-
info: redirect.options?.info
|
|
445
|
+
info: redirect.options?.info,
|
|
566
446
|
}
|
|
567
447
|
|
|
568
448
|
return event
|
|
@@ -573,21 +453,20 @@ export function makeOrUpdateDestination(
|
|
|
573
453
|
navigationState: NavigationState,
|
|
574
454
|
url: URL,
|
|
575
455
|
state: unknown,
|
|
576
|
-
origin: string
|
|
456
|
+
origin: string,
|
|
577
457
|
) {
|
|
578
|
-
return Effect.gen(function*() {
|
|
458
|
+
return Effect.gen(function* () {
|
|
579
459
|
const current = navigationState.entries[navigationState.index]
|
|
580
|
-
const isSameOriginAndPath =
|
|
581
|
-
url.pathname === current.url.pathname
|
|
582
|
-
|
|
460
|
+
const isSameOriginAndPath =
|
|
461
|
+
url.origin === current.url.origin && url.pathname === current.url.pathname
|
|
583
462
|
if (isSameOriginAndPath) {
|
|
584
|
-
const id = yield*
|
|
463
|
+
const id = yield* makeUuid4
|
|
585
464
|
const destination: Destination = {
|
|
586
465
|
id,
|
|
587
466
|
key: current.key,
|
|
588
467
|
url,
|
|
589
468
|
state: getOriginalState(state),
|
|
590
|
-
sameDocument: url.origin === origin
|
|
469
|
+
sameDocument: url.origin === origin,
|
|
591
470
|
}
|
|
592
471
|
|
|
593
472
|
return destination
|
|
@@ -598,28 +477,28 @@ export function makeOrUpdateDestination(
|
|
|
598
477
|
}
|
|
599
478
|
|
|
600
479
|
export function makeDestination(url: URL, state: unknown, origin: string) {
|
|
601
|
-
return Effect.gen(function*() {
|
|
480
|
+
return Effect.gen(function* () {
|
|
602
481
|
if (isPatchedState(state)) {
|
|
603
482
|
const destination: Destination = {
|
|
604
483
|
id: state.__typed__navigation__id__,
|
|
605
484
|
key: state.__typed__navigation__key__,
|
|
606
485
|
url,
|
|
607
486
|
state: state.__typed__navigation__state__,
|
|
608
|
-
sameDocument: url.origin === origin
|
|
487
|
+
sameDocument: url.origin === origin,
|
|
609
488
|
}
|
|
610
489
|
|
|
611
490
|
return destination
|
|
612
491
|
}
|
|
613
492
|
|
|
614
|
-
const id = yield*
|
|
615
|
-
const key = yield*
|
|
493
|
+
const id = yield* makeUuid4
|
|
494
|
+
const key = yield* makeUuid4
|
|
616
495
|
|
|
617
496
|
const destination: Destination = {
|
|
618
497
|
id,
|
|
619
498
|
key,
|
|
620
499
|
url,
|
|
621
500
|
state,
|
|
622
|
-
sameDocument: url.origin === origin
|
|
501
|
+
sameDocument: url.origin === origin,
|
|
623
502
|
}
|
|
624
503
|
|
|
625
504
|
return destination
|
|
@@ -627,16 +506,16 @@ export function makeDestination(url: URL, state: unknown, origin: string) {
|
|
|
627
506
|
}
|
|
628
507
|
|
|
629
508
|
export function upgradeProposedDestination(proposed: ProposedDestination) {
|
|
630
|
-
return Effect.gen(function*() {
|
|
631
|
-
const id = yield*
|
|
632
|
-
const key = yield*
|
|
509
|
+
return Effect.gen(function* () {
|
|
510
|
+
const id = yield* makeUuid4
|
|
511
|
+
const key = yield* makeUuid4
|
|
633
512
|
|
|
634
513
|
const destination: Destination = {
|
|
635
514
|
id,
|
|
636
515
|
key,
|
|
637
516
|
url: proposed.url,
|
|
638
517
|
state: proposed.state,
|
|
639
|
-
sameDocument: proposed.sameDocument
|
|
518
|
+
sameDocument: proposed.sameDocument,
|
|
640
519
|
}
|
|
641
520
|
|
|
642
521
|
return destination
|
|
@@ -644,19 +523,16 @@ export function upgradeProposedDestination(proposed: ProposedDestination) {
|
|
|
644
523
|
}
|
|
645
524
|
|
|
646
525
|
export type PatchedState = {
|
|
647
|
-
readonly __typed__navigation__id__:
|
|
648
|
-
readonly __typed__navigation__key__:
|
|
526
|
+
readonly __typed__navigation__id__: Uuid4
|
|
527
|
+
readonly __typed__navigation__key__: Uuid4
|
|
649
528
|
readonly __typed__navigation__state__: unknown
|
|
650
529
|
}
|
|
651
530
|
|
|
652
531
|
export function isPatchedState(state: unknown): state is PatchedState {
|
|
653
|
-
if (state === null || !(typeof state ===
|
|
532
|
+
if (state === null || !(typeof state === 'object') || Array.isArray(state)) {
|
|
654
533
|
return false
|
|
655
534
|
}
|
|
656
|
-
if (
|
|
657
|
-
"__typed__navigation__id__" in state &&
|
|
658
|
-
"__typed__navigation__key__" in state
|
|
659
|
-
) {
|
|
535
|
+
if ('__typed__navigation__id__' in state && '__typed__navigation__key__' in state) {
|
|
660
536
|
return true
|
|
661
537
|
}
|
|
662
538
|
return false
|
|
@@ -669,71 +545,36 @@ export function getOriginalState(state: unknown) {
|
|
|
669
545
|
|
|
670
546
|
export function getOriginFromUrl(url: string | URL) {
|
|
671
547
|
try {
|
|
672
|
-
if (typeof url ===
|
|
548
|
+
if (typeof url === 'string') {
|
|
673
549
|
return new URL(url).origin
|
|
674
550
|
} else {
|
|
675
551
|
return url.origin
|
|
676
552
|
}
|
|
677
553
|
} catch {
|
|
678
|
-
return
|
|
554
|
+
return 'http://localhost'
|
|
679
555
|
}
|
|
680
556
|
}
|
|
681
557
|
|
|
682
|
-
export function isDestination(
|
|
683
|
-
proposed
|
|
684
|
-
): proposed is Destination {
|
|
685
|
-
return "id" in proposed && "key" in proposed
|
|
558
|
+
export function isDestination(proposed: ProposedDestination): proposed is Destination {
|
|
559
|
+
return 'id' in proposed && 'key' in proposed
|
|
686
560
|
}
|
|
687
561
|
|
|
688
562
|
const strictEqual = <A>(a: A, b: A) => a === b
|
|
689
563
|
|
|
690
|
-
export
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
readonly [NavigationHandler<any, any>, Context.Context<any>]
|
|
706
|
-
>()
|
|
707
|
-
),
|
|
708
|
-
{ eq: strictEqual }
|
|
709
|
-
)
|
|
710
|
-
const formDataHandlers = yield* RefSubject.fromEffect(
|
|
711
|
-
Effect.sync(
|
|
712
|
-
() => new Set<readonly [FormDataHandler<any, any>, Context.Context<any>]>()
|
|
713
|
-
),
|
|
714
|
-
{ eq: strictEqual }
|
|
715
|
-
)
|
|
716
|
-
|
|
717
|
-
return {
|
|
718
|
-
beforeHandlers,
|
|
719
|
-
handlers,
|
|
720
|
-
formDataHandlers
|
|
721
|
-
}
|
|
722
|
-
})
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
function makeFormDataRequest(event: FormDataEvent, url: string) {
|
|
726
|
-
const headers = new globalThis.Headers()
|
|
727
|
-
|
|
728
|
-
if (Option.isSome(event.encoding)) {
|
|
729
|
-
headers.set("Content-Type", event.encoding.value)
|
|
564
|
+
export const makeHandlersState = Effect.gen(function* () {
|
|
565
|
+
const beforeHandlers = yield* LazyRef.fromEffect(
|
|
566
|
+
Effect.sync(
|
|
567
|
+
() => new Set<readonly [BeforeNavigationHandler<any, any>, Context.Context<any>]>(),
|
|
568
|
+
),
|
|
569
|
+
{ eq: strictEqual },
|
|
570
|
+
)
|
|
571
|
+
const handlers = yield* LazyRef.fromEffect(
|
|
572
|
+
Effect.sync(() => new Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>()),
|
|
573
|
+
{ eq: strictEqual },
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
beforeHandlers,
|
|
578
|
+
handlers,
|
|
730
579
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
return Effect.flatMap(HttpClient.HttpClient, (client) =>
|
|
734
|
-
client.execute(
|
|
735
|
-
HttpClientRequest.make(method as "POST")(url, {
|
|
736
|
-
headers: Headers.fromInput(headers)
|
|
737
|
-
}).pipe(HttpClientRequest.bodyFormData(event.data))
|
|
738
|
-
))
|
|
739
|
-
}
|
|
580
|
+
})
|