synstate 2.0.2 → 3.0.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 (161) hide show
  1. package/README.md +2 -2
  2. package/dist/core/base/create-child-observable.d.mts +65 -0
  3. package/dist/core/base/create-child-observable.d.mts.map +1 -0
  4. package/dist/core/base/create-child-observable.mjs +157 -0
  5. package/dist/core/base/create-child-observable.mjs.map +1 -0
  6. package/dist/core/base/create-observable-base.d.mts +82 -0
  7. package/dist/core/base/create-observable-base.d.mts.map +1 -0
  8. package/dist/core/base/create-observable-base.mjs +160 -0
  9. package/dist/core/base/create-observable-base.mjs.map +1 -0
  10. package/dist/core/base/create-root-observable.d.mts +37 -0
  11. package/dist/core/base/create-root-observable.d.mts.map +1 -0
  12. package/dist/core/base/create-root-observable.mjs +36 -0
  13. package/dist/core/base/create-root-observable.mjs.map +1 -0
  14. package/dist/core/base/index.d.mts +3 -0
  15. package/dist/core/base/index.d.mts.map +1 -0
  16. package/dist/core/base/index.mjs +3 -0
  17. package/dist/core/{class → base}/index.mjs.map +1 -1
  18. package/dist/core/combine/combine.d.mts.map +1 -1
  19. package/dist/core/combine/combine.mjs +28 -27
  20. package/dist/core/combine/combine.mjs.map +1 -1
  21. package/dist/core/combine/merge.d.mts.map +1 -1
  22. package/dist/core/combine/merge.mjs +11 -13
  23. package/dist/core/combine/merge.mjs.map +1 -1
  24. package/dist/core/combine/zip.d.mts.map +1 -1
  25. package/dist/core/combine/zip.mjs +32 -31
  26. package/dist/core/combine/zip.mjs.map +1 -1
  27. package/dist/core/create/counter.d.mts.map +1 -1
  28. package/dist/core/create/counter.mjs +39 -50
  29. package/dist/core/create/counter.mjs.map +1 -1
  30. package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
  31. package/dist/core/create/from-abortable-promise.mjs +33 -32
  32. package/dist/core/create/from-abortable-promise.mjs.map +1 -1
  33. package/dist/core/create/from-promise.d.mts.map +1 -1
  34. package/dist/core/create/from-promise.mjs +12 -12
  35. package/dist/core/create/from-promise.mjs.map +1 -1
  36. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  37. package/dist/core/create/from-subscribable.mjs +10 -10
  38. package/dist/core/create/from-subscribable.mjs.map +1 -1
  39. package/dist/core/create/just.d.mts.map +1 -1
  40. package/dist/core/create/just.mjs +5 -4
  41. package/dist/core/create/just.mjs.map +1 -1
  42. package/dist/core/create/source.mjs +11 -12
  43. package/dist/core/create/source.mjs.map +1 -1
  44. package/dist/core/create/timer.d.mts.map +1 -1
  45. package/dist/core/create/timer.mjs +32 -38
  46. package/dist/core/create/timer.mjs.map +1 -1
  47. package/dist/core/index.d.mts +0 -1
  48. package/dist/core/index.d.mts.map +1 -1
  49. package/dist/core/index.mjs +0 -3
  50. package/dist/core/index.mjs.map +1 -1
  51. package/dist/core/operators/audit.d.mts.map +1 -1
  52. package/dist/core/operators/audit.mjs +28 -36
  53. package/dist/core/operators/audit.mjs.map +1 -1
  54. package/dist/core/operators/debounce.d.mts.map +1 -1
  55. package/dist/core/operators/debounce.mjs +26 -33
  56. package/dist/core/operators/debounce.mjs.map +1 -1
  57. package/dist/core/operators/filter.d.mts.map +1 -1
  58. package/dist/core/operators/filter.mjs +23 -27
  59. package/dist/core/operators/filter.mjs.map +1 -1
  60. package/dist/core/operators/map.d.mts.map +1 -1
  61. package/dist/core/operators/map.mjs +21 -28
  62. package/dist/core/operators/map.mjs.map +1 -1
  63. package/dist/core/operators/merge-map.d.mts.map +1 -1
  64. package/dist/core/operators/merge-map.mjs +30 -39
  65. package/dist/core/operators/merge-map.mjs.map +1 -1
  66. package/dist/core/operators/pairwise.d.mts.map +1 -1
  67. package/dist/core/operators/pairwise.mjs +22 -24
  68. package/dist/core/operators/pairwise.mjs.map +1 -1
  69. package/dist/core/operators/scan.d.mts.map +1 -1
  70. package/dist/core/operators/scan.mjs +13 -18
  71. package/dist/core/operators/scan.mjs.map +1 -1
  72. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  73. package/dist/core/operators/skip-if-no-change.mjs +22 -26
  74. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  75. package/dist/core/operators/skip-until.d.mts.map +1 -1
  76. package/dist/core/operators/skip-until.mjs +23 -24
  77. package/dist/core/operators/skip-until.mjs.map +1 -1
  78. package/dist/core/operators/skip-while.d.mts.map +1 -1
  79. package/dist/core/operators/skip-while.mjs +27 -32
  80. package/dist/core/operators/skip-while.mjs.map +1 -1
  81. package/dist/core/operators/switch-map.d.mts.map +1 -1
  82. package/dist/core/operators/switch-map.mjs +28 -34
  83. package/dist/core/operators/switch-map.mjs.map +1 -1
  84. package/dist/core/operators/take-until.d.mts.map +1 -1
  85. package/dist/core/operators/take-until.mjs +22 -22
  86. package/dist/core/operators/take-until.mjs.map +1 -1
  87. package/dist/core/operators/take-while.d.mts.map +1 -1
  88. package/dist/core/operators/take-while.mjs +25 -29
  89. package/dist/core/operators/take-while.mjs.map +1 -1
  90. package/dist/core/operators/throttle.d.mts.map +1 -1
  91. package/dist/core/operators/throttle.mjs +27 -35
  92. package/dist/core/operators/throttle.mjs.map +1 -1
  93. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  94. package/dist/core/operators/with-buffered-from.mjs +24 -24
  95. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  96. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  97. package/dist/core/operators/with-current-value-from.mjs +20 -25
  98. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  99. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  100. package/dist/core/operators/with-initial-value.mjs +12 -15
  101. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  102. package/dist/entry-point.mjs +0 -3
  103. package/dist/entry-point.mjs.map +1 -1
  104. package/dist/index.mjs +0 -3
  105. package/dist/index.mjs.map +1 -1
  106. package/package.json +15 -16
  107. package/src/core/{class → base}/circular-dependency-comparison.test.mts +28 -18
  108. package/src/core/base/circular-dependency.test.mts +322 -0
  109. package/src/core/base/create-child-observable.mts +340 -0
  110. package/src/core/base/create-observable-base.mts +358 -0
  111. package/src/core/base/create-root-observable.mts +102 -0
  112. package/src/core/base/index.mts +2 -0
  113. package/src/core/{class/observable.class.test.mts → base/observable-base.test.mts} +25 -17
  114. package/src/core/combine/combine.mts +51 -53
  115. package/src/core/combine/merge.mts +21 -23
  116. package/src/core/combine/zip.mts +54 -58
  117. package/src/core/create/counter.mts +42 -59
  118. package/src/core/create/from-abortable-promise.mts +38 -45
  119. package/src/core/create/from-promise.mts +25 -28
  120. package/src/core/create/from-subscribable.mts +23 -27
  121. package/src/core/create/just.mts +5 -4
  122. package/src/core/create/source.mts +11 -17
  123. package/src/core/create/timer.mts +32 -47
  124. package/src/core/index.mts +0 -1
  125. package/src/core/operators/audit.mts +43 -57
  126. package/src/core/operators/debounce.mts +35 -45
  127. package/src/core/operators/filter.mts +31 -39
  128. package/src/core/operators/map.mts +32 -40
  129. package/src/core/operators/merge-map.mts +44 -59
  130. package/src/core/operators/pairwise.mts +30 -33
  131. package/src/core/operators/scan.mts +24 -35
  132. package/src/core/operators/skip-if-no-change.mts +31 -36
  133. package/src/core/operators/skip-until.mts +32 -35
  134. package/src/core/operators/skip-while.mts +31 -40
  135. package/src/core/operators/switch-map.mts +36 -48
  136. package/src/core/operators/take-until.mts +31 -29
  137. package/src/core/operators/take-while.mts +33 -41
  138. package/src/core/operators/throttle.mts +37 -51
  139. package/src/core/operators/with-buffered-from.mts +35 -30
  140. package/src/core/operators/with-current-value-from.mts +23 -28
  141. package/src/core/operators/with-initial-value.mts +21 -22
  142. package/dist/core/class/child-observable-class.d.mts +0 -39
  143. package/dist/core/class/child-observable-class.d.mts.map +0 -1
  144. package/dist/core/class/child-observable-class.mjs +0 -173
  145. package/dist/core/class/child-observable-class.mjs.map +0 -1
  146. package/dist/core/class/index.d.mts +0 -4
  147. package/dist/core/class/index.d.mts.map +0 -1
  148. package/dist/core/class/index.mjs +0 -4
  149. package/dist/core/class/observable-base-class.d.mts +0 -28
  150. package/dist/core/class/observable-base-class.d.mts.map +0 -1
  151. package/dist/core/class/observable-base-class.mjs +0 -120
  152. package/dist/core/class/observable-base-class.mjs.map +0 -1
  153. package/dist/core/class/root-observable-class.d.mts +0 -14
  154. package/dist/core/class/root-observable-class.d.mts.map +0 -1
  155. package/dist/core/class/root-observable-class.mjs +0 -43
  156. package/dist/core/class/root-observable-class.mjs.map +0 -1
  157. package/src/core/class/child-observable-class.mts +0 -286
  158. package/src/core/class/circular-dependency.test.mts +0 -251
  159. package/src/core/class/index.mts +0 -3
  160. package/src/core/class/observable-base-class.mts +0 -187
  161. package/src/core/class/root-observable-class.mts +0 -73
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![npm version](https://img.shields.io/npm/v/synstate.svg)](https://www.npmjs.com/package/synstate)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/synstate.svg)](https://www.npmjs.com/package/synstate)
11
11
  [![License](https://img.shields.io/npm/l/synstate.svg)](https://github.com/noshiro-pf/mono/blob/main/libs/synstate/LICENSE)
12
- [![codecov](https://codecov.io/gh/noshiro-pf/mono/graph/badge.svg)](https://codecov.io/gh/noshiro-pf/mono)
12
+ [![codecov](https://codecov.io/gh/noshiro-pf/mono/graph/badge.svg?component=synstate)](https://codecov.io/gh/noshiro-pf/mono)
13
13
 
14
14
  </p>
15
15
 
@@ -24,7 +24,7 @@
24
24
  - 🎯 **Simple State Management**: Easy-to-use `createState` and `createReducer` similar to React useState/useReducer for global state
25
25
  - ⚡ **High Performance**: Optimized for fast state updates and minimal re-renders
26
26
  - 🎨 **Type-Safe**: Full TypeScript support with precise type inference
27
- - 🚀 **Lightweight**: <!-- bundle-size:synstate -->~4.4 kB min+gzip<!-- /bundle-size:synstate --> with only one external runtime dependency ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge))
27
+ - 🚀 **Lightweight**: <!-- bundle-size:synstate -->~4.6 kB min+gzip<!-- /bundle-size:synstate --> with only one external runtime dependency ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge))
28
28
  - 🌐 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
29
29
  - 🔄 **Reactive Updates**: Automatic propagation of state changes to all subscribers
30
30
  - 📡 **Event System**: Built-in `createValueEmitter`, `createEventEmitter` for event-driven architecture
@@ -0,0 +1,65 @@
1
+ import { type Optional, type Some } from 'ts-data-forge';
2
+ import { type AsyncChildObservable, type InitializedSyncChildObservable, type NonEmptyUnknownList, type SyncChildObservable, type UpdateToken, type Wrap } from '../types/index.mjs';
3
+ /**
4
+ * The internal tools a sync-child leaf factory (`map`, `filter`, `zip`, …)
5
+ * receives — the replacement for the `protected` methods a subclass of
6
+ * `SyncChildObservableClass` used to inherit.
7
+ */
8
+ export type SyncChildObservableTools<A> = Readonly<{
9
+ /** Emits `nextValue` under `updateToken` and notifies subscribers. */
10
+ setNext: (nextValue: A, updateToken: UpdateToken) => void;
11
+ getSnapshot: () => Optional<A>;
12
+ /** Completes this observable (leaf teardown included). */
13
+ complete: () => void;
14
+ }>;
15
+ /**
16
+ * The internal tools an async-child leaf factory (`debounce`, `switchMap`, …)
17
+ * receives. Async children additionally start their own update propagation.
18
+ */
19
+ export type AsyncChildObservableTools<A> = Readonly<{
20
+ /** Emits `nextValue` and propagates the update to all descendants. */
21
+ startUpdate: (nextValue: A) => void;
22
+ }> & SyncChildObservableTools<A>;
23
+ type ChildObservableConfigBase<P extends NonEmptyUnknownList> = Readonly<{
24
+ parents: Wrap<P>;
25
+ depth?: number;
26
+ /**
27
+ * Leaf teardown (clear timers, unsubscribe inner observables, …), run at
28
+ * the beginning of `complete()` — the closure replacement for
29
+ * `override complete() { teardown(); super.complete(); }`.
30
+ */
31
+ onComplete?: () => void;
32
+ }>;
33
+ export type ChildObservableConfig<A, P extends NonEmptyUnknownList> = ChildObservableConfigBase<P> & Readonly<{
34
+ initialValue: Optional<A>;
35
+ }>;
36
+ export type InitializedChildObservableConfig<A, P extends NonEmptyUnknownList> = ChildObservableConfigBase<P> & Readonly<{
37
+ initialValue: Some<A>;
38
+ }>;
39
+ /**
40
+ * Creates a sync-child observable — the classless replacement for extending
41
+ * `SyncChildObservableClass`.
42
+ *
43
+ * `createTryUpdate` receives the internal tools and returns the `tryUpdate`
44
+ * implementation (the former `override tryUpdate`). It runs during
45
+ * construction, before the observable is registered to its parents; leaf
46
+ * state can live either in the enclosing factory scope or inside the
47
+ * `createTryUpdate` closure.
48
+ */
49
+ export declare const createSyncChildObservable: <A, P extends NonEmptyUnknownList>({ parents, depth, initialValue, onComplete, }: ChildObservableConfig<A, P>, createTryUpdate: (tools: SyncChildObservableTools<A>) => (updateToken: UpdateToken) => void) => SyncChildObservable<A, P>;
50
+ /**
51
+ * Creates a sync-child observable that always holds a value.
52
+ *
53
+ * Runtime-wise this is exactly `createSyncChildObservable`; only the types of
54
+ * `getSnapshot` and `pipe` are narrowed, which is what the
55
+ * `InitializedSyncChildObservableClass` overrides existed for.
56
+ */
57
+ export declare const createInitializedSyncChildObservable: <A, P extends NonEmptyUnknownList>(config: InitializedChildObservableConfig<A, P>, createTryUpdate: (tools: SyncChildObservableTools<A>) => (updateToken: UpdateToken) => void) => InitializedSyncChildObservable<A, P>;
58
+ /**
59
+ * Creates an async-child observable — the classless replacement for extending
60
+ * `AsyncChildObservableClass`. See {@link createSyncChildObservable} for the
61
+ * `createTryUpdate` contract.
62
+ */
63
+ export declare const createAsyncChildObservable: <A, P extends NonEmptyUnknownList>({ parents, depth, initialValue, onComplete, }: ChildObservableConfig<A, P>, createTryUpdate: (tools: AsyncChildObservableTools<A>) => (updateToken: UpdateToken) => void) => AsyncChildObservable<A, P>;
64
+ export {};
65
+ //# sourceMappingURL=create-child-observable.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-child-observable.d.mts","sourceRoot":"","sources":["../../../src/core/base/create-child-observable.mts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAE9D,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,8BAA8B,EACnC,KAAK,mBAAmB,EAGxB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,IAAI,EACV,MAAM,oBAAoB,CAAC;AAQ5B;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI,QAAQ,CAAC;IACjD,sEAAsE;IACtE,OAAO,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;IAE1D,WAAW,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE/B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAClD,sEAAsE;IACtE,WAAW,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAC;CACrC,CAAC,GACA,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAE9B,KAAK,yBAAyB,CAAC,CAAC,SAAS,mBAAmB,IAAI,QAAQ,CAAC;IACvE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAEjB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,qBAAqB,CAC/B,CAAC,EACD,CAAC,SAAS,mBAAmB,IAC3B,yBAAyB,CAAC,CAAC,CAAC,GAC9B,QAAQ,CAAC;IACP,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEL,MAAM,MAAM,gCAAgC,CAC1C,CAAC,EACD,CAAC,SAAS,mBAAmB,IAC3B,yBAAyB,CAAC,CAAC,CAAC,GAC9B,QAAQ,CAAC;IACP,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GAAI,CAAC,EAAE,CAAC,SAAS,mBAAmB,iDAMrE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,mBACb,CACf,KAAK,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAC/B,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,KACtC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CA0C1B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAC/C,CAAC,EACD,CAAC,SAAS,mBAAmB,UAErB,gCAAgC,CAAC,CAAC,EAAE,CAAC,CAAC,mBAC7B,CACf,KAAK,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAC/B,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,KACtC,8BAA8B,CAAC,CAAC,EAAE,CAAC,CASrC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,EAAE,CAAC,SAAS,mBAAmB,iDAMtE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,mBACb,CACf,KAAK,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAChC,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,KACtC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CA6C3B,CAAC"}
@@ -0,0 +1,157 @@
1
+ import { Arr } from 'ts-data-forge';
2
+ import { isManagerObservable, isChildObservable } from '../types/observable.mjs';
3
+ import '../utils/id-maker.mjs';
4
+ import { maxDepth } from '../utils/max-depth.mjs';
5
+ import { createObservableBaseHandle, createManagerObservableParts, assembleObservable } from './create-observable-base.mjs';
6
+
7
+ const createSyncChildObservable = ({
8
+ parents,
9
+ depth = 1 + maxDepth(parents),
10
+ initialValue,
11
+ onComplete
12
+ }, createTryUpdate) => {
13
+ const handle = createObservableBaseHandle(initialValue);
14
+ const complete = () => {
15
+ onComplete?.();
16
+ handle.completeBase();
17
+ for (const par of parents) {
18
+ par.tryComplete();
19
+ }
20
+ };
21
+ const tryComplete = () => {
22
+ tryCompleteChild({
23
+ hasSubscriber: handle.hasSubscriber(),
24
+ hasActiveChild: handle.hasActiveChild(),
25
+ parents,
26
+ complete
27
+ });
28
+ };
29
+ const tryUpdate = createTryUpdate({
30
+ setNext: handle.setNext,
31
+ getSnapshot: handle.getSnapshot,
32
+ complete
33
+ });
34
+ const observable = assembleObservable({
35
+ kind: "sync child",
36
+ depth,
37
+ handle,
38
+ tryUpdate,
39
+ tryComplete,
40
+ complete,
41
+ extra: { parents }
42
+ });
43
+ registerChild(observable, parents);
44
+ return observable;
45
+ };
46
+ const createInitializedSyncChildObservable = (config, createTryUpdate) => {
47
+ const observable = createSyncChildObservable(config, createTryUpdate);
48
+ return (
49
+ // `getSnapshot` always returns `Some` here: `initialValue` is a `Some` and
50
+ // `setNext` only ever stores a `Some`.
51
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
52
+ observable
53
+ );
54
+ };
55
+ const createAsyncChildObservable = ({
56
+ parents,
57
+ depth = 1 + maxDepth(parents),
58
+ initialValue,
59
+ onComplete
60
+ }, createTryUpdate) => {
61
+ const handle = createObservableBaseHandle(initialValue);
62
+ const { addDescendant, startUpdate } = createManagerObservableParts(handle);
63
+ const complete = () => {
64
+ onComplete?.();
65
+ handle.completeBase();
66
+ for (const par of parents) {
67
+ par.tryComplete();
68
+ }
69
+ };
70
+ const tryComplete = () => {
71
+ tryCompleteChild({
72
+ hasSubscriber: handle.hasSubscriber(),
73
+ hasActiveChild: handle.hasActiveChild(),
74
+ parents,
75
+ complete
76
+ });
77
+ };
78
+ const tryUpdate = createTryUpdate({
79
+ setNext: handle.setNext,
80
+ getSnapshot: handle.getSnapshot,
81
+ complete,
82
+ startUpdate
83
+ });
84
+ const observable = assembleObservable({
85
+ kind: "async child",
86
+ depth,
87
+ handle,
88
+ tryUpdate,
89
+ tryComplete,
90
+ complete,
91
+ extra: { parents, addDescendant }
92
+ });
93
+ registerChild(observable, parents);
94
+ return observable;
95
+ };
96
+ const hasCircularDependencyFrom = (node, mut_visited, mut_inPath) => {
97
+ if (mut_inPath.has(node.id)) return true;
98
+ if (mut_visited.has(node.id)) return false;
99
+ mut_visited.add(node.id);
100
+ mut_inPath.add(node.id);
101
+ if (isChildObservable(node)) {
102
+ for (const parent of node.parents) {
103
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
104
+ return true;
105
+ }
106
+ }
107
+ }
108
+ mut_inPath.delete(node.id);
109
+ return false;
110
+ };
111
+ const detectCircularDependency = (child, parents) => {
112
+ const mut_visited = /* @__PURE__ */ new Set();
113
+ const mut_inPath = /* @__PURE__ */ new Set([child.id]);
114
+ for (const parent of parents) {
115
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
116
+ throw new Error(
117
+ "Circular dependency detected in observable graph: a child observable cannot be its own ancestor."
118
+ );
119
+ }
120
+ }
121
+ };
122
+ const registerChild = (child, parents) => {
123
+ detectCircularDependency(child, parents);
124
+ for (const p of parents) {
125
+ p.addChild(child);
126
+ }
127
+ const mut_rest = Array.from(parents);
128
+ while (Arr.isNonEmpty(mut_rest)) {
129
+ const p = mut_rest.pop();
130
+ if (p === void 0) break;
131
+ if (isManagerObservable(p)) {
132
+ p.addDescendant(child);
133
+ } else {
134
+ mut_rest.push(...p.parents);
135
+ }
136
+ }
137
+ };
138
+ const tryCompleteChild = ({
139
+ hasSubscriber,
140
+ hasActiveChild,
141
+ parents,
142
+ complete
143
+ }) => {
144
+ if (parents.every((r) => r.isCompleted)) {
145
+ complete();
146
+ return;
147
+ }
148
+ if (!hasSubscriber && !hasActiveChild) {
149
+ complete();
150
+ }
151
+ for (const par of parents) {
152
+ par.tryComplete();
153
+ }
154
+ };
155
+
156
+ export { createAsyncChildObservable, createInitializedSyncChildObservable, createSyncChildObservable };
157
+ //# sourceMappingURL=create-child-observable.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-child-observable.mjs","sources":["../../../src/core/base/create-child-observable.mts"],"sourcesContent":["import { Arr, type Optional, type Some } from 'ts-data-forge';\nimport { type MutableSet } from 'ts-type-forge';\nimport {\n isChildObservable,\n isManagerObservable,\n type AsyncChildObservable,\n type ChildObservable,\n type InitializedSyncChildObservable,\n type NonEmptyUnknownList,\n type Observable,\n type ObservableId,\n type SyncChildObservable,\n type UpdateToken,\n type Wrap,\n} from '../types/index.mjs';\nimport { maxDepth } from '../utils/index.mjs';\nimport {\n assembleObservable,\n createManagerObservableParts,\n createObservableBaseHandle,\n} from './create-observable-base.mjs';\n\n/**\n * The internal tools a sync-child leaf factory (`map`, `filter`, `zip`, …)\n * receives — the replacement for the `protected` methods a subclass of\n * `SyncChildObservableClass` used to inherit.\n */\nexport type SyncChildObservableTools<A> = Readonly<{\n /** Emits `nextValue` under `updateToken` and notifies subscribers. */\n setNext: (nextValue: A, updateToken: UpdateToken) => void;\n\n getSnapshot: () => Optional<A>;\n\n /** Completes this observable (leaf teardown included). */\n complete: () => void;\n}>;\n\n/**\n * The internal tools an async-child leaf factory (`debounce`, `switchMap`, …)\n * receives. Async children additionally start their own update propagation.\n */\nexport type AsyncChildObservableTools<A> = Readonly<{\n /** Emits `nextValue` and propagates the update to all descendants. */\n startUpdate: (nextValue: A) => void;\n}> &\n SyncChildObservableTools<A>;\n\ntype ChildObservableConfigBase<P extends NonEmptyUnknownList> = Readonly<{\n parents: Wrap<P>;\n\n depth?: number;\n\n /**\n * Leaf teardown (clear timers, unsubscribe inner observables, …), run at\n * the beginning of `complete()` — the closure replacement for\n * `override complete() { teardown(); super.complete(); }`.\n */\n onComplete?: () => void;\n}>;\n\nexport type ChildObservableConfig<\n A,\n P extends NonEmptyUnknownList,\n> = ChildObservableConfigBase<P> &\n Readonly<{\n initialValue: Optional<A>;\n }>;\n\nexport type InitializedChildObservableConfig<\n A,\n P extends NonEmptyUnknownList,\n> = ChildObservableConfigBase<P> &\n Readonly<{\n initialValue: Some<A>;\n }>;\n\n/**\n * Creates a sync-child observable — the classless replacement for extending\n * `SyncChildObservableClass`.\n *\n * `createTryUpdate` receives the internal tools and returns the `tryUpdate`\n * implementation (the former `override tryUpdate`). It runs during\n * construction, before the observable is registered to its parents; leaf\n * state can live either in the enclosing factory scope or inside the\n * `createTryUpdate` closure.\n */\nexport const createSyncChildObservable = <A, P extends NonEmptyUnknownList>(\n {\n parents,\n depth = 1 + maxDepth(parents),\n initialValue,\n onComplete,\n }: ChildObservableConfig<A, P>,\n createTryUpdate: (\n tools: SyncChildObservableTools<A>,\n ) => (updateToken: UpdateToken) => void,\n): SyncChildObservable<A, P> => {\n const handle = createObservableBaseHandle<A>(initialValue);\n\n const complete = (): void => {\n onComplete?.();\n\n handle.completeBase();\n\n // propagate to parents\n for (const par of parents) {\n par.tryComplete();\n }\n };\n\n const tryComplete = (): void => {\n tryCompleteChild({\n hasSubscriber: handle.hasSubscriber(),\n hasActiveChild: handle.hasActiveChild(),\n parents,\n complete,\n });\n };\n\n const tryUpdate = createTryUpdate({\n setNext: handle.setNext,\n getSnapshot: handle.getSnapshot,\n complete,\n });\n\n const observable: SyncChildObservable<A, P> = assembleObservable({\n kind: 'sync child',\n depth,\n handle,\n tryUpdate,\n tryComplete,\n complete,\n extra: { parents },\n });\n\n registerChild(observable, parents);\n\n return observable;\n};\n\n/**\n * Creates a sync-child observable that always holds a value.\n *\n * Runtime-wise this is exactly `createSyncChildObservable`; only the types of\n * `getSnapshot` and `pipe` are narrowed, which is what the\n * `InitializedSyncChildObservableClass` overrides existed for.\n */\nexport const createInitializedSyncChildObservable = <\n A,\n P extends NonEmptyUnknownList,\n>(\n config: InitializedChildObservableConfig<A, P>,\n createTryUpdate: (\n tools: SyncChildObservableTools<A>,\n ) => (updateToken: UpdateToken) => void,\n): InitializedSyncChildObservable<A, P> => {\n const observable = createSyncChildObservable<A, P>(config, createTryUpdate);\n\n return (\n // `getSnapshot` always returns `Some` here: `initialValue` is a `Some` and\n // `setNext` only ever stores a `Some`.\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n observable as InitializedSyncChildObservable<A, P>\n );\n};\n\n/**\n * Creates an async-child observable — the classless replacement for extending\n * `AsyncChildObservableClass`. See {@link createSyncChildObservable} for the\n * `createTryUpdate` contract.\n */\nexport const createAsyncChildObservable = <A, P extends NonEmptyUnknownList>(\n {\n parents,\n depth = 1 + maxDepth(parents),\n initialValue,\n onComplete,\n }: ChildObservableConfig<A, P>,\n createTryUpdate: (\n tools: AsyncChildObservableTools<A>,\n ) => (updateToken: UpdateToken) => void,\n): AsyncChildObservable<A, P> => {\n const handle = createObservableBaseHandle<A>(initialValue);\n\n const { addDescendant, startUpdate } = createManagerObservableParts(handle);\n\n const complete = (): void => {\n onComplete?.();\n\n handle.completeBase();\n\n // propagate to parents\n for (const par of parents) {\n par.tryComplete();\n }\n };\n\n const tryComplete = (): void => {\n tryCompleteChild({\n hasSubscriber: handle.hasSubscriber(),\n hasActiveChild: handle.hasActiveChild(),\n parents,\n complete,\n });\n };\n\n const tryUpdate = createTryUpdate({\n setNext: handle.setNext,\n getSnapshot: handle.getSnapshot,\n complete,\n startUpdate,\n });\n\n const observable: AsyncChildObservable<A, P> = assembleObservable({\n kind: 'async child',\n depth,\n handle,\n tryUpdate,\n tryComplete,\n complete,\n extra: { parents, addDescendant },\n });\n\n registerChild(observable, parents);\n\n return observable;\n};\n\n/* helpers */\n\n/**\n * Detects circular dependencies by walking the full ancestor chain of the\n * given parents and checking whether `child` already appears among them.\n *\n * @throws {Error} if a circular dependency is detected\n */\nconst hasCircularDependencyFrom = (\n node: Observable<unknown>,\n mut_visited: MutableSet<ObservableId>,\n mut_inPath: MutableSet<ObservableId>,\n): boolean => {\n if (mut_inPath.has(node.id)) return true;\n\n if (mut_visited.has(node.id)) return false;\n\n mut_visited.add(node.id);\n\n mut_inPath.add(node.id);\n\n if (isChildObservable(node)) {\n for (const parent of node.parents) {\n if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {\n return true;\n }\n }\n }\n\n mut_inPath.delete(node.id);\n\n return false;\n};\n\nconst detectCircularDependency = (\n child: ChildObservable<unknown>,\n parents: readonly Observable<unknown>[],\n): void => {\n const mut_visited = new Set<ObservableId>();\n\n const mut_inPath = new Set<ObservableId>([child.id]);\n\n for (const parent of parents) {\n if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {\n throw new Error(\n 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',\n );\n }\n }\n};\n\n/**\n * Registers `child` to its parents and to all reachable manager observables.\n * Runs after the child object is fully assembled, so parents only ever hold a\n * reference to the final composed object (the class version registered `this`\n * from inside the constructor instead).\n */\nconst registerChild = <A,>(\n child: ChildObservable<A>,\n parents: ChildObservable<A>['parents'],\n): void => {\n detectCircularDependency(child, parents);\n\n for (const p of parents) {\n p.addChild(child);\n }\n\n // register child to all reachable ManagerObservables\n const mut_rest = Array.from(parents);\n\n while (Arr.isNonEmpty(mut_rest)) {\n const p = mut_rest.pop();\n\n if (p === undefined) break;\n\n if (isManagerObservable(p)) {\n p.addDescendant(child);\n } else {\n // trace back dependency graph\n mut_rest.push(...p.parents);\n }\n }\n};\n\nconst tryCompleteChild = <A,>({\n hasSubscriber,\n hasActiveChild,\n parents,\n complete,\n}: Readonly<{\n hasSubscriber: boolean;\n hasActiveChild: boolean;\n parents: ChildObservable<A>['parents'];\n complete: () => void;\n}>): void => {\n // If there is no working parent node\n if (parents.every((r) => r.isCompleted)) {\n complete();\n\n return;\n }\n\n // If there are no active child node\n if (!hasSubscriber && !hasActiveChild) {\n complete();\n }\n\n // propagate to parents\n for (const par of parents) {\n par.tryComplete();\n }\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { type ReadonlyRecord } from 'ts-type-forge';
3
+ import { type ChildObservable, type ObservableBase, type ObservableId, type ObservableKind, type Subscription, type UpdateToken } from '../types/index.mjs';
4
+ /**
5
+ * The mutable core shared by every observable, expressed as closures instead of
6
+ * a base class.
7
+ *
8
+ * The returned handle is the "protected" surface of the former
9
+ * `ObservableBaseClass`: the layer factories (`createRootObservable`,
10
+ * `createSyncChildObservable`, `createAsyncChildObservable`) build the public
11
+ * object from it via {@link assembleObservable}, and leaf factories receive
12
+ * only the narrow tools they need. Neither the handle nor its type is exported
13
+ * from the package (`core/base/**` is excluded from index generation), so
14
+ * `setNext` and friends stay internal, as `protected` kept them before.
15
+ */
16
+ export type ObservableBaseHandle<A> = Readonly<{
17
+ id: ObservableId;
18
+ addChild: <B>(child: ChildObservable<B>) => void;
19
+ getSnapshot: () => Optional<A>;
20
+ isCompleted: () => boolean;
21
+ updateToken: () => UpdateToken;
22
+ hasSubscriber: () => boolean;
23
+ hasChild: () => boolean;
24
+ hasActiveChild: () => boolean;
25
+ subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;
26
+ /** Stores `nextValue` as the current value and notifies subscribers. */
27
+ setNext: (nextValue: A, updateToken: UpdateToken) => void;
28
+ /**
29
+ * The innermost part of `complete()` (what `ObservableBaseClass.complete`
30
+ * used to do): marks this observable completed exactly once, notifies and
31
+ * removes all subscribers, and propagates `tryComplete` to children. Layer
32
+ * factories wrap this with leaf teardown and propagation to parents.
33
+ */
34
+ completeBase: () => void;
35
+ }>;
36
+ export declare const createObservableBaseHandle: <A>(initialValue: Optional<A>) => ObservableBaseHandle<A>;
37
+ /**
38
+ * The extra members every manager observable — a root or an async child — owns:
39
+ * the depth-ordered descendant list and the update propagation that walks it.
40
+ */
41
+ export type ManagerObservableParts<A> = Readonly<{
42
+ addDescendant: <B>(child: ChildObservable<B>) => void;
43
+ /** Emits `nextValue` and propagates the update to all descendants. */
44
+ startUpdate: (nextValue: A) => void;
45
+ }>;
46
+ /**
47
+ * Builds the manager half of a root or async-child observable. Both start their
48
+ * own update propagation, so both keep a descendant set and a depth-ordered
49
+ * propagation list; this is the single implementation of that pair (the class
50
+ * version duplicated it across `RootObservableClass` and
51
+ * `AsyncChildObservableClass`).
52
+ */
53
+ export declare const createManagerObservableParts: <A>(handle: ObservableBaseHandle<A>) => ManagerObservableParts<A>;
54
+ /** Default `tryUpdate` for observables that never receive parent updates. */
55
+ export declare const createTryUpdateNotImplemented: () => ((_updateToken: UpdateToken) => void);
56
+ type AssembleObservableArgs<A, Kind extends ObservableKind, Extra extends ReadonlyRecord<string, unknown>> = Readonly<{
57
+ kind: Kind;
58
+ depth: number;
59
+ handle: ObservableBaseHandle<A>;
60
+ tryUpdate: (updateToken: UpdateToken) => void;
61
+ tryComplete: () => void;
62
+ complete: () => void;
63
+ /**
64
+ * Kind-specific public members (`parents`, `addDescendant`) and leaf
65
+ * extensions (`next`, `start`). They are merged here, in the one place the
66
+ * final object literal is created, because the public getters below must not
67
+ * be copied by a spread (spreading would freeze their current values).
68
+ */
69
+ extra: Extra;
70
+ }>;
71
+ /**
72
+ * Builds the public observable object from a base handle. This replaces the
73
+ * implicit assembly a `class` performs: state stays in the handle's closures,
74
+ * `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become getters
75
+ * delegating to the handle, and the kind-specific behavior (`tryUpdate`,
76
+ * `tryComplete`, `complete`) is passed in already composed.
77
+ */
78
+ export declare const assembleObservable: <A, Kind extends ObservableKind, Extra extends ReadonlyRecord<string, unknown>>({ kind, depth, handle, tryUpdate, tryComplete, complete, extra, }: AssembleObservableArgs<A, Kind, Extra>) => Extra & ObservableBase<A> & Readonly<{
79
+ kind: Kind;
80
+ }>;
81
+ export {};
82
+ //# sourceMappingURL=create-observable-base.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-observable-base.d.mts","sourceRoot":"","sources":["../../../src/core/base/create-observable-base.mts"],"names":[],"mappings":"AAAA,OAAO,EAAO,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,eAAe,EAGpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EAInB,KAAK,YAAY,EACjB,KAAK,WAAW,EAEjB,MAAM,oBAAoB,CAAC;AAS5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC7C,EAAE,EAAE,YAAY,CAAC;IAEjB,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAEjD,WAAW,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE/B,WAAW,EAAE,MAAM,OAAO,CAAC;IAE3B,WAAW,EAAE,MAAM,WAAW,CAAC;IAE/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAE7B,QAAQ,EAAE,MAAM,OAAO,CAAC;IAExB,cAAc,EAAE,MAAM,OAAO,CAAC;IAE9B,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,KAAK,YAAY,CAAC;IAE7E,wEAAwE;IACxE,OAAO,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;IAE1D;;;;;OAKG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC,CAAC;AAEH,eAAO,MAAM,0BAA0B,GAAI,CAAC,gBAC5B,QAAQ,CAAC,CAAC,CAAC,KACxB,oBAAoB,CAAC,CAAC,CAwHxB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC/C,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAEtD,sEAAsE;IACtE,WAAW,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,CAAC,UACpC,oBAAoB,CAAC,CAAC,CAAC,KAC9B,sBAAsB,CAAC,CAAC,CAiC1B,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B,QAAO,CAAC,CAChD,YAAY,EAAE,WAAW,KACtB,IAAI,CAA4B,CAAC;AAMtC,KAAK,sBAAsB,CACzB,CAAC,EACD,IAAI,SAAS,cAAc,EAC3B,KAAK,SAAS,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,IAC3C,QAAQ,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAChC,SAAS,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;IAC9C,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;;;OAKG;IACH,KAAK,EAAE,KAAK,CAAC;CACd,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,CAAC,EACD,IAAI,SAAS,cAAc,EAC3B,KAAK,SAAS,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,qEAS5C,sBAAsB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAG,KAAK,GAC/C,cAAc,CAAC,CAAC,CAAC,GACjB,QAAQ,CAAC;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAyDxB,CAAC"}
@@ -0,0 +1,160 @@
1
+ import { Arr, Optional } from 'ts-data-forge';
2
+ import { issueObservableId, issueUpdateToken, issueSubscriberId } from '../utils/id-maker.mjs';
3
+ import { toSubscriber } from '../utils/observable-utils.mjs';
4
+ import { binarySearch } from '../utils/utils.mjs';
5
+
6
+ const createObservableBaseHandle = (initialValue) => {
7
+ const id = issueObservableId();
8
+ let mut_children = [];
9
+ const mut_subscribers = /* @__PURE__ */ new Map();
10
+ let mut_currentValue = initialValue;
11
+ let mut_isCompleted = false;
12
+ let mut_updateToken = issueUpdateToken();
13
+ const addSubscriber = (s) => {
14
+ const subscriberId = issueSubscriberId();
15
+ mut_subscribers.set(subscriberId, s);
16
+ return subscriberId;
17
+ };
18
+ const removeSubscriber = (subscriberId) => {
19
+ mut_subscribers.delete(subscriberId);
20
+ };
21
+ const addChild = (child) => {
22
+ mut_children = Arr.toPushed(
23
+ mut_children,
24
+ child
25
+ );
26
+ };
27
+ const getSnapshot = () => mut_currentValue;
28
+ const isCompleted = () => mut_isCompleted;
29
+ const updateToken = () => mut_updateToken;
30
+ const hasSubscriber = () => mut_subscribers.size > 0;
31
+ const hasChild = () => Arr.isNonEmpty(mut_children);
32
+ const hasActiveChild = () => mut_children.some((c) => !c.isCompleted);
33
+ const setNext = (nextValue, nextUpdateToken) => {
34
+ mut_updateToken = nextUpdateToken;
35
+ mut_currentValue = Optional.some(nextValue);
36
+ for (const s of mut_subscribers.values()) {
37
+ s.onNext(nextValue);
38
+ }
39
+ };
40
+ const completeBase = () => {
41
+ if (mut_isCompleted) return;
42
+ mut_isCompleted = true;
43
+ for (const s of mut_subscribers.values()) {
44
+ s.onComplete();
45
+ }
46
+ mut_subscribers.clear();
47
+ for (const o of mut_children) {
48
+ o.tryComplete();
49
+ }
50
+ };
51
+ const subscribe = (onNext, onComplete) => {
52
+ const curr = getSnapshot();
53
+ if (Optional.isSome(curr)) {
54
+ onNext(curr.value);
55
+ }
56
+ if (mut_isCompleted) {
57
+ if (onComplete !== void 0) {
58
+ onComplete();
59
+ }
60
+ return { unsubscribe: () => {
61
+ } };
62
+ }
63
+ const subscriberId = addSubscriber(
64
+ toSubscriber(onNext, onComplete)
65
+ );
66
+ return {
67
+ unsubscribe: () => {
68
+ removeSubscriber(subscriberId);
69
+ }
70
+ };
71
+ };
72
+ return {
73
+ id,
74
+ addChild,
75
+ getSnapshot,
76
+ isCompleted,
77
+ updateToken,
78
+ hasSubscriber,
79
+ hasChild,
80
+ hasActiveChild,
81
+ subscribe,
82
+ setNext,
83
+ completeBase
84
+ };
85
+ };
86
+ const createManagerObservableParts = (handle) => {
87
+ let mut_propagationOrder = [];
88
+ const mut_descendantsIdSet = /* @__PURE__ */ new Set();
89
+ const addDescendant = (child) => {
90
+ if (mut_descendantsIdSet.has(child.id)) return;
91
+ mut_descendantsIdSet.add(child.id);
92
+ const insertPos = binarySearch(
93
+ mut_propagationOrder.map((a) => a.depth),
94
+ child.depth
95
+ );
96
+ mut_propagationOrder = Arr.toInserted(
97
+ mut_propagationOrder,
98
+ insertPos,
99
+ child
100
+ );
101
+ };
102
+ const startUpdate = (nextValue) => {
103
+ const updateToken = issueUpdateToken();
104
+ handle.setNext(nextValue, updateToken);
105
+ for (const p of mut_propagationOrder) {
106
+ p.tryUpdate(updateToken);
107
+ }
108
+ };
109
+ return { addDescendant, startUpdate };
110
+ };
111
+ const createTryUpdateNotImplemented = () => tryUpdateNotImplemented;
112
+ const tryUpdateNotImplemented = (_updateToken) => {
113
+ throw new Error("not implemented");
114
+ };
115
+ const assembleObservable = ({
116
+ kind,
117
+ depth,
118
+ handle,
119
+ tryUpdate,
120
+ tryComplete,
121
+ complete,
122
+ extra
123
+ }) => {
124
+ function pipe(operator) {
125
+ return operator(
126
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
127
+ observable
128
+ );
129
+ }
130
+ const observable = {
131
+ ...extra,
132
+ id: handle.id,
133
+ kind,
134
+ depth,
135
+ addChild: handle.addChild,
136
+ getSnapshot: handle.getSnapshot,
137
+ get isCompleted() {
138
+ return handle.isCompleted();
139
+ },
140
+ get updateToken() {
141
+ return handle.updateToken();
142
+ },
143
+ get hasSubscriber() {
144
+ return handle.hasSubscriber();
145
+ },
146
+ get hasChild() {
147
+ return handle.hasChild();
148
+ },
149
+ hasActiveChild: handle.hasActiveChild,
150
+ tryUpdate,
151
+ tryComplete,
152
+ complete,
153
+ subscribe: handle.subscribe,
154
+ pipe
155
+ };
156
+ return observable;
157
+ };
158
+
159
+ export { assembleObservable, createManagerObservableParts, createObservableBaseHandle, createTryUpdateNotImplemented };
160
+ //# sourceMappingURL=create-observable-base.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-observable-base.mjs","sources":["../../../src/core/base/create-observable-base.mts"],"sourcesContent":["import { Arr, Optional } from 'ts-data-forge';\nimport {\n type MutableMap,\n type MutableSet,\n type ReadonlyRecord,\n} from 'ts-type-forge';\nimport {\n type ChildObservable,\n type InitializedObservable,\n type Observable,\n type ObservableBase,\n type ObservableId,\n type ObservableKind,\n type Operator,\n type Subscriber,\n type SubscriberId,\n type Subscription,\n type UpdateToken,\n type WithInitialValueOperator,\n} from '../types/index.mjs';\nimport {\n binarySearch,\n issueObservableId,\n issueSubscriberId,\n issueUpdateToken,\n toSubscriber,\n} from '../utils/index.mjs';\n\n/**\n * The mutable core shared by every observable, expressed as closures instead of\n * a base class.\n *\n * The returned handle is the \"protected\" surface of the former\n * `ObservableBaseClass`: the layer factories (`createRootObservable`,\n * `createSyncChildObservable`, `createAsyncChildObservable`) build the public\n * object from it via {@link assembleObservable}, and leaf factories receive\n * only the narrow tools they need. Neither the handle nor its type is exported\n * from the package (`core/base/**` is excluded from index generation), so\n * `setNext` and friends stay internal, as `protected` kept them before.\n */\nexport type ObservableBaseHandle<A> = Readonly<{\n id: ObservableId;\n\n addChild: <B>(child: ChildObservable<B>) => void;\n\n getSnapshot: () => Optional<A>;\n\n isCompleted: () => boolean;\n\n updateToken: () => UpdateToken;\n\n hasSubscriber: () => boolean;\n\n hasChild: () => boolean;\n\n hasActiveChild: () => boolean;\n\n subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;\n\n /** Stores `nextValue` as the current value and notifies subscribers. */\n setNext: (nextValue: A, updateToken: UpdateToken) => void;\n\n /**\n * The innermost part of `complete()` (what `ObservableBaseClass.complete`\n * used to do): marks this observable completed exactly once, notifies and\n * removes all subscribers, and propagates `tryComplete` to children. Layer\n * factories wrap this with leaf teardown and propagation to parents.\n */\n completeBase: () => void;\n}>;\n\nexport const createObservableBaseHandle = <A,>(\n initialValue: Optional<A>,\n): ObservableBaseHandle<A> => {\n const id = issueObservableId();\n\n let mut_children: readonly ChildObservable<unknown>[] = [];\n\n const mut_subscribers: MutableMap<SubscriberId, Subscriber<A>> = new Map();\n\n let mut_currentValue: Optional<A> = initialValue;\n\n let mut_isCompleted = false;\n\n let mut_updateToken: UpdateToken = issueUpdateToken();\n\n const addSubscriber = (s: Subscriber<A>): SubscriberId => {\n // return the id of added subscriber\n const subscriberId = issueSubscriberId();\n\n mut_subscribers.set(subscriberId, s);\n\n return subscriberId;\n };\n\n const removeSubscriber = (subscriberId: SubscriberId): void => {\n mut_subscribers.delete(subscriberId);\n };\n\n const addChild = <B,>(child: ChildObservable<B>): void => {\n mut_children = Arr.toPushed(\n mut_children,\n\n child as ChildObservable<unknown>,\n );\n };\n\n const getSnapshot = (): Optional<A> => mut_currentValue;\n\n const isCompleted = (): boolean => mut_isCompleted;\n\n const updateToken = (): UpdateToken => mut_updateToken;\n\n const hasSubscriber = (): boolean => mut_subscribers.size > 0;\n\n const hasChild = (): boolean => Arr.isNonEmpty(mut_children);\n\n const hasActiveChild = (): boolean =>\n mut_children.some((c) => !c.isCompleted);\n\n const setNext = (nextValue: A, nextUpdateToken: UpdateToken): void => {\n mut_updateToken = nextUpdateToken;\n\n mut_currentValue = Optional.some(nextValue);\n\n for (const s of mut_subscribers.values()) {\n s.onNext(nextValue);\n }\n };\n\n const completeBase = (): void => {\n if (mut_isCompleted) return; // terminate only once\n\n // change state\n mut_isCompleted = true;\n\n // run subscribers for the current value\n for (const s of mut_subscribers.values()) {\n s.onComplete();\n }\n\n // remove all subscribers\n mut_subscribers.clear();\n\n // propagate to children\n for (const o of mut_children) {\n o.tryComplete();\n }\n };\n\n const subscribe = (\n onNext: (v: A) => void,\n onComplete?: () => void,\n ): Subscription => {\n // first emit\n const curr = getSnapshot();\n\n if (Optional.isSome(curr)) {\n onNext(curr.value);\n }\n\n if (mut_isCompleted) {\n if (onComplete !== undefined) {\n onComplete();\n }\n\n return { unsubscribe: () => {} };\n }\n\n const subscriberId: SubscriberId = addSubscriber(\n toSubscriber(onNext, onComplete),\n );\n\n return {\n unsubscribe: () => {\n removeSubscriber(subscriberId);\n },\n };\n };\n\n return {\n id,\n addChild,\n getSnapshot,\n isCompleted,\n updateToken,\n hasSubscriber,\n hasChild,\n hasActiveChild,\n subscribe,\n setNext,\n completeBase,\n };\n};\n\n/**\n * The extra members every manager observable — a root or an async child — owns:\n * the depth-ordered descendant list and the update propagation that walks it.\n */\nexport type ManagerObservableParts<A> = Readonly<{\n addDescendant: <B>(child: ChildObservable<B>) => void;\n\n /** Emits `nextValue` and propagates the update to all descendants. */\n startUpdate: (nextValue: A) => void;\n}>;\n\n/**\n * Builds the manager half of a root or async-child observable. Both start their\n * own update propagation, so both keep a descendant set and a depth-ordered\n * propagation list; this is the single implementation of that pair (the class\n * version duplicated it across `RootObservableClass` and\n * `AsyncChildObservableClass`).\n */\nexport const createManagerObservableParts = <A,>(\n handle: ObservableBaseHandle<A>,\n): ManagerObservableParts<A> => {\n let mut_propagationOrder: readonly ChildObservable<unknown>[] = [];\n\n const mut_descendantsIdSet: MutableSet<ObservableId> = new Set();\n\n const addDescendant = <B,>(child: ChildObservable<B>): void => {\n if (mut_descendantsIdSet.has(child.id)) return;\n\n mut_descendantsIdSet.add(child.id);\n\n const insertPos = binarySearch(\n mut_propagationOrder.map((a) => a.depth),\n child.depth,\n );\n\n mut_propagationOrder = Arr.toInserted(\n mut_propagationOrder,\n insertPos,\n child,\n );\n };\n\n const startUpdate = (nextValue: A): void => {\n const updateToken = issueUpdateToken();\n\n handle.setNext(nextValue, updateToken);\n\n for (const p of mut_propagationOrder) {\n p.tryUpdate(updateToken);\n }\n };\n\n return { addDescendant, startUpdate };\n};\n\n/** Default `tryUpdate` for observables that never receive parent updates. */\nexport const createTryUpdateNotImplemented = (): ((\n _updateToken: UpdateToken,\n) => void) => tryUpdateNotImplemented;\n\nconst tryUpdateNotImplemented = (_updateToken: UpdateToken): void => {\n throw new Error('not implemented');\n};\n\ntype AssembleObservableArgs<\n A,\n Kind extends ObservableKind,\n Extra extends ReadonlyRecord<string, unknown>,\n> = Readonly<{\n kind: Kind;\n depth: number;\n handle: ObservableBaseHandle<A>;\n tryUpdate: (updateToken: UpdateToken) => void;\n tryComplete: () => void;\n complete: () => void;\n /**\n * Kind-specific public members (`parents`, `addDescendant`) and leaf\n * extensions (`next`, `start`). They are merged here, in the one place the\n * final object literal is created, because the public getters below must not\n * be copied by a spread (spreading would freeze their current values).\n */\n extra: Extra;\n}>;\n\n/**\n * Builds the public observable object from a base handle. This replaces the\n * implicit assembly a `class` performs: state stays in the handle's closures,\n * `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become getters\n * delegating to the handle, and the kind-specific behavior (`tryUpdate`,\n * `tryComplete`, `complete`) is passed in already composed.\n */\nexport const assembleObservable = <\n A,\n Kind extends ObservableKind,\n Extra extends ReadonlyRecord<string, unknown>,\n>({\n kind,\n depth,\n handle,\n tryUpdate,\n tryComplete,\n complete,\n extra,\n}: AssembleObservableArgs<A, Kind, Extra>): Extra &\n ObservableBase<A> &\n Readonly<{ kind: Kind }> => {\n function pipe<B>(\n operator: WithInitialValueOperator<A, B>,\n ): InitializedObservable<B>;\n\n function pipe<B>(operator: Operator<A, B>): Observable<B>;\n\n function pipe<B>(operator: Operator<A, B>): Observable<B> {\n return operator(\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n observable as unknown as InitializedObservable<A>,\n );\n }\n\n const observable = {\n ...extra,\n\n id: handle.id,\n\n kind,\n\n depth,\n\n addChild: handle.addChild,\n\n getSnapshot: handle.getSnapshot,\n\n get isCompleted(): boolean {\n return handle.isCompleted();\n },\n\n get updateToken(): UpdateToken {\n return handle.updateToken();\n },\n\n get hasSubscriber(): boolean {\n return handle.hasSubscriber();\n },\n\n get hasChild(): boolean {\n return handle.hasChild();\n },\n\n hasActiveChild: handle.hasActiveChild,\n\n tryUpdate,\n\n tryComplete,\n\n complete,\n\n subscribe: handle.subscribe,\n\n pipe,\n } as const;\n\n return observable;\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { type ReadonlyRecord } from 'ts-type-forge';
3
+ import { type RootObservable } from '../types/index.mjs';
4
+ /**
5
+ * The internal tools a root leaf factory (`source`, `timer`, `fromPromise`, …)
6
+ * receives — the replacement for the `protected` methods a subclass of
7
+ * `RootObservableClass` used to inherit.
8
+ */
9
+ export type RootObservableTools<A> = Readonly<{
10
+ /** Emits `nextValue` and propagates the update to all descendants. */
11
+ startUpdate: (nextValue: A) => void;
12
+ isCompleted: () => boolean;
13
+ /** Completes this observable (leaf teardown included). */
14
+ complete: () => void;
15
+ }>;
16
+ export type RootObservableConfig<A> = Readonly<{
17
+ initialValue: Optional<A>;
18
+ /**
19
+ * Leaf teardown (clear timers, abort requests, …), run at the beginning of
20
+ * `complete()` — the closure replacement for
21
+ * `override complete() { teardown(); super.complete(); }`.
22
+ */
23
+ onComplete?: () => void;
24
+ }>;
25
+ /**
26
+ * Creates a root observable — the classless replacement for extending
27
+ * `RootObservableClass`.
28
+ *
29
+ * The optional `init` callback receives the internal tools and returns extra
30
+ * public members to merge into the observable (e.g. `next` for `source`,
31
+ * `start` for `timer`). `init` runs during construction; it may register
32
+ * asynchronous callbacks that use the tools, and may call `complete`
33
+ * synchronously, but the observable object itself does not exist yet at that
34
+ * point.
35
+ */
36
+ export declare const createRootObservable: <A, Extension extends ReadonlyRecord<string, unknown> = ReadonlyRecord<never, never>>({ initialValue, onComplete }: RootObservableConfig<A>, init: (tools: RootObservableTools<A>) => Extension) => Extension & RootObservable<A>;
37
+ //# sourceMappingURL=create-root-observable.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-root-observable.d.mts","sourceRoot":"","sources":["../../../src/core/base/create-root-observable.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ3E;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC5C,sEAAsE;IACtE,WAAW,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAC;IAEpC,WAAW,EAAE,MAAM,OAAO,CAAC;IAE3B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC7C,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAC/B,CAAC,EACD,SAAS,SAAS,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAChE,KAAK,EACL,KAAK,CACN,gCAE6B,oBAAoB,CAAC,CAAC,CAAC,QAC/C,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,SAAS,KACjD,SAAS,GAAG,cAAc,CAAC,CAAC,CAmC9B,CAAC"}
@@ -0,0 +1,36 @@
1
+ import 'ts-data-forge';
2
+ import { createObservableBaseHandle, createManagerObservableParts, assembleObservable, createTryUpdateNotImplemented } from './create-observable-base.mjs';
3
+
4
+ const createRootObservable = ({ initialValue, onComplete }, init) => {
5
+ const handle = createObservableBaseHandle(initialValue);
6
+ const { addDescendant, startUpdate } = createManagerObservableParts(handle);
7
+ const complete = () => {
8
+ onComplete?.();
9
+ handle.completeBase();
10
+ };
11
+ const tryComplete = () => {
12
+ if (!handle.hasSubscriber() && !handle.hasActiveChild()) {
13
+ complete();
14
+ }
15
+ };
16
+ const extension = init({
17
+ startUpdate,
18
+ isCompleted: handle.isCompleted,
19
+ complete
20
+ });
21
+ return assembleObservable({
22
+ kind: "root",
23
+ depth: 0,
24
+ handle,
25
+ tryUpdate: createTryUpdateNotImplemented,
26
+ tryComplete,
27
+ complete,
28
+ extra: {
29
+ ...extension,
30
+ addDescendant
31
+ }
32
+ });
33
+ };
34
+
35
+ export { createRootObservable };
36
+ //# sourceMappingURL=create-root-observable.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-root-observable.mjs","sources":["../../../src/core/base/create-root-observable.mts"],"sourcesContent":["import { Optional } from 'ts-data-forge';\nimport { type ReadonlyRecord } from 'ts-type-forge';\nimport { isRootObservable, type RootObservable } from '../types/index.mjs';\nimport {\n assembleObservable,\n createManagerObservableParts,\n createObservableBaseHandle,\n createTryUpdateNotImplemented,\n} from './create-observable-base.mjs';\n\n/**\n * The internal tools a root leaf factory (`source`, `timer`, `fromPromise`, …)\n * receives — the replacement for the `protected` methods a subclass of\n * `RootObservableClass` used to inherit.\n */\nexport type RootObservableTools<A> = Readonly<{\n /** Emits `nextValue` and propagates the update to all descendants. */\n startUpdate: (nextValue: A) => void;\n\n isCompleted: () => boolean;\n\n /** Completes this observable (leaf teardown included). */\n complete: () => void;\n}>;\n\nexport type RootObservableConfig<A> = Readonly<{\n initialValue: Optional<A>;\n\n /**\n * Leaf teardown (clear timers, abort requests, …), run at the beginning of\n * `complete()` — the closure replacement for\n * `override complete() { teardown(); super.complete(); }`.\n */\n onComplete?: () => void;\n}>;\n\n/**\n * Creates a root observable — the classless replacement for extending\n * `RootObservableClass`.\n *\n * The optional `init` callback receives the internal tools and returns extra\n * public members to merge into the observable (e.g. `next` for `source`,\n * `start` for `timer`). `init` runs during construction; it may register\n * asynchronous callbacks that use the tools, and may call `complete`\n * synchronously, but the observable object itself does not exist yet at that\n * point.\n */\nexport const createRootObservable = <\n A,\n Extension extends ReadonlyRecord<string, unknown> = ReadonlyRecord<\n never,\n never\n >,\n>(\n { initialValue, onComplete }: RootObservableConfig<A>,\n init: (tools: RootObservableTools<A>) => Extension,\n): Extension & RootObservable<A> => {\n const handle = createObservableBaseHandle<A>(initialValue);\n\n const { addDescendant, startUpdate } = createManagerObservableParts(handle);\n\n const complete = (): void => {\n onComplete?.();\n\n handle.completeBase();\n };\n\n const tryComplete = (): void => {\n if (!handle.hasSubscriber() && !handle.hasActiveChild()) {\n complete();\n }\n };\n\n const extension: Extension = init({\n startUpdate,\n isCompleted: handle.isCompleted,\n complete,\n });\n\n return assembleObservable({\n kind: 'root',\n depth: 0,\n handle,\n tryUpdate: createTryUpdateNotImplemented,\n tryComplete,\n complete,\n extra: {\n ...extension,\n addDescendant,\n },\n });\n};\n\nif (import.meta.vitest !== undefined) {\n test('isRootObservable', () => {\n assert.isTrue(\n isRootObservable(\n createRootObservable({ initialValue: Optional.some(0) }, () => ({})),\n ),\n );\n });\n}\n"],"names":[],"mappings":";;;AA+CO,MAAM,uBAAuB,CAOlC,EAAE,YAAA,EAAc,UAAA,IAChB,IAAA,KACkC;AAClC,EAAA,MAAM,MAAA,GAAS,2BAA8B,YAAY,CAAA;AAEzD,EAAA,MAAM,EAAE,aAAA,EAAe,WAAA,EAAY,GAAI,6BAA6B,MAAM,CAAA;AAE1E,EAAA,MAAM,WAAW,MAAY;AAC3B,IAAA,UAAA,IAAa;AAEb,IAAA,MAAA,CAAO,YAAA,EAAa;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,cAAc,MAAY;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,aAAA,MAAmB,CAAC,MAAA,CAAO,gBAAe,EAAG;AACvD,MAAA,QAAA,EAAS;AAAA,IACX;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAuB,IAAA,CAAK;AAAA,IAChC,WAAA;AAAA,IACA,aAAa,MAAA,CAAO,WAAA;AAAA,IACpB;AAAA,GACD,CAAA;AAED,EAAA,OAAO,kBAAA,CAAmB;AAAA,IACxB,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA;AAAA,IACA,SAAA,EAAW,6BAAA;AAAA,IACX,WAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA,EAAO;AAAA,MACL,GAAG,SAAA;AAAA,MACH;AAAA;AACF,GACD,CAAA;AACH;;;;"}
@@ -0,0 +1,3 @@
1
+ export * from './create-child-observable.mjs';
2
+ export * from './create-root-observable.mjs';
3
+ //# sourceMappingURL=index.d.mts.map