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