@planet-matrix/mobius-model 0.6.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.
Files changed (233) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/index.js +706 -36
  3. package/dist/index.js.map +855 -59
  4. package/package.json +28 -16
  5. package/src/ai/README.md +1 -0
  6. package/src/ai/ai.ts +107 -0
  7. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  8. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  9. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  10. package/src/ai/chat-completion-ai/index.ts +7 -0
  11. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  12. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  13. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  14. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  15. package/src/ai/embedding-ai/embedding.ts +50 -0
  16. package/src/ai/embedding-ai/index.ts +4 -0
  17. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  18. package/src/ai/index.ts +4 -0
  19. package/src/aio/README.md +100 -0
  20. package/src/aio/content.ts +141 -0
  21. package/src/aio/index.ts +3 -0
  22. package/src/aio/json.ts +127 -0
  23. package/src/aio/prompt.ts +246 -0
  24. package/src/basic/README.md +20 -15
  25. package/src/basic/error.ts +19 -5
  26. package/src/basic/function.ts +2 -2
  27. package/src/basic/index.ts +1 -0
  28. package/src/basic/schedule.ts +111 -0
  29. package/src/basic/stream.ts +135 -25
  30. package/src/credential/README.md +107 -0
  31. package/src/credential/api-key.ts +158 -0
  32. package/src/credential/bearer.ts +73 -0
  33. package/src/credential/index.ts +4 -0
  34. package/src/credential/json-web-token.ts +96 -0
  35. package/src/credential/password.ts +170 -0
  36. package/src/cron/README.md +86 -0
  37. package/src/cron/cron.ts +87 -0
  38. package/src/cron/index.ts +1 -0
  39. package/src/drizzle/README.md +1 -0
  40. package/src/drizzle/drizzle.ts +1 -0
  41. package/src/drizzle/helper.ts +47 -0
  42. package/src/drizzle/index.ts +5 -0
  43. package/src/drizzle/infer.ts +52 -0
  44. package/src/drizzle/kysely.ts +8 -0
  45. package/src/drizzle/pagination.ts +200 -0
  46. package/src/email/README.md +1 -0
  47. package/src/email/index.ts +1 -0
  48. package/src/email/resend.ts +25 -0
  49. package/src/event/class-event-proxy.ts +6 -5
  50. package/src/event/common.ts +13 -3
  51. package/src/event/event-manager.ts +3 -3
  52. package/src/event/instance-event-proxy.ts +6 -5
  53. package/src/event/internal.ts +4 -4
  54. package/src/form/README.md +25 -0
  55. package/src/form/index.ts +1 -0
  56. package/src/form/inputor-controller/base.ts +874 -0
  57. package/src/form/inputor-controller/boolean.ts +39 -0
  58. package/src/form/inputor-controller/file.ts +39 -0
  59. package/src/form/inputor-controller/form.ts +181 -0
  60. package/src/form/inputor-controller/helper.ts +117 -0
  61. package/src/form/inputor-controller/index.ts +17 -0
  62. package/src/form/inputor-controller/multi-select.ts +99 -0
  63. package/src/form/inputor-controller/number.ts +116 -0
  64. package/src/form/inputor-controller/select.ts +109 -0
  65. package/src/form/inputor-controller/text.ts +82 -0
  66. package/src/http/READMD.md +1 -0
  67. package/src/http/api/api-core.ts +84 -0
  68. package/src/http/api/api-handler.ts +79 -0
  69. package/src/http/api/api-host.ts +47 -0
  70. package/src/http/api/api-result.ts +56 -0
  71. package/src/http/api/api-schema.ts +154 -0
  72. package/src/http/api/api-server.ts +130 -0
  73. package/src/http/api/api-test.ts +142 -0
  74. package/src/http/api/api-type.ts +37 -0
  75. package/src/http/api/api.ts +81 -0
  76. package/src/http/api/index.ts +11 -0
  77. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  78. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  79. package/src/http/api-adapter/api-result-arktype.ts +297 -0
  80. package/src/http/api-adapter/api-result-zod.ts +286 -0
  81. package/src/http/api-adapter/index.ts +5 -0
  82. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  83. package/src/http/bin/gen-api-list/index.ts +1 -0
  84. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  85. package/src/http/bin/gen-api-test/index.ts +1 -0
  86. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  87. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  88. package/src/http/bin/gen-api-type/index.ts +2 -0
  89. package/src/http/bin/index.ts +2 -0
  90. package/src/http/index.ts +3 -0
  91. package/src/huawei/README.md +1 -0
  92. package/src/huawei/index.ts +2 -0
  93. package/src/huawei/moderation/index.ts +1 -0
  94. package/src/huawei/moderation/moderation.ts +355 -0
  95. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  96. package/src/huawei/obs/index.ts +1 -0
  97. package/src/huawei/obs/obs.ts +42 -0
  98. package/src/index.ts +19 -2
  99. package/src/json/README.md +92 -0
  100. package/src/json/index.ts +1 -0
  101. package/src/json/repair.ts +18 -0
  102. package/src/log/logger.ts +15 -4
  103. package/src/openai/README.md +1 -0
  104. package/src/openai/index.ts +1 -0
  105. package/src/openai/openai.ts +510 -0
  106. package/src/orchestration/README.md +9 -7
  107. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  108. package/src/orchestration/dispatching/index.ts +2 -0
  109. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  110. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  111. package/src/orchestration/dispatching/selector/index.ts +2 -0
  112. package/src/orchestration/index.ts +2 -0
  113. package/src/orchestration/scheduling/index.ts +2 -0
  114. package/src/orchestration/scheduling/scheduler.ts +103 -0
  115. package/src/orchestration/scheduling/task.ts +32 -0
  116. package/src/random/README.md +8 -7
  117. package/src/random/base.ts +66 -0
  118. package/src/random/index.ts +5 -1
  119. package/src/random/random-boolean.ts +40 -0
  120. package/src/random/random-integer.ts +60 -0
  121. package/src/random/random-number.ts +72 -0
  122. package/src/random/random-string.ts +66 -0
  123. package/src/request/README.md +108 -0
  124. package/src/request/fetch/base.ts +108 -0
  125. package/src/request/fetch/browser.ts +285 -0
  126. package/src/request/fetch/general.ts +20 -0
  127. package/src/request/fetch/index.ts +4 -0
  128. package/src/request/fetch/nodejs.ts +285 -0
  129. package/src/request/index.ts +2 -0
  130. package/src/request/request/base.ts +250 -0
  131. package/src/request/request/general.ts +64 -0
  132. package/src/request/request/index.ts +3 -0
  133. package/src/request/request/resource.ts +68 -0
  134. package/src/result/README.md +4 -0
  135. package/src/result/controller.ts +54 -0
  136. package/src/result/either.ts +193 -0
  137. package/src/result/index.ts +2 -0
  138. package/src/route/README.md +105 -0
  139. package/src/route/adapter/browser.ts +122 -0
  140. package/src/route/adapter/driver.ts +56 -0
  141. package/src/route/adapter/index.ts +2 -0
  142. package/src/route/index.ts +3 -0
  143. package/src/route/router/index.ts +2 -0
  144. package/src/route/router/route.ts +630 -0
  145. package/src/route/router/router.ts +1642 -0
  146. package/src/route/uri/hash.ts +308 -0
  147. package/src/route/uri/index.ts +7 -0
  148. package/src/route/uri/pathname.ts +376 -0
  149. package/src/route/uri/search.ts +413 -0
  150. package/src/socket/README.md +105 -0
  151. package/src/socket/client/index.ts +2 -0
  152. package/src/socket/client/socket-unit.ts +660 -0
  153. package/src/socket/client/socket.ts +203 -0
  154. package/src/socket/common/index.ts +2 -0
  155. package/src/socket/common/socket-unit-common.ts +23 -0
  156. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  157. package/src/socket/index.ts +3 -0
  158. package/src/socket/server/index.ts +3 -0
  159. package/src/socket/server/server.ts +183 -0
  160. package/src/socket/server/socket-unit.ts +449 -0
  161. package/src/socket/server/socket.ts +264 -0
  162. package/src/storage/table.ts +3 -3
  163. package/src/timer/expiration/expiration-manager.ts +3 -3
  164. package/src/timer/expiration/remaining-manager.ts +3 -3
  165. package/src/tube/README.md +99 -0
  166. package/src/tube/helper.ts +138 -0
  167. package/src/tube/index.ts +2 -0
  168. package/src/tube/tube.ts +880 -0
  169. package/src/weixin/README.md +1 -0
  170. package/src/weixin/index.ts +2 -0
  171. package/src/weixin/official-account/authorization.ts +159 -0
  172. package/src/weixin/official-account/index.ts +2 -0
  173. package/src/weixin/official-account/js-api.ts +134 -0
  174. package/src/weixin/open/index.ts +1 -0
  175. package/src/weixin/open/oauth2.ts +133 -0
  176. package/tests/unit/ai/ai.spec.ts +85 -0
  177. package/tests/unit/aio/content.spec.ts +105 -0
  178. package/tests/unit/aio/json.spec.ts +147 -0
  179. package/tests/unit/aio/prompt.spec.ts +111 -0
  180. package/tests/unit/basic/error.spec.ts +16 -4
  181. package/tests/unit/basic/schedule.spec.ts +74 -0
  182. package/tests/unit/basic/stream.spec.ts +90 -37
  183. package/tests/unit/credential/api-key.spec.ts +37 -0
  184. package/tests/unit/credential/bearer.spec.ts +23 -0
  185. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  186. package/tests/unit/credential/password.spec.ts +41 -0
  187. package/tests/unit/cron/cron.spec.ts +84 -0
  188. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  189. package/tests/unit/event/event-manager.spec.ts +3 -3
  190. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  191. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  192. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  193. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  194. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  195. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  196. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  197. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  198. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  199. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  200. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  201. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  202. package/tests/unit/http/api/api-server.spec.ts +363 -0
  203. package/tests/unit/http/api/api-test.spec.ts +117 -0
  204. package/tests/unit/http/api/api.spec.ts +121 -0
  205. package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
  206. package/tests/unit/json/repair.spec.ts +11 -0
  207. package/tests/unit/log/logger.spec.ts +19 -4
  208. package/tests/unit/openai/openai.spec.ts +64 -0
  209. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  210. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  211. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  212. package/tests/unit/random/base.spec.ts +58 -0
  213. package/tests/unit/random/random-boolean.spec.ts +25 -0
  214. package/tests/unit/random/random-integer.spec.ts +32 -0
  215. package/tests/unit/random/random-number.spec.ts +33 -0
  216. package/tests/unit/random/random-string.spec.ts +22 -0
  217. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  218. package/tests/unit/request/fetch/general.spec.ts +43 -0
  219. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  220. package/tests/unit/request/request/base.spec.ts +385 -0
  221. package/tests/unit/request/request/general.spec.ts +161 -0
  222. package/tests/unit/route/router/route.spec.ts +431 -0
  223. package/tests/unit/route/router/router.spec.ts +407 -0
  224. package/tests/unit/route/uri/hash.spec.ts +72 -0
  225. package/tests/unit/route/uri/pathname.spec.ts +147 -0
  226. package/tests/unit/route/uri/search.spec.ts +107 -0
  227. package/tests/unit/socket/client.spec.ts +208 -0
  228. package/tests/unit/socket/server.spec.ts +135 -0
  229. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  230. package/tests/unit/tube/helper.spec.ts +139 -0
  231. package/tests/unit/tube/tube.spec.ts +501 -0
  232. package/src/random/string.ts +0 -35
  233. package/tests/unit/random/string.spec.ts +0 -11
