@planet-matrix/mobius-model 0.5.0 → 0.9.0
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/CHANGELOG.md +61 -0
- package/README.md +123 -36
- package/dist/index.js +715 -4
- package/dist/index.js.map +981 -13
- package/oxlint.config.ts +6 -0
- package/package.json +36 -18
- package/src/abort/README.md +92 -0
- package/src/abort/abort-manager.ts +278 -0
- package/src/abort/abort-signal-listener-manager.ts +81 -0
- package/src/abort/index.ts +2 -0
- package/src/ai/README.md +1 -0
- package/src/ai/ai.ts +107 -0
- package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
- package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
- package/src/ai/chat-completion-ai/index.ts +7 -0
- package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
- package/src/ai/embedding-ai/embedding-ai.ts +63 -0
- package/src/ai/embedding-ai/embedding.ts +50 -0
- package/src/ai/embedding-ai/index.ts +4 -0
- package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
- package/src/ai/index.ts +4 -0
- package/src/aio/README.md +100 -0
- package/src/aio/content.ts +141 -0
- package/src/aio/index.ts +3 -0
- package/src/aio/json.ts +127 -0
- package/src/aio/prompt.ts +246 -0
- package/src/basic/README.md +72 -116
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +83 -64
- package/src/basic/index.ts +1 -0
- package/src/basic/is.ts +152 -71
- package/src/basic/promise.ts +29 -8
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- package/src/basic/string.ts +2 -33
- package/src/color/README.md +105 -0
- package/src/color/index.ts +3 -0
- package/src/color/internal.ts +42 -0
- package/src/color/rgb/analyze.ts +236 -0
- package/src/color/rgb/construct.ts +130 -0
- package/src/color/rgb/convert.ts +227 -0
- package/src/color/rgb/derive.ts +303 -0
- package/src/color/rgb/index.ts +6 -0
- package/src/color/rgb/internal.ts +208 -0
- package/src/color/rgb/parse.ts +302 -0
- package/src/color/rgb/serialize.ts +144 -0
- package/src/color/types.ts +57 -0
- package/src/color/xyz/analyze.ts +80 -0
- package/src/color/xyz/construct.ts +19 -0
- package/src/color/xyz/convert.ts +71 -0
- package/src/color/xyz/index.ts +3 -0
- package/src/color/xyz/internal.ts +23 -0
- package/src/credential/README.md +107 -0
- package/src/credential/api-key.ts +158 -0
- package/src/credential/bearer.ts +73 -0
- package/src/credential/index.ts +4 -0
- package/src/credential/json-web-token.ts +96 -0
- package/src/credential/password.ts +170 -0
- package/src/cron/README.md +86 -0
- package/src/cron/cron.ts +87 -0
- package/src/cron/index.ts +1 -0
- package/src/css/README.md +93 -0
- package/src/css/class.ts +559 -0
- package/src/css/index.ts +1 -0
- package/src/drizzle/README.md +1 -0
- package/src/drizzle/drizzle.ts +1 -0
- package/src/drizzle/helper.ts +47 -0
- package/src/drizzle/index.ts +5 -0
- package/src/drizzle/infer.ts +52 -0
- package/src/drizzle/kysely.ts +8 -0
- package/src/drizzle/pagination.ts +200 -0
- package/src/email/README.md +1 -0
- package/src/email/index.ts +1 -0
- package/src/email/resend.ts +25 -0
- package/src/encoding/README.md +66 -79
- package/src/encoding/base64.ts +13 -4
- package/src/environment/README.md +97 -0
- package/src/environment/basic.ts +26 -0
- package/src/environment/device.ts +311 -0
- package/src/environment/feature.ts +285 -0
- package/src/environment/geo.ts +337 -0
- package/src/environment/index.ts +7 -0
- package/src/environment/runtime.ts +400 -0
- package/src/environment/snapshot.ts +60 -0
- package/src/environment/variable.ts +239 -0
- package/src/event/README.md +90 -0
- package/src/event/class-event-proxy.ts +229 -0
- package/src/event/common.ts +29 -0
- package/src/event/event-manager.ts +203 -0
- package/src/event/index.ts +4 -0
- package/src/event/instance-event-proxy.ts +187 -0
- package/src/event/internal.ts +24 -0
- package/src/exception/README.md +96 -0
- package/src/exception/browser.ts +219 -0
- package/src/exception/index.ts +4 -0
- package/src/exception/nodejs.ts +169 -0
- package/src/exception/normalize.ts +106 -0
- package/src/exception/types.ts +99 -0
- package/src/form/README.md +25 -0
- package/src/form/index.ts +1 -0
- package/src/form/inputor-controller/base.ts +874 -0
- package/src/form/inputor-controller/boolean.ts +39 -0
- package/src/form/inputor-controller/file.ts +39 -0
- package/src/form/inputor-controller/form.ts +181 -0
- package/src/form/inputor-controller/helper.ts +117 -0
- package/src/form/inputor-controller/index.ts +17 -0
- package/src/form/inputor-controller/multi-select.ts +99 -0
- package/src/form/inputor-controller/number.ts +116 -0
- package/src/form/inputor-controller/select.ts +109 -0
- package/src/form/inputor-controller/text.ts +82 -0
- package/src/http/READMD.md +1 -0
- package/src/http/api/api-core.ts +84 -0
- package/src/http/api/api-handler.ts +79 -0
- package/src/http/api/api-host.ts +47 -0
- package/src/http/api/api-result.ts +56 -0
- package/src/http/api/api-schema.ts +154 -0
- package/src/http/api/api-server.ts +130 -0
- package/src/http/api/api-test.ts +142 -0
- package/src/http/api/api-type.ts +37 -0
- package/src/http/api/api.ts +81 -0
- package/src/http/api/index.ts +11 -0
- package/src/http/api-adapter/api-core-node-http.ts +260 -0
- package/src/http/api-adapter/api-host-node-http.ts +156 -0
- package/src/http/api-adapter/api-result-arktype.ts +297 -0
- package/src/http/api-adapter/api-result-zod.ts +286 -0
- package/src/http/api-adapter/index.ts +5 -0
- package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
- package/src/http/bin/gen-api-list/index.ts +1 -0
- package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
- package/src/http/bin/gen-api-test/index.ts +1 -0
- package/src/http/bin/gen-api-type/calc-code.ts +25 -0
- package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
- package/src/http/bin/gen-api-type/index.ts +2 -0
- package/src/http/bin/index.ts +2 -0
- package/src/http/index.ts +3 -0
- package/src/huawei/README.md +1 -0
- package/src/huawei/index.ts +2 -0
- package/src/huawei/moderation/index.ts +1 -0
- package/src/huawei/moderation/moderation.ts +355 -0
- package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
- package/src/huawei/obs/index.ts +1 -0
- package/src/huawei/obs/obs.ts +42 -0
- package/src/identifier/README.md +92 -0
- package/src/identifier/id.ts +119 -0
- package/src/identifier/index.ts +2 -0
- package/src/identifier/uuid.ts +187 -0
- package/src/index.ts +33 -1
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/README.md +79 -0
- package/src/log/index.ts +5 -0
- package/src/log/log-emitter.ts +72 -0
- package/src/log/log-record.ts +10 -0
- package/src/log/log-scheduler.ts +74 -0
- package/src/log/log-type.ts +8 -0
- package/src/log/logger.ts +554 -0
- package/src/openai/README.md +1 -0
- package/src/openai/index.ts +1 -0
- package/src/openai/openai.ts +510 -0
- package/src/orchestration/README.md +91 -0
- package/src/orchestration/coordination/barrier.ts +214 -0
- package/src/orchestration/coordination/count-down-latch.ts +215 -0
- package/src/orchestration/coordination/errors.ts +98 -0
- package/src/orchestration/coordination/index.ts +16 -0
- package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
- package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
- package/src/orchestration/coordination/keyed-lock.ts +168 -0
- package/src/orchestration/coordination/mutex.ts +257 -0
- package/src/orchestration/coordination/permit.ts +127 -0
- package/src/orchestration/coordination/read-write-lock.ts +444 -0
- package/src/orchestration/coordination/semaphore.ts +280 -0
- package/src/orchestration/dispatching/dispatcher.ts +83 -0
- package/src/orchestration/dispatching/index.ts +2 -0
- package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
- package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
- package/src/orchestration/dispatching/selector/index.ts +2 -0
- package/src/orchestration/index.ts +3 -0
- package/src/orchestration/scheduling/index.ts +2 -0
- package/src/orchestration/scheduling/scheduler.ts +103 -0
- package/src/orchestration/scheduling/task.ts +32 -0
- package/src/random/README.md +56 -86
- package/src/random/base.ts +66 -0
- package/src/random/index.ts +5 -1
- package/src/random/random-boolean.ts +40 -0
- package/src/random/random-integer.ts +60 -0
- package/src/random/random-number.ts +72 -0
- package/src/random/random-string.ts +66 -0
- package/src/reactor/README.md +4 -0
- package/src/reactor/reactor-core/primitive.ts +9 -9
- package/src/reactor/reactor-core/reactive-system.ts +5 -5
- package/src/request/README.md +108 -0
- package/src/request/fetch/base.ts +108 -0
- package/src/request/fetch/browser.ts +285 -0
- package/src/request/fetch/general.ts +20 -0
- package/src/request/fetch/index.ts +4 -0
- package/src/request/fetch/nodejs.ts +285 -0
- package/src/request/index.ts +2 -0
- package/src/request/request/base.ts +250 -0
- package/src/request/request/general.ts +64 -0
- package/src/request/request/index.ts +3 -0
- package/src/request/request/resource.ts +68 -0
- package/src/result/README.md +4 -0
- package/src/result/controller.ts +54 -0
- package/src/result/either.ts +193 -0
- package/src/result/index.ts +2 -0
- package/src/route/README.md +105 -0
- package/src/route/adapter/browser.ts +122 -0
- package/src/route/adapter/driver.ts +56 -0
- package/src/route/adapter/index.ts +2 -0
- package/src/route/index.ts +3 -0
- package/src/route/router/index.ts +2 -0
- package/src/route/router/route.ts +630 -0
- package/src/route/router/router.ts +1642 -0
- package/src/route/uri/hash.ts +308 -0
- package/src/route/uri/index.ts +7 -0
- package/src/route/uri/pathname.ts +376 -0
- package/src/route/uri/search.ts +413 -0
- package/src/singleton/README.md +79 -0
- package/src/singleton/factory.ts +55 -0
- package/src/singleton/index.ts +2 -0
- package/src/singleton/manager.ts +204 -0
- package/src/socket/README.md +105 -0
- package/src/socket/client/index.ts +2 -0
- package/src/socket/client/socket-unit.ts +660 -0
- package/src/socket/client/socket.ts +203 -0
- package/src/socket/common/index.ts +2 -0
- package/src/socket/common/socket-unit-common.ts +23 -0
- package/src/socket/common/socket-unit-heartbeat.ts +427 -0
- package/src/socket/index.ts +3 -0
- package/src/socket/server/index.ts +3 -0
- package/src/socket/server/server.ts +183 -0
- package/src/socket/server/socket-unit.ts +449 -0
- package/src/socket/server/socket.ts +264 -0
- package/src/storage/README.md +107 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/table.ts +449 -0
- package/src/timer/README.md +86 -0
- package/src/timer/expiration/expiration-manager.ts +594 -0
- package/src/timer/expiration/index.ts +3 -0
- package/src/timer/expiration/min-heap.ts +208 -0
- package/src/timer/expiration/remaining-manager.ts +241 -0
- package/src/timer/index.ts +1 -0
- package/src/tube/README.md +99 -0
- package/src/tube/helper.ts +138 -0
- package/src/tube/index.ts +2 -0
- package/src/tube/tube.ts +880 -0
- package/src/type/README.md +54 -307
- package/src/type/class.ts +2 -2
- package/src/type/index.ts +14 -14
- package/src/type/is.ts +265 -2
- package/src/type/object.ts +37 -0
- package/src/type/string.ts +7 -2
- package/src/type/tuple.ts +6 -6
- package/src/type/union.ts +16 -0
- package/src/web/README.md +77 -0
- package/src/web/capture.ts +35 -0
- package/src/web/clipboard.ts +97 -0
- package/src/web/dom.ts +117 -0
- package/src/web/download.ts +16 -0
- package/src/web/event.ts +46 -0
- package/src/web/index.ts +10 -0
- package/src/web/local-storage.ts +113 -0
- package/src/web/location.ts +28 -0
- package/src/web/permission.ts +172 -0
- package/src/web/script-loader.ts +432 -0
- package/src/weixin/README.md +1 -0
- package/src/weixin/index.ts +2 -0
- package/src/weixin/official-account/authorization.ts +159 -0
- package/src/weixin/official-account/index.ts +2 -0
- package/src/weixin/official-account/js-api.ts +134 -0
- package/src/weixin/open/index.ts +1 -0
- package/src/weixin/open/oauth2.ts +133 -0
- package/tests/unit/abort/abort-manager.spec.ts +225 -0
- package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
- package/tests/unit/ai/ai.spec.ts +85 -0
- package/tests/unit/aio/content.spec.ts +105 -0
- package/tests/unit/aio/json.spec.ts +147 -0
- package/tests/unit/aio/prompt.spec.ts +111 -0
- package/tests/unit/basic/array.spec.ts +1 -1
- package/tests/unit/basic/error.spec.ts +16 -4
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +91 -38
- package/tests/unit/basic/string.spec.ts +0 -9
- package/tests/unit/color/rgb/analyze.spec.ts +110 -0
- package/tests/unit/color/rgb/construct.spec.ts +56 -0
- package/tests/unit/color/rgb/convert.spec.ts +60 -0
- package/tests/unit/color/rgb/derive.spec.ts +103 -0
- package/tests/unit/color/rgb/parse.spec.ts +66 -0
- package/tests/unit/color/rgb/serialize.spec.ts +46 -0
- package/tests/unit/color/xyz/analyze.spec.ts +33 -0
- package/tests/unit/color/xyz/construct.spec.ts +10 -0
- package/tests/unit/color/xyz/convert.spec.ts +18 -0
- package/tests/unit/credential/api-key.spec.ts +37 -0
- package/tests/unit/credential/bearer.spec.ts +23 -0
- package/tests/unit/credential/json-web-token.spec.ts +23 -0
- package/tests/unit/credential/password.spec.ts +41 -0
- package/tests/unit/cron/cron.spec.ts +84 -0
- package/tests/unit/css/class.spec.ts +157 -0
- package/tests/unit/environment/basic.spec.ts +20 -0
- package/tests/unit/environment/device.spec.ts +146 -0
- package/tests/unit/environment/feature.spec.ts +388 -0
- package/tests/unit/environment/geo.spec.ts +111 -0
- package/tests/unit/environment/runtime.spec.ts +364 -0
- package/tests/unit/environment/snapshot.spec.ts +4 -0
- package/tests/unit/environment/variable.spec.ts +190 -0
- package/tests/unit/event/class-event-proxy.spec.ts +225 -0
- package/tests/unit/event/event-manager.spec.ts +246 -0
- package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
- package/tests/unit/exception/browser.spec.ts +213 -0
- package/tests/unit/exception/nodejs.spec.ts +144 -0
- package/tests/unit/exception/normalize.spec.ts +57 -0
- package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
- package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
- package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
- package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
- package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
- package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
- package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
- package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
- package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
- package/tests/unit/http/api/api-core-host.spec.ts +207 -0
- package/tests/unit/http/api/api-schema.spec.ts +120 -0
- package/tests/unit/http/api/api-server.spec.ts +363 -0
- package/tests/unit/http/api/api-test.spec.ts +117 -0
- package/tests/unit/http/api/api.spec.ts +121 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
- package/tests/unit/identifier/id.spec.ts +71 -0
- package/tests/unit/identifier/uuid.spec.ts +85 -0
- package/tests/unit/json/repair.spec.ts +11 -0
- package/tests/unit/log/log-emitter.spec.ts +33 -0
- package/tests/unit/log/log-scheduler.spec.ts +40 -0
- package/tests/unit/log/log-type.spec.ts +7 -0
- package/tests/unit/log/logger.spec.ts +237 -0
- package/tests/unit/openai/openai.spec.ts +64 -0
- package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
- package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
- package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
- package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
- package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
- package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
- package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
- package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
- package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
- package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
- package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
- package/tests/unit/random/base.spec.ts +58 -0
- package/tests/unit/random/random-boolean.spec.ts +25 -0
- package/tests/unit/random/random-integer.spec.ts +32 -0
- package/tests/unit/random/random-number.spec.ts +33 -0
- package/tests/unit/random/random-string.spec.ts +22 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
- package/tests/unit/reactor/preact-signal.spec.ts +1 -2
- package/tests/unit/request/fetch/browser.spec.ts +222 -0
- package/tests/unit/request/fetch/general.spec.ts +43 -0
- package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
- package/tests/unit/request/request/base.spec.ts +385 -0
- package/tests/unit/request/request/general.spec.ts +161 -0
- package/tests/unit/route/router/route.spec.ts +431 -0
- package/tests/unit/route/router/router.spec.ts +407 -0
- package/tests/unit/route/uri/hash.spec.ts +72 -0
- package/tests/unit/route/uri/pathname.spec.ts +147 -0
- package/tests/unit/route/uri/search.spec.ts +107 -0
- package/tests/unit/singleton/singleton.spec.ts +49 -0
- package/tests/unit/socket/client.spec.ts +208 -0
- package/tests/unit/socket/server.spec.ts +135 -0
- package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
- package/tests/unit/storage/table.spec.ts +620 -0
- package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
- package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
- package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
- package/tests/unit/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/.oxlintrc.json +0 -5
- package/src/random/uuid.ts +0 -103
- package/tests/unit/random/uuid.spec.ts +0 -37
|
@@ -0,0 +1,1642 @@
|
|
|
1
|
+
|
|
2
|
+
import type { BuildEvents } from "#Source/event/index.ts"
|
|
3
|
+
import { EventManager } from "#Source/event/index.ts"
|
|
4
|
+
import { generateUuidV4FromUrl } from "#Source/identifier/index.ts"
|
|
5
|
+
|
|
6
|
+
import type { HashUnion, SearchUnion } from "../uri/index.ts"
|
|
7
|
+
import { Route } from "./route.ts"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Router 内部历史动作的种类。
|
|
11
|
+
*/
|
|
12
|
+
export const ROUTE_ACTION_TYPE = {
|
|
13
|
+
MOVE: "move",
|
|
14
|
+
TRIM: "trim",
|
|
15
|
+
PUSH: "push",
|
|
16
|
+
REPLACE: "replace"
|
|
17
|
+
} as const
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Router 内部历史动作的联合字面量类型。
|
|
21
|
+
*/
|
|
22
|
+
export type RouteActionType = (typeof ROUTE_ACTION_TYPE)[keyof typeof ROUTE_ACTION_TYPE]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 在历史链表上移动当前位置时使用的输入项。
|
|
26
|
+
*/
|
|
27
|
+
export interface RouteMoveOptions {
|
|
28
|
+
step: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 在历史链表上移动当前位置后返回的结果。
|
|
33
|
+
*/
|
|
34
|
+
export interface RouteMoveResult {
|
|
35
|
+
fromRouteEntry: RouteEntry
|
|
36
|
+
toRouteEntry: RouteEntry
|
|
37
|
+
direction: "forward" | "backward" | "none"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 截断某个历史节点之后分支时使用的输入项。
|
|
42
|
+
*/
|
|
43
|
+
export interface RouteTrimOptions {
|
|
44
|
+
routeEntry: RouteEntry
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 截断历史分支后返回的结果。
|
|
49
|
+
*/
|
|
50
|
+
export interface RouteTrimResult {
|
|
51
|
+
fromRouteEntry: RouteEntry
|
|
52
|
+
trimmedRouteEntries: RouteEntry[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 向历史尾部压入新路由时使用的输入项。
|
|
57
|
+
*/
|
|
58
|
+
export interface RoutePushOptions {
|
|
59
|
+
route: Route
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 向历史尾部压入新路由后返回的结果。
|
|
64
|
+
*/
|
|
65
|
+
export interface RoutePushResult {
|
|
66
|
+
fromRouteEntry: RouteEntry
|
|
67
|
+
toRouteEntry: RouteEntry
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 用新路由替换当前位置时使用的输入项。
|
|
72
|
+
*/
|
|
73
|
+
export interface RouteReplaceOptions {
|
|
74
|
+
route: Route
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 用新路由替换当前位置后返回的结果。
|
|
79
|
+
*/
|
|
80
|
+
export interface RouteReplaceResult {
|
|
81
|
+
fromRouteEntry: RouteEntry
|
|
82
|
+
toRouteEntry: RouteEntry
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 对外可见的路由历史操作类型。
|
|
87
|
+
*/
|
|
88
|
+
export const ROUTE_TYPE = {
|
|
89
|
+
INITIALIZE: "initialize",
|
|
90
|
+
REFRESH: "refresh",
|
|
91
|
+
REPLACE_TO: "replace-to",
|
|
92
|
+
SWITCH_TO: "switch-to",
|
|
93
|
+
NAVIGATE_TO: "navigate-to",
|
|
94
|
+
NAVIGATE_SEARCH: "navigate-search",
|
|
95
|
+
NAVIGATE_HASH: "navigate-hash",
|
|
96
|
+
REDIRECT_TO: "redirect-to",
|
|
97
|
+
REDIRECT_SEARCH: "redirect-search",
|
|
98
|
+
REDIRECT_HASH: "redirect-hash",
|
|
99
|
+
ROAMING_BY: "roaming-by",
|
|
100
|
+
ROAMING_FORWARD_BY: "roaming-forward-by",
|
|
101
|
+
ROAMING_BACKWARD_BY: "roaming-backward-by",
|
|
102
|
+
ROAMING_TO: "roaming-to",
|
|
103
|
+
ROAMING_FORWARD_TO: "roaming-forward-to",
|
|
104
|
+
ROAMING_BACKWARD_TO: "roaming-backward-to",
|
|
105
|
+
GO_BY: "go-by",
|
|
106
|
+
GO_FORWARD_BY: "go-forward-by",
|
|
107
|
+
GO_BACKWARD_BY: "go-backward-by",
|
|
108
|
+
GO_TO: "go-to",
|
|
109
|
+
GO_FORWARD_TO: "go-forward-to",
|
|
110
|
+
GO_BACKWARD_TO: "go-backward-to",
|
|
111
|
+
} as const
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 对外可见的路由历史操作类型联合。
|
|
115
|
+
*/
|
|
116
|
+
export type RouteType = (typeof ROUTE_TYPE)[keyof typeof ROUTE_TYPE]
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 初始化 Router 时使用的输入项。
|
|
120
|
+
*/
|
|
121
|
+
export interface RouteInitializeOptions {
|
|
122
|
+
url: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 刷新当前路由时使用的输入项。
|
|
127
|
+
*/
|
|
128
|
+
export interface RouteRefreshOptions {
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 用新地址替换当前位置时使用的输入项。
|
|
133
|
+
*/
|
|
134
|
+
export interface RouteReplaceToOptions {
|
|
135
|
+
url: string
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 切换到历史尾部并替换目标地址时使用的输入项。
|
|
140
|
+
*/
|
|
141
|
+
export interface RouteSwitchToOptions {
|
|
142
|
+
url: string
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 以追加历史的方式导航到新地址时使用的输入项。
|
|
147
|
+
*/
|
|
148
|
+
export interface RouteNavigateToOptions {
|
|
149
|
+
url: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 以追加历史的方式导航到新 search 时使用的输入项。
|
|
154
|
+
*/
|
|
155
|
+
export interface RouteNavigateSearchOptions {
|
|
156
|
+
search: SearchUnion
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 以追加历史的方式导航到新 hash 时使用的输入项。
|
|
161
|
+
*/
|
|
162
|
+
export interface RouteNavigateHashOptions {
|
|
163
|
+
hash: HashUnion
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 以替换当前位置的方式重定向到新地址时使用的输入项。
|
|
168
|
+
*/
|
|
169
|
+
export interface RouteRedirectToOptions {
|
|
170
|
+
url: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 以替换当前位置的方式重定向到新 search 时使用的输入项。
|
|
175
|
+
*/
|
|
176
|
+
export interface RouteRedirectSearchOptions {
|
|
177
|
+
search: SearchUnion
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 以替换当前位置的方式重定向到新 hash 时使用的输入项。
|
|
182
|
+
*/
|
|
183
|
+
export interface RouteRedirectHashOptions {
|
|
184
|
+
hash: HashUnion
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 在历史链表中按相对步数漫游时使用的输入项。
|
|
189
|
+
*/
|
|
190
|
+
export interface RouteRoamingByOptions {
|
|
191
|
+
step: number
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 在历史链表中向前漫游时使用的输入项。
|
|
196
|
+
*/
|
|
197
|
+
export interface RouteRoamingForwardByOptions {
|
|
198
|
+
step: number
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 在历史链表中向后漫游时使用的输入项。
|
|
203
|
+
*/
|
|
204
|
+
export interface RouteRoamingBackwardByOptions {
|
|
205
|
+
step: number
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 在全部历史范围内按条件漫游时使用的输入项。
|
|
210
|
+
*/
|
|
211
|
+
export interface RouteRoamingToOptions {
|
|
212
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 在当前位置之后按条件漫游时使用的输入项。
|
|
217
|
+
*/
|
|
218
|
+
export interface RouteRoamingForwardToOptions {
|
|
219
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 在当前位置之前按条件漫游时使用的输入项。
|
|
224
|
+
*/
|
|
225
|
+
export interface RouteRoamingBackwardToOptions {
|
|
226
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 在历史链表中按相对步数移动并截断后续分支时使用的输入项。
|
|
231
|
+
*/
|
|
232
|
+
export interface RouteGoByOptions {
|
|
233
|
+
step: number
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 在历史链表中向前移动并截断后续分支时使用的输入项。
|
|
238
|
+
*/
|
|
239
|
+
export interface RouteGoForwardByOptions {
|
|
240
|
+
step: number
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 在历史链表中向后移动并截断后续分支时使用的输入项。
|
|
245
|
+
*/
|
|
246
|
+
export interface RouteGoBackwardByOptions {
|
|
247
|
+
step: number
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 在全部历史范围内按条件移动并截断后续分支时使用的输入项。
|
|
252
|
+
*/
|
|
253
|
+
export interface RouteGoToOptions {
|
|
254
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 在当前位置之后按条件移动并截断后续分支时使用的输入项。
|
|
259
|
+
*/
|
|
260
|
+
export interface RouteGoForwardToOptions {
|
|
261
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 在当前位置之前按条件移动并截断后续分支时使用的输入项。
|
|
266
|
+
*/
|
|
267
|
+
export interface RouteGoBackwardToOptions {
|
|
268
|
+
predicate: (routeEntry: RouteEntry) => boolean
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Router 在一次历史记录中执行的底层动作。
|
|
273
|
+
*/
|
|
274
|
+
export type RouteHistoryAction =
|
|
275
|
+
| { type: "move", options: RouteMoveOptions, result: RouteMoveResult }
|
|
276
|
+
| { type: "trim", options: RouteTrimOptions, result: RouteTrimResult }
|
|
277
|
+
| { type: "push", options: RoutePushOptions, result: RoutePushResult }
|
|
278
|
+
| { type: "replace", options: RouteReplaceOptions, result: RouteReplaceResult }
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Router 对外暴露的历史记录条目。
|
|
282
|
+
*/
|
|
283
|
+
export type RouteHistoryEntry =
|
|
284
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "initialize", options: RouteInitializeOptions, actions: RouteHistoryAction[] }
|
|
285
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "refresh", options: RouteRefreshOptions, actions: RouteHistoryAction[] }
|
|
286
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "replace-to", options: RouteReplaceToOptions, actions: RouteHistoryAction[] }
|
|
287
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "switch-to", options: RouteSwitchToOptions, actions: RouteHistoryAction[] }
|
|
288
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "navigate-to", options: RouteNavigateToOptions, actions: RouteHistoryAction[] }
|
|
289
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "navigate-search", options: RouteNavigateSearchOptions, actions: RouteHistoryAction[] }
|
|
290
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "navigate-hash", options: RouteNavigateHashOptions, actions: RouteHistoryAction[] }
|
|
291
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "redirect-to", options: RouteRedirectToOptions, actions: RouteHistoryAction[] }
|
|
292
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "redirect-search", options: RouteRedirectSearchOptions, actions: RouteHistoryAction[] }
|
|
293
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "redirect-hash", options: RouteRedirectHashOptions, actions: RouteHistoryAction[] }
|
|
294
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-by", options: RouteRoamingByOptions, actions: RouteHistoryAction[] }
|
|
295
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-forward-by", options: RouteRoamingForwardByOptions, actions: RouteHistoryAction[] }
|
|
296
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-backward-by", options: RouteRoamingBackwardByOptions, actions: RouteHistoryAction[] }
|
|
297
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-to", options: RouteRoamingToOptions, actions: RouteHistoryAction[] }
|
|
298
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-forward-to", options: RouteRoamingForwardToOptions, actions: RouteHistoryAction[] }
|
|
299
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "roaming-backward-to", options: RouteRoamingBackwardToOptions, actions: RouteHistoryAction[] }
|
|
300
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-by", options: RouteGoByOptions, actions: RouteHistoryAction[] }
|
|
301
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-forward-by", options: RouteGoForwardByOptions, actions: RouteHistoryAction[] }
|
|
302
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-backward-by", options: RouteGoBackwardByOptions, actions: RouteHistoryAction[] }
|
|
303
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-to", options: RouteGoToOptions, actions: RouteHistoryAction[] }
|
|
304
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-forward-to", options: RouteGoForwardToOptions, actions: RouteHistoryAction[] }
|
|
305
|
+
| { id: string, from: RouteEntry, to: RouteEntry, type: "go-backward-to", options: RouteGoBackwardToOptions, actions: RouteHistoryAction[] }
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Router 历史链表中的节点。
|
|
309
|
+
*/
|
|
310
|
+
export interface RouteEntry {
|
|
311
|
+
route: Route
|
|
312
|
+
prevEntry?: RouteEntry | undefined
|
|
313
|
+
nextEntry?: RouteEntry | undefined
|
|
314
|
+
history: RouteHistoryEntry[]
|
|
315
|
+
index: number
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 1. `history.options` 中涉及 `predicate` 的地方替换为 `(): boolean => false`。
|
|
320
|
+
* 2. 所有涉及 `Route` 的地方使用 `Route.clone()` 方法进行克隆。
|
|
321
|
+
* 3. `RouteEntry.prevEntry` 和 `RouteEntry.nextEntry` 递归克隆。
|
|
322
|
+
* 4. `RouteEntry.history` 中的每个 `RouteHistoryEntry` 都进行克隆。
|
|
323
|
+
* 5. 克隆前后保持引用关系不变。
|
|
324
|
+
*/
|
|
325
|
+
const internalRouteMap = new Map<Route, Route>()
|
|
326
|
+
const internalRouteEntryMap = new Map<RouteEntry, RouteEntry>()
|
|
327
|
+
const internalRouteHistoryEntryMap = new Map<RouteHistoryEntry, RouteHistoryEntry>()
|
|
328
|
+
const internalCloneReset = (): void => {
|
|
329
|
+
internalRouteMap.clear()
|
|
330
|
+
internalRouteEntryMap.clear()
|
|
331
|
+
internalRouteHistoryEntryMap.clear()
|
|
332
|
+
}
|
|
333
|
+
const internalCloneRoute = (sourceRoute: Route): Route => {
|
|
334
|
+
const cachedRoute = internalRouteMap.get(sourceRoute)
|
|
335
|
+
if (cachedRoute !== undefined) {
|
|
336
|
+
return cachedRoute
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const clonedRoute = sourceRoute.clone()
|
|
340
|
+
internalRouteMap.set(sourceRoute, clonedRoute)
|
|
341
|
+
return clonedRoute
|
|
342
|
+
}
|
|
343
|
+
const internalCloneRouteEntry = (sourceRouteEntry: RouteEntry): RouteEntry => {
|
|
344
|
+
const cachedRouteEntry = internalRouteEntryMap.get(sourceRouteEntry)
|
|
345
|
+
if (cachedRouteEntry !== undefined) {
|
|
346
|
+
return cachedRouteEntry
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const clonedRouteEntry: RouteEntry = {
|
|
350
|
+
route: internalCloneRoute(sourceRouteEntry.route),
|
|
351
|
+
prevEntry: undefined,
|
|
352
|
+
nextEntry: undefined,
|
|
353
|
+
history: [],
|
|
354
|
+
index: sourceRouteEntry.index
|
|
355
|
+
}
|
|
356
|
+
internalRouteEntryMap.set(sourceRouteEntry, clonedRouteEntry)
|
|
357
|
+
|
|
358
|
+
clonedRouteEntry.prevEntry = sourceRouteEntry.prevEntry === undefined
|
|
359
|
+
? undefined
|
|
360
|
+
: internalCloneRouteEntry(sourceRouteEntry.prevEntry)
|
|
361
|
+
clonedRouteEntry.nextEntry = sourceRouteEntry.nextEntry === undefined
|
|
362
|
+
? undefined
|
|
363
|
+
: internalCloneRouteEntry(sourceRouteEntry.nextEntry)
|
|
364
|
+
clonedRouteEntry.history = sourceRouteEntry.history.map((historyEntry) => {
|
|
365
|
+
return internalCloneRouteHistoryEntry(historyEntry)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
return clonedRouteEntry
|
|
369
|
+
}
|
|
370
|
+
const internalCloneRouteHistoryEntry = (historyEntry: RouteHistoryEntry): RouteHistoryEntry => {
|
|
371
|
+
const cloneHistoryOptions = (sourceHistoryEntry: RouteHistoryEntry): RouteHistoryEntry["options"] => {
|
|
372
|
+
switch (sourceHistoryEntry.type) {
|
|
373
|
+
case ROUTE_TYPE.INITIALIZE: {
|
|
374
|
+
return {
|
|
375
|
+
url: sourceHistoryEntry.options.url
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
case ROUTE_TYPE.REFRESH: {
|
|
379
|
+
return {}
|
|
380
|
+
}
|
|
381
|
+
case ROUTE_TYPE.SWITCH_TO:
|
|
382
|
+
case ROUTE_TYPE.REPLACE_TO:
|
|
383
|
+
case ROUTE_TYPE.NAVIGATE_TO:
|
|
384
|
+
case ROUTE_TYPE.REDIRECT_TO: {
|
|
385
|
+
return {
|
|
386
|
+
url: sourceHistoryEntry.options.url
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
case ROUTE_TYPE.NAVIGATE_SEARCH:
|
|
390
|
+
case ROUTE_TYPE.REDIRECT_SEARCH: {
|
|
391
|
+
return {
|
|
392
|
+
search: structuredClone(sourceHistoryEntry.options.search)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
case ROUTE_TYPE.NAVIGATE_HASH:
|
|
396
|
+
case ROUTE_TYPE.REDIRECT_HASH: {
|
|
397
|
+
return {
|
|
398
|
+
hash: structuredClone(sourceHistoryEntry.options.hash)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
case ROUTE_TYPE.ROAMING_BY:
|
|
402
|
+
case ROUTE_TYPE.ROAMING_FORWARD_BY:
|
|
403
|
+
case ROUTE_TYPE.ROAMING_BACKWARD_BY:
|
|
404
|
+
case ROUTE_TYPE.GO_BY:
|
|
405
|
+
case ROUTE_TYPE.GO_FORWARD_BY:
|
|
406
|
+
case ROUTE_TYPE.GO_BACKWARD_BY: {
|
|
407
|
+
return {
|
|
408
|
+
step: sourceHistoryEntry.options.step
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
case ROUTE_TYPE.ROAMING_TO:
|
|
412
|
+
case ROUTE_TYPE.ROAMING_FORWARD_TO:
|
|
413
|
+
case ROUTE_TYPE.ROAMING_BACKWARD_TO:
|
|
414
|
+
case ROUTE_TYPE.GO_TO:
|
|
415
|
+
case ROUTE_TYPE.GO_FORWARD_TO:
|
|
416
|
+
case ROUTE_TYPE.GO_BACKWARD_TO: {
|
|
417
|
+
return {
|
|
418
|
+
predicate: (_routeEntry: RouteEntry): boolean => false
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
default: {
|
|
422
|
+
throw new Error(`Unsupported route history entry type`)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const cloneRouteHistoryAction = (sourceAction: RouteHistoryAction): RouteHistoryAction => {
|
|
428
|
+
switch (sourceAction.type) {
|
|
429
|
+
case ROUTE_ACTION_TYPE.MOVE: {
|
|
430
|
+
return {
|
|
431
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
432
|
+
options: { step: sourceAction.options.step },
|
|
433
|
+
result: {
|
|
434
|
+
fromRouteEntry: internalCloneRouteEntry(sourceAction.result.fromRouteEntry),
|
|
435
|
+
toRouteEntry: internalCloneRouteEntry(sourceAction.result.toRouteEntry),
|
|
436
|
+
direction: sourceAction.result.direction
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
case ROUTE_ACTION_TYPE.TRIM: {
|
|
441
|
+
return {
|
|
442
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
443
|
+
options: { routeEntry: internalCloneRouteEntry(sourceAction.options.routeEntry) },
|
|
444
|
+
result: {
|
|
445
|
+
fromRouteEntry: internalCloneRouteEntry(sourceAction.result.fromRouteEntry),
|
|
446
|
+
trimmedRouteEntries: sourceAction.result.trimmedRouteEntries.map((entry) => {
|
|
447
|
+
return internalCloneRouteEntry(entry)
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
case ROUTE_ACTION_TYPE.PUSH: {
|
|
453
|
+
return {
|
|
454
|
+
type: ROUTE_ACTION_TYPE.PUSH,
|
|
455
|
+
options: { route: internalCloneRoute(sourceAction.options.route) },
|
|
456
|
+
result: {
|
|
457
|
+
fromRouteEntry: internalCloneRouteEntry(sourceAction.result.fromRouteEntry),
|
|
458
|
+
toRouteEntry: internalCloneRouteEntry(sourceAction.result.toRouteEntry)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
case ROUTE_ACTION_TYPE.REPLACE: {
|
|
463
|
+
return {
|
|
464
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
465
|
+
options: { route: internalCloneRoute(sourceAction.options.route) },
|
|
466
|
+
result: {
|
|
467
|
+
fromRouteEntry: internalCloneRouteEntry(sourceAction.result.fromRouteEntry),
|
|
468
|
+
toRouteEntry: internalCloneRouteEntry(sourceAction.result.toRouteEntry)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
default: {
|
|
473
|
+
throw new Error(`Unsupported route history action type`)
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const cloneRouteHistoryEntry = (sourceHistoryEntry: RouteHistoryEntry): RouteHistoryEntry => {
|
|
479
|
+
const cachedRouteHistoryEntry = internalRouteHistoryEntryMap.get(sourceHistoryEntry)
|
|
480
|
+
if (cachedRouteHistoryEntry !== undefined) {
|
|
481
|
+
return cachedRouteHistoryEntry
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
485
|
+
const clonedRouteHistoryEntry = {
|
|
486
|
+
id: sourceHistoryEntry.id,
|
|
487
|
+
from: internalCloneRouteEntry(sourceHistoryEntry.from),
|
|
488
|
+
to: internalCloneRouteEntry(sourceHistoryEntry.to),
|
|
489
|
+
type: sourceHistoryEntry.type,
|
|
490
|
+
options: cloneHistoryOptions(sourceHistoryEntry),
|
|
491
|
+
actions: []
|
|
492
|
+
} as RouteHistoryEntry
|
|
493
|
+
internalRouteHistoryEntryMap.set(sourceHistoryEntry, clonedRouteHistoryEntry)
|
|
494
|
+
|
|
495
|
+
clonedRouteHistoryEntry.actions = sourceHistoryEntry.actions.map((action) => {
|
|
496
|
+
return cloneRouteHistoryAction(action)
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
return clonedRouteHistoryEntry
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return cloneRouteHistoryEntry(historyEntry)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Router 对外发出的事件集合。
|
|
507
|
+
*/
|
|
508
|
+
export type RouterEvents = BuildEvents<{
|
|
509
|
+
history: (routeHistoryEntry: RouteHistoryEntry) => void
|
|
510
|
+
}>
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* 创建 Router 时使用的输入项。
|
|
514
|
+
*/
|
|
515
|
+
export interface RouterOptions {
|
|
516
|
+
url: string
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* 路由历史管理器。
|
|
521
|
+
*
|
|
522
|
+
* Router 维护一条可漫游、可裁剪、可替换的路由历史链表,并通过 history 事件向外暴露每次动作对应的历史记录。
|
|
523
|
+
*/
|
|
524
|
+
export class Router {
|
|
525
|
+
protected options: RouterOptions
|
|
526
|
+
protected history: RouteHistoryEntry[]
|
|
527
|
+
protected headRouteEntry!: RouteEntry
|
|
528
|
+
protected tailRouteEntry!: RouteEntry
|
|
529
|
+
protected routeEntry!: RouteEntry
|
|
530
|
+
|
|
531
|
+
eventManager: EventManager<RouterEvents>
|
|
532
|
+
|
|
533
|
+
constructor(options: RouterOptions) {
|
|
534
|
+
this.options = options
|
|
535
|
+
this.history = []
|
|
536
|
+
this.eventManager = new EventManager<RouterEvents>()
|
|
537
|
+
this.initialize({ url: options.url })
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
protected initialize(options: RouteInitializeOptions): void {
|
|
541
|
+
const { url } = options
|
|
542
|
+
|
|
543
|
+
const newRoute = new Route({ partialUrl: url })
|
|
544
|
+
const initialRouteEntry: RouteEntry = {
|
|
545
|
+
route: newRoute,
|
|
546
|
+
prevEntry: undefined,
|
|
547
|
+
nextEntry: undefined,
|
|
548
|
+
history: [],
|
|
549
|
+
index: -1
|
|
550
|
+
}
|
|
551
|
+
this.headRouteEntry = initialRouteEntry
|
|
552
|
+
this.tailRouteEntry = initialRouteEntry
|
|
553
|
+
this.routeEntry = initialRouteEntry
|
|
554
|
+
|
|
555
|
+
const historyEntry: RouteHistoryEntry = {
|
|
556
|
+
id: generateUuidV4FromUrl(),
|
|
557
|
+
from: initialRouteEntry,
|
|
558
|
+
to: initialRouteEntry,
|
|
559
|
+
type: ROUTE_TYPE.INITIALIZE,
|
|
560
|
+
options,
|
|
561
|
+
actions: []
|
|
562
|
+
}
|
|
563
|
+
this.pushHistory(historyEntry)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* 返回当前最新的历史记录快照。
|
|
568
|
+
*/
|
|
569
|
+
getCurrentRouteHistoryEntry(): RouteHistoryEntry {
|
|
570
|
+
const currentRouteHistoryEntry = this.history.at(-1)
|
|
571
|
+
if (currentRouteHistoryEntry === undefined) {
|
|
572
|
+
throw new Error(`No current route history entry`)
|
|
573
|
+
}
|
|
574
|
+
internalCloneReset()
|
|
575
|
+
const clonedRouteHistoryEntry = internalCloneRouteHistoryEntry(currentRouteHistoryEntry)
|
|
576
|
+
return clonedRouteHistoryEntry
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* 返回当前最新历史记录的标识。
|
|
581
|
+
*/
|
|
582
|
+
getCurrentRouteHistoryEntryId(): string {
|
|
583
|
+
const currentRouteHistoryEntry = this.history.at(-1)
|
|
584
|
+
if (currentRouteHistoryEntry === undefined) {
|
|
585
|
+
throw new Error(`No current route history entry`)
|
|
586
|
+
}
|
|
587
|
+
const currentRouteHistoryEntryId = currentRouteHistoryEntry.id
|
|
588
|
+
return currentRouteHistoryEntryId
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
protected pushHistory(routeHistoryEntry: RouteHistoryEntry): void {
|
|
592
|
+
this.history.push(routeHistoryEntry)
|
|
593
|
+
internalCloneReset()
|
|
594
|
+
const clonedRouteHistoryEntry = internalCloneRouteHistoryEntry(routeHistoryEntry)
|
|
595
|
+
this.eventManager.emit("history", clonedRouteHistoryEntry)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* 判断当前位置是否处于历史尾部之前。
|
|
600
|
+
*/
|
|
601
|
+
isRoaming(): boolean {
|
|
602
|
+
return this.routeEntry !== this.tailRouteEntry
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* 返回当前位置最多还能向前漫游多少步。
|
|
607
|
+
*/
|
|
608
|
+
getMaxRoamingForwardSteps(): number {
|
|
609
|
+
return this.tailRouteEntry.index - this.routeEntry.index
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* 返回当前位置最多还能向后漫游多少步。
|
|
614
|
+
*/
|
|
615
|
+
getMaxRoamingBackwardSteps(): number {
|
|
616
|
+
return this.routeEntry.index - this.headRouteEntry.index
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* 判断当前位置是否还能继续向前漫游。
|
|
621
|
+
*/
|
|
622
|
+
canRoamingForward(): boolean {
|
|
623
|
+
return this.getMaxRoamingForwardSteps() > 0
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* 判断当前位置是否还能继续向后漫游。
|
|
628
|
+
*/
|
|
629
|
+
canRoamingBackward(): boolean {
|
|
630
|
+
return this.getMaxRoamingBackwardSteps() > 0
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
protected move(options: RouteMoveOptions): RouteMoveResult {
|
|
634
|
+
const { step } = options
|
|
635
|
+
|
|
636
|
+
const fromRouteEntry = this.routeEntry
|
|
637
|
+
let toRouteEntry = this.routeEntry
|
|
638
|
+
const direction = step > 0 ? "forward" : step < 0 ? "backward" : "none"
|
|
639
|
+
|
|
640
|
+
if (step > 0) {
|
|
641
|
+
for (let currentStep = 0; currentStep < step; currentStep = currentStep + 1) {
|
|
642
|
+
if (toRouteEntry.nextEntry === undefined) {
|
|
643
|
+
break
|
|
644
|
+
}
|
|
645
|
+
toRouteEntry = toRouteEntry.nextEntry
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
else if (step < 0) {
|
|
649
|
+
for (let currentStep = 0; currentStep > step; currentStep = currentStep - 1) {
|
|
650
|
+
if (toRouteEntry.prevEntry === undefined) {
|
|
651
|
+
break
|
|
652
|
+
}
|
|
653
|
+
toRouteEntry = toRouteEntry.prevEntry
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
this.routeEntry = toRouteEntry
|
|
658
|
+
|
|
659
|
+
return { fromRouteEntry, toRouteEntry, direction }
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
protected trim(options: RouteTrimOptions): RouteTrimResult {
|
|
663
|
+
const { routeEntry } = options
|
|
664
|
+
|
|
665
|
+
const fromRouteEntry = routeEntry
|
|
666
|
+
const trimmedRouteEntries: RouteEntry[] = []
|
|
667
|
+
|
|
668
|
+
let nextEntry = fromRouteEntry.nextEntry
|
|
669
|
+
while (nextEntry !== undefined) {
|
|
670
|
+
trimmedRouteEntries.push(nextEntry)
|
|
671
|
+
nextEntry = nextEntry.nextEntry
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
fromRouteEntry.nextEntry = undefined
|
|
675
|
+
this.tailRouteEntry = fromRouteEntry
|
|
676
|
+
|
|
677
|
+
return { fromRouteEntry, trimmedRouteEntries }
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
protected push(options: RoutePushOptions): RoutePushResult {
|
|
681
|
+
const { route } = options
|
|
682
|
+
|
|
683
|
+
const fromRouteEntry = this.routeEntry
|
|
684
|
+
|
|
685
|
+
const toRouteEntry: RouteEntry = {
|
|
686
|
+
route,
|
|
687
|
+
prevEntry: this.routeEntry,
|
|
688
|
+
nextEntry: undefined,
|
|
689
|
+
history: [],
|
|
690
|
+
index: this.routeEntry.index + 1
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
this.routeEntry.nextEntry = toRouteEntry
|
|
694
|
+
|
|
695
|
+
this.tailRouteEntry = toRouteEntry
|
|
696
|
+
this.routeEntry = toRouteEntry
|
|
697
|
+
|
|
698
|
+
return { fromRouteEntry, toRouteEntry }
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
protected replace(options: RouteReplaceOptions): RouteReplaceResult {
|
|
702
|
+
const { route } = options
|
|
703
|
+
|
|
704
|
+
const fromRouteEntry = this.routeEntry
|
|
705
|
+
|
|
706
|
+
const prevEntry = this.routeEntry.prevEntry
|
|
707
|
+
const nextEntry = this.routeEntry.nextEntry
|
|
708
|
+
const toRouteEntry: RouteEntry = {
|
|
709
|
+
route,
|
|
710
|
+
prevEntry,
|
|
711
|
+
nextEntry,
|
|
712
|
+
history: [],
|
|
713
|
+
index: fromRouteEntry.index
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (prevEntry !== undefined) {
|
|
717
|
+
prevEntry.nextEntry = toRouteEntry
|
|
718
|
+
}
|
|
719
|
+
if (nextEntry !== undefined) {
|
|
720
|
+
nextEntry.prevEntry = toRouteEntry
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (prevEntry === undefined) {
|
|
724
|
+
this.headRouteEntry = toRouteEntry
|
|
725
|
+
}
|
|
726
|
+
if (nextEntry === undefined) {
|
|
727
|
+
this.tailRouteEntry = toRouteEntry
|
|
728
|
+
}
|
|
729
|
+
this.routeEntry = toRouteEntry
|
|
730
|
+
|
|
731
|
+
return { fromRouteEntry, toRouteEntry }
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
protected internalFind(predicate: (routeEntry: RouteEntry) => boolean): RouteEntry | undefined {
|
|
735
|
+
let currentEntry: RouteEntry | undefined = this.headRouteEntry
|
|
736
|
+
while (currentEntry !== undefined) {
|
|
737
|
+
if (predicate(currentEntry)) {
|
|
738
|
+
return currentEntry
|
|
739
|
+
}
|
|
740
|
+
currentEntry = currentEntry.nextEntry
|
|
741
|
+
}
|
|
742
|
+
return undefined
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
protected internalFindForward(predicate: (routeEntry: RouteEntry) => boolean): RouteEntry | undefined {
|
|
746
|
+
let currentEntry = this.routeEntry.nextEntry
|
|
747
|
+
while (currentEntry !== undefined) {
|
|
748
|
+
if (predicate(currentEntry)) {
|
|
749
|
+
return currentEntry
|
|
750
|
+
}
|
|
751
|
+
currentEntry = currentEntry.nextEntry
|
|
752
|
+
}
|
|
753
|
+
return undefined
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
protected internalFindBackward(predicate: (routeEntry: RouteEntry) => boolean): RouteEntry | undefined {
|
|
757
|
+
let currentEntry = this.routeEntry.prevEntry
|
|
758
|
+
while (currentEntry !== undefined) {
|
|
759
|
+
if (predicate(currentEntry)) {
|
|
760
|
+
return currentEntry
|
|
761
|
+
}
|
|
762
|
+
currentEntry = currentEntry.prevEntry
|
|
763
|
+
}
|
|
764
|
+
return undefined
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* 在当前位置生成一条 refresh 历史记录。
|
|
769
|
+
*/
|
|
770
|
+
refresh(options: RouteRefreshOptions): void {
|
|
771
|
+
const actions: RouteHistoryAction[] = []
|
|
772
|
+
|
|
773
|
+
const moveOptions: RouteMoveOptions = { step: 0 }
|
|
774
|
+
const moveResult = this.move(moveOptions)
|
|
775
|
+
actions.push({
|
|
776
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
777
|
+
options: moveOptions,
|
|
778
|
+
result: moveResult
|
|
779
|
+
})
|
|
780
|
+
|
|
781
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
782
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
783
|
+
const historyEntry: RouteHistoryEntry = {
|
|
784
|
+
id: generateUuidV4FromUrl(),
|
|
785
|
+
from: fromRouteEntry,
|
|
786
|
+
to: toRouteEntry,
|
|
787
|
+
type: ROUTE_TYPE.REFRESH,
|
|
788
|
+
options,
|
|
789
|
+
actions,
|
|
790
|
+
}
|
|
791
|
+
toRouteEntry.history.push(historyEntry)
|
|
792
|
+
this.pushHistory(historyEntry)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* 用新地址替换当前位置,并记录 replace-to 历史。
|
|
797
|
+
*/
|
|
798
|
+
replaceTo(options: RouteReplaceToOptions): void {
|
|
799
|
+
const { url } = options
|
|
800
|
+
|
|
801
|
+
const newRoute = new Route({ partialUrl: url })
|
|
802
|
+
|
|
803
|
+
const actions: RouteHistoryAction[] = []
|
|
804
|
+
|
|
805
|
+
const replaceOptions: RouteReplaceOptions = { route: newRoute }
|
|
806
|
+
const replaceResult = this.replace(replaceOptions)
|
|
807
|
+
actions.push({
|
|
808
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
809
|
+
options: replaceOptions,
|
|
810
|
+
result: replaceResult
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
const fromRouteEntry = replaceResult.fromRouteEntry
|
|
814
|
+
const toRouteEntry = replaceResult.toRouteEntry
|
|
815
|
+
const historyEntry: RouteHistoryEntry = {
|
|
816
|
+
id: generateUuidV4FromUrl(),
|
|
817
|
+
from: fromRouteEntry,
|
|
818
|
+
to: toRouteEntry,
|
|
819
|
+
type: ROUTE_TYPE.REPLACE_TO,
|
|
820
|
+
options,
|
|
821
|
+
actions,
|
|
822
|
+
}
|
|
823
|
+
toRouteEntry.history.push(historyEntry)
|
|
824
|
+
this.pushHistory(historyEntry)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* 切换到历史尾部并用新地址替换该位置。
|
|
829
|
+
*/
|
|
830
|
+
switchTo(options: RouteSwitchToOptions): void {
|
|
831
|
+
const { url } = options
|
|
832
|
+
|
|
833
|
+
const newRoute = new Route({ partialUrl: url })
|
|
834
|
+
const targetStep = this.getMaxRoamingForwardSteps()
|
|
835
|
+
|
|
836
|
+
const actions: RouteHistoryAction[] = []
|
|
837
|
+
|
|
838
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
839
|
+
const moveResult = this.move(moveOptions)
|
|
840
|
+
actions.push({
|
|
841
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
842
|
+
options: moveOptions,
|
|
843
|
+
result: moveResult
|
|
844
|
+
})
|
|
845
|
+
|
|
846
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
847
|
+
const trimResult = this.trim(trimOptions)
|
|
848
|
+
actions.push({
|
|
849
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
850
|
+
options: trimOptions,
|
|
851
|
+
result: trimResult
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
const replaceOptions: RouteReplaceOptions = { route: newRoute }
|
|
855
|
+
const replaceResult = this.replace(replaceOptions)
|
|
856
|
+
actions.push({
|
|
857
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
858
|
+
options: replaceOptions,
|
|
859
|
+
result: replaceResult
|
|
860
|
+
})
|
|
861
|
+
|
|
862
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
863
|
+
const toRouteEntry = replaceResult.toRouteEntry
|
|
864
|
+
const historyEntry: RouteHistoryEntry = {
|
|
865
|
+
id: generateUuidV4FromUrl(),
|
|
866
|
+
from: fromRouteEntry,
|
|
867
|
+
to: toRouteEntry,
|
|
868
|
+
type: ROUTE_TYPE.SWITCH_TO,
|
|
869
|
+
options,
|
|
870
|
+
actions
|
|
871
|
+
}
|
|
872
|
+
toRouteEntry.history.push(historyEntry)
|
|
873
|
+
this.pushHistory(historyEntry)
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* 裁剪当前位置之后的分支,并把新地址压入历史尾部。
|
|
878
|
+
*/
|
|
879
|
+
navigateTo(options: RouteNavigateToOptions): void {
|
|
880
|
+
const { url } = options
|
|
881
|
+
|
|
882
|
+
const newRoute = new Route({ partialUrl: url })
|
|
883
|
+
|
|
884
|
+
const actions: RouteHistoryAction[] = []
|
|
885
|
+
|
|
886
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
887
|
+
const trimResult = this.trim(trimOptions)
|
|
888
|
+
actions.push({
|
|
889
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
890
|
+
options: trimOptions,
|
|
891
|
+
result: trimResult
|
|
892
|
+
})
|
|
893
|
+
|
|
894
|
+
const pushOptions: RoutePushOptions = { route: newRoute }
|
|
895
|
+
const pushResult = this.push(pushOptions)
|
|
896
|
+
actions.push({
|
|
897
|
+
type: ROUTE_ACTION_TYPE.PUSH,
|
|
898
|
+
options: pushOptions,
|
|
899
|
+
result: pushResult
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
903
|
+
const toRouteEntry = pushResult.toRouteEntry
|
|
904
|
+
const historyEntry: RouteHistoryEntry = {
|
|
905
|
+
id: generateUuidV4FromUrl(),
|
|
906
|
+
from: fromRouteEntry,
|
|
907
|
+
to: toRouteEntry,
|
|
908
|
+
type: ROUTE_TYPE.NAVIGATE_TO,
|
|
909
|
+
options,
|
|
910
|
+
actions
|
|
911
|
+
}
|
|
912
|
+
toRouteEntry.history.push(historyEntry)
|
|
913
|
+
this.pushHistory(historyEntry)
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* 基于当前 route 更新 search,并以追加历史的方式导航。
|
|
918
|
+
*/
|
|
919
|
+
navigateSearch(options: RouteNavigateSearchOptions): void {
|
|
920
|
+
const { search } = options
|
|
921
|
+
|
|
922
|
+
const newRoute = this.routeEntry.route.clone().search({ search })
|
|
923
|
+
|
|
924
|
+
const actions: RouteHistoryAction[] = []
|
|
925
|
+
|
|
926
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
927
|
+
const trimResult = this.trim(trimOptions)
|
|
928
|
+
actions.push({
|
|
929
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
930
|
+
options: trimOptions,
|
|
931
|
+
result: trimResult
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
const pushOptions: RoutePushOptions = { route: newRoute }
|
|
935
|
+
const pushResult = this.push(pushOptions)
|
|
936
|
+
actions.push({
|
|
937
|
+
type: ROUTE_ACTION_TYPE.PUSH,
|
|
938
|
+
options: pushOptions,
|
|
939
|
+
result: pushResult
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
943
|
+
const toRouteEntry = pushResult.toRouteEntry
|
|
944
|
+
const historyEntry: RouteHistoryEntry = {
|
|
945
|
+
id: generateUuidV4FromUrl(),
|
|
946
|
+
from: fromRouteEntry,
|
|
947
|
+
to: toRouteEntry,
|
|
948
|
+
type: ROUTE_TYPE.NAVIGATE_SEARCH,
|
|
949
|
+
options,
|
|
950
|
+
actions
|
|
951
|
+
}
|
|
952
|
+
toRouteEntry.history.push(historyEntry)
|
|
953
|
+
this.pushHistory(historyEntry)
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* 基于当前 route 更新 hash,并以追加历史的方式导航。
|
|
958
|
+
*/
|
|
959
|
+
navigateHash(options: RouteNavigateHashOptions): void {
|
|
960
|
+
const { hash } = options
|
|
961
|
+
|
|
962
|
+
const newRoute = this.routeEntry.route.clone().hash({ hash })
|
|
963
|
+
|
|
964
|
+
const actions: RouteHistoryAction[] = []
|
|
965
|
+
|
|
966
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
967
|
+
const trimResult = this.trim(trimOptions)
|
|
968
|
+
actions.push({
|
|
969
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
970
|
+
options: trimOptions,
|
|
971
|
+
result: trimResult
|
|
972
|
+
})
|
|
973
|
+
|
|
974
|
+
const pushOptions: RoutePushOptions = { route: newRoute }
|
|
975
|
+
const pushResult = this.push(pushOptions)
|
|
976
|
+
actions.push({
|
|
977
|
+
type: ROUTE_ACTION_TYPE.PUSH,
|
|
978
|
+
options: pushOptions,
|
|
979
|
+
result: pushResult
|
|
980
|
+
})
|
|
981
|
+
|
|
982
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
983
|
+
const toRouteEntry = pushResult.toRouteEntry
|
|
984
|
+
const historyEntry: RouteHistoryEntry = {
|
|
985
|
+
id: generateUuidV4FromUrl(),
|
|
986
|
+
from: fromRouteEntry,
|
|
987
|
+
to: toRouteEntry,
|
|
988
|
+
type: ROUTE_TYPE.NAVIGATE_HASH,
|
|
989
|
+
options,
|
|
990
|
+
actions
|
|
991
|
+
}
|
|
992
|
+
toRouteEntry.history.push(historyEntry)
|
|
993
|
+
this.pushHistory(historyEntry)
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* 裁剪当前位置之后的分支,并直接替换当前位置为新地址。
|
|
998
|
+
*/
|
|
999
|
+
redirectTo(options: RouteRedirectToOptions): void {
|
|
1000
|
+
const { url } = options
|
|
1001
|
+
|
|
1002
|
+
const newRoute = new Route({ partialUrl: url })
|
|
1003
|
+
|
|
1004
|
+
const actions: RouteHistoryAction[] = []
|
|
1005
|
+
|
|
1006
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
1007
|
+
const trimResult = this.trim(trimOptions)
|
|
1008
|
+
actions.push({
|
|
1009
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1010
|
+
options: trimOptions,
|
|
1011
|
+
result: trimResult
|
|
1012
|
+
})
|
|
1013
|
+
|
|
1014
|
+
const replaceOptions: RouteReplaceOptions = { route: newRoute }
|
|
1015
|
+
const replaceResult = this.replace(replaceOptions)
|
|
1016
|
+
actions.push({
|
|
1017
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
1018
|
+
options: replaceOptions,
|
|
1019
|
+
result: replaceResult
|
|
1020
|
+
})
|
|
1021
|
+
|
|
1022
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
1023
|
+
const toRouteEntry = replaceResult.toRouteEntry
|
|
1024
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1025
|
+
id: generateUuidV4FromUrl(),
|
|
1026
|
+
from: fromRouteEntry,
|
|
1027
|
+
to: toRouteEntry,
|
|
1028
|
+
type: ROUTE_TYPE.REDIRECT_TO,
|
|
1029
|
+
options,
|
|
1030
|
+
actions
|
|
1031
|
+
}
|
|
1032
|
+
toRouteEntry.history.push(historyEntry)
|
|
1033
|
+
this.pushHistory(historyEntry)
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* 基于当前 route 更新 search,并直接替换当前位置。
|
|
1038
|
+
*/
|
|
1039
|
+
redirectSearch(options: RouteRedirectSearchOptions): void {
|
|
1040
|
+
const { search } = options
|
|
1041
|
+
|
|
1042
|
+
const newRoute = this.routeEntry.route.clone().search({ search })
|
|
1043
|
+
|
|
1044
|
+
const actions: RouteHistoryAction[] = []
|
|
1045
|
+
|
|
1046
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
1047
|
+
const trimResult = this.trim(trimOptions)
|
|
1048
|
+
actions.push({
|
|
1049
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1050
|
+
options: trimOptions,
|
|
1051
|
+
result: trimResult
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
const replaceOptions: RouteReplaceOptions = { route: newRoute }
|
|
1055
|
+
const replaceResult = this.replace(replaceOptions)
|
|
1056
|
+
actions.push({
|
|
1057
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
1058
|
+
options: replaceOptions,
|
|
1059
|
+
result: replaceResult
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
1063
|
+
const toRouteEntry = replaceResult.toRouteEntry
|
|
1064
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1065
|
+
id: generateUuidV4FromUrl(),
|
|
1066
|
+
from: fromRouteEntry,
|
|
1067
|
+
to: toRouteEntry,
|
|
1068
|
+
type: ROUTE_TYPE.REDIRECT_SEARCH,
|
|
1069
|
+
options,
|
|
1070
|
+
actions
|
|
1071
|
+
}
|
|
1072
|
+
toRouteEntry.history.push(historyEntry)
|
|
1073
|
+
this.pushHistory(historyEntry)
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* 基于当前 route 更新 hash,并直接替换当前位置。
|
|
1078
|
+
*/
|
|
1079
|
+
redirectHash(options: RouteRedirectHashOptions): void {
|
|
1080
|
+
const { hash } = options
|
|
1081
|
+
|
|
1082
|
+
const newRoute = this.routeEntry.route.clone().hash({ hash })
|
|
1083
|
+
|
|
1084
|
+
const actions: RouteHistoryAction[] = []
|
|
1085
|
+
|
|
1086
|
+
const trimOptions: RouteTrimOptions = { routeEntry: this.routeEntry }
|
|
1087
|
+
const trimResult = this.trim(trimOptions)
|
|
1088
|
+
actions.push({
|
|
1089
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1090
|
+
options: trimOptions,
|
|
1091
|
+
result: trimResult
|
|
1092
|
+
})
|
|
1093
|
+
|
|
1094
|
+
const replaceOptions: RouteReplaceOptions = { route: newRoute }
|
|
1095
|
+
const replaceResult = this.replace(replaceOptions)
|
|
1096
|
+
actions.push({
|
|
1097
|
+
type: ROUTE_ACTION_TYPE.REPLACE,
|
|
1098
|
+
options: replaceOptions,
|
|
1099
|
+
result: replaceResult
|
|
1100
|
+
})
|
|
1101
|
+
|
|
1102
|
+
const fromRouteEntry = trimResult.fromRouteEntry
|
|
1103
|
+
const toRouteEntry = replaceResult.toRouteEntry
|
|
1104
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1105
|
+
id: generateUuidV4FromUrl(),
|
|
1106
|
+
from: fromRouteEntry,
|
|
1107
|
+
to: toRouteEntry,
|
|
1108
|
+
type: ROUTE_TYPE.REDIRECT_HASH,
|
|
1109
|
+
options,
|
|
1110
|
+
actions
|
|
1111
|
+
}
|
|
1112
|
+
toRouteEntry.history.push(historyEntry)
|
|
1113
|
+
this.pushHistory(historyEntry)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* 按相对步数在历史链表中漫游,但不裁剪分支。
|
|
1118
|
+
*/
|
|
1119
|
+
roamingBy(options: RouteRoamingByOptions): void {
|
|
1120
|
+
const { step } = options
|
|
1121
|
+
|
|
1122
|
+
if (Number.isInteger(step) === false) {
|
|
1123
|
+
throw new TypeError("Step must be an integer")
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const actions: RouteHistoryAction[] = []
|
|
1127
|
+
|
|
1128
|
+
const moveOptions: RouteMoveOptions = { step }
|
|
1129
|
+
const moveResult = this.move(moveOptions)
|
|
1130
|
+
actions.push({
|
|
1131
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1132
|
+
options: moveOptions,
|
|
1133
|
+
result: moveResult
|
|
1134
|
+
})
|
|
1135
|
+
|
|
1136
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1137
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1138
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1139
|
+
id: generateUuidV4FromUrl(),
|
|
1140
|
+
from: fromRouteEntry,
|
|
1141
|
+
to: toRouteEntry,
|
|
1142
|
+
type: ROUTE_TYPE.ROAMING_BY,
|
|
1143
|
+
options,
|
|
1144
|
+
actions
|
|
1145
|
+
}
|
|
1146
|
+
toRouteEntry.history.push(historyEntry)
|
|
1147
|
+
this.pushHistory(historyEntry)
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* 按非负步数向前漫游,但不裁剪分支。
|
|
1152
|
+
*/
|
|
1153
|
+
roamingForwardBy(options: RouteRoamingForwardByOptions): void {
|
|
1154
|
+
const { step } = options
|
|
1155
|
+
|
|
1156
|
+
if (Number.isInteger(step) === false) {
|
|
1157
|
+
throw new TypeError("Step must be an integer")
|
|
1158
|
+
}
|
|
1159
|
+
if (step < 0) {
|
|
1160
|
+
throw new RangeError("Step must be a non-negative integer")
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const actions: RouteHistoryAction[] = []
|
|
1164
|
+
|
|
1165
|
+
const moveOptions: RouteMoveOptions = { step }
|
|
1166
|
+
const moveResult = this.move(moveOptions)
|
|
1167
|
+
actions.push({
|
|
1168
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1169
|
+
options: moveOptions,
|
|
1170
|
+
result: moveResult
|
|
1171
|
+
})
|
|
1172
|
+
|
|
1173
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1174
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1175
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1176
|
+
id: generateUuidV4FromUrl(),
|
|
1177
|
+
from: fromRouteEntry,
|
|
1178
|
+
to: toRouteEntry,
|
|
1179
|
+
type: ROUTE_TYPE.ROAMING_FORWARD_BY,
|
|
1180
|
+
options,
|
|
1181
|
+
actions
|
|
1182
|
+
}
|
|
1183
|
+
toRouteEntry.history.push(historyEntry)
|
|
1184
|
+
this.pushHistory(historyEntry)
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* 按非负步数向后漫游,但不裁剪分支。
|
|
1189
|
+
*/
|
|
1190
|
+
roamingBackwardBy(options: RouteRoamingBackwardByOptions): void {
|
|
1191
|
+
const { step } = options
|
|
1192
|
+
|
|
1193
|
+
if (Number.isInteger(step) === false) {
|
|
1194
|
+
throw new TypeError("Step must be an integer")
|
|
1195
|
+
}
|
|
1196
|
+
if (step < 0) {
|
|
1197
|
+
throw new RangeError("Step must be a non-negative integer")
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const actions: RouteHistoryAction[] = []
|
|
1201
|
+
|
|
1202
|
+
const moveOptions: RouteMoveOptions = { step: -step }
|
|
1203
|
+
const moveResult = this.move(moveOptions)
|
|
1204
|
+
actions.push({
|
|
1205
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1206
|
+
options: moveOptions,
|
|
1207
|
+
result: moveResult
|
|
1208
|
+
})
|
|
1209
|
+
|
|
1210
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1211
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1212
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1213
|
+
id: generateUuidV4FromUrl(),
|
|
1214
|
+
from: fromRouteEntry,
|
|
1215
|
+
to: toRouteEntry,
|
|
1216
|
+
type: ROUTE_TYPE.ROAMING_BACKWARD_BY,
|
|
1217
|
+
options,
|
|
1218
|
+
actions
|
|
1219
|
+
}
|
|
1220
|
+
toRouteEntry.history.push(historyEntry)
|
|
1221
|
+
this.pushHistory(historyEntry)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* 在整条历史链表中查找首个匹配项并漫游到该位置。
|
|
1226
|
+
*/
|
|
1227
|
+
roamingTo(options: RouteRoamingToOptions): void {
|
|
1228
|
+
const { predicate } = options
|
|
1229
|
+
|
|
1230
|
+
const targetRouteEntry = this.internalFind(predicate)
|
|
1231
|
+
let targetStep: number
|
|
1232
|
+
if (targetRouteEntry === undefined) {
|
|
1233
|
+
targetStep = 0
|
|
1234
|
+
} else {
|
|
1235
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
const actions: RouteHistoryAction[] = []
|
|
1239
|
+
|
|
1240
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1241
|
+
const moveResult = this.move(moveOptions)
|
|
1242
|
+
actions.push({
|
|
1243
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1244
|
+
options: moveOptions,
|
|
1245
|
+
result: moveResult
|
|
1246
|
+
})
|
|
1247
|
+
|
|
1248
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1249
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1250
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1251
|
+
id: generateUuidV4FromUrl(),
|
|
1252
|
+
from: fromRouteEntry,
|
|
1253
|
+
to: toRouteEntry,
|
|
1254
|
+
type: ROUTE_TYPE.ROAMING_TO,
|
|
1255
|
+
options,
|
|
1256
|
+
actions
|
|
1257
|
+
}
|
|
1258
|
+
toRouteEntry.history.push(historyEntry)
|
|
1259
|
+
this.pushHistory(historyEntry)
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* 在当前位置之后查找首个匹配项并漫游到该位置。
|
|
1264
|
+
*/
|
|
1265
|
+
roamingForwardTo(options: RouteRoamingForwardToOptions): void {
|
|
1266
|
+
const { predicate } = options
|
|
1267
|
+
|
|
1268
|
+
const targetRouteEntry = this.internalFindForward(predicate)
|
|
1269
|
+
let targetStep: number
|
|
1270
|
+
if (targetRouteEntry === undefined) {
|
|
1271
|
+
targetStep = 0
|
|
1272
|
+
} else {
|
|
1273
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
const actions: RouteHistoryAction[] = []
|
|
1277
|
+
|
|
1278
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1279
|
+
const moveResult = this.move(moveOptions)
|
|
1280
|
+
actions.push({
|
|
1281
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1282
|
+
options: moveOptions,
|
|
1283
|
+
result: moveResult
|
|
1284
|
+
})
|
|
1285
|
+
|
|
1286
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1287
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1288
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1289
|
+
id: generateUuidV4FromUrl(),
|
|
1290
|
+
from: fromRouteEntry,
|
|
1291
|
+
to: toRouteEntry,
|
|
1292
|
+
type: ROUTE_TYPE.ROAMING_FORWARD_TO,
|
|
1293
|
+
options,
|
|
1294
|
+
actions
|
|
1295
|
+
}
|
|
1296
|
+
toRouteEntry.history.push(historyEntry)
|
|
1297
|
+
this.pushHistory(historyEntry)
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* 在当前位置之前查找首个匹配项并漫游到该位置。
|
|
1302
|
+
*/
|
|
1303
|
+
roamingBackwardTo(options: RouteRoamingBackwardToOptions): void {
|
|
1304
|
+
const { predicate } = options
|
|
1305
|
+
|
|
1306
|
+
const targetRouteEntry = this.internalFindBackward(predicate)
|
|
1307
|
+
let targetStep: number
|
|
1308
|
+
if (targetRouteEntry === undefined) {
|
|
1309
|
+
targetStep = 0
|
|
1310
|
+
} else {
|
|
1311
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
const actions: RouteHistoryAction[] = []
|
|
1315
|
+
|
|
1316
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1317
|
+
const moveResult = this.move(moveOptions)
|
|
1318
|
+
actions.push({
|
|
1319
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1320
|
+
options: moveOptions,
|
|
1321
|
+
result: moveResult
|
|
1322
|
+
})
|
|
1323
|
+
|
|
1324
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1325
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1326
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1327
|
+
id: generateUuidV4FromUrl(),
|
|
1328
|
+
from: fromRouteEntry,
|
|
1329
|
+
to: toRouteEntry,
|
|
1330
|
+
type: ROUTE_TYPE.ROAMING_BACKWARD_TO,
|
|
1331
|
+
options,
|
|
1332
|
+
actions
|
|
1333
|
+
}
|
|
1334
|
+
toRouteEntry.history.push(historyEntry)
|
|
1335
|
+
this.pushHistory(historyEntry)
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* 按相对步数在历史链表中移动,并裁剪目标位置之后的分支。
|
|
1340
|
+
*/
|
|
1341
|
+
goBy(options: RouteGoByOptions): void {
|
|
1342
|
+
const { step } = options
|
|
1343
|
+
|
|
1344
|
+
if (Number.isInteger(step) === false) {
|
|
1345
|
+
throw new TypeError("Step must be an integer")
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
const actions: RouteHistoryAction[] = []
|
|
1349
|
+
|
|
1350
|
+
const moveOptions: RouteMoveOptions = { step }
|
|
1351
|
+
const moveResult = this.move(moveOptions)
|
|
1352
|
+
actions.push({
|
|
1353
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1354
|
+
options: moveOptions,
|
|
1355
|
+
result: moveResult
|
|
1356
|
+
})
|
|
1357
|
+
|
|
1358
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1359
|
+
const trimResult = this.trim(trimOptions)
|
|
1360
|
+
actions.push({
|
|
1361
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1362
|
+
options: trimOptions,
|
|
1363
|
+
result: trimResult
|
|
1364
|
+
})
|
|
1365
|
+
|
|
1366
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1367
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1368
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1369
|
+
id: generateUuidV4FromUrl(),
|
|
1370
|
+
from: fromRouteEntry,
|
|
1371
|
+
to: toRouteEntry,
|
|
1372
|
+
type: ROUTE_TYPE.GO_BY,
|
|
1373
|
+
options,
|
|
1374
|
+
actions
|
|
1375
|
+
}
|
|
1376
|
+
toRouteEntry.history.push(historyEntry)
|
|
1377
|
+
this.pushHistory(historyEntry)
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* 按非负步数向前移动,并裁剪目标位置之后的分支。
|
|
1382
|
+
*/
|
|
1383
|
+
goForwardBy(options: RouteGoForwardByOptions): void {
|
|
1384
|
+
const { step } = options
|
|
1385
|
+
|
|
1386
|
+
if (Number.isInteger(step) === false) {
|
|
1387
|
+
throw new TypeError("Step must be an integer")
|
|
1388
|
+
}
|
|
1389
|
+
if (step < 0) {
|
|
1390
|
+
throw new RangeError("Step must be a non-negative integer")
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
const actions: RouteHistoryAction[] = []
|
|
1394
|
+
|
|
1395
|
+
const moveOptions: RouteMoveOptions = { step }
|
|
1396
|
+
const moveResult = this.move(moveOptions)
|
|
1397
|
+
actions.push({
|
|
1398
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1399
|
+
options: moveOptions,
|
|
1400
|
+
result: moveResult
|
|
1401
|
+
})
|
|
1402
|
+
|
|
1403
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1404
|
+
const trimResult = this.trim(trimOptions)
|
|
1405
|
+
actions.push({
|
|
1406
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1407
|
+
options: trimOptions,
|
|
1408
|
+
result: trimResult
|
|
1409
|
+
})
|
|
1410
|
+
|
|
1411
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1412
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1413
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1414
|
+
id: generateUuidV4FromUrl(),
|
|
1415
|
+
from: fromRouteEntry,
|
|
1416
|
+
to: toRouteEntry,
|
|
1417
|
+
type: ROUTE_TYPE.GO_FORWARD_BY,
|
|
1418
|
+
options,
|
|
1419
|
+
actions
|
|
1420
|
+
}
|
|
1421
|
+
toRouteEntry.history.push(historyEntry)
|
|
1422
|
+
this.pushHistory(historyEntry)
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* 按非负步数向后移动,并裁剪目标位置之后的分支。
|
|
1427
|
+
*/
|
|
1428
|
+
goBackwardBy(options: RouteGoBackwardByOptions): void {
|
|
1429
|
+
const { step } = options
|
|
1430
|
+
|
|
1431
|
+
if (Number.isInteger(step) === false) {
|
|
1432
|
+
throw new TypeError("Step must be an integer")
|
|
1433
|
+
}
|
|
1434
|
+
if (step < 0) {
|
|
1435
|
+
throw new RangeError("Step must be a non-negative integer")
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
const actions: RouteHistoryAction[] = []
|
|
1439
|
+
|
|
1440
|
+
const moveOptions: RouteMoveOptions = { step: -step }
|
|
1441
|
+
const moveResult = this.move(moveOptions)
|
|
1442
|
+
actions.push({
|
|
1443
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1444
|
+
options: moveOptions,
|
|
1445
|
+
result: moveResult
|
|
1446
|
+
})
|
|
1447
|
+
|
|
1448
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1449
|
+
const trimResult = this.trim(trimOptions)
|
|
1450
|
+
actions.push({
|
|
1451
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1452
|
+
options: trimOptions,
|
|
1453
|
+
result: trimResult
|
|
1454
|
+
})
|
|
1455
|
+
|
|
1456
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1457
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1458
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1459
|
+
id: generateUuidV4FromUrl(),
|
|
1460
|
+
from: fromRouteEntry,
|
|
1461
|
+
to: toRouteEntry,
|
|
1462
|
+
type: ROUTE_TYPE.GO_BACKWARD_BY,
|
|
1463
|
+
options,
|
|
1464
|
+
actions
|
|
1465
|
+
}
|
|
1466
|
+
toRouteEntry.history.push(historyEntry)
|
|
1467
|
+
this.pushHistory(historyEntry)
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* 在整条历史链表中查找首个匹配项并移动到该位置,同时裁剪后续分支。
|
|
1472
|
+
*/
|
|
1473
|
+
goTo(options: RouteGoToOptions): void {
|
|
1474
|
+
const { predicate } = options
|
|
1475
|
+
|
|
1476
|
+
const targetRouteEntry = this.internalFind(predicate)
|
|
1477
|
+
let targetStep: number
|
|
1478
|
+
if (targetRouteEntry === undefined) {
|
|
1479
|
+
targetStep = 0
|
|
1480
|
+
} else {
|
|
1481
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
const actions: RouteHistoryAction[] = []
|
|
1485
|
+
|
|
1486
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1487
|
+
const moveResult = this.move(moveOptions)
|
|
1488
|
+
actions.push({
|
|
1489
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1490
|
+
options: moveOptions,
|
|
1491
|
+
result: moveResult
|
|
1492
|
+
})
|
|
1493
|
+
|
|
1494
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1495
|
+
const trimResult = this.trim(trimOptions)
|
|
1496
|
+
actions.push({
|
|
1497
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1498
|
+
options: trimOptions,
|
|
1499
|
+
result: trimResult
|
|
1500
|
+
})
|
|
1501
|
+
|
|
1502
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1503
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1504
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1505
|
+
id: generateUuidV4FromUrl(),
|
|
1506
|
+
from: fromRouteEntry,
|
|
1507
|
+
to: toRouteEntry,
|
|
1508
|
+
type: ROUTE_TYPE.GO_TO,
|
|
1509
|
+
options,
|
|
1510
|
+
actions
|
|
1511
|
+
}
|
|
1512
|
+
toRouteEntry.history.push(historyEntry)
|
|
1513
|
+
this.pushHistory(historyEntry)
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* 在当前位置之后查找首个匹配项并移动到该位置,同时裁剪后续分支。
|
|
1518
|
+
*/
|
|
1519
|
+
goForwardTo(options: RouteGoForwardToOptions): void {
|
|
1520
|
+
const { predicate } = options
|
|
1521
|
+
|
|
1522
|
+
const targetRouteEntry = this.internalFindForward(predicate)
|
|
1523
|
+
let targetStep: number
|
|
1524
|
+
if (targetRouteEntry === undefined) {
|
|
1525
|
+
targetStep = 0
|
|
1526
|
+
} else {
|
|
1527
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const actions: RouteHistoryAction[] = []
|
|
1531
|
+
|
|
1532
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1533
|
+
const moveResult = this.move(moveOptions)
|
|
1534
|
+
actions.push({
|
|
1535
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1536
|
+
options: moveOptions,
|
|
1537
|
+
result: moveResult
|
|
1538
|
+
})
|
|
1539
|
+
|
|
1540
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1541
|
+
const trimResult = this.trim(trimOptions)
|
|
1542
|
+
actions.push({
|
|
1543
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1544
|
+
options: trimOptions,
|
|
1545
|
+
result: trimResult
|
|
1546
|
+
})
|
|
1547
|
+
|
|
1548
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1549
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1550
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1551
|
+
id: generateUuidV4FromUrl(),
|
|
1552
|
+
from: fromRouteEntry,
|
|
1553
|
+
to: toRouteEntry,
|
|
1554
|
+
type: ROUTE_TYPE.GO_FORWARD_TO,
|
|
1555
|
+
options,
|
|
1556
|
+
actions
|
|
1557
|
+
}
|
|
1558
|
+
toRouteEntry.history.push(historyEntry)
|
|
1559
|
+
this.pushHistory(historyEntry)
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* 在当前位置之前查找首个匹配项并移动到该位置,同时裁剪后续分支。
|
|
1564
|
+
*/
|
|
1565
|
+
goBackwardTo(options: RouteGoBackwardToOptions): void {
|
|
1566
|
+
const { predicate } = options
|
|
1567
|
+
|
|
1568
|
+
const targetRouteEntry = this.internalFindBackward(predicate)
|
|
1569
|
+
let targetStep: number
|
|
1570
|
+
if (targetRouteEntry === undefined) {
|
|
1571
|
+
targetStep = 0
|
|
1572
|
+
} else {
|
|
1573
|
+
targetStep = targetRouteEntry.index - this.routeEntry.index
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
const actions: RouteHistoryAction[] = []
|
|
1577
|
+
|
|
1578
|
+
const moveOptions: RouteMoveOptions = { step: targetStep }
|
|
1579
|
+
const moveResult = this.move(moveOptions)
|
|
1580
|
+
actions.push({
|
|
1581
|
+
type: ROUTE_ACTION_TYPE.MOVE,
|
|
1582
|
+
options: moveOptions,
|
|
1583
|
+
result: moveResult
|
|
1584
|
+
})
|
|
1585
|
+
|
|
1586
|
+
const trimOptions: RouteTrimOptions = { routeEntry: moveResult.toRouteEntry }
|
|
1587
|
+
const trimResult = this.trim(trimOptions)
|
|
1588
|
+
actions.push({
|
|
1589
|
+
type: ROUTE_ACTION_TYPE.TRIM,
|
|
1590
|
+
options: trimOptions,
|
|
1591
|
+
result: trimResult
|
|
1592
|
+
})
|
|
1593
|
+
|
|
1594
|
+
const fromRouteEntry = moveResult.fromRouteEntry
|
|
1595
|
+
const toRouteEntry = moveResult.toRouteEntry
|
|
1596
|
+
const historyEntry: RouteHistoryEntry = {
|
|
1597
|
+
id: generateUuidV4FromUrl(),
|
|
1598
|
+
from: fromRouteEntry,
|
|
1599
|
+
to: toRouteEntry,
|
|
1600
|
+
type: ROUTE_TYPE.GO_BACKWARD_TO,
|
|
1601
|
+
options,
|
|
1602
|
+
actions
|
|
1603
|
+
}
|
|
1604
|
+
toRouteEntry.history.push(historyEntry)
|
|
1605
|
+
this.pushHistory(historyEntry)
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* 优先向后漫游指定步数;若无法向后漫游,则重定向到指定地址。
|
|
1610
|
+
*/
|
|
1611
|
+
roamingBackwardByOrRedirectTo(options: { step: number, url: string }): void {
|
|
1612
|
+
const { step, url } = options
|
|
1613
|
+
|
|
1614
|
+
const canRoamingBackward = this.canRoamingBackward()
|
|
1615
|
+
if (canRoamingBackward === true) {
|
|
1616
|
+
return this.roamingBackwardBy({ step })
|
|
1617
|
+
} else {
|
|
1618
|
+
return this.redirectTo({ url })
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/**
|
|
1623
|
+
* 优先向后移动并裁剪历史;若无法向后移动,则重定向到指定地址。
|
|
1624
|
+
*/
|
|
1625
|
+
goBackwardByOrRedirectTo(options: { step: number, url: string }): void {
|
|
1626
|
+
const { step, url } = options
|
|
1627
|
+
|
|
1628
|
+
const canRoamingBackward = this.canRoamingBackward()
|
|
1629
|
+
if (canRoamingBackward === true) {
|
|
1630
|
+
return this.goBackwardBy({ step })
|
|
1631
|
+
} else {
|
|
1632
|
+
return this.redirectTo({ url })
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/**
|
|
1638
|
+
* 创建一个 Router 实例。
|
|
1639
|
+
*/
|
|
1640
|
+
export const createRouter = (options: RouterOptions): Router => {
|
|
1641
|
+
return new Router(options)
|
|
1642
|
+
}
|