@typed/navigation 0.17.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.
Files changed (115) hide show
  1. package/.nvmrc +1 -0
  2. package/biome.json +39 -0
  3. package/dist/Blocking.d.ts +23 -0
  4. package/dist/Blocking.js +41 -0
  5. package/dist/Blocking.js.map +1 -0
  6. package/dist/Destination.d.ts +11 -0
  7. package/dist/Destination.js +10 -0
  8. package/dist/Destination.js.map +1 -0
  9. package/dist/Error.d.ts +33 -0
  10. package/dist/Error.js +22 -0
  11. package/dist/Error.js.map +1 -0
  12. package/dist/Event.d.ts +45 -0
  13. package/dist/Event.js +17 -0
  14. package/dist/Event.js.map +1 -0
  15. package/dist/Forms.d.ts +79 -0
  16. package/dist/Forms.js +111 -0
  17. package/dist/Forms.js.map +1 -0
  18. package/dist/Handler.d.ts +6 -0
  19. package/dist/Handler.js +2 -0
  20. package/dist/Handler.js.map +1 -0
  21. package/dist/{dts/Layer.d.ts → Layer.d.ts} +11 -8
  22. package/dist/{esm/Layer.js → Layer.js} +2 -2
  23. package/dist/Layer.js.map +1 -0
  24. package/dist/NavigateOptions.d.ts +7 -0
  25. package/dist/NavigateOptions.js +7 -0
  26. package/dist/NavigateOptions.js.map +1 -0
  27. package/dist/Navigation.d.ts +79 -0
  28. package/dist/Navigation.js +49 -0
  29. package/dist/Navigation.js.map +1 -0
  30. package/dist/NavigationType.d.ts +3 -0
  31. package/dist/NavigationType.js +3 -0
  32. package/dist/NavigationType.js.map +1 -0
  33. package/dist/ProposedDestination.d.ts +13 -0
  34. package/dist/ProposedDestination.js +4 -0
  35. package/dist/ProposedDestination.js.map +1 -0
  36. package/dist/Url.d.ts +13 -0
  37. package/dist/Url.js +72 -0
  38. package/dist/Url.js.map +1 -0
  39. package/dist/index.d.ts +12 -0
  40. package/dist/index.js +13 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/internal/fromWindow.d.ts +4 -0
  43. package/dist/internal/fromWindow.js +358 -0
  44. package/dist/internal/fromWindow.js.map +1 -0
  45. package/dist/internal/memory.d.ts +6 -0
  46. package/dist/internal/memory.js +59 -0
  47. package/dist/internal/memory.js.map +1 -0
  48. package/dist/internal/shared.d.ts +109 -0
  49. package/dist/{esm/internal → internal}/shared.js +134 -165
  50. package/dist/internal/shared.js.map +1 -0
  51. package/package.json +35 -52
  52. package/readme.md +243 -0
  53. package/src/Blocking.ts +65 -65
  54. package/src/Destination.ts +14 -0
  55. package/src/Error.ts +28 -0
  56. package/src/Event.ts +26 -0
  57. package/src/Forms.ts +216 -0
  58. package/src/Handler.ts +16 -0
  59. package/src/Layer.ts +20 -9
  60. package/src/NavigateOptions.ts +9 -0
  61. package/src/Navigation.test.ts +697 -0
  62. package/src/Navigation.ts +133 -468
  63. package/src/NavigationType.ts +5 -0
  64. package/src/ProposedDestination.ts +8 -0
  65. package/src/Url.ts +106 -0
  66. package/src/index.ts +12 -17
  67. package/src/internal/fromWindow.ts +250 -180
  68. package/src/internal/memory.ts +62 -49
  69. package/src/internal/shared.ts +238 -305
  70. package/tsconfig.json +30 -0
  71. package/Blocking/package.json +0 -6
  72. package/LICENSE +0 -21
  73. package/Layer/package.json +0 -6
  74. package/Navigation/package.json +0 -6
  75. package/README.md +0 -5
  76. package/dist/cjs/Blocking.js +0 -58
  77. package/dist/cjs/Blocking.js.map +0 -1
  78. package/dist/cjs/Layer.js +0 -27
  79. package/dist/cjs/Layer.js.map +0 -1
  80. package/dist/cjs/Navigation.js +0 -275
  81. package/dist/cjs/Navigation.js.map +0 -1
  82. package/dist/cjs/index.js +0 -39
  83. package/dist/cjs/index.js.map +0 -1
  84. package/dist/cjs/internal/fromWindow.js +0 -421
  85. package/dist/cjs/internal/fromWindow.js.map +0 -1
  86. package/dist/cjs/internal/memory.js +0 -72
  87. package/dist/cjs/internal/memory.js.map +0 -1
  88. package/dist/cjs/internal/shared.js +0 -522
  89. package/dist/cjs/internal/shared.js.map +0 -1
  90. package/dist/dts/Blocking.d.ts +0 -34
  91. package/dist/dts/Blocking.d.ts.map +0 -1
  92. package/dist/dts/Layer.d.ts.map +0 -1
  93. package/dist/dts/Navigation.d.ts +0 -462
  94. package/dist/dts/Navigation.d.ts.map +0 -1
  95. package/dist/dts/index.d.ts +0 -17
  96. package/dist/dts/index.d.ts.map +0 -1
  97. package/dist/dts/internal/fromWindow.d.ts +0 -12
  98. package/dist/dts/internal/fromWindow.d.ts.map +0 -1
  99. package/dist/dts/internal/memory.d.ts +0 -6
  100. package/dist/dts/internal/memory.d.ts.map +0 -1
  101. package/dist/dts/internal/shared.d.ts +0 -114
  102. package/dist/dts/internal/shared.d.ts.map +0 -1
  103. package/dist/esm/Blocking.js +0 -46
  104. package/dist/esm/Blocking.js.map +0 -1
  105. package/dist/esm/Layer.js.map +0 -1
  106. package/dist/esm/Navigation.js +0 -237
  107. package/dist/esm/Navigation.js.map +0 -1
  108. package/dist/esm/index.js +0 -17
  109. package/dist/esm/index.js.map +0 -1
  110. package/dist/esm/internal/fromWindow.js +0 -310
  111. package/dist/esm/internal/fromWindow.js.map +0 -1
  112. package/dist/esm/internal/memory.js +0 -56
  113. package/dist/esm/internal/memory.js.map +0 -1
  114. package/dist/esm/internal/shared.js.map +0 -1
  115. package/dist/esm/package.json +0 -4
