hyperscript-rxjs 1.3.14 → 1.3.15

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 (279) hide show
  1. package/package.json +13 -22
  2. package/src/array/advance.d.ts +9 -0
  3. package/src/array/advance.js +13 -0
  4. package/src/array/advance.test.js +12 -0
  5. package/src/array/arrayInsert.d.ts +8 -0
  6. package/src/array/arrayInsert.js +13 -0
  7. package/src/array/arrayInsert.test.js +13 -0
  8. package/src/array/arrayRemove.d.ts +7 -0
  9. package/src/array/arrayRemove.js +15 -0
  10. package/src/array/arrayRemove.test.js +13 -0
  11. package/src/array/findLastIndex.d.ts +14 -0
  12. package/src/array/findLastIndex.js +20 -0
  13. package/src/array/findLastIndex.test.js +41 -0
  14. package/src/array/index.d.ts +9 -0
  15. package/src/array/index.js +9 -0
  16. package/src/array/isRange.d.ts +7 -0
  17. package/src/array/isRange.js +15 -0
  18. package/src/array/isRange.test.js +6 -0
  19. package/src/array/rangeArray.d.ts +7 -0
  20. package/src/array/rangeArray.js +10 -0
  21. package/src/array/rangeArray.test.js +11 -0
  22. package/src/array/unwrapArgs.d.ts +10 -0
  23. package/src/array/unwrapArgs.js +15 -0
  24. package/src/array/unwrapArgs.test.js +33 -0
  25. package/src/array/zipArray.d.ts +11 -0
  26. package/src/array/zipArray.js +24 -0
  27. package/src/array/zipArray.test.js +16 -0
  28. package/src/comparers/Comparer.d.ts +101 -0
  29. package/src/comparers/Comparer.js +149 -0
  30. package/src/comparers/comparers.d.ts +21 -0
  31. package/src/comparers/comparers.js +10 -0
  32. package/src/comparers/differenceSet.d.ts +20 -0
  33. package/src/comparers/differenceSet.js +35 -0
  34. package/src/comparers/differenceSet.test.js +11 -0
  35. package/src/comparers/distinctArray.d.ts +13 -0
  36. package/src/comparers/distinctArray.js +30 -0
  37. package/src/comparers/distinctArray.test.js +10 -0
  38. package/src/comparers/findIndexInSet.d.ts +20 -0
  39. package/src/comparers/findIndexInSet.js +27 -0
  40. package/src/comparers/findIndexInSet.test.js +8 -0
  41. package/src/comparers/groupArrayBy.d.ts +19 -0
  42. package/src/comparers/groupArrayBy.js +29 -0
  43. package/src/comparers/groupArrayBy.test.js +38 -0
  44. package/src/comparers/groupSortedEntries.d.ts +17 -0
  45. package/src/comparers/groupSortedEntries.js +38 -0
  46. package/src/comparers/groupSortedEntries.test.js +46 -0
  47. package/src/comparers/index.d.ts +14 -0
  48. package/src/comparers/index.js +14 -0
  49. package/src/comparers/intersectSet.d.ts +19 -0
  50. package/src/comparers/intersectSet.js +35 -0
  51. package/src/comparers/intersectSet.test.js +14 -0
  52. package/src/comparers/isEqualset.d.ts +22 -0
  53. package/src/comparers/isEqualset.js +33 -0
  54. package/src/comparers/isEqualset.test.js +22 -0
  55. package/src/comparers/isSubset.d.ts +21 -0
  56. package/src/comparers/isSubset.js +33 -0
  57. package/src/comparers/isSubset.test.js +21 -0
  58. package/src/comparers/isSuperset.d.ts +21 -0
  59. package/src/comparers/isSuperset.js +13 -0
  60. package/src/comparers/isSuperset.test.js +21 -0
  61. package/src/comparers/sortedArrayToSet.d.ts +20 -0
  62. package/src/comparers/sortedArrayToSet.js +35 -0
  63. package/src/comparers/sortedArrayToSet.test.js +11 -0
  64. package/src/comparers/unionSet.d.ts +21 -0
  65. package/src/comparers/unionSet.js +34 -0
  66. package/src/comparers/unionSet.test.js +11 -0
  67. package/src/comparison/compareDate.d.ts +8 -0
  68. package/src/comparison/compareDate.js +11 -0
  69. package/src/comparison/compareEntries.d.ts +13 -0
  70. package/src/comparison/compareEntries.js +13 -0
  71. package/src/comparison/compareKey.d.ts +11 -0
  72. package/src/comparison/compareKey.js +25 -0
  73. package/src/comparison/compareKey.test.js +21 -0
  74. package/src/comparison/compareKeyPath.d.ts +15 -0
  75. package/src/comparison/compareKeyPath.js +33 -0
  76. package/src/comparison/compareKeyPath.test.js +28 -0
  77. package/src/comparison/compareNumber.d.ts +11 -0
  78. package/src/comparison/compareNumber.js +27 -0
  79. package/src/comparison/compareNumber.test.js +21 -0
  80. package/src/comparison/defaultCompare.d.ts +8 -0
  81. package/src/comparison/defaultCompare.js +12 -0
  82. package/src/comparison/defaultCompare.test.js +24 -0
  83. package/src/comparison/index.d.ts +7 -0
  84. package/src/comparison/index.js +7 -0
  85. package/src/comparison/infinity.test.js +122 -0
  86. package/src/comparison/typeof.test.js +64 -0
  87. package/src/comparison/types.d.ts +5 -0
  88. package/src/comparison/types.js +11 -0
  89. package/src/deep/Deep.d.ts +58 -0
  90. package/src/deep/Deep.js +267 -0
  91. package/src/deep/Deep.test.js +130 -0
  92. package/src/deep/deepCombineLatest.test.js +36 -0
  93. package/src/deep/deepMerge.test.js +34 -0
  94. package/src/deep/differenceDeep.test.js +31 -0
  95. package/src/deep/freshValueDeep.test.js +17 -0
  96. package/src/deep/index.d.ts +1 -0
  97. package/src/deep/index.js +2 -0
  98. package/src/deep/intersectDeep.test.js +25 -0
  99. package/src/deep/intersectEntries.d.ts +13 -0
  100. package/src/deep/intersectEntries.js +37 -0
  101. package/src/deep/intersectEntries.test.js +20 -0
  102. package/src/deep/objectToDeep.test.js +31 -0
  103. package/src/deep/replaceValueDeep.test.js +21 -0
  104. package/src/deep/unionDeep.test.js +30 -0
  105. package/src/deep/zipValueDeep.test.js +21 -0
  106. package/src/deep-rxjs/ObservableArray.d.ts +55 -0
  107. package/src/deep-rxjs/ObservableArray.js +94 -0
  108. package/src/deep-rxjs/ObservableArray.test.js +117 -0
  109. package/src/deep-rxjs/index.d.ts +2 -0
  110. package/src/deep-rxjs/index.js +2 -0
  111. package/src/deep-rxjs/isRxType.d.ts +9 -0
  112. package/src/deep-rxjs/isRxType.js +15 -0
  113. package/src/deep-rxjs/isRxType.test.js +43 -0
  114. package/src/hyperscript-rxjs/HyperscriptExtensions.d.ts +20 -0
  115. package/src/hyperscript-rxjs/checkbox.d.ts +13 -0
  116. package/src/hyperscript-rxjs/checkbox.js +47 -0
  117. package/src/hyperscript-rxjs/checkbox.test.js +68 -0
  118. package/src/hyperscript-rxjs/choice.d.ts +13 -0
  119. package/src/hyperscript-rxjs/choice.js +24 -0
  120. package/src/hyperscript-rxjs/choice.test.js +108 -0
  121. package/src/hyperscript-rxjs/collapse.d.ts +14 -0
  122. package/src/hyperscript-rxjs/collapse.js +32 -0
  123. package/src/hyperscript-rxjs/collapse.test.js +67 -0
  124. package/src/hyperscript-rxjs/displays/blockLevelFamily.d.ts +5 -0
  125. package/src/hyperscript-rxjs/displays/blockLevelFamily.js +51 -0
  126. package/src/hyperscript-rxjs/displays/getDisplay.d.ts +7 -0
  127. package/src/hyperscript-rxjs/displays/getDisplay.js +51 -0
  128. package/src/hyperscript-rxjs/displays/getDisplay.test.js +56 -0
  129. package/src/hyperscript-rxjs/displays/index.d.ts +3 -0
  130. package/src/hyperscript-rxjs/displays/index.js +3 -0
  131. package/src/hyperscript-rxjs/displays/inlineFamily.d.ts +5 -0
  132. package/src/hyperscript-rxjs/displays/inlineFamily.js +73 -0
  133. package/src/hyperscript-rxjs/flip.d.ts +15 -0
  134. package/src/hyperscript-rxjs/flip.js +29 -0
  135. package/src/hyperscript-rxjs/flip.test.js +85 -0
  136. package/src/hyperscript-rxjs/fragment.d.ts +10 -0
  137. package/src/hyperscript-rxjs/fragment.js +22 -0
  138. package/src/hyperscript-rxjs/fragment.test.js +70 -0
  139. package/src/hyperscript-rxjs/hyperscript.d.ts +15 -0
  140. package/src/hyperscript-rxjs/hyperscript.js +170 -0
  141. package/src/hyperscript-rxjs/hyperscript.test.js +75 -0
  142. package/src/hyperscript-rxjs/index.d.ts +19 -0
  143. package/src/hyperscript-rxjs/index.js +19 -0
  144. package/src/hyperscript-rxjs/multiselect.d.ts +18 -0
  145. package/src/hyperscript-rxjs/multiselect.js +41 -0
  146. package/src/hyperscript-rxjs/multiselect.test.js +121 -0
  147. package/src/hyperscript-rxjs/numberbox.d.ts +14 -0
  148. package/src/hyperscript-rxjs/numberbox.js +73 -0
  149. package/src/hyperscript-rxjs/numberbox.test.js +84 -0
  150. package/src/hyperscript-rxjs/radio.d.ts +15 -0
  151. package/src/hyperscript-rxjs/radio.js +53 -0
  152. package/src/hyperscript-rxjs/radio.test.js +59 -0
  153. package/src/hyperscript-rxjs/select.d.ts +28 -0
  154. package/src/hyperscript-rxjs/select.js +88 -0
  155. package/src/hyperscript-rxjs/select.test.js +101 -0
  156. package/src/hyperscript-rxjs/tabControls/bindTabIndex.d.ts +12 -0
  157. package/src/hyperscript-rxjs/tabControls/bindTabIndex.js +59 -0
  158. package/src/hyperscript-rxjs/tabControls/index.d.ts +8 -0
  159. package/src/hyperscript-rxjs/tabControls/index.js +10 -0
  160. package/src/hyperscript-rxjs/tabControls/tabControl.d.ts +19 -0
  161. package/src/hyperscript-rxjs/tabControls/tabControl.js +40 -0
  162. package/src/hyperscript-rxjs/tabControls/tabControl.test.js +98 -0
  163. package/src/hyperscript-rxjs/tabControls/tabNavItem.d.ts +9 -0
  164. package/src/hyperscript-rxjs/tabControls/tabNavItem.js +30 -0
  165. package/src/hyperscript-rxjs/tabControls/tabPanel.d.ts +9 -0
  166. package/src/hyperscript-rxjs/tabControls/tabPanel.js +21 -0
  167. package/src/hyperscript-rxjs/tabControls/tabRoot.d.ts +7 -0
  168. package/src/hyperscript-rxjs/tabControls/tabRoot.js +26 -0
  169. package/src/hyperscript-rxjs/tags.d.ts +193 -0
  170. package/src/hyperscript-rxjs/tags.js +751 -0
  171. package/src/hyperscript-rxjs/tags.test.js +75 -0
  172. package/src/hyperscript-rxjs/textNode.d.ts +11 -0
  173. package/src/hyperscript-rxjs/textNode.js +51 -0
  174. package/src/hyperscript-rxjs/textNode.test.js +56 -0
  175. package/src/hyperscript-rxjs/textarea.d.ts +17 -0
  176. package/src/hyperscript-rxjs/textarea.js +45 -0
  177. package/src/hyperscript-rxjs/textarea.test.js +52 -0
  178. package/src/hyperscript-rxjs/textbox.d.ts +15 -0
  179. package/src/hyperscript-rxjs/textbox.js +42 -0
  180. package/src/hyperscript-rxjs/textbox.test.js +52 -0
  181. package/src/index.d.ts +19 -0
  182. package/src/index.js +19 -0
  183. package/src/nodes/attachSubscriptionToNode.d.ts +13 -0
  184. package/src/nodes/attachSubscriptionToNode.js +25 -0
  185. package/src/nodes/attachSubscriptionToNode.test.js +73 -0
  186. package/src/nodes/index.d.ts +6 -0
  187. package/src/nodes/index.js +6 -0
  188. package/src/nodes/normalizeChildNodes.d.ts +9 -0
  189. package/src/nodes/normalizeChildNodes.js +15 -0
  190. package/src/nodes/normalizeChildNodes.test.js +55 -0
  191. package/src/nodes/parseHyperscriptArgs.d.ts +10 -0
  192. package/src/nodes/parseHyperscriptArgs.js +57 -0
  193. package/src/nodes/parseHyperscriptArgs.test.js +85 -0
  194. package/src/nodes/pipeEvent.d.ts +15 -0
  195. package/src/nodes/pipeEvent.js +49 -0
  196. package/src/nodes/pipeEvent.test.js +97 -0
  197. package/src/nodes/subscribeEvent.d.ts +15 -0
  198. package/src/nodes/subscribeEvent.js +56 -0
  199. package/src/nodes/subscribeEvent.test.js +88 -0
  200. package/src/object/index.d.ts +10 -0
  201. package/src/object/index.js +11 -0
  202. package/src/object/intersectObject.d.ts +12 -0
  203. package/src/object/intersectObject.js +23 -0
  204. package/src/object/intersectObject.test.js +69 -0
  205. package/src/object/isEmptyObject.d.ts +7 -0
  206. package/src/object/isEmptyObject.js +13 -0
  207. package/src/object/isEmptyObject.test.js +33 -0
  208. package/src/object/isPlainObject.d.ts +11 -0
  209. package/src/object/isPlainObject.js +18 -0
  210. package/src/object/nestedCombineLatest.d.ts +11 -0
  211. package/src/object/nestedCombineLatest.js +18 -0
  212. package/src/object/nestedCombineLatest.test.js +25 -0
  213. package/src/object/nestedMerge.d.ts +11 -0
  214. package/src/object/nestedMerge.js +11 -0
  215. package/src/object/nestedMerge.test.js +61 -0
  216. package/src/object/pickBehaviorSubject.d.ts +13 -0
  217. package/src/object/pickBehaviorSubject.js +81 -0
  218. package/src/object/pickBehaviorSubject.test.js +88 -0
  219. package/src/object/pluckProperty.d.ts +13 -0
  220. package/src/object/pluckProperty.js +24 -0
  221. package/src/object/pluckProperty.test.js +37 -0
  222. package/src/object/restore.d.ts +12 -0
  223. package/src/object/restore.js +69 -0
  224. package/src/object/restore.test.js +124 -0
  225. package/src/object/splitObjectByObservable.d.ts +12 -0
  226. package/src/object/splitObjectByObservable.js +41 -0
  227. package/src/object/splitObjectByObservable.test.js +78 -0
  228. package/src/props/getNestedProperty.d.ts +12 -0
  229. package/src/props/getNestedProperty.js +31 -0
  230. package/src/props/getNestedProperty.test.js +72 -0
  231. package/src/props/index.d.ts +7 -0
  232. package/src/props/index.js +7 -0
  233. package/src/props/parsePropName.d.ts +13 -0
  234. package/src/props/parsePropName.js +45 -0
  235. package/src/props/parsePropName.test.js +67 -0
  236. package/src/props/setProp.d.ts +16 -0
  237. package/src/props/setProp.js +42 -0
  238. package/src/props/setProp.test.js +59 -0
  239. package/src/props/setProps.d.ts +14 -0
  240. package/src/props/setProps.js +47 -0
  241. package/src/props/setProps.test.js +97 -0
  242. package/src/props/subscribeProp.d.ts +16 -0
  243. package/src/props/subscribeProp.js +47 -0
  244. package/src/props/subscribeProp.test.js +81 -0
  245. package/src/ramda/compose.d.ts +10 -0
  246. package/src/ramda/compose.js +36 -0
  247. package/src/ramda/compose.test.js +73 -0
  248. package/src/ramda/cond.d.ts +12 -0
  249. package/src/ramda/cond.js +29 -0
  250. package/src/ramda/cond.test.js +88 -0
  251. package/src/ramda/fold.d.ts +13 -0
  252. package/src/ramda/fold.js +20 -0
  253. package/src/ramda/fold.test.js +51 -0
  254. package/src/ramda/index.d.ts +6 -0
  255. package/src/ramda/index.js +6 -0
  256. package/src/ramda/pipe.d.ts +13 -0
  257. package/src/ramda/pipe.js +27 -0
  258. package/src/ramda/pipe.test.js +77 -0
  259. package/src/ramda/unfold.d.ts +11 -0
  260. package/src/ramda/unfold.js +20 -0
  261. package/src/ramda/unfold.test.js +29 -0
  262. package/src/unquoted-json/ajax.test.js +1074 -0
  263. package/src/unquoted-json/index.d.ts +13 -0
  264. package/src/unquoted-json/index.js +12 -0
  265. package/src/unquoted-json/queryStringify.d.ts +8 -0
  266. package/src/unquoted-json/queryStringify.js +70 -0
  267. package/src/unquoted-json/queryStringify.test.js +110 -0
  268. package/src/unquoted-json/stringifyKey.d.ts +7 -0
  269. package/src/unquoted-json/stringifyKey.js +16 -0
  270. package/src/unquoted-json/stringifyKey.test.js +51 -0
  271. package/src/unquoted-json/stringifyStringValue.d.ts +7 -0
  272. package/src/unquoted-json/stringifyStringValue.js +17 -0
  273. package/src/unquoted-json/stringifyStringValue.test.js +52 -0
  274. package/src/unquoted-json/unquotedJsonStringify.d.ts +7 -0
  275. package/src/unquoted-json/unquotedJsonStringify.js +39 -0
  276. package/src/unquoted-json/unquotedJsonStringify.test.js +52 -0
  277. package/dist/hyperscript-rxjs.d.ts +0 -1412
  278. package/dist/hyperscript-rxjs.js +0 -1
  279. package/dist/tsdoc-metadata.json +0 -11
