@davidcal/frosth 1.5.0 → 1.6.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Frosth - Yet another UI framework
2
2
 
3
- **Latest version**: v1.5.0
3
+ **Latest version**: v1.6.1
4
4
 
5
5
  ## Motivation
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davidcal/frosth",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "Async-first CRUD-last UI framework that treats encapsulation as a solved problem.",
5
5
  "type": "module",
6
6
  "main": "./src/sigew.js",
@@ -0,0 +1,37 @@
1
+ export const create_producer = () => {
2
+ const result = {};
3
+
4
+ const subscriptions = new Set();
5
+
6
+ const create_stream = () => {
7
+ const result = {};
8
+
9
+ result.subscribe = (handler) => {
10
+ subscriptions.add(handler);
11
+
12
+ const unsubscribe = () => {
13
+ subscriptions.delete(handler);
14
+ };
15
+
16
+ return { unsubscribe };
17
+ };
18
+
19
+ return result;
20
+ };
21
+
22
+ result.emit = (message) => {
23
+ return result.emit_many([message]);
24
+ };
25
+
26
+ result.emit_many = (messages) => {
27
+ for (const message of messages) {
28
+ for (const handler of subscriptions) {
29
+ handler(message);
30
+ }
31
+ }
32
+ };
33
+
34
+ result.stream = create_stream();
35
+
36
+ return result;
37
+ };
@@ -25,6 +25,10 @@ class Self {
25
25
  return create_self(this, run_effect, options);
26
26
  }
27
27
 
28
+ _is_effectfully_useful() {
29
+ return this._run_effect !== undefined;
30
+ }
31
+
28
32
  create_effect(callback) {
29
33
  let child;
30
34
 
@@ -0,0 +1,34 @@
1
+ export const create_wrapper_stream = (stream, handle) => {
2
+ const result = {};
3
+
4
+ const subscriptions = new Set();
5
+
6
+ let upstream;
7
+
8
+ result.subscribe = (handler) => {
9
+ subscriptions.add(handler);
10
+
11
+ if (upstream === undefined) {
12
+ upstream = stream.subscribe((calldata) => {
13
+ handle(calldata, (new_calldata) => {
14
+ for (const handler of subscriptions) {
15
+ handler(new_calldata);
16
+ }
17
+ });
18
+ });
19
+ }
20
+
21
+ const unsubscribe = () => {
22
+ if (subscriptions.size === 1) {
23
+ upstream.unsubscribe();
24
+ upstream = undefined;
25
+ }
26
+
27
+ subscriptions.delete(handler);
28
+ };
29
+
30
+ return { unsubscribe };
31
+ };
32
+
33
+ return result;
34
+ };
package/src/sigew.js CHANGED
@@ -8,6 +8,11 @@ import { MARK_INSTALLABLE } from "./constants.js";
8
8
  import { create_memo } from "./create_memo.js";
9
9
  import { create_beacon } from "./create_beacon.js";
10
10
  import { create_stream } from "./create_stream.js";
11
+ import { create_producer } from "./create_producer.js";
12
+ import { stream_filter } from "./stream_filter.js";
13
+ import { stream_map } from "./stream_map.js";
14
+ import { stream_reduce } from "./stream_reduce.js";
15
+ import { stream_latest } from "./stream_latest.js";
11
16
 
12
17
  export { create_self };
13
18
  export { create_signal };
@@ -15,6 +20,11 @@ export { h };
15
20
  export { create_memo };
16
21
  export { create_beacon };
17
22
  export { create_stream };
23
+ export { create_producer };
24
+ export { stream_filter };
25
+ export { stream_map };
26
+ export { stream_reduce };
27
+ export { stream_latest };
18
28
 
19
29
  export const installarea = (s) => {
20
30
  const storage = s.create_housed_signal(() => undefined);
@@ -0,0 +1,9 @@
1
+ import { create_wrapper_stream } from "./create_wrapper_stream.js";
2
+
3
+ export const stream_filter = (stream, predicate) =>
4
+ create_wrapper_stream(stream, (calldata, emit) => {
5
+ if (predicate(calldata)) {
6
+ emit(calldata);
7
+ }
8
+ })
9
+ ;
@@ -0,0 +1,41 @@
1
+ import { create_signal } from "./create_signal.js";
2
+
3
+ export const stream_latest = (stream, initial) => {
4
+ const signal = create_signal(() => initial);
5
+
6
+ let upstream = undefined;
7
+ const registered = new Set();
8
+
9
+ const consider_acquiring = () => {
10
+ if (upstream !== undefined) {
11
+ return;
12
+ }
13
+
14
+ upstream = stream.subscribe((calldata) => {
15
+ signal.set(() => calldata);
16
+ });
17
+ };
18
+
19
+ const consider_releasing = () => {
20
+ if (registered.size !== 0) {
21
+ return;
22
+ }
23
+
24
+ upstream.unsubscribe();
25
+ upstream = undefined;
26
+ };
27
+
28
+ return (self) => {
29
+ if (self._is_effectfully_useful() && !registered.has(self)) {
30
+ registered.add(self);
31
+ consider_acquiring();
32
+
33
+ self.on_dispose(() => {
34
+ registered.delete(self);
35
+ consider_releasing();
36
+ });
37
+ }
38
+
39
+ return signal(self);
40
+ };
41
+ };
@@ -0,0 +1,7 @@
1
+ import { create_wrapper_stream } from "./create_wrapper_stream.js";
2
+
3
+ export const stream_map = (stream, mapper) =>
4
+ create_wrapper_stream(stream, (calldata, emit) => {
5
+ emit(mapper(calldata));
6
+ })
7
+ ;
@@ -0,0 +1,10 @@
1
+ import { create_wrapper_stream } from "./create_wrapper_stream.js";
2
+
3
+ export const stream_reduce = (stream, reducer, initial) => {
4
+ let acc = initial;
5
+
6
+ return create_wrapper_stream(stream, (calldata, emit) => {
7
+ acc = reducer(acc, calldata);
8
+ emit(acc);
9
+ });
10
+ };