package/src/Navigation.ts CHANGED
@@ -1,532 +1,197 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
-
5
- import type * as HttpClient from "@effect/platform/HttpClient"
6
- import { ParseResult } from "@effect/schema"
7
- import * as Equivalence from "@effect/schema/Equivalence"
8
- import * as Schema from "@effect/schema/Schema"
9
- import { Tagged } from "@typed/context"
10
- import * as RefSubject from "@typed/fx/RefSubject"
11
- import type { Uuid } from "@typed/id"
12
- import * as IdSchema from "@typed/id/Schema"
13
- import * as Data from "effect/Data"
14
- import * as Effect from "effect/Effect"
15
- import type * as Option from "effect/Option"
16
- import type * as Scope from "effect/Scope"
17
- import type { Simplify } from "effect/Types"
18
-
19
- /**
20
- * @since 1.0.0
21
- */
1
+ import type { HttpClientResponse } from '@effect/platform'
2
+ import type { HttpClient } from '@effect/platform/HttpClient'
3
+ import * as LazyRef from '@typed/lazy-ref'
4
+ import * as Context from 'effect/Context'
5
+ import * as Effect from 'effect/Effect'
6
+ import type * as Option from 'effect/Option'
7
+ import type * as Scope from 'effect/Scope'
8
+ import type { Destination } from './Destination.js'
9
+ import { CancelNavigation, type FormSubmitError, NavigationError, RedirectError } from './Error.js'
10
+ import type { TransitionEvent } from './Event.js'
11
+ import type { FormSubmit } from './Forms.js'
12
+ import type { BeforeNavigationHandler, NavigationHandler } from './Handler.js'
13
+ import type { NavigateOptions } from './NavigateOptions.js'
14
+
22
15
  export interface Navigation {
23
16
  readonly origin: string
24
17
 
25
18
  readonly base: string
26
19
 
27
- readonly currentEntry: RefSubject.Computed<Destination>
20
+ readonly currentEntry: LazyRef.Computed<Destination>
28
21
 
29
- readonly entries: RefSubject.Computed<ReadonlyArray<Destination>>
22
+ readonly entries: LazyRef.Computed<ReadonlyArray<Destination>>
30
23
 
31
- readonly transition: RefSubject.Computed<Option.Option<Transition>>
24
+ readonly transition: LazyRef.Computed<Option.Option<TransitionEvent>>
32
25
 
33
- readonly canGoBack: RefSubject.Computed<boolean>
26
+ readonly canGoBack: LazyRef.Computed<boolean>
34
27
 
35
- readonly canGoForward: RefSubject.Computed<boolean>
28
+ readonly canGoForward: LazyRef.Computed<boolean>
36
29
 
37
30
  readonly navigate: (
38
31
  url: string | URL,
39
- options?: NavigateOptions
32
+ options?: NavigateOptions,
40
33
  ) => Effect.Effect<Destination, NavigationError>
41
34
 
42
- readonly back: (options?: { readonly info?: unknown }) => Effect.Effect<Destination, NavigationError>
35
+ readonly back: (options?: { readonly info?: unknown }) => Effect.Effect<
36
+ Destination,
37
+ NavigationError
38
+ >
43
39
 
44
- readonly forward: (options?: { readonly info?: unknown }) => Effect.Effect<Destination, NavigationError>
40
+ readonly forward: (options?: { readonly info?: unknown }) => Effect.Effect<
41
+ Destination,
42
+ NavigationError
43
+ >
45
44
 
46
45
  readonly traverseTo: (
47
- key: Destination["key"],
48
- options?: { readonly info?: unknown }
46
+ key: Destination['key'],
47
+ options?: { readonly info?: unknown },
49
48
  ) => Effect.Effect<Destination, NavigationError>
50
49
 
51
- readonly updateCurrentEntry: (
52
- options: { readonly state: unknown }
53
- ) => Effect.Effect<Destination, NavigationError>
50
+ readonly updateCurrentEntry: (options: { readonly state: unknown }) => Effect.Effect<
51
+ Destination,
52
+ NavigationError
53
+ >
54
54
 
55
- readonly reload: (
56
- options?: { readonly info?: unknown; readonly state?: unknown }
57
- ) => Effect.Effect<Destination, NavigationError>
55
+ readonly reload: (options?: {
56
+ readonly info?: unknown
57
+ readonly state?: unknown
58
+ }) => Effect.Effect<Destination, NavigationError>
58
59
 
59
60
  readonly beforeNavigation: <R = never, R2 = never>(
60
- handler: BeforeNavigationHandler<R, R2>
61
+ handler: BeforeNavigationHandler<R, R2>,
61
62
  ) => Effect.Effect<void, never, R | R2 | Scope.Scope>
62
63
 
63
64
  readonly onNavigation: <R = never, R2 = never>(
64
- handler: NavigationHandler<R, R2>
65
+ handler: NavigationHandler<R, R2>,
65
66
  ) => Effect.Effect<void, never, R | R2 | Scope.Scope>
66
67
 
67
68
  readonly submit: (
68
- data: FormData,
69
- formInput?: Simplify<Omit<FormInputFrom, "data">>
69
+ form: FormSubmit,
70
70
  ) => Effect.Effect<
71
- Option.Option<HttpClient.response.ClientResponse>,
72
- NavigationError | HttpClient.error.HttpClientError,
73
- Scope.Scope | HttpClient.client.Client.Default
71
+ readonly [Destination, HttpClientResponse.HttpClientResponse],
72
+ NavigationError | FormSubmitError,
73
+ Navigation | HttpClient | Scope.Scope
74
74
  >
75
-
76
- readonly onFormData: <R = never, R2 = never>(
77
- handler: FormDataHandler<R, R2>
78
- ) => Effect.Effect<void, never, R | R2 | Scope.Scope>
79
75
  }