@@ -0,0 +1,13 @@
1
+ import { isPlainObject } from './isPlainObject'
2
+
3
+ /**
4
+ *
5
+ * 判断对象是否为空对象(非数组、非 null,且无自有属性)。
6
+ *
7
+ * @param {unknown} obj - 要判断的对象。
8
+ * @returns {boolean} 如果是空对象返回 true,否则返回 false。
9
+ *
10
+ */
11
+ export const isEmptyObject = (obj) => {
12
+ return isPlainObject(obj) && Object.keys(obj).length === 0
13
+ }
@@ -0,0 +1,33 @@
1
+ import { isEmptyObject } from './isEmptyObject.js';
2
+
3
+ describe('isEmptyObject', () => {
4
+ test('should return true for an empty object', () => {
5
+ expect(isEmptyObject({})).toBe(true);
6
+ });
7
+
8
+ test('should return false for a non-empty object', () => {
9
+ expect(isEmptyObject({ key: 'value' })).toBe(false);
10
+ });
11
+
12
+ test('should return false for null', () => {
13
+ expect(isEmptyObject(null)).toBe(false);
14
+ });
15
+
16
+ test('should return false for undefined', () => {
17
+ expect(isEmptyObject(undefined)).toBe(false);
18
+ });
19
+
20
+ test('should return false for an array', () => {
21
+ expect(isEmptyObject([])).toBe(false);
22
+ });
23
+
24
+ test('should return false for a non-object value', () => {
25
+ expect(isEmptyObject('string')).toBe(false);
26
+ expect(isEmptyObject(123)).toBe(false);
27
+ expect(isEmptyObject(true)).toBe(false);
28
+ });
29
+
30
+ test('should return false for a function', () => {
31
+ expect(isEmptyObject(() => {})).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ *
3
+ * Type guard for plain objects (objects created via {} or Object.create(null))
4
+ *
5
+ * @param value - The value to check
6
+ * @returns True if the value is a plain object, false otherwise
7
+ *
8
+ */
9
+ export declare function isPlainObject(
10
+ value: unknown
11
+ ): value is Record<string, any>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 检查一个值是否是普通对象(Plain Object)
3
+ * @param {unknown} value - 要检查的值
4
+ * @returns {boolean} - 返回是否是普通对象的类型谓词
5
+ */
6
+ export function isPlainObject(value) {
7
+ // 基本检查:非对象或为null的直接返回false
8
+ if (typeof value !== 'object' || value === null) {
9
+ return false;
10
+ }
11
+
12
+ // 检查原型链
13
+ const proto = Object.getPrototypeOf(value);
14
+
15
+ // 1. 原型为null(如Object.create(null)创建的对象)
16
+ // 2. 原型等于Object.prototype(普通对象)
17
+ return proto === null || proto === Object.prototype;
18
+ }
@@ -0,0 +1,11 @@
1
+ import { Observable } from 'rxjs';
2
+
3
+ /**
4
+ * 将对象格式的 Observable 转换为扁平对象的 Observable。
5
+ *
6
+ * @param model - 对象格式的 Observable。
7
+ * @returns 一个 Observable,发出扁平化后的对象。
8
+ */
9
+ export function nestedCombineLatest(
10
+ model: Record<string|number, any>,
11
+ ): Observable<Record<string|number, any>>;
@@ -0,0 +1,18 @@
1
+ import { Observable } from 'rxjs'
2
+ import { map } from 'rxjs/operators'
3
+
4
+ import { Deep } from '../deep/Deep'
5
+
6
+ ///对象格式的Observale,转化成扁平格式的数据后,整个数组外面Observable
7
+ /**
8
+ *
9
+ * @param {Record<string|number, any>} model
10
+ * @returns {Observable<Record<string|number, any>>}
11
+ */
12
+ export function nestedCombineLatest(model) {
13
+ let deep = Deep.fromObservable(model)
14
+ return deep.combineLatest()
15
+ .pipe(
16
+ map(deep => deep.toObject())
17
+ )
18
+ }
@@ -0,0 +1,25 @@
1
+ import { of } from 'rxjs'
2
+ import { nestedCombineLatest } from './nestedCombineLatest'
3
+
4
+ test('test deepCombineLatest', done => {
5
+ const model = {
6
+ a: {
7
+ b: of(0),
8
+ c: of(1),
9
+ },
10
+ x: [of(2), of(3)],
11
+ }
12
+
13
+ nestedCombineLatest(model).subscribe({
14
+ next: data => {
15
+ expect(data).toEqual({
16
+ a: {
17
+ b: 0,
18
+ c: 1,
19
+ },
20
+ x: [2, 3],
21
+ })
22
+ },
23
+ complete: done,
24
+ })
25
+ })
@@ -0,0 +1,11 @@
1
+ import { Observable } from 'rxjs';
2
+
3
+ /**
4
+ * 追踪对象属性的变化,返回变化属性的键路径和新值。
5
+ *
6
+ * @param model - 任意对象或可观察对象。
7
+ * @returns 合并后的 Observable。
8
+ */
9
+ export function nestedMerge(
10
+ model: Record<string|number, any>
11
+ ): Observable<[ (string | number)[], any ]>;
@@ -0,0 +1,11 @@
1
+ import { Observable } from 'rxjs'
2
+ import { Deep } from '../deep/Deep'
3
+
4
+ /**
5
+ *
6
+ * @param {Record<string|number, any>} model
7
+ * @returns {Observable<[(string | number)[], any]>}
8
+ */
9
+ export function nestedMerge(model) {
10
+ return Deep.fromObservable(model).merge()
11
+ }
@@ -0,0 +1,61 @@
1
+ import { BehaviorSubject } from 'rxjs'
2
+ import { nestedMerge } from './nestedMerge'
3
+ import { pipe } from '../ramda'
4
+
5
+ test('test nestedMerge 1', done => {
6
+ const source = {
7
+ a: {
8
+ b: new BehaviorSubject(0),
9
+ c: new BehaviorSubject(1),
10
+ },
11
+ x: [new BehaviorSubject(2), new BehaviorSubject(3)]
12
+ }
13
+
14
+ let states = []
15
+ nestedMerge(source).subscribe(data => { states.push(data) })
16
+
17
+ source.a.b.next(10)
18
+ source.x[1].next(13)
19
+
20
+ expect(states).toEqual([
21
+ [['a', 'b'], 0],
22
+ [['a', 'c'], 1],
23
+ [['x', 0], 2],
24
+ [['x', 1], 3],
25
+ [['a', 'b'], 10],
26
+ [['x', 1], 13],
27
+ ])
28
+
29
+ done()
30
+ })
31
+
32
+
33
+ test('test nestedMerge', done => {
34
+ const source = {
35
+ a: new BehaviorSubject(true),
36
+ b: new BehaviorSubject(true),
37
+ x: { c: [new BehaviorSubject(true)] },
38
+ }
39
+ let states = []
40
+ let obs =
41
+ pipe(
42
+ source,
43
+ nestedMerge,
44
+
45
+ );
46
+ obs.subscribe(data => { states.push(data) })
47
+
48
+ source.a.next(false)
49
+ source.b.next(false)
50
+ source.x.c[0].next(false)
51
+
52
+ expect(states).toEqual([
53
+ [['a'], true],
54
+ [['b'], true],
55
+ [['x', 'c', 0], true],
56
+ [['a'], false],
57
+ [['b'], false],
58
+ [['x', 'c', 0], false],
59
+ ])
60
+ done()
61
+ })
@@ -0,0 +1,13 @@
1
+
2
+ /**
3
+ *
4
+ * 拾取对象中的 BehaviorSubject 值,作为叶子节点,忽略其他值。
5
+ *
6
+ * @param model - 任意对象,内部递归查找 BehaviorSubject。
7
+ * @returns 只包含 BehaviorSubject 当前值的对象或数组结构。
8
+ * @throws 如果结构不符合预期会抛出错误。
9
+ *
10
+ */
11
+ export function pickBehaviorSubject(
12
+ model: Record<string | number, any> & { pickeys?: string[] }
13
+ ): Record<string | number, any>;
@@ -0,0 +1,81 @@
1
+ import { BehaviorSubject, isObservable } from 'rxjs'
2
+ import { intersectObject } from '.'
3
+ import { isRxType } from '../deep-rxjs/isRxType'
4
+ import { ObservableArray } from '../deep-rxjs/ObservableArray'
5
+
6
+ /**
7
+ *
8
+ * 拾取对象中的BehaviorSubject值,作爲葉子節點,忽略其他值。
9
+ *
10
+ * @param {Record<string|number,any> & { pickeys?: string [] }} model - 输入的数据结构,可以是对象、数组或BehaviorSubject
11
+ * @returns {Record<string|number,any>} - 返回只包含BehaviorSubject当前值的结构
12
+ * @throws {Error} 当输入不包含任何BehaviorSubject或结构不符合预期时抛出错误
13
+ */
14
+ export function pickBehaviorSubject(model) {
15
+ let x = loop(model, '', null)
16
+ if (x.length === 1) {
17
+ return x[0][1]
18
+ } else {
19
+ throw new Error('pickBehaviorSubject')
20
+ }
21
+ }
22
+
23
+ /**
24
+ *
25
+ * 递归遍历数据结构收集BehaviorSubject值
26
+ *
27
+ * @param {any} value - 当前遍历的值
28
+ * @param {string|number} key - 当前键名
29
+ * @param {object|null} parent - 父级对象
30
+ * @returns {[string|number, any][]} - 返回收集到的键值对数组,每个元素是一个[key, value]元组
31
+ * @throws {Error} 当遇到不符合要求的数据结构时抛出错误
32
+ */
33
+ function loop(value, key, parent) {
34
+ if (isObservable(value) && value instanceof BehaviorSubject) {
35
+ return [[key, value.value]] //保存葉節點
36
+ } else if (value === null || typeof value !== 'object' || isRxType(value)) {
37
+ return []
38
+ } else if (value instanceof ObservableArray) {
39
+ let v = [...value]
40
+ .map((e, i) => {
41
+ let pelem = loop(e, i, value)
42
+ if (pelem.length === 1) {
43
+ return pelem[0][1] //表示元素是正常叶节点
44
+ } else {
45
+ throw new Error('dense Array') //非正常叶节点,有洞
46
+ }
47
+ })
48
+ return [[key, v]]
49
+ } else if (value instanceof Array) {
50
+ if (value.length === 0) {
51
+ return [] //空不可变数组,将丢弃。
52
+ } else {
53
+ let v = value.map((e, i) => {
54
+ let pelem = loop(e, i, value)
55
+ if (pelem.length === 1) {
56
+ return pelem[0][1]
57
+ } else {
58
+ throw new Error('dense Array')
59
+ }
60
+ })
61
+
62
+ return [[key, v]]
63
+ }
64
+ } else if ('pickeys' in value) {
65
+ return loop(intersectObject(value, value.pickeys()), key, parent)
66
+ } else {
67
+ let entries = Object.entries(value)
68
+ if (entries.length === 0) {
69
+ return []
70
+ } else {
71
+ let ee = entries
72
+ .map(([k, v]) => loop(v, k, parent))
73
+ .reduce((acc, val) => [...acc, ...val], [])
74
+ if (ee.length === 0) {
75
+ return []
76
+ } else {
77
+ return [[key, Object.fromEntries(ee)]]
78
+ }
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,88 @@
1
+ import { BehaviorSubject } from 'rxjs'
2
+ import { pickBehaviorSubject } from './pickBehaviorSubject'
3
+ import { ObservableArray } from '../deep-rxjs/ObservableArray'
4
+
5
+ test('BehaviorSubject happy', done => {
6
+ let source = new BehaviorSubject(0)
7
+ let y = pickBehaviorSubject(source)
8
+ expect(y).toEqual(0)
9
+ done()
10
+ })
11
+
12
+ test('array happy', done => {
13
+ let source = [new BehaviorSubject(0), new BehaviorSubject(1)]
14
+ let y = pickBehaviorSubject(source)
15
+ expect(y).toEqual([0, 1])
16
+ done()
17
+ })
18
+
19
+ test('array error test', done => {
20
+ let source = [new BehaviorSubject(0), 'error']
21
+ expect(() => {
22
+ pickBehaviorSubject(source)
23
+ }).toThrow('dense Array')
24
+
25
+ done()
26
+ })
27
+
28
+ test('object happy', done => {
29
+ let source = { a: new BehaviorSubject(0), b: new BehaviorSubject(1) }
30
+ let y = pickBehaviorSubject(source)
31
+ expect(y).toEqual({ a: 0, b: 1 })
32
+ done()
33
+ })
34
+
35
+ test('object ignore all', done => {
36
+ let source = { a: 0, b: 1 }
37
+
38
+ expect(() => {
39
+ pickBehaviorSubject(source)
40
+ }).toThrow('pickBehaviorSubject')
41
+
42
+ done()
43
+ })
44
+
45
+ test('object ignore some', done => {
46
+ let source = {
47
+ a: new BehaviorSubject(0), b: 1, c: [], d: {}
48
+ }
49
+ let y = pickBehaviorSubject(source)
50
+
51
+ expect(y).toEqual({ a: 0 })
52
+ done()
53
+ })
54
+
55
+ test('pick observable array ignore some', done => {
56
+ let source = {
57
+ a: new ObservableArray()
58
+ }
59
+
60
+ source.a.insertBefore(new BehaviorSubject(0))
61
+ source.a.insertBefore(new BehaviorSubject(1))
62
+ source.a.insertBefore(new BehaviorSubject(2))
63
+
64
+ source.a[1].next('1')
65
+ let y = pickBehaviorSubject(source)
66
+
67
+ expect(y).toEqual({ a: [0,'1',2] })
68
+ done()
69
+ })
70
+
71
+ test('object pickeys', done => {
72
+ let source = {
73
+ a: new BehaviorSubject(0),
74
+ b: new BehaviorSubject(0),
75
+ pickeys() {
76
+ return ['a']
77
+ }
78
+ }
79
+ let y = pickBehaviorSubject(source)
80
+ expect(y).toEqual({ a: 0 })
81
+
82
+ done()
83
+ })
84
+
85
+
86
+
87
+
88
+
@@ -0,0 +1,13 @@
1
+
2
+ /**
3
+ * 获取对象的嵌套属性。
4
+ *
5
+ * @param obj - 源对象或数组。
6
+ * @param keyPath - 属性访问路径数组。
7
+ * @returns 路径对应的属性值。
8
+ * @throws 当路径不存在时抛出 TypeError。
9
+ */
10
+ export function pluckProperty(
11
+ obj: Record<string | number, any>,
12
+ keyPath: (string | number)[]
13
+ ): any;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 获取对象的嵌套属性
3
+ * @param {Record<string | number, any>} obj - 源对象/数组
4
+ * @param {(string|number)[]} keyPath - 属性访问路径数组
5
+ * @returns {any} 路径对应的属性值
6
+ * @throws {TypeError} 当路径不存在时抛出错误
7
+ */
8
+ export function pluckProperty(obj, keyPath) {
9
+ // 类型检查
10
+ if (typeof obj !== 'object' || obj === null) {
11
+ throw new TypeError('First argument must be an object');
12
+ }
13
+
14
+ if (!Array.isArray(keyPath)) {
15
+ throw new TypeError('keyPath must be an array');
16
+ }
17
+
18
+ return keyPath.reduce((prev, curr) => {
19
+ if (prev == null || !(curr in prev)) {
20
+ throw new TypeError(`Property '${curr}' not found in path`);
21
+ }
22
+ return prev[curr];
23
+ }, obj);
24
+ }
@@ -0,0 +1,37 @@
1
+ import { pluckProperty } from './pluckProperty';
2
+
3
+ describe('pluckProperty', () => {
4
+ test('basic array access', () => {
5
+ const arr = [0, 1];
6
+ expect(pluckProperty(arr, [1])).toBe(1);
7
+ });
8
+
9
+ test('basic object access', () => {
10
+ const obj = { a: 1 };
11
+ expect(pluckProperty(obj, ['a'])).toBe(1);
12
+ });
13
+
14
+ test('nested access', () => {
15
+ const obj = { a: [1, 2] };
16
+ expect(pluckProperty(obj, ['a', 0])).toBe(1);
17
+ });
18
+
19
+ test('should throw on invalid path', () => {
20
+ expect(() => pluckProperty({}, ['a'])).toThrow(TypeError);
21
+ expect(() => pluckProperty({ a: {} }, ['a', 'b'])).toThrow(TypeError);
22
+ });
23
+
24
+ test('should throw on null/undefined', () => {
25
+ expect(() => pluckProperty(null, ['a'])).toThrow(TypeError);
26
+ expect(() => pluckProperty(undefined, ['a'])).toThrow(TypeError);
27
+ });
28
+
29
+ test('should throw if keyPath is not array', () => {
30
+ expect(() => pluckProperty({}, 'a')).toThrow(TypeError);
31
+ });
32
+
33
+ test('empty path returns original object', () => {
34
+ const obj = {};
35
+ expect(pluckProperty(obj, [])).toBe(obj);
36
+ });
37
+ });
@@ -0,0 +1,12 @@
1
+
2
+ /**
3
+ * 更新 ViewModel 为新值 src,递归同步 BehaviorSubject、ObservableArray、数组和对象的内容。
4
+ *
5
+ * @param model - 需要更新的 ViewModel(可为 BehaviorSubject、ObservableArray、数组或对象)。
6
+ * @param src - 新值,结构应与 model 对应。
7
+ * @returns 返回更新后的 model。
8
+ */
9
+ export function restore(
10
+ model: Record<string | number, any>,
11
+ src: Record<string | number, any>
12
+ ): Record<string | number, any>;
@@ -0,0 +1,69 @@
1
+ import { BehaviorSubject } from 'rxjs'
2
+ import { isRxType } from '../deep-rxjs/isRxType'
3
+ import { ObservableArray } from '../deep-rxjs/ObservableArray'
4
+
5
+ /**
6
+ * Updates a reactive model with new values from a source object.
7
+ * Recursively synchronizes BehaviorSubject, ObservableArray, array and object contents.
8
+ *
9
+ * @param {Record<string | number, any>} model - The reactive model to update (can be BehaviorSubject, ObservableArray, array or object)
10
+ * @param {Record<string | number, any>} src - The new values to restore, should match the structure of the model
11
+ * @returns
12
+ */
13
+ export function restore(model, src) {
14
+ loop(model, src)
15
+ // return model
16
+ }
17
+
18
+ /**
19
+ * Recursively updates model values from source
20
+ * @param {any & {appendChild?: (c:any) => void }} o - Current model node to update
21
+ * @param {any} src - Corresponding source value
22
+ */
23
+ function loop(o, src) {
24
+ if (o instanceof BehaviorSubject) {
25
+ // Input value
26
+ o.next(src)
27
+ } else if (o === null || typeof o !== 'object' || isRxType(o)) {
28
+ // noop
29
+ } else if (o instanceof ObservableArray) {
30
+ let len = o.length
31
+ let diffcount = len - src.length
32
+ if (diffcount > 0) {
33
+ // Truncate if longer
34
+ Array.from({ length: diffcount })
35
+ .map((e, i) => len - 1 - i)
36
+ .forEach((last) => {
37
+ o.removeChild(last)
38
+ })
39
+ } else if (diffcount < 0) {
40
+ // Extend if shorter
41
+ Array.from({ length: -diffcount })
42
+ .map((e, i) => len + i)
43
+ .forEach(i => {
44
+ // Add an item to the end of an Array
45
+ o.appendChild()
46
+ })
47
+ }
48
+
49
+ // After alignment, update each element's value
50
+ for (let i of o.keys()) {
51
+ loop(o[i], src[i])
52
+ }
53
+ } else if (o instanceof Array) {
54
+ // Regular array, tuple
55
+ let indexes = o.length < src.length ? o : src
56
+ // Ignore extra src elements, noop for missing elements
57
+ for (let i of indexes.keys()) {
58
+ // todo: Throw error when o[i] is not settable?
59
+ loop(o[i], src[i])
60
+ }
61
+ } else if (typeof o === 'object') {
62
+ // Recursively process object properties
63
+ Object.entries(o)
64
+ .filter(([k, v]) => k in src) // Intersection
65
+ .forEach(([k, v]) => {
66
+ loop(v, src[k])
67
+ })
68
+ }
69
+ }