@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.
- package/CHANGELOG.md +37 -0
- package/dist/index.js +706 -36
- package/dist/index.js.map +855 -59
- package/package.json +28 -16
- 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 +20 -15
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +2 -2
- package/src/basic/index.ts +1 -0
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- 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/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/event/class-event-proxy.ts +6 -5
- package/src/event/common.ts +13 -3
- package/src/event/event-manager.ts +3 -3
- package/src/event/instance-event-proxy.ts +6 -5
- package/src/event/internal.ts +4 -4
- 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/index.ts +19 -2
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/logger.ts +15 -4
- 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 +9 -7
- 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 +2 -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 +8 -7
- 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/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/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/table.ts +3 -3
- package/src/timer/expiration/expiration-manager.ts +3 -3
- package/src/timer/expiration/remaining-manager.ts +3 -3
- 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/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/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/error.spec.ts +16 -4
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +90 -37
- 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/event/class-event-proxy.spec.ts +3 -3
- package/tests/unit/event/event-manager.spec.ts +3 -3
- package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
- 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/json/repair.spec.ts +11 -0
- package/tests/unit/log/logger.spec.ts +19 -4
- package/tests/unit/openai/openai.spec.ts +64 -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/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/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/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/src/random/string.ts +0 -35
- package/tests/unit/random/string.spec.ts +0 -11
|
@@ -0,0 +1,874 @@
|
|
|
1
|
+
|
|
2
|
+
const internalGenerateUuid = (): string => {
|
|
3
|
+
const tempURL = URL.createObjectURL(new Blob())
|
|
4
|
+
const uuidInUrl = tempURL
|
|
5
|
+
URL.revokeObjectURL(tempURL)
|
|
6
|
+
const uuid = uuidInUrl.slice(uuidInUrl.lastIndexOf("/") + 1)
|
|
7
|
+
return uuid
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 这些属性是所有 Inputor 的共同属性。
|
|
12
|
+
*/
|
|
13
|
+
export interface BaseInputorSchema<ExternalValue> {
|
|
14
|
+
inputorTypeName: string
|
|
15
|
+
id: string
|
|
16
|
+
name: string
|
|
17
|
+
placeholder: string
|
|
18
|
+
value: ExternalValue
|
|
19
|
+
isVoid: boolean
|
|
20
|
+
isFocused: boolean
|
|
21
|
+
/**
|
|
22
|
+
* 只有 isFocused 发生了更新才会被设置为 true。
|
|
23
|
+
*/
|
|
24
|
+
isTouched: boolean
|
|
25
|
+
/**
|
|
26
|
+
* 只要 value 发生了更新就会被设置为 true。
|
|
27
|
+
*/
|
|
28
|
+
isDirty: boolean
|
|
29
|
+
isDisabled: boolean
|
|
30
|
+
isDebugMode: boolean
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Inputor 内部工作时的上下文,包含一些不对外暴露的属性和状态,扩展自 {@link BaseInputorSchema}。
|
|
34
|
+
*/
|
|
35
|
+
export interface BaseInputorContext<ExternalValue, InternalValue> extends BaseInputorSchema<ExternalValue> {
|
|
36
|
+
internalValue: InternalValue
|
|
37
|
+
/**
|
|
38
|
+
* 是否可以开始进行交互。
|
|
39
|
+
*/
|
|
40
|
+
isReady: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface BaseInputorControllerOptions<ExternalValue, Schema extends BaseInputorSchema<ExternalValue>> {
|
|
44
|
+
id?: string | undefined
|
|
45
|
+
name?: string | undefined
|
|
46
|
+
placeholder?: string | undefined
|
|
47
|
+
/**
|
|
48
|
+
* 为了让所有的值(包括 undefined、null)都可以作为合法的 Inputor 的 Value,
|
|
49
|
+
* 所以将 voidValue 交给 Inputor 的具体实现者来定义,于是此处就变成了必选。
|
|
50
|
+
*
|
|
51
|
+
* 当然了,具体的 Inputor 为了使用方便,可以在处理好相关逻辑的基础上将其覆盖为可选。
|
|
52
|
+
*/
|
|
53
|
+
value: ExternalValue
|
|
54
|
+
/**
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
isFocused?: boolean | undefined
|
|
58
|
+
/**
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
isTouched?: boolean | undefined
|
|
62
|
+
/**
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
isDirty?: boolean | undefined
|
|
66
|
+
/**
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
isDisabled?: boolean | undefined
|
|
70
|
+
/**
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
isDebugMode?: boolean | undefined
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 在原生 type 为 text 的 input 元素中,input 和 change 是两个独立的事件,
|
|
77
|
+
* 用户的所有修改操作都会实时触发 input 事件,但只有在失去焦点或确认(按回车等)
|
|
78
|
+
* 时才会触发 change 事件。
|
|
79
|
+
*
|
|
80
|
+
* 此参数用于控制是否在 input 事件发生后自动触发 change 事件,此行为受
|
|
81
|
+
* {@link inputDebounce} 影响。
|
|
82
|
+
*
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
enableAutoChangeValueAfterInput?: boolean | undefined
|
|
86
|
+
/**
|
|
87
|
+
* 此参数用于控制 input 事件的触发频率,单位为毫秒,只有当
|
|
88
|
+
* {@link enableAutoChangeValueAfterInput} 为 true 时才会生效。
|
|
89
|
+
*
|
|
90
|
+
* @default 0
|
|
91
|
+
*/
|
|
92
|
+
inputDebounce?: number | undefined
|
|
93
|
+
|
|
94
|
+
onInputChange?: InputChangeSubscriber<ExternalValue> | undefined
|
|
95
|
+
onValueChange?: ValueChangeSubscriber<ExternalValue> | undefined
|
|
96
|
+
onSchemaChange?: SchemaChangeSubscriber<ExternalValue, Schema> | undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface InputChangeContext<ExternalValue> {
|
|
100
|
+
newInput: ExternalValue
|
|
101
|
+
oldInput: ExternalValue
|
|
102
|
+
oldValue: ExternalValue
|
|
103
|
+
}
|
|
104
|
+
export type InputChangeSubscriber<ExternalValue>
|
|
105
|
+
= (context: InputChangeContext<ExternalValue>) => void | Promise<void>
|
|
106
|
+
export interface InputChangeSubscriberEntry<ExternalValue> {
|
|
107
|
+
subscriber: InputChangeSubscriber<ExternalValue>
|
|
108
|
+
unsubscribe: () => void
|
|
109
|
+
}
|
|
110
|
+
export type InputChangeSubscriberMap<ExternalValue>
|
|
111
|
+
= Map<InputChangeSubscriber<ExternalValue>, InputChangeSubscriberEntry<ExternalValue>>
|
|
112
|
+
|
|
113
|
+
export interface ValueChangeContext<ExternalValue> {
|
|
114
|
+
newValue: ExternalValue
|
|
115
|
+
oldValue: ExternalValue
|
|
116
|
+
}
|
|
117
|
+
export type ValueChangeSubscriber<ExternalValue> = (
|
|
118
|
+
context: ValueChangeContext<ExternalValue>
|
|
119
|
+
) => void | Promise<void>
|
|
120
|
+
export interface ValueChangeSubscriberEntry<ExternalValue> {
|
|
121
|
+
subscriber: ValueChangeSubscriber<ExternalValue>
|
|
122
|
+
unsubscribe: () => void
|
|
123
|
+
}
|
|
124
|
+
export type ValueChangeSubscriberMap<ExternalValue> = Map<
|
|
125
|
+
ValueChangeSubscriber<ExternalValue>,
|
|
126
|
+
ValueChangeSubscriberEntry<ExternalValue>
|
|
127
|
+
>
|
|
128
|
+
|
|
129
|
+
export interface SchemaChangeContext<
|
|
130
|
+
ExternalValue,
|
|
131
|
+
Schema extends BaseInputorSchema<ExternalValue>
|
|
132
|
+
> {
|
|
133
|
+
newSchema: Schema
|
|
134
|
+
oldSchema: Schema
|
|
135
|
+
}
|
|
136
|
+
export type SchemaChangeSubscriber<
|
|
137
|
+
ExternalValue,
|
|
138
|
+
Schema extends BaseInputorSchema<ExternalValue>
|
|
139
|
+
> = (
|
|
140
|
+
context: SchemaChangeContext<ExternalValue, Schema>
|
|
141
|
+
) => void | Promise<void>
|
|
142
|
+
export interface SchemaChangeSubscriberEntry<
|
|
143
|
+
ExternalValue,
|
|
144
|
+
Schema extends BaseInputorSchema<ExternalValue>,
|
|
145
|
+
> {
|
|
146
|
+
subscriber: SchemaChangeSubscriber<ExternalValue, Schema>
|
|
147
|
+
unsubscribe: () => void
|
|
148
|
+
}
|
|
149
|
+
export type SchemaChangeSubscriberMap<
|
|
150
|
+
ExternalValue,
|
|
151
|
+
Schema extends BaseInputorSchema<ExternalValue>,
|
|
152
|
+
> = Map<
|
|
153
|
+
SchemaChangeSubscriber<ExternalValue, Schema>,
|
|
154
|
+
SchemaChangeSubscriberEntry<ExternalValue, Schema>
|
|
155
|
+
>
|
|
156
|
+
|
|
157
|
+
export interface InputorControllerReadyState {
|
|
158
|
+
isReady: boolean
|
|
159
|
+
promise: Promise<void>
|
|
160
|
+
resolve: () => void
|
|
161
|
+
}
|
|
162
|
+
export interface InputorControllerInputState<ExternalValue> {
|
|
163
|
+
isInputing: boolean
|
|
164
|
+
previousInputValue: ExternalValue
|
|
165
|
+
currentInputValue: ExternalValue
|
|
166
|
+
timer: ReturnType<typeof setTimeout> | undefined
|
|
167
|
+
}
|
|
168
|
+
export interface InputorControllerInitializeHooks {
|
|
169
|
+
onStart?: () => void | Promise<void>
|
|
170
|
+
onSchemaReady?: () => void | Promise<void>
|
|
171
|
+
onOptionsReady?: () => void | Promise<void>
|
|
172
|
+
onInternalStatesReady?: () => void | Promise<void>
|
|
173
|
+
onAllReady?: () => void | Promise<void>
|
|
174
|
+
onFinish?: () => void | Promise<void>
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface UpdateContext<ExternalValue, InternalValue> extends BaseInputorSchema<ExternalValue> {
|
|
178
|
+
internalValue: InternalValue
|
|
179
|
+
}
|
|
180
|
+
export type Update<ExternalValue, InternalValue> = (
|
|
181
|
+
context: UpdateContext<ExternalValue, InternalValue>
|
|
182
|
+
) => UpdateContext<ExternalValue, InternalValue> | Promise<UpdateContext<ExternalValue, InternalValue>>
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 假设一个 NumberTextInputor 由一个 `type="number"` 的 Input 元素实现,
|
|
186
|
+
* 那么其 InternalValue 就是 number,ExternalValue 就是 string。
|
|
187
|
+
*
|
|
188
|
+
* - ExternalValue - Inputor 对外界声明的值的类型。
|
|
189
|
+
* - InternalValue - Inputor 内部维护的值的类型。
|
|
190
|
+
*
|
|
191
|
+
* 备忘:
|
|
192
|
+
* - 不要试图将 InternalValue 纳入 Schema,因为 InternalValue 和 ExternalValue
|
|
193
|
+
* 之间有特定的转换关系,将二者都放在 Schema 中会提升使用难度和出错概率,换言之,
|
|
194
|
+
* InternalValue 叫 InternalValue 的原因之一就是因为我们决定将它隐藏在 Inputor 内部。
|
|
195
|
+
*/
|
|
196
|
+
export abstract class BaseInputorController<
|
|
197
|
+
ExternalValue,
|
|
198
|
+
InternalValue,
|
|
199
|
+
Schema extends BaseInputorSchema<ExternalValue>,
|
|
200
|
+
Context extends BaseInputorContext<ExternalValue, InternalValue>,
|
|
201
|
+
> {
|
|
202
|
+
/**
|
|
203
|
+
* 方便进行类型计算。
|
|
204
|
+
*/
|
|
205
|
+
abstract declare schemaType: Schema
|
|
206
|
+
/**
|
|
207
|
+
* 方便进行类型计算。
|
|
208
|
+
*/
|
|
209
|
+
abstract declare optionsType: BaseInputorControllerOptions<ExternalValue, Schema>
|
|
210
|
+
/**
|
|
211
|
+
* 方便进行类型计算。
|
|
212
|
+
*/
|
|
213
|
+
abstract declare constructorType: typeof BaseInputorController<ExternalValue, InternalValue, Schema, Context>
|
|
214
|
+
/**
|
|
215
|
+
* 方便进行类型计算。
|
|
216
|
+
*/
|
|
217
|
+
declare instanceType: this
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 每个 Inputor 都应该有一个类型名称,用于将它与其它的 Inputor 区分开来。
|
|
221
|
+
*/
|
|
222
|
+
abstract inputorTypeName: string
|
|
223
|
+
protected abstract voidExternalValue: ExternalValue
|
|
224
|
+
protected abstract voidInternalValue: InternalValue
|
|
225
|
+
|
|
226
|
+
protected id!: string
|
|
227
|
+
protected name!: string
|
|
228
|
+
protected placeholder!: string
|
|
229
|
+
protected value!: ExternalValue
|
|
230
|
+
protected isVoid!: boolean
|
|
231
|
+
protected isFocused!: boolean
|
|
232
|
+
protected isTouched!: boolean
|
|
233
|
+
protected isDirty!: boolean
|
|
234
|
+
protected isDisabled!: boolean
|
|
235
|
+
protected isDebugMode!: boolean
|
|
236
|
+
|
|
237
|
+
protected enableAutoChangeValueAfterInput!: boolean
|
|
238
|
+
protected inputDebounce!: number
|
|
239
|
+
protected onInputChange?: InputChangeSubscriber<ExternalValue> | undefined
|
|
240
|
+
protected onValueChange?: ValueChangeSubscriber<ExternalValue> | undefined
|
|
241
|
+
protected onSchemaChange?: SchemaChangeSubscriber<ExternalValue, Schema> | undefined
|
|
242
|
+
|
|
243
|
+
protected inputChangeSubscriberMap: InputChangeSubscriberMap<ExternalValue>
|
|
244
|
+
protected valueChangeSubscriberMap: ValueChangeSubscriberMap<ExternalValue>
|
|
245
|
+
protected schemaChangeSubscriberMap: SchemaChangeSubscriberMap<ExternalValue, Schema>
|
|
246
|
+
|
|
247
|
+
protected readyState: InputorControllerReadyState
|
|
248
|
+
protected internalValue!: InternalValue
|
|
249
|
+
protected inputState!: InputorControllerInputState<ExternalValue>
|
|
250
|
+
|
|
251
|
+
constructor(options: BaseInputorControllerOptions<ExternalValue, Schema>) {
|
|
252
|
+
this.inputChangeSubscriberMap = new Map()
|
|
253
|
+
this.valueChangeSubscriberMap = new Map()
|
|
254
|
+
this.schemaChangeSubscriberMap = new Map()
|
|
255
|
+
|
|
256
|
+
let _resolve = (): void => {
|
|
257
|
+
// this is just a placeholder, the real resolve function will be assigned
|
|
258
|
+
// below when initializing the readyState
|
|
259
|
+
}
|
|
260
|
+
this.readyState = {
|
|
261
|
+
isReady: false,
|
|
262
|
+
promise: new Promise<void>((resolve) => {
|
|
263
|
+
_resolve = () => {
|
|
264
|
+
this.readyState.isReady = true
|
|
265
|
+
resolve()
|
|
266
|
+
}
|
|
267
|
+
}),
|
|
268
|
+
resolve: _resolve,
|
|
269
|
+
}
|
|
270
|
+
void this.initialize(options)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
protected log(...args: unknown[]): void {
|
|
274
|
+
if (this.isDebugMode === true) {
|
|
275
|
+
console.log(...args)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
protected async initialize(
|
|
280
|
+
options: BaseInputorControllerOptions<ExternalValue, Schema>,
|
|
281
|
+
hooks?: InputorControllerInitializeHooks,
|
|
282
|
+
): Promise<void> {
|
|
283
|
+
const { onStart, onSchemaReady, onOptionsReady, onInternalStatesReady, onAllReady, onFinish } = hooks ?? {}
|
|
284
|
+
|
|
285
|
+
await onStart?.()
|
|
286
|
+
|
|
287
|
+
this.id = options.id ?? internalGenerateUuid()
|
|
288
|
+
this.name = options.name ?? `${this.inputorTypeName}-${this.id}`
|
|
289
|
+
this.placeholder = options.placeholder ?? ""
|
|
290
|
+
this.value = await this.constrainExternalValue(options.value)
|
|
291
|
+
this.isVoid = await this.isVoidExternalValue(this.value)
|
|
292
|
+
this.isFocused = options.isFocused ?? false
|
|
293
|
+
this.isTouched = options.isTouched ?? false
|
|
294
|
+
this.isDirty = options.isDirty ?? false
|
|
295
|
+
this.isDisabled = options.isDisabled ?? false
|
|
296
|
+
this.isDebugMode = options.isDebugMode ?? false
|
|
297
|
+
|
|
298
|
+
await onSchemaReady?.()
|
|
299
|
+
|
|
300
|
+
this.enableAutoChangeValueAfterInput = options.enableAutoChangeValueAfterInput ?? false
|
|
301
|
+
this.inputDebounce = options.inputDebounce ?? 0
|
|
302
|
+
this.onInputChange = options.onInputChange
|
|
303
|
+
this.onValueChange = options.onValueChange
|
|
304
|
+
this.onSchemaChange = options.onSchemaChange
|
|
305
|
+
|
|
306
|
+
if (this.onInputChange !== undefined) {
|
|
307
|
+
this.subscribeInputChange(this.onInputChange)
|
|
308
|
+
}
|
|
309
|
+
if (this.onValueChange !== undefined) {
|
|
310
|
+
this.subscribeValueChange(this.onValueChange)
|
|
311
|
+
}
|
|
312
|
+
if (this.onSchemaChange !== undefined) {
|
|
313
|
+
this.subscribeSchemaChange(this.onSchemaChange)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
await onOptionsReady?.()
|
|
317
|
+
|
|
318
|
+
this.internalValue = await this.externalValueToInternalValue(this.value)
|
|
319
|
+
this.inputState = {
|
|
320
|
+
isInputing: false,
|
|
321
|
+
previousInputValue: this.value,
|
|
322
|
+
currentInputValue: this.value,
|
|
323
|
+
timer: undefined,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
await onInternalStatesReady?.()
|
|
327
|
+
|
|
328
|
+
// update ready states before emitting changes
|
|
329
|
+
this.ready()
|
|
330
|
+
|
|
331
|
+
await onAllReady?.()
|
|
332
|
+
|
|
333
|
+
const currentValue = structuredClone(this.value)
|
|
334
|
+
this.emitValueChange({ newValue: currentValue, oldValue: currentValue })
|
|
335
|
+
const currentSchema = await this.getSchema()
|
|
336
|
+
this.emitSchemaChange({ newSchema: currentSchema, oldSchema: currentSchema })
|
|
337
|
+
|
|
338
|
+
await onFinish?.()
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
protected emitInputChange(context: InputChangeContext<ExternalValue>): void {
|
|
342
|
+
const { newInput, oldInput, oldValue } = context
|
|
343
|
+
this.log("[emitInputChange] emit start", "newInput", newInput, "oldInput", oldInput, "oldValue", oldValue)
|
|
344
|
+
this.log("[emitInputChange] count of subscribers", this.inputChangeSubscriberMap.size)
|
|
345
|
+
for (const state of this.inputChangeSubscriberMap.values()) {
|
|
346
|
+
void state.subscriber(context)
|
|
347
|
+
}
|
|
348
|
+
this.log("[emitInputChange] emit end")
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
subscribeInputChange(subscriber: InputChangeSubscriber<ExternalValue>): () => void {
|
|
352
|
+
this.log("[subscribeInputChange] subscribe start", "subscriber", subscriber)
|
|
353
|
+
const state: InputChangeSubscriberEntry<ExternalValue> = {
|
|
354
|
+
unsubscribe: () => {
|
|
355
|
+
this.log("[subscribeInputChange] unsubscribe start", "subscriber", subscriber)
|
|
356
|
+
this.inputChangeSubscriberMap.delete(subscriber)
|
|
357
|
+
this.log("[subscribeInputChange] unsubscribe end", "count of subscribers", this.inputChangeSubscriberMap.size)
|
|
358
|
+
},
|
|
359
|
+
subscriber,
|
|
360
|
+
}
|
|
361
|
+
this.inputChangeSubscriberMap.set(subscriber, state)
|
|
362
|
+
this.log("[subscribeInputChange] subscribe end", "count of subscribers", this.inputChangeSubscriberMap.size)
|
|
363
|
+
return state.unsubscribe
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
unsubscribeInputChange(subscriber: InputChangeSubscriber<ExternalValue>): void {
|
|
367
|
+
this.log("[unsubscribeInputChange] unsubscribe start", "subscriber", subscriber)
|
|
368
|
+
this.inputChangeSubscriberMap.delete(subscriber)
|
|
369
|
+
this.log("[unsubscribeInputChange] unsubscribe end", "count of subscribers", this.inputChangeSubscriberMap.size)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
clearInputChangeSubscribers(): void {
|
|
373
|
+
this.log("[clearInputChangeSubscribers] clear start, count of subscribers", this.inputChangeSubscriberMap.size)
|
|
374
|
+
this.inputChangeSubscriberMap.clear()
|
|
375
|
+
this.log("[clearInputChangeSubscribers] clear end, count of subscribers", this.inputChangeSubscriberMap.size)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
protected emitValueChange(context: ValueChangeContext<ExternalValue>): void {
|
|
379
|
+
const { newValue, oldValue } = context
|
|
380
|
+
this.log("[emitValueChange] emit start", "newValue", newValue, "oldValue", oldValue)
|
|
381
|
+
this.log("[emitValueChange] count of subscribers", this.valueChangeSubscriberMap.size)
|
|
382
|
+
for (const state of this.valueChangeSubscriberMap.values()) {
|
|
383
|
+
void state.subscriber(context)
|
|
384
|
+
}
|
|
385
|
+
this.log("[emitValueChange] emit end")
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
subscribeValueChange(subscriber: ValueChangeSubscriber<ExternalValue>): () => void {
|
|
389
|
+
this.log("[subscribeValueChange] subscribe start", "subscriber", subscriber)
|
|
390
|
+
const state: ValueChangeSubscriberEntry<ExternalValue> = {
|
|
391
|
+
unsubscribe: () => {
|
|
392
|
+
this.log("[subscribeValueChange] unsubscribe start", "subscriber", subscriber)
|
|
393
|
+
this.valueChangeSubscriberMap.delete(subscriber)
|
|
394
|
+
this.log("[subscribeValueChange] unsubscribe end", "count of subscribers", this.valueChangeSubscriberMap.size)
|
|
395
|
+
},
|
|
396
|
+
subscriber,
|
|
397
|
+
}
|
|
398
|
+
this.valueChangeSubscriberMap.set(subscriber, state)
|
|
399
|
+
this.log("[subscribeValueChange] subscribe end", "count of subscribers", this.valueChangeSubscriberMap.size)
|
|
400
|
+
return state.unsubscribe
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
unsubscribeValueChange(subscriber: ValueChangeSubscriber<ExternalValue>): void {
|
|
404
|
+
this.log("[unsubscribeValueChange] unsubscribe start", "subscriber", subscriber)
|
|
405
|
+
this.valueChangeSubscriberMap.delete(subscriber)
|
|
406
|
+
this.log("[unsubscribeValueChange] unsubscribe end", "count of subscribers", this.valueChangeSubscriberMap.size)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
clearValueChangeSubscribers(): void {
|
|
410
|
+
this.log("[clearValueChangeSubscribers] clear start, count of subscribers", this.valueChangeSubscriberMap.size)
|
|
411
|
+
this.valueChangeSubscriberMap.clear()
|
|
412
|
+
this.log("[clearValueChangeSubscribers] clear end, count of subscribers", this.valueChangeSubscriberMap.size)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
protected emitSchemaChange(context: SchemaChangeContext<ExternalValue, Schema>): void {
|
|
416
|
+
const { newSchema, oldSchema } = context
|
|
417
|
+
this.log("[emitSchemaChange] emit start", "newSchema", newSchema, "oldSchema", oldSchema)
|
|
418
|
+
this.log("[emitSchemaChange] count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
419
|
+
for (const state of this.schemaChangeSubscriberMap.values()) {
|
|
420
|
+
void state.subscriber(context)
|
|
421
|
+
}
|
|
422
|
+
this.log("[emitSchemaChange] emit end")
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
subscribeSchemaChange(subscriber: SchemaChangeSubscriber<ExternalValue, Schema>): () => void {
|
|
426
|
+
this.log("[subscribeSchemaChange] subscribe start", "subscriber", subscriber)
|
|
427
|
+
const state: SchemaChangeSubscriberEntry<ExternalValue, Schema> = {
|
|
428
|
+
unsubscribe: () => {
|
|
429
|
+
this.log("[subscribeSchemaChange] unsubscribe start", "subscriber", subscriber)
|
|
430
|
+
this.schemaChangeSubscriberMap.delete(subscriber)
|
|
431
|
+
this.log("[subscribeSchemaChange] unsubscribe end", "count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
432
|
+
},
|
|
433
|
+
subscriber,
|
|
434
|
+
}
|
|
435
|
+
this.schemaChangeSubscriberMap.set(subscriber, state)
|
|
436
|
+
this.log("[subscribeSchemaChange] subscribe end", "count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
437
|
+
return state.unsubscribe
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
unsubscribeSchemaChange(subscriber: SchemaChangeSubscriber<ExternalValue, Schema>): void {
|
|
441
|
+
this.log("[unsubscribeSchemaChange] unsubscribe start", "subscriber", subscriber)
|
|
442
|
+
this.schemaChangeSubscriberMap.delete(subscriber)
|
|
443
|
+
this.log("[unsubscribeSchemaChange] unsubscribe end", "count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
clearSchemaChangeSubscribers(): void {
|
|
447
|
+
this.log("[clearSchemaChangeSubscribers] clear start, count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
448
|
+
this.schemaChangeSubscriberMap.clear()
|
|
449
|
+
this.log("[clearSchemaChangeSubscribers] clear end, count of subscribers", this.schemaChangeSubscriberMap.size)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
protected ready(): void {
|
|
453
|
+
this.log("[ready] ready start")
|
|
454
|
+
this.readyState.resolve()
|
|
455
|
+
this.log("[ready] ready end")
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
isReady(): boolean {
|
|
459
|
+
return this.readyState.isReady
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async waitForReady(): Promise<void> {
|
|
463
|
+
return await this.readyState.promise
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
getInternalValue(): InternalValue {
|
|
467
|
+
return structuredClone(this.internalValue)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async getSchema(): Promise<Schema> {
|
|
471
|
+
await this.waitForReady()
|
|
472
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
473
|
+
const schema: Schema = {
|
|
474
|
+
inputorTypeName: this.inputorTypeName,
|
|
475
|
+
id: this.id,
|
|
476
|
+
name: this.name,
|
|
477
|
+
placeholder: this.placeholder,
|
|
478
|
+
value: this.value,
|
|
479
|
+
isVoid: this.isVoid,
|
|
480
|
+
isFocused: this.isFocused,
|
|
481
|
+
isTouched: this.isTouched,
|
|
482
|
+
isDirty: this.isDirty,
|
|
483
|
+
isDisabled: this.isDisabled,
|
|
484
|
+
isDebugMode: this.isDebugMode,
|
|
485
|
+
} as unknown as Schema
|
|
486
|
+
return structuredClone(schema)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async getContext(): Promise<Context> {
|
|
490
|
+
const schema = await this.getSchema()
|
|
491
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
492
|
+
const context: Context = {
|
|
493
|
+
...schema,
|
|
494
|
+
internalValue: this.getInternalValue(),
|
|
495
|
+
isReady: this.isReady(),
|
|
496
|
+
} as unknown as Context
|
|
497
|
+
return structuredClone(context)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
protected async isVoidExternalValue(value: ExternalValue): Promise<boolean> {
|
|
501
|
+
const isSame = JSON.stringify(value) === JSON.stringify(this.voidExternalValue)
|
|
502
|
+
return await Promise.resolve(isSame)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
protected async isVoidInternalValue(value: InternalValue): Promise<boolean> {
|
|
506
|
+
const isSame = JSON.stringify(value) === JSON.stringify(this.voidInternalValue)
|
|
507
|
+
return await Promise.resolve(isSame)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
protected async isSameExternalValue(value1: ExternalValue, value2: ExternalValue): Promise<boolean> {
|
|
511
|
+
const isSame = JSON.stringify(value1) === JSON.stringify(value2)
|
|
512
|
+
return await Promise.resolve(isSame)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
protected async isSameInternalValue(value1: InternalValue, value2: InternalValue): Promise<boolean> {
|
|
516
|
+
const isSame = JSON.stringify(value1) === JSON.stringify(value2)
|
|
517
|
+
return await Promise.resolve(isSame)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
protected async isSameSchema(schema1: Schema, schema2: Schema): Promise<boolean> {
|
|
521
|
+
const isSame = JSON.stringify(schema1) === JSON.stringify(schema2)
|
|
522
|
+
return await Promise.resolve(isSame)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* 给定任意可能是 Schema 的 Record,判断其是否与当前 Inputor 的 Schema 相同。
|
|
527
|
+
*/
|
|
528
|
+
async isEqualSchema(schema: unknown): Promise<boolean> {
|
|
529
|
+
if (typeof schema !== "object" || schema === null) {
|
|
530
|
+
return false
|
|
531
|
+
}
|
|
532
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
533
|
+
const externalSchema = schema as Schema
|
|
534
|
+
const internalSchema = await this.getSchema()
|
|
535
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
536
|
+
const partialExternalSchema = {} as Schema
|
|
537
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
538
|
+
const partialInternalSchema = {} as Schema
|
|
539
|
+
Object.keys(externalSchema).forEach((key) => {
|
|
540
|
+
if (key in internalSchema) {
|
|
541
|
+
Object.assign(partialInternalSchema, {
|
|
542
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
543
|
+
[key]: (internalSchema as Record<string, unknown>)[key],
|
|
544
|
+
})
|
|
545
|
+
Object.assign(partialExternalSchema, {
|
|
546
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
547
|
+
[key]: (externalSchema as Record<string, unknown>)[key],
|
|
548
|
+
})
|
|
549
|
+
}
|
|
550
|
+
})
|
|
551
|
+
const isSame = this.isSameSchema(partialExternalSchema, partialInternalSchema)
|
|
552
|
+
return await Promise.resolve(isSame)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
protected async constrainInternalValue(internalValue: InternalValue): Promise<InternalValue> {
|
|
556
|
+
return await Promise.resolve(internalValue)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
protected async constrainExternalValue(externalValue: ExternalValue): Promise<ExternalValue> {
|
|
560
|
+
return await Promise.resolve(externalValue)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
protected async internalValueToExternalValue(internalValue: InternalValue): Promise<ExternalValue> {
|
|
564
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
565
|
+
return await Promise.resolve(internalValue as unknown as ExternalValue)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
protected async externalValueToInternalValue(externalValue: ExternalValue): Promise<InternalValue> {
|
|
569
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
570
|
+
return await Promise.resolve(externalValue as unknown as InternalValue)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
protected async resetInputStates(): Promise<void> {
|
|
574
|
+
clearTimeout(this.inputState.timer)
|
|
575
|
+
this.inputState = {
|
|
576
|
+
isInputing: false,
|
|
577
|
+
previousInputValue: this.value,
|
|
578
|
+
currentInputValue: this.value,
|
|
579
|
+
timer: undefined,
|
|
580
|
+
}
|
|
581
|
+
return await Promise.resolve()
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async applyInputValue(): Promise<void> {
|
|
585
|
+
await this.updateValue(this.inputState.currentInputValue)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async updateInputValue(inputValue: ExternalValue): Promise<void> {
|
|
589
|
+
const previousInputStates = structuredClone(this.inputState)
|
|
590
|
+
await this.resetInputStates()
|
|
591
|
+
this.inputState = {
|
|
592
|
+
isInputing: true,
|
|
593
|
+
previousInputValue: previousInputStates.currentInputValue,
|
|
594
|
+
currentInputValue: inputValue,
|
|
595
|
+
timer: setTimeout(() => {
|
|
596
|
+
this.emitInputChange({
|
|
597
|
+
newInput: this.inputState.currentInputValue,
|
|
598
|
+
oldInput: this.inputState.previousInputValue,
|
|
599
|
+
oldValue: this.value,
|
|
600
|
+
})
|
|
601
|
+
if (this.enableAutoChangeValueAfterInput === true) {
|
|
602
|
+
void this.applyInputValue()
|
|
603
|
+
}
|
|
604
|
+
}, this.inputDebounce),
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
protected async prepareUpdateContext(): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
609
|
+
const schema = await this.getSchema()
|
|
610
|
+
return {
|
|
611
|
+
...schema,
|
|
612
|
+
internalValue: this.internalValue,
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Will produce a new context rather than modifying the original one.
|
|
618
|
+
*/
|
|
619
|
+
protected async doUpdateToContext(
|
|
620
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
621
|
+
update: Update<ExternalValue, InternalValue>,
|
|
622
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
623
|
+
const newContext = update(structuredClone(context))
|
|
624
|
+
return await Promise.resolve(newContext)
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
protected async applyUpdateContext(context: UpdateContext<ExternalValue, InternalValue>): Promise<void> {
|
|
628
|
+
this.id = context.id
|
|
629
|
+
this.name = context.name
|
|
630
|
+
this.placeholder = context.placeholder
|
|
631
|
+
this.value = context.value
|
|
632
|
+
this.isVoid = context.isVoid
|
|
633
|
+
this.isFocused = context.isFocused
|
|
634
|
+
this.isTouched = context.isTouched
|
|
635
|
+
this.isDirty = context.isDirty
|
|
636
|
+
this.isDisabled = context.isDisabled
|
|
637
|
+
this.isDebugMode = context.isDebugMode
|
|
638
|
+
this.internalValue = context.internalValue
|
|
639
|
+
await Promise.resolve()
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
protected async doUpdate(updater: Update<ExternalValue, InternalValue>): Promise<void> {
|
|
643
|
+
this.log("[doUpdate] doUpdate start")
|
|
644
|
+
|
|
645
|
+
const prevContext = await this.prepareUpdateContext()
|
|
646
|
+
const nextContext = await this.doUpdateToContext(prevContext, updater)
|
|
647
|
+
|
|
648
|
+
await this.applyUpdateContext(nextContext)
|
|
649
|
+
|
|
650
|
+
// handle emits & states updates
|
|
651
|
+
const isSameValue = await this.isSameExternalValue(nextContext.value, prevContext.value)
|
|
652
|
+
if (isSameValue === false) {
|
|
653
|
+
this.log("[doUpdate] value changed", "prevValue", prevContext.value, "nextValue", nextContext.value)
|
|
654
|
+
await this.resetInputStates()
|
|
655
|
+
this.emitValueChange({
|
|
656
|
+
newValue: nextContext.value,
|
|
657
|
+
oldValue: prevContext.value,
|
|
658
|
+
})
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const isSameSchema = await this.isSameSchema(
|
|
662
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
663
|
+
nextContext as unknown as Schema,
|
|
664
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
665
|
+
prevContext as unknown as Schema,
|
|
666
|
+
)
|
|
667
|
+
if (isSameSchema === false) {
|
|
668
|
+
this.log("[doUpdate] schema changed", "prevSchema", prevContext, "nextSchema", nextContext)
|
|
669
|
+
this.emitSchemaChange({
|
|
670
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
671
|
+
newSchema: nextContext as unknown as Schema,
|
|
672
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
673
|
+
oldSchema: prevContext as unknown as Schema,
|
|
674
|
+
})
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
this.log("[doUpdate] doUpdate end")
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
protected async updatePlaceholderToContext(
|
|
681
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
682
|
+
placeholder: string,
|
|
683
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
684
|
+
context.placeholder = placeholder
|
|
685
|
+
return await Promise.resolve(context)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async updatePlaceholder(placeholder: string): Promise<void> {
|
|
689
|
+
await this.doUpdate(async (context) => {
|
|
690
|
+
return await this.updatePlaceholderToContext(context, placeholder)
|
|
691
|
+
})
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
protected async updateValueToContext(
|
|
695
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
696
|
+
value: ExternalValue,
|
|
697
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
698
|
+
const clonedContext = structuredClone(context)
|
|
699
|
+
const oldValue = clonedContext.value
|
|
700
|
+
const newValue = await this.constrainExternalValue(value)
|
|
701
|
+
const isSameValue = await this.isSameExternalValue(newValue, oldValue)
|
|
702
|
+
if (isSameValue === true) {
|
|
703
|
+
return clonedContext
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const isVoidValue = await this.isVoidExternalValue(newValue)
|
|
707
|
+
clonedContext.isVoid = isVoidValue
|
|
708
|
+
clonedContext.isDirty = true
|
|
709
|
+
clonedContext.value = newValue
|
|
710
|
+
|
|
711
|
+
const newInternalValue = await this.externalValueToInternalValue(newValue)
|
|
712
|
+
const updatedContext = await this.updateInternalValueToContext(clonedContext, newInternalValue)
|
|
713
|
+
return updatedContext
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
async updateValue(value: ExternalValue): Promise<void> {
|
|
717
|
+
await this.doUpdate(async (context) => {
|
|
718
|
+
return await this.updateValueToContext(context, value)
|
|
719
|
+
})
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
protected async updateIsVoidToContext(
|
|
723
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
724
|
+
isVoid: boolean,
|
|
725
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
726
|
+
const currentIsVoid = context.isVoid
|
|
727
|
+
const isSameIsVoid = isVoid === currentIsVoid
|
|
728
|
+
if (isSameIsVoid === true) {
|
|
729
|
+
return context
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (isVoid === true) {
|
|
733
|
+
const updatedContext = await this.updateValueToContext(context, this.voidExternalValue)
|
|
734
|
+
return updatedContext
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return context
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async updateIsVoid(isVoid: true): Promise<void> {
|
|
741
|
+
await this.doUpdate(async (context) => {
|
|
742
|
+
return await this.updateIsVoidToContext(context, isVoid)
|
|
743
|
+
})
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
protected async updateIsFocusedToContext(
|
|
747
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
748
|
+
isFocused: boolean,
|
|
749
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
750
|
+
const currentIsFocused = context.isFocused
|
|
751
|
+
const isSameIsFocused = isFocused === currentIsFocused
|
|
752
|
+
if (isSameIsFocused === true) {
|
|
753
|
+
return context
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
context.isFocused = isFocused
|
|
757
|
+
const updatedContext = await this.updateIsTouchedToContext(context, true)
|
|
758
|
+
return updatedContext
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
async updateIsFocused(isFocused: boolean): Promise<void> {
|
|
762
|
+
await this.doUpdate(async (context) => {
|
|
763
|
+
return await this.updateIsFocusedToContext(context, isFocused)
|
|
764
|
+
})
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
protected async updateIsTouchedToContext(
|
|
768
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
769
|
+
isTouched: boolean,
|
|
770
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
771
|
+
context.isTouched = isTouched
|
|
772
|
+
return await Promise.resolve(context)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async updateIsTouched(isTouched: boolean): Promise<void> {
|
|
776
|
+
await this.doUpdate(async (context) => {
|
|
777
|
+
return await this.updateIsTouchedToContext(context, isTouched)
|
|
778
|
+
})
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
protected async updateIsDirtyToContext(
|
|
782
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
783
|
+
isDirty: boolean,
|
|
784
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
785
|
+
context.isDirty = isDirty
|
|
786
|
+
return await Promise.resolve(context)
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async updateIsDirty(isDirty: boolean): Promise<void> {
|
|
790
|
+
await this.doUpdate(async (context) => {
|
|
791
|
+
return await this.updateIsDirtyToContext(context, isDirty)
|
|
792
|
+
})
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
protected async updateIsDisabledToContext(
|
|
796
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
797
|
+
isDisabled: boolean,
|
|
798
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
799
|
+
context.isDisabled = isDisabled
|
|
800
|
+
return await Promise.resolve(context)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async updateIsDisabled(isDisabled: boolean): Promise<void> {
|
|
804
|
+
await this.doUpdate(async (context) => {
|
|
805
|
+
return await this.updateIsDisabledToContext(context, isDisabled)
|
|
806
|
+
})
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
protected async updateIsDebugModeToContext(
|
|
810
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
811
|
+
isDebugMode: boolean,
|
|
812
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
813
|
+
context.isDebugMode = isDebugMode
|
|
814
|
+
return await Promise.resolve(context)
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async updateIsDebugMode(isDebugMode: boolean): Promise<void> {
|
|
818
|
+
await this.doUpdate(async (context) => {
|
|
819
|
+
return await this.updateIsDebugModeToContext(context, isDebugMode)
|
|
820
|
+
})
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
protected async updateInternalValueToContext(
|
|
824
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
825
|
+
internalValue: InternalValue,
|
|
826
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
827
|
+
const clonedContext = structuredClone(context)
|
|
828
|
+
const oldInternalValue = clonedContext.internalValue
|
|
829
|
+
const newInternalValue = await this.constrainInternalValue(internalValue)
|
|
830
|
+
const isSameInternalValue = await this.isSameInternalValue(newInternalValue, oldInternalValue)
|
|
831
|
+
if (isSameInternalValue === true) {
|
|
832
|
+
return clonedContext
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
clonedContext.internalValue = newInternalValue
|
|
836
|
+
const newValue = await this.internalValueToExternalValue(newInternalValue)
|
|
837
|
+
const updatedContext = await this.updateValueToContext(clonedContext, newValue)
|
|
838
|
+
return updatedContext
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
protected async updateInternalValue(internalValue: InternalValue): Promise<void> {
|
|
842
|
+
await this.doUpdate(async (context) => {
|
|
843
|
+
return await this.updateInternalValueToContext(context, internalValue)
|
|
844
|
+
})
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
protected async updateSchemaToContext(
|
|
848
|
+
context: UpdateContext<ExternalValue, InternalValue>,
|
|
849
|
+
schema: Schema,
|
|
850
|
+
): Promise<UpdateContext<ExternalValue, InternalValue>> {
|
|
851
|
+
let updatedContext = structuredClone(context)
|
|
852
|
+
updatedContext = await this.updatePlaceholderToContext(updatedContext, schema.placeholder)
|
|
853
|
+
updatedContext = await this.updateValueToContext(updatedContext, schema.value)
|
|
854
|
+
updatedContext = await this.updateIsVoidToContext(updatedContext, schema.isVoid)
|
|
855
|
+
updatedContext = await this.updateIsFocusedToContext(updatedContext, schema.isFocused)
|
|
856
|
+
updatedContext = await this.updateIsTouchedToContext(updatedContext, schema.isTouched)
|
|
857
|
+
updatedContext = await this.updateIsDirtyToContext(updatedContext, schema.isDirty)
|
|
858
|
+
updatedContext = await this.updateIsDisabledToContext(updatedContext, schema.isDisabled)
|
|
859
|
+
updatedContext = await this.updateIsDebugModeToContext(updatedContext, schema.isDebugMode)
|
|
860
|
+
return await Promise.resolve(updatedContext)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
async updateSchema(schema: Schema): Promise<void> {
|
|
864
|
+
await this.doUpdate(async (context) => {
|
|
865
|
+
return await this.updateSchemaToContext(context, schema)
|
|
866
|
+
})
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
destroy(): void {
|
|
870
|
+
this.clearInputChangeSubscribers()
|
|
871
|
+
this.clearValueChangeSubscribers()
|
|
872
|
+
this.clearSchemaChangeSubscribers()
|
|
873
|
+
}
|
|
874
|
+
}
|