80
76
 
81
- /**
82
- * @since 1.0.0
83
- */
84
- export const Navigation: Tagged<Navigation> = Tagged<Navigation, Navigation>("@typed/navigation/Navigation")
85
-
86
- const urlSchema_ = Schema.instanceOf(URL).pipe(Equivalence.equivalence(() => (a, b) => a.href === b.href))
87
-
88
- const urlSchema = Schema.String.pipe(
89
- Schema.transformOrFail(
90
- urlSchema_,
91
- {
92
- decode: (s) =>
93
- Effect.suspend(() => {
94
- try {
95
- return Effect.succeed(new URL(s))
96
- } catch {
97
- return Effect.fail(new ParseResult.Type(urlSchema_.ast, s, `Expected a URL`))
98
- }
99
- }),
100
- encode: (url) => Effect.succeed(url.toString())
101
- }
102
- )
103
- )
77
+ export const Navigation = Context.GenericTag<Navigation>('@typed/Navigation')
78
+
79
+ export const CurrentEntry: LazyRef.Computed<Destination, never, Navigation> =
80
+ LazyRef.computedFromTag(Navigation, (nav) => nav.currentEntry)
104
81
 
105
- /**
106
- * @since 1.0.0
107
- */
108
- export const Destination = Schema.Struct({
109
- id: IdSchema.uuid,
110
- key: IdSchema.uuid,
111
- url: urlSchema,
112
- state: Schema.Unknown,
113
- sameDocument: Schema.Boolean
114
- })
115
-
116
- /**
117
- * @since 1.0.0
118
- */
119
- export type DestinationJson = Schema.Schema.Encoded<typeof Destination>
120
- /**
121
- * @since 1.0.0
122
- */
123
- export interface Destination extends Schema.Schema.Type<typeof Destination> {}
124
-
125
- /**
126
- * @since 1.0.0
127
- */
128
- export const ProposedDestination = Destination.pipe(Schema.omit("id", "key"))
129
-
130
- /**
131
- * @since 1.0.0
132
- */
133
- export type ProposedDestinationJson = Schema.Schema.Encoded<typeof ProposedDestination>
134
- /**
135
- * @since 1.0.0
136
- */
137
- export interface ProposedDestination extends Schema.Schema.Type<typeof ProposedDestination> {}
138
-
139
- /**
140
- * @since 1.0.0
141
- */
142
- export const NavigationType = Schema.Literal("push", "replace", "reload", "traverse")
143
- /**
144
- * @since 1.0.0
145
- */
146
- export type NavigationType = Schema.Schema.Type<typeof NavigationType>
147
-
148
- /**
149
- * @since 1.0.0
150
- */
151
- export const Transition = Schema.Struct({
152
- type: NavigationType,
153
- from: Destination,
154
- to: Schema.Union(ProposedDestination, Destination)
155
- })
156
-
157
- /**
158
- * @since 1.0.0
159
- */
160
- export type TransitionJson = Schema.Schema.Encoded<typeof Transition>
161
- /**
162
- * @since 1.0.0
163
- */
164
- export interface Transition extends Schema.Schema.Type<typeof Transition> {}
165
-
166
- /**
167
- * @since 1.0.0
168
- */
169
- export const BeforeNavigationEvent = Schema.Struct({
170
- type: NavigationType,
171
- from: Destination,
172
- delta: Schema.Number,
173
- to: Schema.Union(ProposedDestination, Destination),
174
- info: Schema.Unknown
175
- })
176
-
177
- /**
178
- * @since 1.0.0
179
- */
180
- export type BeforeNavigationEventJson = Schema.Schema.Encoded<typeof BeforeNavigationEvent>
181
- /**
182
- * @since 1.0.0
183
- */
184
- export interface BeforeNavigationEvent extends Schema.Schema.Type<typeof BeforeNavigationEvent> {}
185
-
186
- /**
187
- * @since 1.0.0
188
- */
189
- export const NavigationEvent = Schema.Struct({
190
- type: NavigationType,
191
- destination: Destination,
192
- info: Schema.Unknown
193
- })
194
-
195
- /**
196
- * @since 1.0.0
197
- */
198
- export type NavigationEventJson = Schema.Schema.Encoded<typeof NavigationEvent>
199
- /**
200
- * @since 1.0.0
201
- */
202
- export interface NavigationEvent extends Schema.Schema.Type<typeof NavigationEvent> {}
203
-
204
- /**
205
- * @since 1.0.0
206
- */
207
- export type BeforeNavigationHandler<R, R2> = (
208
- event: BeforeNavigationEvent
209
- ) => Effect.Effect<
210
- Option.Option<
211
- Effect.Effect<unknown, RedirectError | CancelNavigation, R2>
212
- >,
213
- RedirectError | CancelNavigation,
214
- R
215
- >
216
-
217
- /**
218
- * @since 1.0.0
219
- */
220
- export type NavigationHandler<R, R2> = (
221
- event: NavigationEvent
222
- ) => Effect.Effect<
223
- Option.Option<
224
- Effect.Effect<unknown, never, R2>
225
- >,
82
+ export const Entries: LazyRef.Computed<
83
+ ReadonlyArray<Destination>,
226
84
  never,
