synstate-dom 0.3.0 → 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.
@@ -6,7 +6,8 @@ import type { Bindable, BindableValue } from './bindable.mjs';
6
6
  * When every input is a constant, `fn` runs once and the result is a constant:
7
7
  * a static part of the view never enters the graph. Otherwise the constants
8
8
  * become `just` and the views are turned into observables, and the result is
9
- * `combine(...).pipe(map(...))`.
9
+ * synstate's `derive` over them: one observable, which passes on a result only
10
+ * when it is not `Object.is` the one it holds.
10
11
  */
11
12
  export declare const derive: <const BS extends NonEmptyTuple<Bindable<unknown>>, R>(bindables: BS, fn: (...values: BindableValues<BS>) => R) => Bindable<R>;
12
13
  /** The value types of a tuple of {@link Bindable}s, position by position. */
@@ -1 +1 @@
1
- {"version":3,"file":"derive.d.mts","sourceRoot":"","sources":["../../src/constant/derive.mts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG9D;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,EAAE,SAAS,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,aAC9D,EAAE,MACT,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,KACvC,QAAQ,CAAC,CAAC,CAmBZ,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,cAAc,CAAC,EAAE,SAAS,SAAS,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,QAAQ,CAAC;KAC5E,CAAC,IAAI,MAAM,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC,CAAC"}
1
+ {"version":3,"file":"derive.d.mts","sourceRoot":"","sources":["../../src/constant/derive.mts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG9D;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,EAAE,SAAS,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,aAC9D,EAAE,MACT,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,KACvC,QAAQ,CAAC,CAAC,CAkBZ,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,cAAc,CAAC,EAAE,SAAS,SAAS,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,QAAQ,CAAC;KAC5E,CAAC,IAAI,MAAM,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { combine, isObservable, map, } from 'synstate';
1
+ import { derive as deriveObservable, isObservable, } from 'synstate';
2
2
  import { Arr } from 'ts-data-forge';
3
3
  import { asConstant, isView } from '../internal/index.mjs';
4
4
  import { toObservable } from './to-observable.mjs';
@@ -10,19 +10,19 @@ import { toObservable } from './to-observable.mjs';
10
10
 
11
11
 
12
12
 
13
+
13
14
  export const derive = (bindables, fn) => {
14
15
 
15
16
 
16
17
 
17
18
 
18
- const apply = (values) =>
19
19
 
20
- fn(...values);
20
+ const project = fn;
21
21
  if (Arr.every(bindables, isConstant)) {
22
- return asConstant(apply(bindables));
22
+ return asConstant(project(...bindables));
23
23
  }
24
24
  const observables = Arr.map(bindables, toObservable);
25
- return combine(observables).pipe(map(apply));
25
+ return deriveObservable(observables, project);
26
26
  };
27
27
  const isConstant = (bindable) => !isView(bindable) && !isObservable(bindable);
28
28
  //# sourceMappingURL=derive.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"derive.mjs","sourceRoot":"","sources":["../../src/constant/derive.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,YAAY,EACZ,GAAG,GAEJ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,SAAa,EACb,EAAwC,EAC3B,EAAE;IACf,0EAA0E;IAC1E,2EAA2E;IAC3E,wEAAwE;IACxE,oDAAoD;IACpD,MAAM,KAAK,GAAG,CAAC,MAA0B,EAAK,EAAE;IAC9C,iFAAiF;IACjF,EAAE,CAAC,GAAI,MAA6B,CAAC,CAAC;IAExC,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,WAAW,GAAkD,GAAG,CAAC,GAAG,CACxE,SAAS,EACT,YAAY,CACb,CAAC;IAEF,OAAO,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC,CAAC;AAOF,MAAM,UAAU,GAAG,CAAC,QAA2B,EAAW,EAAE,CAC1D,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"derive.mjs","sourceRoot":"","sources":["../../src/constant/derive.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,IAAI,gBAAgB,EAC1B,YAAY,GAEb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,SAAa,EACb,EAAwC,EAC3B,EAAE;IACf,oEAAoE;IACpE,wEAAwE;IACxE,wEAAwE;IACxE,oCAAoC;IACpC,iFAAiF;IACjF,MAAM,OAAO,GAAG,EAA0C,CAAC;IAE3D,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,WAAW,GAAkD,GAAG,CAAC,GAAG,CACxE,SAAS,EACT,YAAY,CACb,CAAC;IAEF,OAAO,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC,CAAC;AAOF,MAAM,UAAU,GAAG,CAAC,QAA2B,EAAW,EAAE,CAC1D,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synstate-dom",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Compiler-free fine-grained UI library that binds synstate observables directly to the DOM",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,24 +44,24 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@vitest/browser-playwright": "5.0.5",
47
- "eslint-config-typed": "5.19.2",
48
- "eslint-plugin-ts-data-forge": "0.10.3",
49
- "eslint-plugin-ts-fortress": "1.3.8",
50
- "eslint-plugin-ts-type-forge": "0.8.13",
47
+ "eslint-config-typed": "5.20.1",
48
+ "eslint-plugin-ts-data-forge": "0.10.4",
49
+ "eslint-plugin-ts-fortress": "1.3.9",
50
+ "eslint-plugin-ts-type-forge": "0.8.14",
51
51
  "playwright": "1.64.1",
52
52
  "prettier": "3.9.10",
53
53
  "prettier-plugin-organize-imports": "4.3.0",
54
54
  "prettier-plugin-packagejson": "3.0.2",
55
- "synstate": "6.0.0",
55
+ "synstate": "6.1.0",
56
56
  "ts-data-forge": "14.10.0",
57
57
  "ts-repo-utils": "10.7.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "synstate": ">=6.0.0",
60
+ "synstate": ">=6.1.0",
61
61
  "ts-data-forge": ">=14.10.0"
62
62
  },
63
63
  "engines": {
64
- "node": ">=22.22.2",
64
+ "node": ">=22.23.4",
65
65
  "pnpm": ">=9.0.0"
66
66
  },
67
67
  "publishConfig": {
@@ -1,7 +1,6 @@
1
1
  import {
2
- combine,
2
+ derive as deriveObservable,
3
3
  isObservable,
4
- map,
5
4
  type InitializedObservable,
6
5
  } from 'synstate';
7
6
  import { Arr } from 'ts-data-forge';
@@ -16,22 +15,22 @@ import { toObservable } from './to-observable.mjs';
16
15
  * When every input is a constant, `fn` runs once and the result is a constant:
17
16
  * a static part of the view never enters the graph. Otherwise the constants
18
17
  * become `just` and the views are turned into observables, and the result is
19
- * `combine(...).pipe(map(...))`.
18
+ * synstate's `derive` over them: one observable, which passes on a result only
19
+ * when it is not `Object.is` the one it holds.
20
20
  */
21
21
  export const derive = <const BS extends NonEmptyTuple<Bindable<unknown>>, R>(
22
22
  bindables: BS,
23
23
  fn: (...values: BindableValues<BS>) => R,
24
24
  ): Bindable<R> => {
25
- // `values` holds, position by position, the value of each of `bindables`:
26
- // the constants themselves, or what `combine` emits for their observables.
27
- // The checker cannot follow a position through `Arr.map` and `combine`,
28
- // which carry one element type for the whole tuple.
29
- const apply = (values: readonly unknown[]): R =>
30
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion -- see above
31
- fn(...(values as BindableValues<BS>));
25
+ // `fn` is called with the value of each of `bindables`, position by
26
+ // position: the constants themselves, or their observables' values. The
27
+ // checker cannot follow a position through `Arr.map`, which carries one
28
+ // element type for the whole tuple.
29
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion -- see above
30
+ const project = fn as (...values: readonly unknown[]) => R;
32
31
 
33
32
  if (Arr.every(bindables, isConstant)) {
34
- return asConstant(apply(bindables));
33
+ return asConstant(project(...bindables));
35
34
  }
36
35
 
37
36
  const observables: NonEmptyTuple<InitializedObservable<unknown>> = Arr.map(
@@ -39,7 +38,7 @@ export const derive = <const BS extends NonEmptyTuple<Bindable<unknown>>, R>(
39
38
  toObservable,
40
39
  );
41
40
 
42
- return combine(observables).pipe(map(apply));
41
+ return deriveObservable(observables, project);
43
42
  };
44
43
 
45
44
  /** The value types of a tuple of {@link Bindable}s, position by position. */
@@ -1,4 +1,4 @@
1
- import { createState, isObservable } from 'synstate';
1
+ import { createState, isChildObservable, isObservable } from 'synstate';
2
2
  import { expectType } from 'ts-data-forge';
3
3
  import type { Bindable } from './bindable.mjs';
4
4
  import { constant } from './constant.mjs';
@@ -61,4 +61,28 @@ describe(derive, () => {
61
61
 
62
62
  assert.strictEqual(result$.getSnapshot().value, '2-B');
63
63
  });
64
+
65
+ test('observables give one observable, whose parents are the inputs', () => {
66
+ const [a$, setA] = createState(1);
67
+
68
+ const [b$] = createState(2);
69
+
70
+ const fn = vi.fn((a: number, b: number) => a + b);
71
+
72
+ const result = derive([a$, b$], fn);
73
+
74
+ const result$ = toObservable(result);
75
+
76
+ assert.isTrue(isChildObservable(result$));
77
+
78
+ if (isChildObservable(result$)) {
79
+ assert.deepStrictEqual(result$.parents, [a$, b$]);
80
+ }
81
+
82
+ setA(2);
83
+
84
+ assert.strictEqual(result$.getSnapshot().value, 4);
85
+
86
+ expect(fn).toHaveBeenCalledTimes(2);
87
+ });
64
88
  });