@planet-matrix/mobius-model 0.1.4 → 0.4.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 +53 -0
- package/README.md +25 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +27 -6
- package/package.json +9 -9
- package/scripts/build.ts +4 -4
- package/src/basic/README.md +143 -0
- package/src/basic/array.ts +872 -0
- package/src/basic/bigint.ts +114 -0
- package/src/basic/boolean.ts +180 -0
- package/src/basic/error.ts +51 -0
- package/src/basic/function.ts +453 -0
- package/src/basic/helper.ts +276 -0
- package/src/basic/index.ts +15 -0
- package/src/basic/is.ts +320 -0
- package/src/basic/number.ts +178 -0
- package/src/basic/object.ts +58 -0
- package/src/basic/promise.ts +464 -0
- package/src/basic/regexp.ts +7 -0
- package/src/basic/stream.ts +140 -0
- package/src/basic/string.ts +308 -0
- package/src/basic/symbol.ts +164 -0
- package/src/basic/temporal.ts +224 -0
- package/src/index.ts +3 -1
- package/src/reactor/README.md +18 -0
- package/src/reactor/index.ts +2 -0
- package/src/reactor/reactor-core/primitive.ts +1046 -0
- package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
- package/src/reactor/reactor-operators/branch.ts +66 -0
- package/src/reactor/reactor-operators/convert.ts +70 -0
- package/src/reactor/reactor-operators/create.ts +66 -0
- package/src/reactor/reactor-operators/filter.ts +988 -0
- package/src/reactor/reactor-operators/index.ts +7 -0
- package/src/reactor/reactor-operators/join.ts +174 -0
- package/src/reactor/reactor-operators/map.ts +599 -0
- package/src/reactor/reactor-operators/utility.ts +102 -0
- package/src/type/README.md +330 -0
- package/src/type/array.ts +5 -0
- package/src/type/boolean.ts +471 -0
- package/src/type/class.ts +419 -0
- package/src/type/function.ts +1519 -0
- package/src/type/helper.ts +135 -0
- package/src/type/index.ts +14 -0
- package/src/type/intersection.ts +93 -0
- package/src/type/is.ts +247 -0
- package/src/type/iteration.ts +233 -0
- package/src/type/number.ts +732 -0
- package/src/type/object.ts +788 -0
- package/src/type/path.ts +73 -0
- package/src/type/string.ts +1004 -0
- package/src/type/tuple.ts +2424 -0
- package/src/type/union.ts +108 -0
- package/tests/unit/basic/array.spec.ts +290 -0
- package/tests/unit/basic/bigint.spec.ts +50 -0
- package/tests/unit/basic/boolean.spec.ts +74 -0
- package/tests/unit/basic/error.spec.ts +32 -0
- package/tests/unit/basic/function.spec.ts +175 -0
- package/tests/unit/basic/helper.spec.ts +118 -0
- package/tests/unit/basic/number.spec.ts +74 -0
- package/tests/unit/basic/object.spec.ts +15 -0
- package/tests/unit/basic/promise.spec.ts +232 -0
- package/tests/unit/basic/regexp.spec.ts +11 -0
- package/tests/unit/basic/stream.spec.ts +120 -0
- package/tests/unit/basic/string.spec.ts +74 -0
- package/tests/unit/basic/symbol.spec.ts +72 -0
- package/tests/unit/basic/temporal.spec.ts +78 -0
- package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
- package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
- package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
- package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
- package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
- package/tests/unit/reactor/preact-signal.spec.ts +73 -0
- package/tests/unit/reactor/reactor-core.spec.ts +219 -0
- package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
- package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
- package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
- package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
- package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
- package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
- package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/signal/index.d.ts +0 -3
- package/dist/signal/index.d.ts.map +0 -1
- package/dist/signal/signal-core/flags.d.ts +0 -99
- package/dist/signal/signal-core/flags.d.ts.map +0 -1
- package/dist/signal/signal-core/index.d.ts +0 -4
- package/dist/signal/signal-core/index.d.ts.map +0 -1
- package/dist/signal/signal-core/primitive.d.ts +0 -67
- package/dist/signal/signal-core/primitive.d.ts.map +0 -1
- package/dist/signal/signal-core/reactive-system.d.ts +0 -161
- package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
- package/dist/signal/signal-operators/index.d.ts +0 -4
- package/dist/signal/signal-operators/index.d.ts.map +0 -1
- package/src/signal/index.ts +0 -2
- package/src/signal/signal-core/README.md +0 -4
- package/src/signal/signal-core/primitive.ts +0 -275
- package/src/signal/signal-operators/index.ts +0 -19
- package/tests/unit/signal/effect.spec.ts +0 -108
- /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
- /package/src/{signal/signal-core → reactor/reactor-core}/index.ts +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { isDate } from "./is.ts"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check whether a date is outdated (in the past).
|
|
5
|
+
*/
|
|
6
|
+
export const temporalIsOutdated = (target: unknown): boolean => {
|
|
7
|
+
return isDate(target) && (new Date(target).getTime() < Date.now())
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format a timestamp to a YYYY-MM-DD string.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```
|
|
15
|
+
* // Expect: "1970-01-01"
|
|
16
|
+
* const example1 = temporalFormatToYYYYMMDD(0)
|
|
17
|
+
* // Expect: "2000/01/02"
|
|
18
|
+
* const example2 = temporalFormatToYYYYMMDD(946771200000, "/")
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const temporalFormatToYYYYMMDD = (timestamp: number, separator = "-"): string => {
|
|
22
|
+
const date = new Date(timestamp)
|
|
23
|
+
const year = date.getFullYear()
|
|
24
|
+
const month = String(date.getMonth() + 1).padStart(2, "0")
|
|
25
|
+
const day = String(date.getDate()).padStart(2, "0")
|
|
26
|
+
return `${year}${separator}${month}${separator}${day}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Format a timestamp to a hh:mm:ss string.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```
|
|
34
|
+
* // Expect: "00:00:00"
|
|
35
|
+
* const example1 = temporalFormatTohhmmss(0)
|
|
36
|
+
* // Expect: "12:34:56"
|
|
37
|
+
* const example2 = temporalFormatTohhmmss(45296000)
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export const temporalFormatTohhmmss = (timestamp: number, separator = ":"): string => {
|
|
41
|
+
const date = new Date(timestamp)
|
|
42
|
+
const hours = String(date.getHours()).padStart(2, "0")
|
|
43
|
+
const minutes = String(date.getMinutes()).padStart(2, "0")
|
|
44
|
+
const seconds = String(date.getSeconds()).padStart(2, "0")
|
|
45
|
+
return `${hours}${separator}${minutes}${separator}${seconds}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format a timestamp to a YYYY-MM-DD hh:mm:ss string.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```
|
|
53
|
+
* // Expect: "1970-01-01 00:00:00"
|
|
54
|
+
* const example1 = temporalFormatToYYYYMMDDhhmmss(0)
|
|
55
|
+
* // Expect: "2000-01-02 00:00:00"
|
|
56
|
+
* const example2 = temporalFormatToYYYYMMDDhhmmss(946771200000)
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export const temporalFormatToYYYYMMDDhhmmss = (timestamp: number): string => {
|
|
60
|
+
return `${temporalFormatToYYYYMMDD(timestamp)} ${temporalFormatTohhmmss(timestamp)}`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Format a timestamp to a relative time string (e.g., "刚刚", "5 分钟前").
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```
|
|
68
|
+
* // Expect: "刚刚"
|
|
69
|
+
* const originalNow = Date.now
|
|
70
|
+
* Date.now = () => 3_600_000
|
|
71
|
+
* const example1 = temporalFormatToRelativeTime(3_600_000)
|
|
72
|
+
* // Expect: "1 小时前"
|
|
73
|
+
* const example2 = temporalFormatToRelativeTime(0)
|
|
74
|
+
* Date.now = originalNow
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export const temporalFormatToRelativeTime = (timestamp: number): string => {
|
|
78
|
+
const now = Date.now()
|
|
79
|
+
const diffInSeconds = Math.floor((now - timestamp) / 1_000)
|
|
80
|
+
|
|
81
|
+
if (diffInSeconds < 60) {
|
|
82
|
+
return "刚刚"
|
|
83
|
+
}
|
|
84
|
+
else if (diffInSeconds < 3_600) {
|
|
85
|
+
const minutes = Math.floor(diffInSeconds / 60)
|
|
86
|
+
return `${minutes} 分钟前`
|
|
87
|
+
}
|
|
88
|
+
else if (diffInSeconds < 86_400) {
|
|
89
|
+
const hours = Math.floor(diffInSeconds / 3_600)
|
|
90
|
+
return `${hours} 小时前`
|
|
91
|
+
}
|
|
92
|
+
else if (diffInSeconds < 172_800) {
|
|
93
|
+
return "昨天"
|
|
94
|
+
}
|
|
95
|
+
else if (diffInSeconds < 604_800) {
|
|
96
|
+
return "一周内"
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// 格式化为 YYYY-MM-DD hh:ss
|
|
100
|
+
return temporalFormatToYYYYMMDDhhmmss(timestamp)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface TemporalFormatToWechatRelativeTimeOptions {
|
|
105
|
+
timestamp: number
|
|
106
|
+
alwaysShowTime?: boolean | undefined
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Format a timestamp to a WeChat-style relative time string.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```
|
|
113
|
+
* // Expect: "70/01/01"
|
|
114
|
+
* const example1 = temporalFormatToWechatRelativeTime({ timestamp: 0 })
|
|
115
|
+
* // Expect: "70/01/01 00:00"
|
|
116
|
+
* const example2 = temporalFormatToWechatRelativeTime({ timestamp: 0, alwaysShowTime: true })
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export const temporalFormatToWechatRelativeTime = (
|
|
120
|
+
options: TemporalFormatToWechatRelativeTimeOptions
|
|
121
|
+
): string => {
|
|
122
|
+
const {
|
|
123
|
+
timestamp,
|
|
124
|
+
alwaysShowTime = false,
|
|
125
|
+
} = options
|
|
126
|
+
|
|
127
|
+
const now = new Date()
|
|
128
|
+
const targetDate = new Date(timestamp)
|
|
129
|
+
|
|
130
|
+
const padZero = (num: number): string => num.toString().padStart(2, "0")
|
|
131
|
+
|
|
132
|
+
const isSameDay = (date1: Date, date2: Date): boolean => {
|
|
133
|
+
return date1.getFullYear() === date2.getFullYear()
|
|
134
|
+
&& date1.getMonth() === date2.getMonth()
|
|
135
|
+
&& date1.getDate() === date2.getDate()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const isYesterday = (date1: Date, date2: Date): boolean => {
|
|
139
|
+
const yesterday = new Date(date1)
|
|
140
|
+
yesterday.setDate(date1.getDate() - 1)
|
|
141
|
+
return isSameDay(yesterday, date2)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const getWeekDayName = (date: Date): string => {
|
|
145
|
+
const weekdays = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
|
|
146
|
+
return weekdays[date.getDay()]!
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const isSameWeek = (date1: Date, date2: Date): boolean => {
|
|
150
|
+
const startOfWeek = (date: Date): Date => {
|
|
151
|
+
const result = new Date(date)
|
|
152
|
+
const day = result.getDay()
|
|
153
|
+
const diff = result.getDate() - day + (day === 0 ? -6 : 1)
|
|
154
|
+
result.setDate(diff)
|
|
155
|
+
return new Date(result.getFullYear(), result.getMonth(), result.getDate())
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return startOfWeek(date1).getTime() === startOfWeek(date2).getTime()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const date = `${padZero(targetDate.getFullYear() % 100)}/${padZero(targetDate.getMonth() + 1)}/${padZero(targetDate.getDate())}`
|
|
162
|
+
const time = `${padZero(targetDate.getHours())}:${padZero(targetDate.getMinutes())}`
|
|
163
|
+
|
|
164
|
+
if (isSameDay(now, targetDate)) {
|
|
165
|
+
return time
|
|
166
|
+
}
|
|
167
|
+
else if (isYesterday(now, targetDate)) {
|
|
168
|
+
return alwaysShowTime ? `昨天 ${time}` : "昨天"
|
|
169
|
+
}
|
|
170
|
+
else if (isSameWeek(now, targetDate)) {
|
|
171
|
+
return alwaysShowTime ? `${getWeekDayName(targetDate)} ${time}` : getWeekDayName(targetDate)
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
return alwaysShowTime ? `${date} ${time}` : date
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
type InternalUnitsDict = Record<string, string>
|
|
179
|
+
const INTERNAL_UNITS_DICT: InternalUnitsDict = {
|
|
180
|
+
year: "年",
|
|
181
|
+
month: "月",
|
|
182
|
+
week: "周",
|
|
183
|
+
day: "天",
|
|
184
|
+
hour: "小时",
|
|
185
|
+
minute: "分钟",
|
|
186
|
+
second: "秒",
|
|
187
|
+
millisecond: "毫秒",
|
|
188
|
+
}
|
|
189
|
+
type InternalUnitsMilliDict = Record<string, number>
|
|
190
|
+
const INTERNAL_UNITS_MILLI_DICT: InternalUnitsMilliDict = {
|
|
191
|
+
year: 31_557_600_000,
|
|
192
|
+
month: 2_629_800_000,
|
|
193
|
+
week: 604_800_000,
|
|
194
|
+
day: 86_400_000,
|
|
195
|
+
hour: 3_600_000,
|
|
196
|
+
minute: 60_000,
|
|
197
|
+
second: 1_000,
|
|
198
|
+
millisecond: 1,
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Convert a timestamp to a human-readable relative time string in Chinese.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```
|
|
205
|
+
* // Expect: "刚刚"
|
|
206
|
+
* const originalNow = Date.now
|
|
207
|
+
* Date.now = () => 3_600_000
|
|
208
|
+
* const example1 = temporalHumanize(3_600_000)
|
|
209
|
+
* // Expect: "1 小时前"
|
|
210
|
+
* const example2 = temporalHumanize(0)
|
|
211
|
+
* Date.now = originalNow
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export const temporalHumanize = (timestamp: number): string => {
|
|
215
|
+
let res = ""
|
|
216
|
+
const milliseconds = Date.now() - timestamp
|
|
217
|
+
for (const key in INTERNAL_UNITS_MILLI_DICT) {
|
|
218
|
+
if (milliseconds >= INTERNAL_UNITS_MILLI_DICT[key]!) {
|
|
219
|
+
res = `${Math.floor(milliseconds / INTERNAL_UNITS_MILLI_DICT[key]!)} ${INTERNAL_UNITS_DICT[key]}前`
|
|
220
|
+
break
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return res !== "" ? res : "刚刚"
|
|
224
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Reactor
|
|
2
|
+
|
|
3
|
+
Reactor 的实现与以下项目有关:
|
|
4
|
+
|
|
5
|
+
- [alien-signals](https://github.com/stackblitz/alien-signals): Reactor 源于该项目,是在对该项目进行重构的基础上添加了其它个性化实现得到的,两者核心区别包括:1、alien-signals 的 API 更加“函数式”,而 Reactor 的 API 更加“对象式”;2、alien-signals 的源码更注重性能,因而比较晦涩,而 Reactor 的实现更注重可维护性与可扩展性因而比较易懂;3、alien-signals 的功能更单一更克制,几乎只提供了核心的响应式算法,而 Reactor 则提供了更多的相关功能与工具函数。
|
|
6
|
+
- [Preact signals](https://github.com/preactjs/signals):受该项目启发,Reactor 补充了一些额外能力,包括但不限于 `options parameter`、`signal.getWithoutTrack()`(同 `signal.peek()`)、`batch()`、`withoutTracking()`(同 `untracked()`)。
|
|
7
|
+
- [Angular signals](https://angular.dev/guide/signals): 受该项目启发,Reactor 补充了一些额外能力,包括但不限于 `assertWithoutTracking()`(同 `assertNotInReactiveContext()`)、`options.isEqual()`(同 `options.equal()`)、`signal.setWithoutCalculate()`(同 `signal.update()`)、`derived()`(原语层面的 `linkedSignal()`)。
|
|
8
|
+
- [RxJS](https://github.com/ReactiveX/rxjs):Reactor 的 operators 参考了 RxJS 的 operators 列表与功能,但两者有着本质的区别,RxJS 的 operators 负责管理值的流动与转换,而 Reactor 的 operators 只是围绕 Reactor 原语提供的一些便捷工具函数,Reactor 的核心价值在于其响应式原语,而非 operators,operators 对于 RxJS 来说不可或缺,但 operators 对于 Reactor 来说并非必需。
|
|
9
|
+
|
|
10
|
+
Reactor operators 分为以下几类:
|
|
11
|
+
|
|
12
|
+
1. utility:实用工具函数。
|
|
13
|
+
2. convert:类型转换相关。
|
|
14
|
+
3. create:创建相关。
|
|
15
|
+
4. join:合并相关(多个 Reactor 组合为一个 Reactor)。
|
|
16
|
+
5. branch:分支相关(一个 Reactor 变为多个 Reactor)。
|
|
17
|
+
6. map:映射相关(一个 Reactor 变换为另一个 Reactor)。
|
|
18
|
+
7. filter:过滤相关(一个 Reactor 变换为另一个 Reactor,本质是特殊的 map)。
|