227
- R
228
- >
229
-
230
- /**
231
- * @since 1.0.0
232
- */
233
- export type FormDataHandler<R, R2> = (
234
- event: FormDataEvent
235
- ) => Effect.Effect<
236
- Option.Option<
237
- Effect.Effect<Option.Option<HttpClient.response.ClientResponse>, RedirectError | CancelNavigation, R2>
238
- >,
239
- RedirectError | CancelNavigation,
240
- R
241
- >
242
-
243
- /**
244
- * @since 1.0.0
245
- */
246
- export class NavigationError extends Data.TaggedError("NavigationError")<{ readonly error: unknown }> {}
247
-
248
- /**
249
- * @since 1.0.0
250
- */
251
- export class RedirectError extends Data.TaggedError("RedirectError")<
252
- {
253
- readonly path: string | URL
254
- readonly options?: { readonly state?: unknown; readonly info?: unknown } | undefined
255
- }
256
- > {}
257
-
258
- /**
259
- * @since 1.0.0
260
- */
261
- export class CancelNavigation extends Data.TaggedError("CancelNavigation")<{}> {}
262
-
263
- /**
264
- * @since 1.0.0
265
- */
266
- export interface NavigateOptions {
267
- readonly history?: "replace" | "push" | "auto"
268
- readonly state?: unknown
269
- readonly info?: unknown
85
+ Navigation
86
+ > = LazyRef.computedFromTag(Navigation, (nav) => nav.entries)
87
+
88
+ export function getCurrentPathFromUrl(location: Pick<URL, 'pathname' | 'search' | 'hash'>): string {
89
+ return location.pathname + location.search + location.hash
270
90
  }
271
91
 
272
- /**
273
- * @since 1.0.0
274
- */
275
- export const FileSchemaFrom = Schema.Struct({
276
- _id: Schema.Literal("File"),
277
- name: Schema.String,
278
- data: Schema.String // Base64 encoded
279
- })
280
-
281
- /**
282
- * @since 1.0.0
283
- */
284
- export type FileSchemaFrom = Schema.Schema.Encoded<typeof FileSchemaFrom>
285
-
286
- const decodeBase64 = ParseResult.decode(Schema.Base64)
287
- const encodeBase64 = ParseResult.encode(Schema.Base64)
288
-
289
- /**
290
- * @since 1.0.0
291
- */
292
- export const FileSchema = FileSchemaFrom.pipe(
293
- Schema.transformOrFail(
294
- Schema.instanceOf(File),
295
- {
296
- decode: ({ data, name }) => Effect.map(decodeBase64(data), (buffer) => new File([buffer], name)),
297
- encode: (file) =>
298
- Effect.promise(() => file.arrayBuffer()).pipe(
299
- Effect.flatMap((buffer) => encodeBase64(new Uint8Array(buffer))),
300
- Effect.map((data): FileSchemaFrom => ({ _id: "File", name: file.name, data }))
301
- )
302
- }
303
- )
92
+ export const CurrentPath: LazyRef.Computed<string, never, Navigation> = LazyRef.computedFromTag(
93
+ Navigation,
94
+ (nav) => LazyRef.map(nav.currentEntry, (e) => getCurrentPathFromUrl(e.url)),
95
+ )
96
+
97
+ export const CanGoForward: LazyRef.Computed<boolean, never, Navigation> = LazyRef.computedFromTag(
98
+ Navigation,
99
+ (nav) => nav.canGoForward,
304
100
  )
305
101
 
306
- /**
307
- * @since 1.0.0
308
- */
309
- export const FormDataSchema = Schema.Record(Schema.String, Schema.Union(Schema.String, FileSchema)).pipe(
310
- Schema.transform(
311
- Schema.instanceOf(FormData),
312
- {
313
- decode: (formData) => {
314
- const data = new FormData()
315
-
316
- for (const [key, value] of Object.entries(formData)) {
317
- if (value instanceof File) {
318
- data.append(key, value, value.name)
319
- } else {
320
- data.append(key, value)
321
- }
322
- }
323
-
324
- return data
325
- },
326
- encode: (formData) => Object.fromEntries(formData.entries())
327
- }
328
- )
102
+ export const CanGoBack: LazyRef.Computed<boolean, never, Navigation> = LazyRef.computedFromTag(
103
+ Navigation,
104
+ (nav) => nav.canGoBack,
329
105
  )
330
106
 
331
- const optionNullable = { as: "Option", nullable: true } as const
332
-
333
- /**
334
- * @since 1.0.0
335
- */
336
- export const FormInputSchema = Schema.Struct({
337
- name: Schema.optional(Schema.String, optionNullable),
338
- action: Schema.optional(Schema.String, optionNullable),
339
- method: Schema.optional(Schema.String, optionNullable),
340
- encoding: Schema.optional(Schema.String, optionNullable),
341
- data: FormDataSchema
342
- })
343
-
344
- /**
345
- * @since 1.0.0
346
- */
347
- export type FormInputFrom = Schema.Schema.Encoded<typeof FormInputSchema>
348
-
349
- /**
350
- * @since 1.0.0
351
- */
352
- export interface FormInput extends Schema.Schema.Type<typeof FormInputSchema> {}
353
-
354
- /**
355
- * @since 1.0.0
356
- */
357
- export const FormDataEvent = Schema.extend(Schema.Struct({ from: Destination }), FormInputSchema)
358
-
359
- /**
360
- * @since 1.0.0
361
- */
362
- export type FormDataEventJson = Schema.Schema.Encoded<typeof FormDataEvent>
363
-
364
- /**
365
- * @since 1.0.0
366
- */
367
- export interface FormDataEvent extends Schema.Schema.Type<typeof FormDataEvent> {}
368
-
369
- /**
370
- * @since 1.0.0
371
- */
372
- export const cancelNavigation: CancelNavigation = new CancelNavigation()
373
-
374
- /**
375
- * @since 1.0.0
376
- */
107
+ export const navigate = (
108
+ url: string | URL,
109
+ options?: NavigateOptions,
110
+ ): Effect.Effect<Destination, NavigationError, Navigation> =>
111
+ Effect.flatMap(Navigation, (nav) => nav.navigate(url, options))
112
+
113
+ export const back: (options?: { readonly info?: unknown }) => Effect.Effect<
114
+ Destination,
115
+ NavigationError,
116
+ Navigation
117
+ > = (opts) => Effect.flatMap(Navigation, (nav) => nav.back(opts))
118
+
119
+ export const forward: (options?: { readonly info?: unknown }) => Effect.Effect<
120
+ Destination,
121
+ NavigationError,
122
+ Navigation
123
+ > = (opts) => Effect.flatMap(Navigation, (nav) => nav.forward(opts))
124
+
125
+ export const traverseTo: (
126
+ key: string,
127
+ options?: { readonly info?: unknown },
128
+ ) => Effect.Effect<Destination, NavigationError, Navigation> = (key, opts) =>
129
+ Effect.flatMap(Navigation, (nav) => nav.traverseTo(key, opts))
130
+
131
+ export const updateCurrentEntry: (options: { readonly state: unknown }) => Effect.Effect<
132
+ Destination,
133
+ NavigationError,
134
+ Navigation
135
+ > = (opts) => Effect.flatMap(Navigation, (nav) => nav.updateCurrentEntry(opts))
136
+
137
+ export const reload: (options?: {
138
+ readonly info?: unknown
139
+ readonly state?: unknown
140
+ }) => Effect.Effect<Destination, NavigationError, Navigation> = (opts) =>
141
+ Effect.flatMap(Navigation, (nav) => nav.reload(opts))
142
+
143
+ export const Transition: LazyRef.Computed<
144
+ Option.Option<TransitionEvent>,
145
+ never,
146
+ Navigation
147
+ > = LazyRef.computedFromTag(Navigation, (nav) => nav.transition)
148
+
149
+ export function handleRedirect(error: RedirectError) {
150
+ return navigate(error.path, {
151
+ history: 'replace',
152
+ ...error.options,
153
+ })
154
+ }
155
+
377
156
  export function redirectToPath(
378
157
  path: string | URL,
379
- options?: { readonly state?: unknown; readonly info?: unknown }
158
+ options?: { readonly state?: unknown; readonly info?: unknown },
380
159
  ): RedirectError {
381
160
  return new RedirectError({ path, options })
382
161
  }
383
162
 
384
- /**
385
- * @since 1.0.0
386
- */
387
163
  export function isNavigationError(e: unknown): e is NavigationError {
388
- return e instanceof NavigationError
164
+ return NavigationError.is(e)
389
165
  }
390
166
 
391
- /**
392
- * @since 1.0.0
393
- */
394
167
  export function isRedirectError(e: unknown): e is RedirectError {
395
- return e instanceof RedirectError
168
+ return RedirectError.is(e)
396
169
  }
397
170
 
398
- /**
399
- * @since 1.0.0
400
- */
401
171
  export function isCancelNavigation(e: unknown): e is CancelNavigation {
402
- return e instanceof CancelNavigation
172
+ return CancelNavigation.is(e)
403
173
  }
404
174
 
405
- /**
406
- * @since 1.0.0
407
- */
408
- export const navigate = (
409
- url: string | URL,
410
- options?: NavigateOptions
411
- ): Effect.Effect<Destination, NavigationError, Navigation> => Navigation.withEffect((n) => n.navigate(url, options))
412
-
413
- /**
414
- * @since 1.0.0
415
- */
416
- export const back: (options?: { readonly info?: unknown }) => Effect.Effect<Destination, NavigationError, Navigation> =
417
- (opts) => Navigation.withEffect((n) => n.back(opts))
418
-
419
- /**
420
- * @since 1.0.0
421
- */
422
- export const forward: (
423
- options?: { readonly info?: unknown }
424
- ) => Effect.Effect<Destination, NavigationError, Navigation> = (
425
- opts
426
- ) => Navigation.withEffect((n) => n.forward(opts))
427
-
428
- /**
429
- * @since 1.0.0
430
- */
431
- export const traverseTo: (
432
- key: Uuid,
433
- options?: { readonly info?: unknown }
434
- ) => Effect.Effect<Destination, NavigationError, Navigation> = (key, opts) =>
435
- Navigation.withEffect((n) => n.traverseTo(key, opts))
436
-
437
- /**
438
- * @since 1.0.0
439
- */
440
- export const updateCurrentEntry: (
441
- options: { readonly state: unknown }
442
- ) => Effect.Effect<Destination, NavigationError, Navigation> = (opts) =>
443
- Navigation.withEffect((n) => n.updateCurrentEntry(opts))
444
-
445
- /**
446
- * @since 1.0.0
447
- */
448
- export const reload: (
449
- options?: { readonly info?: unknown; readonly state?: unknown }
450
- ) => Effect.Effect<Destination, NavigationError, Navigation> = (
451
- opts
452
- ) => Navigation.withEffect((n) => n.reload(opts))
453
-
454
- /**
455
- * @since 1.0.0
456
- */
457
- export const CurrentEntry: RefSubject.Computed<Destination, never, Navigation> = RefSubject.computedFromTag(
458
- Navigation,
459
- (nav) => nav.currentEntry
460
- )
175
+ export const cancelNavigation = Effect.suspend(() => new CancelNavigation())
461
176
 
462
- /**
463
- * @since 1.0.0
464
- */
465
- export function getCurrentPathFromUrl(location: Pick<URL, "pathname" | "search" | "hash">): string {
466
- return location.pathname + location.search + location.hash
177
+ export function beforeNavigation<R = never, R2 = never>(
178
+ handler: BeforeNavigationHandler<R, R2>,
179
+ ): Effect.Effect<void, never, Navigation | R | R2 | Scope.Scope> {
180
+ return Effect.flatMap(Navigation, (nav) => nav.beforeNavigation(handler))
467
181
  }
468
182
 
469
- /**
470
- * @since 1.0.0
471
- */
472
- export const CurrentPath: RefSubject.Computed<string, never, Navigation> = RefSubject.computedFromTag(
473
- Navigation,
474
- (nav) => RefSubject.map(nav.currentEntry, (e) => getCurrentPathFromUrl(e.url))
475
- )
476
- /**
477
- * @since 1.0.0
478
- */
479
- export const CurrentEntries: RefSubject.Computed<ReadonlyArray<Destination>, never, Navigation> = RefSubject
480
- .computedFromTag(
481
- Navigation,
482
- (n) => n.entries
483
- )
484
-
485
- /**
486
- * @since 1.0.0
487
- */
488
- export const CanGoForward: RefSubject.Computed<boolean, never, Navigation> = RefSubject.computedFromTag(
489
- Navigation,
490
- (n) => n.canGoForward
491
- )
492
-
493
- /**
494
- * @since 1.0.0
495
- */
496
- export const CanGoBack: RefSubject.Computed<boolean, never, Navigation> = RefSubject.computedFromTag(
497
- Navigation,
498
- (n) => n.canGoBack
499
- )
500
-
501
- /**
502
- * @since 1.0.0
503
- */
504
- export function handleRedirect(error: RedirectError) {
505
- return navigate(error.path, {
506
- history: "replace",
507
- ...error.options
508
- })
183
+ export function onNavigation<R = never, R2 = never>(
184
+ handler: NavigationHandler<R, R2>,
185
+ ): Effect.Effect<void, never, Navigation | R | R2 | Scope.Scope> {
186
+ return Effect.flatMap(Navigation, (nav) => nav.onNavigation(handler))
509
187
  }
510
188
 
511
- /**
512
- * @since 1.0.0
513
- */
514
189
  export function submit(
515
- data: FormData,
516
- formInput?: Simplify<Omit<FormInputFrom, "data">>
190
+ form: FormSubmit,
517
191
  ): Effect.Effect<
518
- Option.Option<HttpClient.response.ClientResponse>,
519
- NavigationError | HttpClient.error.HttpClientError,
520
- Navigation | HttpClient.client.Client.Default | Scope.Scope
192
+ readonly [Destination, HttpClientResponse.HttpClientResponse],
193
+ NavigationError | FormSubmitError,
194
+ Navigation | HttpClient | Scope.Scope
521
195
  > {
522
- return Navigation.withEffect((n) => n.submit(data, formInput))
523
- }
524
-
525
- /**
526
- * @since 1.0.0
527
- */
528
- export function onFormData<R = never, R2 = never>(
529
- handler: FormDataHandler<R, R2>
530
- ): Effect.Effect<void, never, Navigation | R | R2 | Scope.Scope> {
531
- return Navigation.withEffect((n) => n.onFormData(handler))
196
+ return Effect.flatMap(Navigation, (nav) => nav.submit(form))
532
197
  }
@@ -0,0 +1,5 @@
1
+ import * as Schema from 'effect/Schema'
2
+
3
+ export const NavigationType = Schema.Literal('push', 'replace', 'reload', 'traverse')
4
+
5
+ export type NavigationType = typeof NavigationType.Type
@@ -0,0 +1,8 @@
1
+ import * as Schema from 'effect/Schema'
2
+ import { Destination } from './Destination.js'
3
+
4
+ export const ProposedDestination = Destination.pipe(Schema.omit('id', 'key'))
5
+
6
+ export type ProposedDestinationEncoded = Schema.Schema.Encoded<typeof ProposedDestination>
7
+
8
+ export interface ProposedDestination extends Schema.Schema.Type<typeof ProposedDestination> {}