@@ -0,0 +1,630 @@
1
+ import type {
2
+ SearchUnion,
3
+ PathnameUnion,
4
+ HashUnion,
5
+ } from "../uri/index.ts"
6
+ import {
7
+ isHashIncludes,
8
+ isHashLooseEqual,
9
+ isHashLooseIncludes,
10
+ isHashStrictEqual,
11
+ isHashStrictIncludes,
12
+ isSearchIncludes,
13
+ isSearchLooseEqual,
14
+ isSearchLooseIncludes,
15
+ isSearchStrictEqual,
16
+ isSearchStrictIncludes,
17
+ toPathnameArray,
18
+ toPathnameString,
19
+ toHashObject,
20
+ toHashString,
21
+ toQueryObject,
22
+ toQueryString,
23
+ toSearch,
24
+ isPathnameLooseIncludes,
25
+ isPathnameStrictIncludes,
26
+ isPathnameLooseEqual,
27
+ isPathnameStrictEqual
28
+ } from "../uri/index.ts"
29
+
30
+ const isString = (target: unknown): target is string => {
31
+ return typeof target === "string"
32
+ }
33
+
34
+ const isObject = (target: unknown): target is object => {
35
+ if (typeof target !== "object" && typeof target !== "function") {
36
+ return false
37
+ }
38
+ if (target === null) {
39
+ return false
40
+ }
41
+ return true
42
+ }
43
+
44
+ const isPlainObject = (target: unknown): target is Record<PropertyKey, unknown> => {
45
+ if (isObject(target) === false) {
46
+ return false
47
+ }
48
+
49
+ if (Object.prototype.toString.call(target) !== "[object Object]") {
50
+ return false
51
+ }
52
+
53
+ // oxlint-disable-next-line typescript/no-unsafe-assignment
54
+ const prototype = Object.getPrototypeOf(target)
55
+ if (prototype !== null && prototype !== Object.prototype) {
56
+ return false
57
+ }
58
+
59
+ return true
60
+ }
61
+
62
+ /**
63
+ * URL 中 pathname 及其后续部分的组合值。
64
+ */
65
+ export type PartialUrl = string
66
+
67
+ /**
68
+ * 判断给定值是否可以视为 PartialUrl。
69
+ */
70
+ export const isPartialUrl = (target: unknown): target is PartialUrl => {
71
+ return isString(target)
72
+ }
73
+
74
+ /**
75
+ * Route 的标准化记录结构。
76
+ */
77
+ export interface RouteRecord {
78
+ partialUrl: PartialUrl
79
+ pathname: string
80
+ search: string
81
+ hash: string
82
+
83
+ pathnameString: string
84
+ pathnameArray: string[]
85
+ query: string
86
+ queryString: string
87
+ queryObject: Record<string, string>
88
+ hashString: string
89
+ hashObject: Record<string, string>
90
+ }
91
+
92
+ const DEFAULT_ROUTE_RECORD: RouteRecord = {
93
+ partialUrl: "",
94
+ pathname: "",
95
+ search: "",
96
+ hash: "",
97
+
98
+ pathnameString: "",
99
+ pathnameArray: [],
100
+ query: "",
101
+ queryString: "",
102
+ queryObject: {},
103
+ hashString: "",
104
+ hashObject: {},
105
+ }
106
+
107
+ /**
108
+ * 判断给定值是否满足 RouteRecord 的最小公共结构。
109
+ */
110
+ export const isRouteRecord = (target: unknown): target is RouteRecord => {
111
+ if (isPlainObject(target) === false) {
112
+ return false
113
+ }
114
+ if ("partialUrl" in target === false) {
115
+ return false
116
+ }
117
+ if (isPartialUrl(target["partialUrl"]) === false) {
118
+ return false
119
+ }
120
+ if ("pathname" in target === false) {
121
+ return false
122
+ }
123
+ if (isString(target["pathname"]) === false) {
124
+ return false
125
+ }
126
+ if ("search" in target === false) {
127
+ return false
128
+ }
129
+ if (isString(target["search"]) === false) {
130
+ return false
131
+ }
132
+ if ("hash" in target === false) {
133
+ return false
134
+ }
135
+ if (isString(target["hash"]) === false) {
136
+ return false
137
+ }
138
+
139
+ return true
140
+ }
141
+
142
+ /**
143
+ * 将 PartialUrl 解析为 URL 时使用的选项。
144
+ */
145
+ export interface PartialUrlToUrlOptions {
146
+ partialUrl: PartialUrl
147
+ baseUrl?: string | undefined
148
+ }
149
+
150
+ /**
151
+ * 将 PartialUrl 解析为完整的 URL 对象。
152
+ */
153
+ export const partialUrlToUrl = (options: PartialUrlToUrlOptions): URL => {
154
+ const { partialUrl, baseUrl } = options
155
+
156
+ // JavaScript 的 URL 构造函数在处理相对路径时,必须提供一个完整的基准 URL。
157
+ const PLACEHOLDER_URL_BASE = "https://url.example.com/"
158
+ const intermediateUrl = new URL(baseUrl ?? "/", PLACEHOLDER_URL_BASE)
159
+ const resultUrl = new URL(partialUrl, intermediateUrl)
160
+
161
+ return resultUrl
162
+ }
163
+
164
+ const constrainSearch = (search: string): string => {
165
+ return search === "?" ? "" : search
166
+ }
167
+
168
+ const constrainHash = (hash: string): string => {
169
+ return hash === "#" ? "" : hash
170
+ }
171
+
172
+ /**
173
+ * 将 URL 拆解为标准化的 RouteRecord。
174
+ */
175
+ export const urlToRouteRecord = (url: URL): RouteRecord => {
176
+ const pathname = toPathnameString(url.pathname)
177
+ const search = constrainSearch(toSearch(url.search))
178
+ const hash = constrainHash(toHashString(url.hash))
179
+
180
+ const pathnameString = pathname
181
+ const pathnameArray = toPathnameArray(pathname)
182
+ const query = toQueryString(search)
183
+ const queryString = toQueryString(search)
184
+ const queryObject = toQueryObject(search)
185
+ const hashString = hash
186
+ const hashObject = toHashObject(hash)
187
+
188
+ const partialUrl = `${pathnameString}${search}${hashString}`
189
+
190
+ return {
191
+ ...DEFAULT_ROUTE_RECORD,
192
+
193
+ partialUrl,
194
+ pathname,
195
+ search,
196
+ hash,
197
+
198
+ pathnameString,
199
+ pathnameArray,
200
+ query,
201
+ queryString,
202
+ queryObject,
203
+ hashString,
204
+ hashObject,
205
+ }
206
+ }
207
+
208
+ /**
209
+ * 将 PartialUrl 直接转换为标准化的 RouteRecord。
210
+ */
211
+ export const partialUrlToRouteRecord = (partialUrl: PartialUrl): RouteRecord => {
212
+ return urlToRouteRecord(partialUrlToUrl({ partialUrl }))
213
+ }
214
+
215
+ /**
216
+ * Route 构造时需要的输入选项。
217
+ */
218
+ export interface RouteOptions {
219
+ partialUrl: PartialUrl
220
+ }
221
+
222
+ /**
223
+ * 判断给定值是否满足 RouteOptions 结构。
224
+ */
225
+ export const isRouteOptions = (target: unknown): target is RouteOptions => {
226
+ if (isPlainObject(target) === false) {
227
+ return false
228
+ }
229
+ if ("partialUrl" in target === false) {
230
+ return false
231
+ }
232
+ if (isPartialUrl(target["partialUrl"]) === false) {
233
+ return false
234
+ }
235
+
236
+ return true
237
+ }
238
+ /**
239
+ * 维护 pathname、search 与 hash 一致性的路由模型。
240
+ *
241
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL}
242
+ */
243
+ export class Route<Payload = unknown> {
244
+ protected options: RouteOptions
245
+ protected record: RouteRecord
246
+ protected payload: Payload | undefined
247
+
248
+ constructor(options: RouteOptions) {
249
+ this.options = options
250
+ this.record = partialUrlToRouteRecord(options.partialUrl)
251
+ this.payload = undefined
252
+ }
253
+
254
+ /**
255
+ * 创建当前 Route 的深拷贝。
256
+ *
257
+ * 克隆结果会复制当前的标准化地址记录与 payload,避免后续修改影响原对象。
258
+ */
259
+ clone(): Route<Payload> {
260
+ const newRoute = new Route<Payload>({ partialUrl: this.record.partialUrl })
261
+ newRoute.setPayload(this.payload)
262
+ return newRoute
263
+ }
264
+
265
+ /**
266
+ * 返回当前挂载在 Route 上的业务负载。
267
+ */
268
+ getPayload(): Payload | undefined {
269
+ return this.payload
270
+ }
271
+
272
+ /**
273
+ * 设置当前 Route 对应的业务负载。
274
+ */
275
+ setPayload(payload: Payload | undefined): this {
276
+ this.payload = payload
277
+ return this
278
+ }
279
+
280
+ /**
281
+ * 返回当前 RouteRecord 的深拷贝快照。
282
+ */
283
+ getRecord(): RouteRecord {
284
+ return structuredClone(this.record)
285
+ }
286
+
287
+ protected correctPartialUrl(): this {
288
+ const { pathname, search, hash } = this.record
289
+ this.record.partialUrl = `${pathname}${search}${hash}`
290
+ return this
291
+ }
292
+
293
+ /**
294
+ * 清空当前 search 及其派生的 query 表达。
295
+ */
296
+ resetSearch(): this {
297
+ this.record.search = DEFAULT_ROUTE_RECORD.search
298
+ this.record.query = DEFAULT_ROUTE_RECORD.query
299
+ this.record.queryString = DEFAULT_ROUTE_RECORD.queryString
300
+ this.record.queryObject = DEFAULT_ROUTE_RECORD.queryObject
301
+ this.correctPartialUrl()
302
+ return this
303
+ }
304
+
305
+ /**
306
+ * 清空当前 query 表达。
307
+ */
308
+ resetQuery(): this {
309
+ this.resetSearch()
310
+ return this
311
+ }
312
+
313
+ /**
314
+ * 清空当前 hash 及其对象化表达。
315
+ */
316
+ resetHash(): this {
317
+ this.record.hash = DEFAULT_ROUTE_RECORD.hash
318
+ this.record.hashString = DEFAULT_ROUTE_RECORD.hashString
319
+ this.record.hashObject = DEFAULT_ROUTE_RECORD.hashObject
320
+ this.correctPartialUrl()
321
+ return this
322
+ }
323
+
324
+ /**
325
+ * 用完整 URL 替换当前 RouteRecord。
326
+ */
327
+ url(url: URL): this {
328
+ this.record = urlToRouteRecord(url)
329
+ return this
330
+ }
331
+
332
+ /**
333
+ * 用 PartialUrl 替换当前 RouteRecord。
334
+ */
335
+ partialUrl(partialUrl: PartialUrl): this {
336
+ this.record = partialUrlToRouteRecord(partialUrl)
337
+ return this
338
+ }
339
+
340
+ /**
341
+ * 更新 pathname,并在路径变化时重置 search 与 hash。
342
+ */
343
+ pathname(options: { pathname: PathnameUnion }): this {
344
+ const { pathname } = options
345
+
346
+ const oldPathnameString = this.record.pathnameString
347
+
348
+ const newPathnameString = toPathnameString(pathname)
349
+ this.record.pathname = newPathnameString
350
+ this.record.pathnameString = newPathnameString
351
+ this.record.pathnameArray = toPathnameArray(newPathnameString)
352
+
353
+ // when path is changed, reset query and hash
354
+ if (oldPathnameString !== newPathnameString) {
355
+ this.resetSearch()
356
+ this.resetQuery()
357
+ this.resetHash()
358
+ }
359
+
360
+ this.correctPartialUrl()
361
+ return this
362
+ }
363
+
364
+ /**
365
+ * 更新 search,并同步刷新 query 的字符串与对象表示。
366
+ */
367
+ search(options: { search: SearchUnion }): this {
368
+ const { search } = options
369
+
370
+ this.record.search = constrainSearch(toSearch(search))
371
+ this.record.query = toQueryString(search)
372
+ this.record.queryString = toQueryString(search)
373
+ this.record.queryObject = toQueryObject(search)
374
+
375
+ this.correctPartialUrl()
376
+ return this
377
+ }
378
+
379
+ /**
380
+ * 以合并方式写入 search,不存在的键会新增,已存在的键会覆盖。
381
+ */
382
+ upsertSearch(options: { search: SearchUnion }): this {
383
+ const { search } = options
384
+
385
+ const patchQueryObject = isString(search) ? toQueryObject(search) : search
386
+ const newQueryObject = { ...this.record.queryObject, ...patchQueryObject }
387
+
388
+ this.search({ search: toQueryString(newQueryObject) })
389
+
390
+ return this
391
+ }
392
+
393
+ /**
394
+ * 更新 hash,并同步刷新 hash 的对象表示。
395
+ */
396
+ hash(options: { hash: HashUnion }): this {
397
+ const { hash } = options
398
+
399
+ if (hash === "") {
400
+ this.resetHash()
401
+ return this
402
+ }
403
+
404
+ const hashString = constrainHash(toHashString(hash))
405
+
406
+ this.record.hash = hashString
407
+ this.record.hashString = hashString
408
+ this.record.hashObject = toHashObject(hashString)
409
+
410
+ this.correctPartialUrl()
411
+ return this
412
+ }
413
+
414
+ /**
415
+ * 比较两个 Route 或 RouteRecord 的 partialUrl 是否不同。
416
+ */
417
+ static distinctByPartialUrl(routeA: Route | RouteRecord, routeB: Route | RouteRecord): boolean {
418
+ const routeRecordA = isRoute(routeA) ? routeA.getRecord() : routeA
419
+ const routeRecordB = isRoute(routeB) ? routeB.getRecord() : routeB
420
+ return routeRecordA.partialUrl !== routeRecordB.partialUrl
421
+ }
422
+
423
+ /**
424
+ * 比较两个 Route 或 RouteRecord 的 pathname 是否不同。
425
+ */
426
+ static distinctByPathname(routeA: Route | RouteRecord, routeB: Route | RouteRecord): boolean {
427
+ const routeRecordA = isRoute(routeA) ? routeA.getRecord() : routeA
428
+ const routeRecordB = isRoute(routeB) ? routeB.getRecord() : routeB
429
+ return routeRecordA.pathnameString !== routeRecordB.pathnameString
430
+ }
431
+
432
+ /**
433
+ * 比较两个 Route 或 RouteRecord 的 search 是否不同。
434
+ */
435
+ static distinctBySearch(routeA: Route | RouteRecord, routeB: Route | RouteRecord): boolean {
436
+ const routeRecordA = isRoute(routeA) ? routeA.getRecord() : routeA
437
+ const routeRecordB = isRoute(routeB) ? routeB.getRecord() : routeB
438
+ return routeRecordA.search !== routeRecordB.search
439
+ }
440
+
441
+ /**
442
+ * 比较两个 Route 或 RouteRecord 的 hash 是否不同。
443
+ */
444
+ static distinctByHash(routeA: Route | RouteRecord, routeB: Route | RouteRecord): boolean {
445
+ const routeRecordA = isRoute(routeA) ? routeA.getRecord() : routeA
446
+ const routeRecordB = isRoute(routeB) ? routeB.getRecord() : routeB
447
+ return routeRecordA.hashString !== routeRecordB.hashString
448
+ }
449
+
450
+ /**
451
+ * 判断当前 Route 与目标 Route 或 RouteRecord 的 partialUrl 是否不同。
452
+ */
453
+ isDistinctByPartialUrl(route: Route | RouteRecord): boolean {
454
+ const targetRouteRecord = isRoute(route) ? route.getRecord() : route
455
+ return this.record.partialUrl !== targetRouteRecord.partialUrl
456
+ }
457
+
458
+ /**
459
+ * 判断当前 Route 与目标 Route 或 RouteRecord 的 pathname 是否不同。
460
+ */
461
+ isDistinctByPathname(route: Route | RouteRecord): boolean {
462
+ const targetRouteRecord = isRoute(route) ? route.getRecord() : route
463
+ return this.record.pathnameString !== targetRouteRecord.pathnameString
464
+ }
465
+
466
+ /**
467
+ * 判断当前 Route 与目标 Route 或 RouteRecord 的 search 是否不同。
468
+ */
469
+ isDistinctBySearch(route: Route | RouteRecord): boolean {
470
+ const targetRouteRecord = isRoute(route) ? route.getRecord() : route
471
+ return this.record.search !== targetRouteRecord.search
472
+ }
473
+
474
+ /**
475
+ * 判断当前 Route 与目标 Route 或 RouteRecord 的 hash 是否不同。
476
+ */
477
+ isDistinctByHash(route: Route | RouteRecord): boolean {
478
+ const targetRouteRecord = isRoute(route) ? route.getRecord() : route
479
+ return this.record.hashString !== targetRouteRecord.hashString
480
+ }
481
+
482
+ /**
483
+ * 以宽松模式判断当前 pathname 是否包含指定路径段。
484
+ */
485
+ isPathnameLooseIncludes(pieces: string | string[]): boolean {
486
+ return isPathnameLooseIncludes(this.record.pathname, pieces)
487
+ }
488
+
489
+ /**
490
+ * 以严格模式判断当前 pathname 是否包含指定路径片段。
491
+ */
492
+ isPathnameStrictIncludes(pieces: string | string[]): boolean {
493
+ return isPathnameStrictIncludes(this.record.pathname, pieces)
494
+ }
495
+
496
+ /**
497
+ * 以宽松模式判断当前 pathname 是否与目标值相等。
498
+ */
499
+ isPathnameLooseEqual(pathname: PathnameUnion): boolean {
500
+ return isPathnameLooseEqual(this.record.pathname, pathname)
501
+ }
502
+
503
+ /**
504
+ * 以严格模式判断当前 pathname 是否与目标值相等。
505
+ */
506
+ isPathnameStrictEqual(pathname: PathnameUnion): boolean {
507
+ return isPathnameStrictEqual(this.record.pathname, pathname)
508
+ }
509
+
510
+ /**
511
+ * 宽松包含关系:查询参数的键集合是目标查询参数键集合的子集。
512
+ * 例如:`?a=1&b=2` 包含 `?a=1`,但不包含 `?a=1&c=3`。
513
+ */
514
+ isSearchLooseIncludes(queryKeys: string | string[]): boolean {
515
+ return isSearchLooseIncludes(this.record.search, queryKeys)
516
+ }
517
+
518
+ /**
519
+ * 严格包含关系:查询参数的键集合与目标查询参数键集合完全相同。
520
+ * 例如:`?a=1&b=2` 严格包含 `?a=1&b=2`,但不严格包含 `?a=1` 或 `?a=1&c=3`。
521
+ */
522
+ isSearchStrictIncludes(queryKeys: string | string[]): boolean {
523
+ return isSearchStrictIncludes(this.record.search, queryKeys)
524
+ }
525
+
526
+ /**
527
+ * 包含关系:查询参数的键集合必须包含目标查询参数键集合,并且不能包含除目标查询参数键集合之外的其他键。
528
+ * 例如:`?a=1&b=2` 包含 `?a=1`,但不包含 `?a=1&c=3` 或 `?a=1&b=2&c=3`。
529
+ */
530
+ isSearchIncludes(query: Record<string, "required" | "optional">): boolean {
531
+ return isSearchIncludes(this.record.search, query)
532
+ }
533
+
534
+ /**
535
+ * 此处的 Equal 语义与 Match 类似。
536
+ *
537
+ * 宽松相等:查询参数的键集合是目标查询参数键集合的子集,并且对应的值相等。
538
+ * 例如:`?a=1&b=2` 宽松相等 `?a=1`,但不宽松相等 `?a=1&c=3`。
539
+ */
540
+ isSearchLooseEqual(query: SearchUnion): boolean {
541
+ return isSearchLooseEqual(this.record.search, query)
542
+ }
543
+
544
+ /**
545
+ * 严格相等:查询参数的键集合与目标查询参数键集合完全相同,并且对应的值相等。
546
+ * 例如:`?a=1&b=2` 严格相等 `?a=1&b=2`,但不严格相等 `?a=1` 或 `?a=1&c=3`。
547
+ */
548
+ isSearchStrictEqual(query: SearchUnion): boolean {
549
+ return isSearchStrictEqual(this.record.search, query)
550
+ }
551
+
552
+ /**
553
+ * 宽松包含关系:hash 参数的键集合是目标 hash 参数键集合的子集。
554
+ * 例如:`#a=1&b=2` 包含 `#a=1`,但不包含 `#a=1&c=3`。
555
+ */
556
+ isHashLooseIncludes(hashKeys: string | string[]): boolean {
557
+ return isHashLooseIncludes(this.record.hash, hashKeys)
558
+ }
559
+
560
+ /**
561
+ * 严格包含关系:hash 参数的键集合与目标 hash 参数键集合完全相同。
562
+ * 例如:`#a=1&b=2` 严格包含 `#a=1&b=2`,但不严格包含 `#a=1` 或 `#a=1&c=3`。
563
+ */
564
+ isHashStrictIncludes(hashKeys: string | string[]): boolean {
565
+ return isHashStrictIncludes(this.record.hash, hashKeys)
566
+ }
567
+
568
+ /**
569
+ * 包含关系:hash 参数的键集合必须包含目标 hash 参数键集合,并且不能包含除目标 hash 参数键集合之外的其他键。
570
+ * 例如:`#a=1&b=2` 包含 `#a=1`,但不包含 `#a=1&c=3` 或 `#a=1&b=2&c=3`。
571
+ */
572
+ isHashIncludes(hash: Record<string, "required" | "optional">): boolean {
573
+ return isHashIncludes(this.record.hash, hash)
574
+ }
575
+
576
+ /**
577
+ * 此处的 Equal 语义与 Match 类似。
578
+ *
579
+ * 宽松相等:hash 参数的键集合是目标 hash 参数键集合的子集,并且对应的值相等。
580
+ * 例如:`#a=1&b=2` 宽松相等 `#a=1`,但不宽松相等 `#a=1&c=3`。
581
+ */
582
+ isHashLooseEqual(hash: HashUnion): boolean {
583
+ return isHashLooseEqual(this.record.hash, hash)
584
+ }
585
+
586
+ /**
587
+ * 严格相等:hash 参数的键集合与目标 hash 参数键集合完全相同,并且对应的值相等。
588
+ * 例如:`#a=1&b=2` 严格相等 `#a=1&b=2`,但不严格相等 `#a=1` 或 `#a=1&c=3`。
589
+ */
590
+ isHashStrictEqual(hash: HashUnion): boolean {
591
+ return isHashStrictEqual(this.record.hash, hash)
592
+ }
593
+ }
594
+
595
+ /**
596
+ * 判断给定值是否为 Route 实例。
597
+ */
598
+ export const isRoute = (target: unknown): target is Route => {
599
+ return target instanceof Route
600
+ }
601
+
602
+ /**
603
+ * 将多种 Route 输入统一转换为 Route 实例。
604
+ *
605
+ * 如果输入已经是 Route 实例,则直接返回原对象;否则会基于可识别的路由表达创建新实例。
606
+ */
607
+ export const createRoute = (target: PartialUrl | RouteRecord | RouteOptions | Route): Route => {
608
+ if (isPartialUrl(target)) {
609
+ return new Route({ partialUrl: target })
610
+ }
611
+ else if (isRouteRecord(target)) {
612
+ return new Route({ partialUrl: target.partialUrl })
613
+ }
614
+ else if (isRouteOptions(target)) {
615
+ return new Route(target)
616
+ }
617
+ else if (isRoute(target)) {
618
+ return target
619
+ }
620
+ else {
621
+ throw (new TypeError("\"target\" is expected to be type of \"String\" or \"Route\" or \"RouteRecord\" or \"RouteOptions\"."))
622
+ }
623
+ }
624
+
625
+ /**
626
+ * 创建一个指向根路径的空 Route。
627
+ */
628
+ export const emptyRoute = (): Route => {
629
+ return new Route({ partialUrl: DEFAULT_ROUTE_RECORD.partialUrl })
630
+ }