@typed/navigation 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -0
- package/biome.json +39 -0
- package/dist/Blocking.d.ts +23 -0
- package/dist/Blocking.js +41 -0
- package/dist/Blocking.js.map +1 -0
- package/dist/Destination.d.ts +11 -0
- package/dist/Destination.js +10 -0
- package/dist/Destination.js.map +1 -0
- package/dist/Error.d.ts +33 -0
- package/dist/Error.js +22 -0
- package/dist/Error.js.map +1 -0
- package/dist/Event.d.ts +45 -0
- package/dist/Event.js +17 -0
- package/dist/Event.js.map +1 -0
- package/dist/Forms.d.ts +79 -0
- package/dist/Forms.js +111 -0
- package/dist/Forms.js.map +1 -0
- package/dist/Handler.d.ts +6 -0
- package/dist/Handler.js +2 -0
- package/dist/Handler.js.map +1 -0
- package/dist/{dts/Layer.d.ts → Layer.d.ts} +11 -8
- package/dist/{esm/Layer.js → Layer.js} +2 -2
- package/dist/Layer.js.map +1 -0
- package/dist/NavigateOptions.d.ts +7 -0
- package/dist/NavigateOptions.js +7 -0
- package/dist/NavigateOptions.js.map +1 -0
- package/dist/Navigation.d.ts +79 -0
- package/dist/Navigation.js +49 -0
- package/dist/Navigation.js.map +1 -0
- package/dist/NavigationType.d.ts +3 -0
- package/dist/NavigationType.js +3 -0
- package/dist/NavigationType.js.map +1 -0
- package/dist/ProposedDestination.d.ts +13 -0
- package/dist/ProposedDestination.js +4 -0
- package/dist/ProposedDestination.js.map +1 -0
- package/dist/Url.d.ts +13 -0
- package/dist/Url.js +72 -0
- package/dist/Url.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/fromWindow.d.ts +4 -0
- package/dist/{esm/internal → internal}/fromWindow.js +125 -136
- package/dist/internal/fromWindow.js.map +1 -0
- package/dist/internal/memory.d.ts +6 -0
- package/dist/internal/memory.js +59 -0
- package/dist/internal/memory.js.map +1 -0
- package/dist/{dts/internal → internal}/shared.d.ts +44 -46
- package/dist/{esm/internal → internal}/shared.js +121 -168
- package/dist/internal/shared.js.map +1 -0
- package/package.json +35 -53
- package/readme.md +243 -0
- package/src/Blocking.ts +65 -65
- package/src/Destination.ts +14 -0
- package/src/Error.ts +28 -0
- package/src/Event.ts +26 -0
- package/src/Forms.ts +216 -0
- package/src/Handler.ts +16 -0
- package/src/Layer.ts +20 -9
- package/src/NavigateOptions.ts +9 -0
- package/src/Navigation.test.ts +697 -0
- package/src/Navigation.ts +135 -472
- package/src/NavigationType.ts +5 -0
- package/src/ProposedDestination.ts +8 -0
- package/src/Url.ts +106 -0
- package/src/index.ts +12 -17
- package/src/internal/fromWindow.ts +163 -234
- package/src/internal/memory.ts +62 -49
- package/src/internal/shared.ts +218 -377
- package/tsconfig.json +30 -0
- package/Blocking/package.json +0 -6
- package/LICENSE +0 -21
- package/Layer/package.json +0 -6
- package/Navigation/package.json +0 -6
- package/README.md +0 -5
- package/dist/cjs/Blocking.js +0 -58
- package/dist/cjs/Blocking.js.map +0 -1
- package/dist/cjs/Layer.js +0 -27
- package/dist/cjs/Layer.js.map +0 -1
- package/dist/cjs/Navigation.js +0 -278
- package/dist/cjs/Navigation.js.map +0 -1
- package/dist/cjs/index.js +0 -39
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/internal/fromWindow.js +0 -436
- package/dist/cjs/internal/fromWindow.js.map +0 -1
- package/dist/cjs/internal/memory.js +0 -72
- package/dist/cjs/internal/memory.js.map +0 -1
- package/dist/cjs/internal/shared.js +0 -525
- package/dist/cjs/internal/shared.js.map +0 -1
- package/dist/dts/Blocking.d.ts +0 -34
- package/dist/dts/Blocking.d.ts.map +0 -1
- package/dist/dts/Layer.d.ts.map +0 -1
- package/dist/dts/Navigation.d.ts +0 -451
- package/dist/dts/Navigation.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -17
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/internal/fromWindow.d.ts +0 -12
- package/dist/dts/internal/fromWindow.d.ts.map +0 -1
- package/dist/dts/internal/memory.d.ts +0 -6
- package/dist/dts/internal/memory.d.ts.map +0 -1
- package/dist/dts/internal/shared.d.ts.map +0 -1
- package/dist/esm/Blocking.js +0 -46
- package/dist/esm/Blocking.js.map +0 -1
- package/dist/esm/Layer.js.map +0 -1
- package/dist/esm/Navigation.js +0 -238
- package/dist/esm/Navigation.js.map +0 -1
- package/dist/esm/index.js +0 -17
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal/fromWindow.js.map +0 -1
- package/dist/esm/internal/memory.js +0 -56
- package/dist/esm/internal/memory.js.map +0 -1
- package/dist/esm/internal/shared.js.map +0 -1
- package/dist/esm/package.json +0 -4
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import * as
|
|
9
|
-
import
|
|
10
|
-
import type
|
|
11
|
-
import
|
|
12
|
-
import type {
|
|
13
|
-
import {
|
|
1
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
2
|
+
import { GetRandomValues, type Uuid4 } from '@typed/id';
|
|
3
|
+
import * as LazyRef from '@typed/lazy-ref';
|
|
4
|
+
import { Schema } from 'effect';
|
|
5
|
+
import type * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Option from 'effect/Option';
|
|
8
|
+
import type * as Scope from 'effect/Scope';
|
|
9
|
+
import { Destination } from '../Destination.js';
|
|
10
|
+
import { FormSubmitError, type NavigationError } from '../Error.js';
|
|
11
|
+
import { TransitionEvent } from '../Event.js';
|
|
12
|
+
import type { FormSubmit } from '../Forms.js';
|
|
13
|
+
import type { BeforeNavigationHandler, NavigationHandler } from '../Handler.js';
|
|
14
|
+
import type { Commit } from '../Layer.js';
|
|
15
|
+
import type { NavigateOptions } from '../NavigateOptions.js';
|
|
16
|
+
import type { ProposedDestination } from '../ProposedDestination.js';
|
|
14
17
|
export type NavigationState = {
|
|
15
18
|
readonly entries: ReadonlyArray<Destination>;
|
|
16
19
|
readonly index: number;
|
|
17
|
-
readonly transition: Option.Option<
|
|
20
|
+
readonly transition: Option.Option<TransitionEvent>;
|
|
18
21
|
};
|
|
19
22
|
export declare const NavigationState: Schema.Struct<{
|
|
20
23
|
entries: Schema.Array$<Schema.Struct<{
|
|
21
|
-
id: Schema.
|
|
22
|
-
key: Schema.
|
|
23
|
-
url: Schema.transformOrFail<typeof Schema.String, Schema.
|
|
24
|
+
id: typeof Schema.UUID;
|
|
25
|
+
key: typeof Schema.UUID;
|
|
26
|
+
url: Schema.transformOrFail<typeof Schema.String, Schema.instanceOf<URL>, never>;
|
|
24
27
|
state: typeof Schema.Unknown;
|
|
25
28
|
sameDocument: typeof Schema.Boolean;
|
|
26
29
|
}>>;
|
|
@@ -28,12 +31,13 @@ export declare const NavigationState: Schema.Struct<{
|
|
|
28
31
|
transition: Schema.OptionFromNullishOr<Schema.Struct<{
|
|
29
32
|
type: Schema.Literal<["push", "replace", "reload", "traverse"]>;
|
|
30
33
|
from: Schema.Struct<{
|
|
31
|
-
id: Schema.
|
|
32
|
-
key: Schema.
|
|
33
|
-
url: Schema.transformOrFail<typeof Schema.String, Schema.
|
|
34
|
+
id: typeof Schema.UUID;
|
|
35
|
+
key: typeof Schema.UUID;
|
|
36
|
+
url: Schema.transformOrFail<typeof Schema.String, Schema.instanceOf<URL>, never>;
|
|
34
37
|
state: typeof Schema.Unknown;
|
|
35
38
|
sameDocument: typeof Schema.Boolean;
|
|
36
39
|
}>;
|
|
40
|
+
delta: typeof Schema.Number;
|
|
37
41
|
to: Schema.Union<[Schema.SchemaClass<{
|
|
38
42
|
readonly url: URL;
|
|
39
43
|
readonly state: unknown;
|
|
@@ -43,34 +47,32 @@ export declare const NavigationState: Schema.Struct<{
|
|
|
43
47
|
readonly state: unknown;
|
|
44
48
|
readonly sameDocument: boolean;
|
|
45
49
|
}, never>, Schema.Struct<{
|
|
46
|
-
id: Schema.
|
|
47
|
-
key: Schema.
|
|
48
|
-
url: Schema.transformOrFail<typeof Schema.String, Schema.
|
|
50
|
+
id: typeof Schema.UUID;
|
|
51
|
+
key: typeof Schema.UUID;
|
|
52
|
+
url: Schema.transformOrFail<typeof Schema.String, Schema.instanceOf<URL>, never>;
|
|
49
53
|
state: typeof Schema.Unknown;
|
|
50
54
|
sameDocument: typeof Schema.Boolean;
|
|
51
55
|
}>]>;
|
|
56
|
+
info: typeof Schema.Unknown;
|
|
52
57
|
}>>;
|
|
53
58
|
}>;
|
|
54
|
-
export declare
|
|
59
|
+
export declare function getUrl(origin: string, urlOrPath: string | URL, base: string): URL;
|
|
55
60
|
export type ModelAndIntent = {
|
|
56
|
-
readonly state:
|
|
57
|
-
readonly
|
|
58
|
-
readonly
|
|
59
|
-
readonly beforeHandlers: RefSubject.RefSubject<Set<readonly [BeforeNavigationHandler<any, any>, Context.Context<any>]>>;
|
|
60
|
-
readonly handlers: RefSubject.RefSubject<Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>>;
|
|
61
|
-
readonly formDataHandlers: RefSubject.RefSubject<Set<readonly [FormDataHandler<any, any>, Context.Context<any>]>>;
|
|
61
|
+
readonly state: LazyRef.LazyRef<NavigationState>;
|
|
62
|
+
readonly beforeHandlers: LazyRef.LazyRef<Set<readonly [BeforeNavigationHandler<any, any>, Context.Context<any>]>>;
|
|
63
|
+
readonly handlers: LazyRef.LazyRef<Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>>;
|
|
62
64
|
readonly commit: Commit;
|
|
63
65
|
};
|
|
64
|
-
export declare function setupFromModelAndIntent(modelAndIntent: ModelAndIntent, origin: string, base: string, getRandomValues: Context.
|
|
66
|
+
export declare function setupFromModelAndIntent(modelAndIntent: ModelAndIntent, origin: string, base: string, getRandomValues: Context.Tag.Service<typeof GetRandomValues>, newNavigationState?: () => NavigationState): {
|
|
65
67
|
back: (options?: {
|
|
66
68
|
readonly info?: unknown;
|
|
67
69
|
}, skipCommit?: boolean) => Effect.Effect<Destination, NavigationError, never>;
|
|
68
70
|
base: string;
|
|
69
71
|
beforeNavigation: <R = never, R2 = never>(handler: BeforeNavigationHandler<R, R2>) => Effect.Effect<void, never, R | R2 | Scope.Scope>;
|
|
70
|
-
canGoBack:
|
|
71
|
-
canGoForward:
|
|
72
|
-
currentEntry:
|
|
73
|
-
entries:
|
|
72
|
+
canGoBack: LazyRef.Computed<boolean, never, never>;
|
|
73
|
+
canGoForward: LazyRef.Computed<boolean, never, never>;
|
|
74
|
+
currentEntry: LazyRef.Computed<Destination, never, never>;
|
|
75
|
+
entries: LazyRef.Computed<readonly Destination[], never, never>;
|
|
74
76
|
forward: (options?: {
|
|
75
77
|
readonly info?: unknown;
|
|
76
78
|
}, skipCommit?: boolean) => Effect.Effect<Destination, NavigationError, never>;
|
|
@@ -80,32 +82,28 @@ export declare function setupFromModelAndIntent(modelAndIntent: ModelAndIntent,
|
|
|
80
82
|
reload: (options?: {
|
|
81
83
|
readonly info?: unknown;
|
|
82
84
|
}, skipCommit?: boolean) => Effect.Effect<Destination, NavigationError, never>;
|
|
83
|
-
transition:
|
|
85
|
+
transition: LazyRef.Computed<Option.Option<TransitionEvent>, never, never>;
|
|
84
86
|
traverseTo: (key: Destination["key"], options?: {
|
|
85
87
|
readonly info?: unknown;
|
|
86
88
|
}, skipCommit?: boolean) => Effect.Effect<Destination, NavigationError, never>;
|
|
87
89
|
updateCurrentEntry: (options: {
|
|
88
90
|
readonly state: unknown;
|
|
89
91
|
}) => Effect.Effect<Destination, NavigationError, never>;
|
|
90
|
-
submit: (
|
|
91
|
-
onFormData: <R = never, R2 = never>(handler: FormDataHandler<R, R2>) => Effect.Effect<void, never, R | R2 | Scope.Scope>;
|
|
92
|
+
submit: (form: FormSubmit) => Effect.Effect<readonly [Destination, import("@effect/platform/HttpClientResponse").HttpClientResponse], NavigationError | FormSubmitError, Scope.Scope | HttpClient.HttpClient>;
|
|
92
93
|
};
|
|
93
|
-
export declare function makeRedirectEvent(origin: string, redirect: RedirectError, from: Destination): Effect.Effect<BeforeNavigationEvent, never, GetRandomValues>;
|
|
94
94
|
export declare function makeOrUpdateDestination(navigationState: NavigationState, url: URL, state: unknown, origin: string): Effect.Effect<Destination, never, GetRandomValues>;
|
|
95
95
|
export declare function makeDestination(url: URL, state: unknown, origin: string): Effect.Effect<Destination, never, GetRandomValues>;
|
|
96
96
|
export declare function upgradeProposedDestination(proposed: ProposedDestination): Effect.Effect<Destination, never, GetRandomValues>;
|
|
97
97
|
export type PatchedState = {
|
|
98
|
-
readonly __typed__navigation__id__:
|
|
99
|
-
readonly __typed__navigation__key__:
|
|
98
|
+
readonly __typed__navigation__id__: Uuid4;
|
|
99
|
+
readonly __typed__navigation__key__: Uuid4;
|
|
100
100
|
readonly __typed__navigation__state__: unknown;
|
|
101
101
|
};
|
|
102
102
|
export declare function isPatchedState(state: unknown): state is PatchedState;
|
|
103
103
|
export declare function getOriginalState(state: unknown): unknown;
|
|
104
104
|
export declare function getOriginFromUrl(url: string | URL): string;
|
|
105
105
|
export declare function isDestination(proposed: ProposedDestination): proposed is Destination;
|
|
106
|
-
export declare
|
|
107
|
-
beforeHandlers:
|
|
108
|
-
handlers:
|
|
109
|
-
formDataHandlers: RefSubject.RefSubject<Set<readonly [FormDataHandler<any, any>, Context.Context<any>]>, never, never>;
|
|
106
|
+
export declare const makeHandlersState: Effect.Effect<{
|
|
107
|
+
beforeHandlers: LazyRef.LazyRef<Set<readonly [BeforeNavigationHandler<any, any>, Context.Context<any>]>, never, never>;
|
|
108
|
+
handlers: LazyRef.LazyRef<Set<readonly [NavigationHandler<any, any>, Context.Context<any>]>, never, never>;
|
|
110
109
|
}, never, Scope.Scope>;
|
|
111
|
-
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -1,25 +1,54 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import * as
|
|
7
|
-
import * as
|
|
8
|
-
import
|
|
1
|
+
import * as Headers from '@effect/platform/Headers';
|
|
2
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
3
|
+
import { GetRandomValues, makeUuid4 } from '@typed/id';
|
|
4
|
+
import * as LazyRef from '@typed/lazy-ref';
|
|
5
|
+
import { Cause, Schema } from 'effect';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Either from 'effect/Either';
|
|
8
|
+
import * as Option from 'effect/Option';
|
|
9
|
+
import { Destination } from '../Destination.js';
|
|
10
|
+
import { FormSubmitError, } from '../Error.js';
|
|
11
|
+
import { TransitionEvent } from '../Event.js';
|
|
9
12
|
export const NavigationState = Schema.Struct({
|
|
10
13
|
entries: Schema.Array(Destination),
|
|
11
14
|
index: Schema.Number,
|
|
12
|
-
transition: Schema.OptionFromNullishOr(
|
|
15
|
+
transition: Schema.OptionFromNullishOr(TransitionEvent, null),
|
|
13
16
|
});
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
export function getUrl(origin, urlOrPath, base) {
|
|
18
|
+
if (typeof urlOrPath === 'string') {
|
|
19
|
+
const url = new URL(urlOrPath, origin);
|
|
20
|
+
// If the URL is relative, join it with the base
|
|
21
|
+
if (isRelativeUrl(urlOrPath)) {
|
|
22
|
+
url.pathname = joinPath(base, url.pathname);
|
|
23
|
+
}
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
return urlOrPath;
|
|
27
|
+
}
|
|
28
|
+
function isRelativeUrl(url) {
|
|
29
|
+
if (url.includes('://'))
|
|
30
|
+
return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
function joinPath(a, b) {
|
|
34
|
+
const aEndsWithSlash = a[a.length - 1] === '/';
|
|
35
|
+
const bStartsWithSlash = b[0] === '/';
|
|
36
|
+
if (aEndsWithSlash && bStartsWithSlash) {
|
|
37
|
+
return a + b.slice(1);
|
|
38
|
+
}
|
|
39
|
+
if (!aEndsWithSlash && !bStartsWithSlash) {
|
|
40
|
+
return `${a}/${b}`;
|
|
41
|
+
}
|
|
42
|
+
return a + b;
|
|
43
|
+
}
|
|
17
44
|
const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308]);
|
|
18
45
|
export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomValues, newNavigationState) {
|
|
19
|
-
const { beforeHandlers,
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
46
|
+
const { beforeHandlers, commit, handlers, state } = modelAndIntent;
|
|
47
|
+
const canGoBack = LazyRef.map(state, (s) => s.index > 0);
|
|
48
|
+
const canGoForward = LazyRef.map(state, (s) => s.index < s.entries.length - 1);
|
|
49
|
+
const entries = LazyRef.map(state, (s) => s.entries);
|
|
50
|
+
const currentEntry = LazyRef.map(state, (s) => s.entries[s.index]);
|
|
51
|
+
const transition = LazyRef.map(state, (s) => s.transition);
|
|
23
52
|
const runBeforeHandlers = (event) => Effect.gen(function* () {
|
|
24
53
|
const handlers = yield* beforeHandlers;
|
|
25
54
|
const matches = [];
|
|
@@ -27,7 +56,7 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
27
56
|
const exit = yield* handler(event).pipe(Effect.provide(ctx), Effect.either);
|
|
28
57
|
if (Either.isRight(exit)) {
|
|
29
58
|
const match = exit.right;
|
|
30
|
-
if (Option.isSome(match)) {
|
|
59
|
+
if (match !== undefined && Option.isSome(match)) {
|
|
31
60
|
matches.push(Effect.provide(match.value, ctx));
|
|
32
61
|
}
|
|
33
62
|
}
|
|
@@ -50,7 +79,7 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
50
79
|
const matches = [];
|
|
51
80
|
for (const [handler, ctx] of eventHandlers) {
|
|
52
81
|
const match = yield* Effect.provide(handler(event), ctx);
|
|
53
|
-
if (Option.isSome(match)) {
|
|
82
|
+
if (match !== undefined && Option.isSome(match)) {
|
|
54
83
|
matches.push(Effect.provide(match.value, ctx));
|
|
55
84
|
}
|
|
56
85
|
}
|
|
@@ -58,44 +87,10 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
58
87
|
yield* Effect.all(matches, { discard: true });
|
|
59
88
|
}
|
|
60
89
|
});
|
|
61
|
-
const runFormDataHandlers = (event) => Effect.gen(function* () {
|
|
62
|
-
const handlers = yield* formDataHandlers;
|
|
63
|
-
const matches = [];
|
|
64
|
-
for (const [handler, ctx] of handlers) {
|
|
65
|
-
const exit = yield* handler(event).pipe(Effect.provide(ctx), Effect.either);
|
|
66
|
-
if (Either.isRight(exit)) {
|
|
67
|
-
const match = exit.right;
|
|
68
|
-
if (Option.isSome(match)) {
|
|
69
|
-
matches.push(Effect.provide(match.value, ctx));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
return Either.left(exit.left);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (matches.length > 0) {
|
|
77
|
-
for (const match of matches) {
|
|
78
|
-
const exit = yield* Effect.either(match);
|
|
79
|
-
if (Either.isLeft(exit)) {
|
|
80
|
-
return Either.left(exit.left);
|
|
81
|
-
}
|
|
82
|
-
else if (Option.isSome(exit.right)) {
|
|
83
|
-
return Either.right(exit.right);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// Only if there are 0 matches, we'll make a request to the server ourselves
|
|
89
|
-
const response = yield* makeFormDataRequest(event, Option.getOrElse(event.action, () => event.from.url.href));
|
|
90
|
-
return Either.right(Option.some(response));
|
|
91
|
-
}
|
|
92
|
-
return Either.right(Option.none());
|
|
93
|
-
});
|
|
94
90
|
const runNavigationEvent = (beforeEvent, get, set, depth, skipCommit = false) => Effect.gen(function* () {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
transition: Option.some(beforeEvent)
|
|
91
|
+
const current = yield* set({
|
|
92
|
+
...(yield* get),
|
|
93
|
+
transition: Option.some(beforeEvent),
|
|
99
94
|
});
|
|
100
95
|
if (!skipCommit) {
|
|
101
96
|
const beforeError = yield* runBeforeHandlers(beforeEvent);
|
|
@@ -109,7 +104,7 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
109
104
|
if (!skipCommit) {
|
|
110
105
|
yield* commit(to, beforeEvent);
|
|
111
106
|
}
|
|
112
|
-
if (newNavigationState) {
|
|
107
|
+
if (newNavigationState !== undefined) {
|
|
113
108
|
const { entries, index } = yield* set(newNavigationState());
|
|
114
109
|
return entries[index];
|
|
115
110
|
}
|
|
@@ -117,21 +112,21 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
117
112
|
const event = {
|
|
118
113
|
type: beforeEvent.type,
|
|
119
114
|
info: beforeEvent.info,
|
|
120
|
-
destination: to
|
|
115
|
+
destination: to,
|
|
121
116
|
};
|
|
122
|
-
if (beforeEvent.type ===
|
|
117
|
+
if (beforeEvent.type === 'push') {
|
|
123
118
|
const index = current.index + 1;
|
|
124
119
|
const entries = current.entries.slice(0, index).concat([to]);
|
|
125
120
|
yield* set({ entries, index, transition: Option.none() });
|
|
126
121
|
}
|
|
127
|
-
else if (beforeEvent.type ===
|
|
122
|
+
else if (beforeEvent.type === 'replace') {
|
|
128
123
|
const index = current.index;
|
|
129
124
|
const before = current.entries.slice(0, index);
|
|
130
125
|
const after = current.entries.slice(index + 1);
|
|
131
126
|
const entries = [...before, to, ...after];
|
|
132
127
|
yield* set({ entries, index, transition: Option.none() });
|
|
133
128
|
}
|
|
134
|
-
else if (beforeEvent.type ===
|
|
129
|
+
else if (beforeEvent.type === 'reload') {
|
|
135
130
|
yield* set({ ...current, transition: Option.none() });
|
|
136
131
|
}
|
|
137
132
|
else {
|
|
@@ -140,44 +135,40 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
140
135
|
yield* set({
|
|
141
136
|
...current,
|
|
142
137
|
index: nextIndex,
|
|
143
|
-
transition: Option.none()
|
|
138
|
+
transition: Option.none(),
|
|
144
139
|
});
|
|
145
140
|
}
|
|
146
141
|
yield* runHandlers(event);
|
|
147
142
|
}
|
|
148
143
|
return to;
|
|
149
|
-
}).pipe(
|
|
144
|
+
}).pipe(Effect.provideService(GetRandomValues, getRandomValues));
|
|
150
145
|
const handleError = (error, get, set, depth) => Effect.gen(function* () {
|
|
151
146
|
if (depth >= 25) {
|
|
152
|
-
return yield* Effect.dieMessage(
|
|
147
|
+
return yield* Effect.dieMessage('Redirect loop detected.');
|
|
153
148
|
}
|
|
154
149
|
const { entries, index } = yield* get;
|
|
155
150
|
const from = entries[index];
|
|
156
|
-
if (error._tag ===
|
|
151
|
+
if (error._tag === 'CancelNavigation') {
|
|
157
152
|
yield* set({ entries, index, transition: Option.none() });
|
|
158
153
|
return from;
|
|
159
154
|
}
|
|
160
155
|
else {
|
|
161
|
-
const event = yield* makeRedirectEvent(origin, error, from);
|
|
156
|
+
const event = yield* makeRedirectEvent(origin, error, from, base);
|
|
162
157
|
return yield* runNavigationEvent(event, get, set, depth + 1);
|
|
163
158
|
}
|
|
164
|
-
}).pipe(
|
|
159
|
+
}).pipe(Effect.provideService(GetRandomValues, getRandomValues));
|
|
165
160
|
const navigate = (pathOrUrl, options, skipCommit = false) => state.runUpdates(({ get, set }) => Effect.gen(function* () {
|
|
166
161
|
const state = yield* get;
|
|
167
162
|
const from = state.entries[state.index];
|
|
168
|
-
const history = options?.history ??
|
|
169
|
-
const to = yield* makeOrUpdateDestination(state, getUrl(origin, pathOrUrl), options?.state, origin).pipe(
|
|
170
|
-
const type = history ===
|
|
171
|
-
? from.key === to.key
|
|
172
|
-
? "replace"
|
|
173
|
-
: "push"
|
|
174
|
-
: history;
|
|
163
|
+
const history = options?.history ?? 'auto';
|
|
164
|
+
const to = yield* makeOrUpdateDestination(state, getUrl(origin, pathOrUrl, base), options?.state, origin).pipe(Effect.provideService(GetRandomValues, getRandomValues));
|
|
165
|
+
const type = history === 'auto' ? (from.key === to.key ? 'replace' : 'push') : history;
|
|
175
166
|
const event = {
|
|
176
167
|
type,
|
|
177
168
|
from,
|
|
178
169
|
to,
|
|
179
|
-
delta: type ===
|
|
180
|
-
info: options?.info
|
|
170
|
+
delta: type === 'replace' ? 0 : 1,
|
|
171
|
+
info: options?.info,
|
|
181
172
|
};
|
|
182
173
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit);
|
|
183
174
|
}));
|
|
@@ -188,15 +179,15 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
188
179
|
const nextIndex = entries.findIndex((e) => e.key === key);
|
|
189
180
|
if (nextIndex === -1)
|
|
190
181
|
return from;
|
|
191
|
-
const id = yield*
|
|
182
|
+
const id = yield* makeUuid4.pipe(Effect.provideService(GetRandomValues, getRandomValues));
|
|
192
183
|
const to = { ...entries[nextIndex], id };
|
|
193
184
|
const delta = nextIndex - index;
|
|
194
185
|
const event = {
|
|
195
|
-
type:
|
|
186
|
+
type: 'traverse',
|
|
196
187
|
from,
|
|
197
188
|
to,
|
|
198
189
|
delta,
|
|
199
|
-
info: options?.info
|
|
190
|
+
info: options?.info,
|
|
200
191
|
};
|
|
201
192
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit);
|
|
202
193
|
}));
|
|
@@ -218,82 +209,61 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
218
209
|
const { entries, index } = yield* state;
|
|
219
210
|
const current = entries[index];
|
|
220
211
|
const event = {
|
|
221
|
-
type:
|
|
212
|
+
type: 'reload',
|
|
222
213
|
from: current,
|
|
223
214
|
to: current,
|
|
224
215
|
delta: 0,
|
|
225
|
-
info: options?.info
|
|
216
|
+
info: options?.info,
|
|
226
217
|
};
|
|
227
218
|
return yield* runNavigationEvent(event, get, set, 0, skipCommit);
|
|
228
219
|
}));
|
|
229
220
|
const beforeNavigation = (handler) => Effect.contextWithEffect((ctx) => {
|
|
230
221
|
const entry = [handler, ctx];
|
|
231
|
-
|
|
222
|
+
const acquire = LazyRef.update(beforeHandlers, (handlers) => new Set([...handlers, entry]));
|
|
223
|
+
const release = Effect.ignoreLogged(LazyRef.update(beforeHandlers, (handlers) => {
|
|
232
224
|
const updated = new Set(handlers);
|
|
233
225
|
updated.delete(entry);
|
|
234
226
|
return updated;
|
|
235
|
-
}))
|
|
227
|
+
}));
|
|
228
|
+
return Effect.acquireRelease(acquire, () => release);
|
|
236
229
|
});
|
|
237
230
|
const onNavigation = (handler) => Effect.contextWithEffect((ctx) => {
|
|
238
231
|
const entry = [handler, ctx];
|
|
239
|
-
|
|
232
|
+
const acquire = LazyRef.update(handlers, (handlers) => new Set([...handlers, entry]));
|
|
233
|
+
const release = Effect.ignoreLogged(LazyRef.update(handlers, (handlers) => {
|
|
240
234
|
const updated = new Set(handlers);
|
|
241
235
|
updated.delete(entry);
|
|
242
236
|
return updated;
|
|
243
|
-
}))
|
|
237
|
+
}));
|
|
238
|
+
return Effect.acquireRelease(acquire, () => release);
|
|
244
239
|
});
|
|
245
240
|
const updateCurrentEntry = (options) => state.runUpdates(({ get, set }) => Effect.gen(function* () {
|
|
246
241
|
const { entries, index } = yield* get;
|
|
247
242
|
const current = entries[index];
|
|
248
243
|
const event = {
|
|
249
|
-
type:
|
|
244
|
+
type: 'replace',
|
|
250
245
|
from: current,
|
|
251
246
|
to: { ...current, state: options.state },
|
|
252
247
|
delta: 0,
|
|
253
|
-
info: null
|
|
248
|
+
info: null,
|
|
254
249
|
};
|
|
255
250
|
return yield* runNavigationEvent(event, get, set, 0);
|
|
256
251
|
}));
|
|
257
|
-
const submit = (
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (Either.isLeft(either)) {
|
|
270
|
-
yield* handleError(either.left, get, set, 0);
|
|
271
|
-
return Option.none();
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
if (Option.isNone(either.right)) {
|
|
275
|
-
return either.right;
|
|
276
|
-
}
|
|
277
|
-
const response = either.right.value;
|
|
278
|
-
// If it is a redirect
|
|
279
|
-
if (REDIRECT_STATUS_CODES.has(response.status)) {
|
|
280
|
-
const location = Headers.get(response.headers, "location");
|
|
281
|
-
// And we have a location header
|
|
282
|
-
if (Option.isSome(location)) {
|
|
283
|
-
// Then we navigate to that location
|
|
284
|
-
yield* navigate(location.value, { history: "replace" });
|
|
285
|
-
}
|
|
252
|
+
const submit = (form) => Effect.gen(function* () {
|
|
253
|
+
const current = yield* currentEntry;
|
|
254
|
+
// Utilize the action URL if provided, otherwise use the current URL
|
|
255
|
+
const url = form.action ? getUrl(origin, form.action, base) : current.url;
|
|
256
|
+
// Submit the HTTP request
|
|
257
|
+
const response = yield* HttpClient[form.method](url, form).pipe(Effect.mapErrorCause((cause) => Cause.fail(new FormSubmitError({ cause }))));
|
|
258
|
+
// If the response is a redirect, navigate to the new URL
|
|
259
|
+
if (REDIRECT_STATUS_CODES.has(response.status)) {
|
|
260
|
+
// Get the location header
|
|
261
|
+
const location = Headers.get(response.headers, 'location');
|
|
262
|
+
if (Option.isSome(location)) {
|
|
263
|
+
return [yield* navigate(getUrl(origin, location.value, base), form), response];
|
|
286
264
|
}
|
|
287
|
-
return Option.some(response);
|
|
288
265
|
}
|
|
289
|
-
|
|
290
|
-
const onFormData = (handler) => Effect.contextWithEffect((ctx) => {
|
|
291
|
-
const entry = [handler, ctx];
|
|
292
|
-
return Effect.zipRight(RefSubject.update(formDataHandlers, (handlers) => new Set([...handlers, entry])), Effect.addFinalizer(() => RefSubject.update(formDataHandlers, (handlers) => {
|
|
293
|
-
const updated = new Set(handlers);
|
|
294
|
-
updated.delete(entry);
|
|
295
|
-
return updated;
|
|
296
|
-
})));
|
|
266
|
+
return [current, response];
|
|
297
267
|
});
|
|
298
268
|
const navigation = {
|
|
299
269
|
back,
|
|
@@ -312,20 +282,19 @@ export function setupFromModelAndIntent(modelAndIntent, origin, base, getRandomV
|
|
|
312
282
|
traverseTo,
|
|
313
283
|
updateCurrentEntry,
|
|
314
284
|
submit,
|
|
315
|
-
onFormData
|
|
316
285
|
};
|
|
317
286
|
return navigation;
|
|
318
287
|
}
|
|
319
|
-
|
|
288
|
+
function makeRedirectEvent(origin, redirect, from, base) {
|
|
320
289
|
return Effect.gen(function* () {
|
|
321
|
-
const url = getUrl(origin, redirect.path);
|
|
290
|
+
const url = getUrl(origin, redirect.path, base);
|
|
322
291
|
const to = yield* makeDestination(url, redirect.options?.state, origin);
|
|
323
292
|
const event = {
|
|
324
|
-
type:
|
|
293
|
+
type: 'replace',
|
|
325
294
|
from,
|
|
326
295
|
to,
|
|
327
296
|
delta: 0,
|
|
328
|
-
info: redirect.options?.info
|
|
297
|
+
info: redirect.options?.info,
|
|
329
298
|
};
|
|
330
299
|
return event;
|
|
331
300
|
});
|
|
@@ -333,16 +302,15 @@ export function makeRedirectEvent(origin, redirect, from) {
|
|
|
333
302
|
export function makeOrUpdateDestination(navigationState, url, state, origin) {
|
|
334
303
|
return Effect.gen(function* () {
|
|
335
304
|
const current = navigationState.entries[navigationState.index];
|
|
336
|
-
const isSameOriginAndPath = url.origin === current.url.origin &&
|
|
337
|
-
url.pathname === current.url.pathname;
|
|
305
|
+
const isSameOriginAndPath = url.origin === current.url.origin && url.pathname === current.url.pathname;
|
|
338
306
|
if (isSameOriginAndPath) {
|
|
339
|
-
const id = yield*
|
|
307
|
+
const id = yield* makeUuid4;
|
|
340
308
|
const destination = {
|
|
341
309
|
id,
|
|
342
310
|
key: current.key,
|
|
343
311
|
url,
|
|
344
312
|
state: getOriginalState(state),
|
|
345
|
-
sameDocument: url.origin === origin
|
|
313
|
+
sameDocument: url.origin === origin,
|
|
346
314
|
};
|
|
347
315
|
return destination;
|
|
348
316
|
}
|
|
@@ -359,42 +327,41 @@ export function makeDestination(url, state, origin) {
|
|
|
359
327
|
key: state.__typed__navigation__key__,
|
|
360
328
|
url,
|
|
361
329
|
state: state.__typed__navigation__state__,
|
|
362
|
-
sameDocument: url.origin === origin
|
|
330
|
+
sameDocument: url.origin === origin,
|
|
363
331
|
};
|
|
364
332
|
return destination;
|
|
365
333
|
}
|
|
366
|
-
const id = yield*
|
|
367
|
-
const key = yield*
|
|
334
|
+
const id = yield* makeUuid4;
|
|
335
|
+
const key = yield* makeUuid4;
|
|
368
336
|
const destination = {
|
|
369
337
|
id,
|
|
370
338
|
key,
|
|
371
339
|
url,
|
|
372
340
|
state,
|
|
373
|
-
sameDocument: url.origin === origin
|
|
341
|
+
sameDocument: url.origin === origin,
|
|
374
342
|
};
|
|
375
343
|
return destination;
|
|
376
344
|
});
|
|
377
345
|
}
|
|
378
346
|
export function upgradeProposedDestination(proposed) {
|
|
379
347
|
return Effect.gen(function* () {
|
|
380
|
-
const id = yield*
|
|
381
|
-
const key = yield*
|
|
348
|
+
const id = yield* makeUuid4;
|
|
349
|
+
const key = yield* makeUuid4;
|
|
382
350
|
const destination = {
|
|
383
351
|
id,
|
|
384
352
|
key,
|
|
385
353
|
url: proposed.url,
|
|
386
354
|
state: proposed.state,
|
|
387
|
-
sameDocument: proposed.sameDocument
|
|
355
|
+
sameDocument: proposed.sameDocument,
|
|
388
356
|
};
|
|
389
357
|
return destination;
|
|
390
358
|
});
|
|
391
359
|
}
|
|
392
360
|
export function isPatchedState(state) {
|
|
393
|
-
if (state === null || !(typeof state ===
|
|
361
|
+
if (state === null || !(typeof state === 'object') || Array.isArray(state)) {
|
|
394
362
|
return false;
|
|
395
363
|
}
|
|
396
|
-
if (
|
|
397
|
-
"__typed__navigation__key__" in state) {
|
|
364
|
+
if ('__typed__navigation__id__' in state && '__typed__navigation__key__' in state) {
|
|
398
365
|
return true;
|
|
399
366
|
}
|
|
400
367
|
return false;
|
|
@@ -406,7 +373,7 @@ export function getOriginalState(state) {
|
|
|
406
373
|
}
|
|
407
374
|
export function getOriginFromUrl(url) {
|
|
408
375
|
try {
|
|
409
|
-
if (typeof url ===
|
|
376
|
+
if (typeof url === 'string') {
|
|
410
377
|
return new URL(url).origin;
|
|
411
378
|
}
|
|
412
379
|
else {
|
|
@@ -414,33 +381,19 @@ export function getOriginFromUrl(url) {
|
|
|
414
381
|
}
|
|
415
382
|
}
|
|
416
383
|
catch {
|
|
417
|
-
return
|
|
384
|
+
return 'http://localhost';
|
|
418
385
|
}
|
|
419
386
|
}
|
|
420
387
|
export function isDestination(proposed) {
|
|
421
|
-
return
|
|
388
|
+
return 'id' in proposed && 'key' in proposed;
|
|
422
389
|
}
|
|
423
390
|
const strictEqual = (a, b) => a === b;
|
|
424
|
-
export
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
formDataHandlers
|
|
433
|
-
};
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
function makeFormDataRequest(event, url) {
|
|
437
|
-
const headers = new globalThis.Headers();
|
|
438
|
-
if (Option.isSome(event.encoding)) {
|
|
439
|
-
headers.set("Content-Type", event.encoding.value);
|
|
440
|
-
}
|
|
441
|
-
const method = Option.getOrElse(event.method, () => "POST");
|
|
442
|
-
return Effect.flatMap(HttpClient.HttpClient, (client) => client.execute(HttpClientRequest.make(method)(url, {
|
|
443
|
-
headers: Headers.fromInput(headers)
|
|
444
|
-
}).pipe(HttpClientRequest.bodyFormData(event.data))));
|
|
445
|
-
}
|
|
391
|
+
export const makeHandlersState = Effect.gen(function* () {
|
|
392
|
+
const beforeHandlers = yield* LazyRef.fromEffect(Effect.sync(() => new Set()), { eq: strictEqual });
|
|
393
|
+
const handlers = yield* LazyRef.fromEffect(Effect.sync(() => new Set()), { eq: strictEqual });
|
|
394
|
+
return {
|
|
395
|
+
beforeHandlers,
|
|
396
|
+
handlers,
|
|
397
|
+
};
|
|
398
|
+
});
|
|
446
399
|
//# sourceMappingURL=shared.js.map
|