@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +25 -1
  3. package/dist/index.js +4 -2
  4. package/dist/index.js.map +27 -6
  5. package/package.json +9 -9
  6. package/scripts/build.ts +4 -4
  7. package/src/basic/README.md +143 -0
  8. package/src/basic/array.ts +872 -0
  9. package/src/basic/bigint.ts +114 -0
  10. package/src/basic/boolean.ts +180 -0
  11. package/src/basic/error.ts +51 -0
  12. package/src/basic/function.ts +453 -0
  13. package/src/basic/helper.ts +276 -0
  14. package/src/basic/index.ts +15 -0
  15. package/src/basic/is.ts +320 -0
  16. package/src/basic/number.ts +178 -0
  17. package/src/basic/object.ts +58 -0
  18. package/src/basic/promise.ts +464 -0
  19. package/src/basic/regexp.ts +7 -0
  20. package/src/basic/stream.ts +140 -0
  21. package/src/basic/string.ts +308 -0
  22. package/src/basic/symbol.ts +164 -0
  23. package/src/basic/temporal.ts +224 -0
  24. package/src/index.ts +3 -1
  25. package/src/reactor/README.md +18 -0
  26. package/src/reactor/index.ts +2 -0
  27. package/src/reactor/reactor-core/primitive.ts +1046 -0
  28. package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
  29. package/src/reactor/reactor-operators/branch.ts +66 -0
  30. package/src/reactor/reactor-operators/convert.ts +70 -0
  31. package/src/reactor/reactor-operators/create.ts +66 -0
  32. package/src/reactor/reactor-operators/filter.ts +988 -0
  33. package/src/reactor/reactor-operators/index.ts +7 -0
  34. package/src/reactor/reactor-operators/join.ts +174 -0
  35. package/src/reactor/reactor-operators/map.ts +599 -0
  36. package/src/reactor/reactor-operators/utility.ts +102 -0
  37. package/src/type/README.md +330 -0
  38. package/src/type/array.ts +5 -0
  39. package/src/type/boolean.ts +471 -0
  40. package/src/type/class.ts +419 -0
  41. package/src/type/function.ts +1519 -0
  42. package/src/type/helper.ts +135 -0
  43. package/src/type/index.ts +14 -0
  44. package/src/type/intersection.ts +93 -0
  45. package/src/type/is.ts +247 -0
  46. package/src/type/iteration.ts +233 -0
  47. package/src/type/number.ts +732 -0
  48. package/src/type/object.ts +788 -0
  49. package/src/type/path.ts +73 -0
  50. package/src/type/string.ts +1004 -0
  51. package/src/type/tuple.ts +2424 -0
  52. package/src/type/union.ts +108 -0
  53. package/tests/unit/basic/array.spec.ts +290 -0
  54. package/tests/unit/basic/bigint.spec.ts +50 -0
  55. package/tests/unit/basic/boolean.spec.ts +74 -0
  56. package/tests/unit/basic/error.spec.ts +32 -0
  57. package/tests/unit/basic/function.spec.ts +175 -0
  58. package/tests/unit/basic/helper.spec.ts +118 -0
  59. package/tests/unit/basic/number.spec.ts +74 -0
  60. package/tests/unit/basic/object.spec.ts +15 -0
  61. package/tests/unit/basic/promise.spec.ts +232 -0
  62. package/tests/unit/basic/regexp.spec.ts +11 -0
  63. package/tests/unit/basic/stream.spec.ts +120 -0
  64. package/tests/unit/basic/string.spec.ts +74 -0
  65. package/tests/unit/basic/symbol.spec.ts +72 -0
  66. package/tests/unit/basic/temporal.spec.ts +78 -0
  67. package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
  68. package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
  69. package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
  70. package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
  71. package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
  72. package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
  73. package/tests/unit/reactor/preact-signal.spec.ts +73 -0
  74. package/tests/unit/reactor/reactor-core.spec.ts +219 -0
  75. package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
  76. package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
  77. package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
  78. package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
  79. package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
  80. package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
  81. package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
  82. package/dist/index.d.ts +0 -2
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/signal/index.d.ts +0 -3
  85. package/dist/signal/index.d.ts.map +0 -1
  86. package/dist/signal/signal-core/flags.d.ts +0 -99
  87. package/dist/signal/signal-core/flags.d.ts.map +0 -1
  88. package/dist/signal/signal-core/index.d.ts +0 -4
  89. package/dist/signal/signal-core/index.d.ts.map +0 -1
  90. package/dist/signal/signal-core/primitive.d.ts +0 -67
  91. package/dist/signal/signal-core/primitive.d.ts.map +0 -1
  92. package/dist/signal/signal-core/reactive-system.d.ts +0 -161
  93. package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
  94. package/dist/signal/signal-operators/index.d.ts +0 -4
  95. package/dist/signal/signal-operators/index.d.ts.map +0 -1
  96. package/src/signal/index.ts +0 -2
  97. package/src/signal/signal-core/README.md +0 -4
  98. package/src/signal/signal-core/primitive.ts +0 -275
  99. package/src/signal/signal-operators/index.ts +0 -19
  100. package/tests/unit/signal/effect.spec.ts +0 -108
  101. /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
  102. /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
@@ -1 +1,3 @@
1
- export * from "./signal/index.ts"
1
+ export * as Basic from "./basic/index.ts"
2
+ export * as Type from "./type/index.ts"
3
+ export * as Reactor from "./reactor/index.ts"
@@ -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)。
@@ -0,0 +1,2 @@
1
+ export * from "./reactor-core/index.ts"
2
+ export * from "./reactor-operators/index.ts"