@okikio/observables 1.0.2

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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +578 -0
  3. package/esm/_dnt.polyfills.d.ts +20 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +12 -0
  6. package/esm/_spec.d.ts +260 -0
  7. package/esm/_spec.d.ts.map +1 -0
  8. package/esm/_spec.js +1 -0
  9. package/esm/_types.d.ts +141 -0
  10. package/esm/_types.d.ts.map +1 -0
  11. package/esm/_types.js +20 -0
  12. package/esm/error.d.ts +331 -0
  13. package/esm/error.d.ts.map +1 -0
  14. package/esm/error.js +408 -0
  15. package/esm/events.d.ts +320 -0
  16. package/esm/events.d.ts.map +1 -0
  17. package/esm/events.js +451 -0
  18. package/esm/helpers/_types.d.ts +188 -0
  19. package/esm/helpers/_types.d.ts.map +1 -0
  20. package/esm/helpers/_types.js +1 -0
  21. package/esm/helpers/mod.d.ts +90 -0
  22. package/esm/helpers/mod.d.ts.map +1 -0
  23. package/esm/helpers/mod.js +90 -0
  24. package/esm/helpers/operations/batch.d.ts +109 -0
  25. package/esm/helpers/operations/batch.d.ts.map +1 -0
  26. package/esm/helpers/operations/batch.js +140 -0
  27. package/esm/helpers/operations/combination.d.ts +162 -0
  28. package/esm/helpers/operations/combination.d.ts.map +1 -0
  29. package/esm/helpers/operations/combination.js +350 -0
  30. package/esm/helpers/operations/conditional.d.ts +211 -0
  31. package/esm/helpers/operations/conditional.d.ts.map +1 -0
  32. package/esm/helpers/operations/conditional.js +280 -0
  33. package/esm/helpers/operations/core.d.ts +198 -0
  34. package/esm/helpers/operations/core.d.ts.map +1 -0
  35. package/esm/helpers/operations/core.js +264 -0
  36. package/esm/helpers/operations/errors.d.ts +277 -0
  37. package/esm/helpers/operations/errors.d.ts.map +1 -0
  38. package/esm/helpers/operations/errors.js +378 -0
  39. package/esm/helpers/operations/mod.d.ts +26 -0
  40. package/esm/helpers/operations/mod.d.ts.map +1 -0
  41. package/esm/helpers/operations/mod.js +25 -0
  42. package/esm/helpers/operations/timing.d.ts +206 -0
  43. package/esm/helpers/operations/timing.d.ts.map +1 -0
  44. package/esm/helpers/operations/timing.js +457 -0
  45. package/esm/helpers/operators.d.ts +520 -0
  46. package/esm/helpers/operators.d.ts.map +1 -0
  47. package/esm/helpers/operators.js +563 -0
  48. package/esm/helpers/pipe.d.ts +118 -0
  49. package/esm/helpers/pipe.d.ts.map +1 -0
  50. package/esm/helpers/pipe.js +129 -0
  51. package/esm/helpers/utils.d.ts +142 -0
  52. package/esm/helpers/utils.d.ts.map +1 -0
  53. package/esm/helpers/utils.js +193 -0
  54. package/esm/mod.d.ts +863 -0
  55. package/esm/mod.d.ts.map +1 -0
  56. package/esm/mod.js +861 -0
  57. package/esm/observable.d.ts +1610 -0
  58. package/esm/observable.d.ts.map +1 -0
  59. package/esm/observable.js +1970 -0
  60. package/esm/package.json +3 -0
  61. package/esm/queue.d.ts +201 -0
  62. package/esm/queue.d.ts.map +1 -0
  63. package/esm/queue.js +273 -0
  64. package/esm/symbol.d.ts +60 -0
  65. package/esm/symbol.d.ts.map +1 -0
  66. package/esm/symbol.js +132 -0
  67. package/package.json +96 -0
  68. package/script/_dnt.polyfills.d.ts +20 -0
  69. package/script/_dnt.polyfills.d.ts.map +1 -0
  70. package/script/_dnt.polyfills.js +13 -0
  71. package/script/_spec.d.ts +260 -0
  72. package/script/_spec.d.ts.map +1 -0
  73. package/script/_spec.js +2 -0
  74. package/script/_types.d.ts +141 -0
  75. package/script/_types.d.ts.map +1 -0
  76. package/script/_types.js +22 -0
  77. package/script/error.d.ts +331 -0
  78. package/script/error.d.ts.map +1 -0
  79. package/script/error.js +414 -0
  80. package/script/events.d.ts +320 -0
  81. package/script/events.d.ts.map +1 -0
  82. package/script/events.js +458 -0
  83. package/script/helpers/_types.d.ts +188 -0
  84. package/script/helpers/_types.d.ts.map +1 -0
  85. package/script/helpers/_types.js +2 -0
  86. package/script/helpers/mod.d.ts +90 -0
  87. package/script/helpers/mod.d.ts.map +1 -0
  88. package/script/helpers/mod.js +106 -0
  89. package/script/helpers/operations/batch.d.ts +109 -0
  90. package/script/helpers/operations/batch.d.ts.map +1 -0
  91. package/script/helpers/operations/batch.js +144 -0
  92. package/script/helpers/operations/combination.d.ts +162 -0
  93. package/script/helpers/operations/combination.d.ts.map +1 -0
  94. package/script/helpers/operations/combination.js +355 -0
  95. package/script/helpers/operations/conditional.d.ts +211 -0
  96. package/script/helpers/operations/conditional.d.ts.map +1 -0
  97. package/script/helpers/operations/conditional.js +286 -0
  98. package/script/helpers/operations/core.d.ts +198 -0
  99. package/script/helpers/operations/core.d.ts.map +1 -0
  100. package/script/helpers/operations/core.js +272 -0
  101. package/script/helpers/operations/errors.d.ts +277 -0
  102. package/script/helpers/operations/errors.d.ts.map +1 -0
  103. package/script/helpers/operations/errors.js +387 -0
  104. package/script/helpers/operations/mod.d.ts +26 -0
  105. package/script/helpers/operations/mod.d.ts.map +1 -0
  106. package/script/helpers/operations/mod.js +41 -0
  107. package/script/helpers/operations/timing.d.ts +206 -0
  108. package/script/helpers/operations/timing.d.ts.map +1 -0
  109. package/script/helpers/operations/timing.js +464 -0
  110. package/script/helpers/operators.d.ts +520 -0
  111. package/script/helpers/operators.d.ts.map +1 -0
  112. package/script/helpers/operators.js +570 -0
  113. package/script/helpers/pipe.d.ts +118 -0
  114. package/script/helpers/pipe.d.ts.map +1 -0
  115. package/script/helpers/pipe.js +132 -0
  116. package/script/helpers/utils.d.ts +142 -0
  117. package/script/helpers/utils.d.ts.map +1 -0
  118. package/script/helpers/utils.js +200 -0
  119. package/script/mod.d.ts +863 -0
  120. package/script/mod.d.ts.map +1 -0
  121. package/script/mod.js +877 -0
  122. package/script/observable.d.ts +1610 -0
  123. package/script/observable.d.ts.map +1 -0
  124. package/script/observable.js +1984 -0
  125. package/script/package.json +3 -0
  126. package/script/queue.d.ts +201 -0
  127. package/script/queue.d.ts.map +1 -0
  128. package/script/queue.js +286 -0
  129. package/script/symbol.d.ts +60 -0
  130. package/script/symbol.d.ts.map +1 -0
  131. package/script/symbol.js +135 -0
@@ -0,0 +1,1610 @@
1
+ /**
2
+ * A **spec-faithful** yet ergonomic TC39-inspired Observable implementation with detailed TSDocs and examples.
3
+ *
4
+ * A **push‑based stream abstraction** for events, data, and long‑running
5
+ * operations. Think of it as a **multi‑value Promise** that keeps sending
6
+ * values until you tell it to stop.
7
+ *
8
+ * ## Why This Exists
9
+ * Apps juggle many async sources, mouse clicks, HTTP requests, timers,
10
+ * WebSockets, file watchers. Before Observables you glued those together with a
11
+ * mish‑mash of callbacks, Promises, `EventTarget`s and async iterators, each
12
+ * with different rules for cleanup and error handling. **Observables give you
13
+ * one mental model** for subscription → cancellation → propagation → teardown.
14
+ *
15
+ * ## ✨ Feature Highlights
16
+ * - **Unified push + pull** – use callbacks *or* `for await … of` on the same
17
+ * stream.
18
+ * - **Cold by default** – each subscriber gets an independent execution (great
19
+ * for predictable side‑effects).
20
+ * - **Deterministic teardown** – return a function/`unsubscribe`/`[Symbol.dispose]`
21
+ * and it *always* runs once, even if the observable errors synchronously.
22
+ * - **Back‑pressure helper** – `pull()` converts to an `AsyncGenerator` backed
23
+ * by `ReadableStream` so the producer slows down when the consumer lags.
24
+ * - **Tiny surface** – <1 kB min+gzip of logic; treeshakes cleanly.
25
+ *
26
+ * ## Error Propagation Policy
27
+ * 1. **Local catch** – If your observer supplies an `error` callback, **all**
28
+ * upstream errors funnel there.
29
+ * 2. **Unhandled‑rejection style** – If no `error` handler is provided the
30
+ * exception is re‑thrown on the micro‑task queue (same timing semantics as
31
+ * an unhandled Promise rejection).
32
+ * 3. **Observer callback failures** – Exceptions thrown inside `next()` or
33
+ * `complete()` are routed to `error()` if present, otherwise bubble as in
34
+ * (2).
35
+ * 4. **Errors inside `error()`** – A second‑level failure is *always* queued to
36
+ * the micro‑task queue to avoid infinite recursion.
37
+ *
38
+ * ## Edge‑Cases & Gotchas
39
+ * - `subscribe()` can synchronously call `complete()`/`error()` and still have
40
+ * its teardown captured – **ordering is guaranteed**.
41
+ * - Subscribing twice to a *cold* observable triggers two side‑effects (e.g.
42
+ * two HTTP requests). Share the source if you want fan‑out.
43
+ * - Infinite streams leak unless you call `unsubscribe()` or wrap them in a
44
+ * `using` block.
45
+ * - The helper `pull()` encodes thrown errors as `ObservableError` *values* so
46
+ * buffered items are not lost – remember to `instanceof` check if you rely
47
+ * on it.
48
+ *
49
+ * @example Common Patterns
50
+ * ```ts
51
+ * // DOM events → Observable
52
+ * const clicks = new Observable<Event>(obs => {
53
+ * const h = (e: Event) => obs.next(e);
54
+ * button.addEventListener("click", h);
55
+ * return () => button.removeEventListener("click", h);
56
+ * });
57
+ *
58
+ * // HTTP polling every 5 s
59
+ * const poll = new Observable<Response>(obs => {
60
+ * const id = setInterval(async () => {
61
+ * try { obs.next(await fetch("/api/data")); }
62
+ * catch (e) { obs.error(e); }
63
+ * }, 5000);
64
+ * return () => clearInterval(id);
65
+ * });
66
+ *
67
+ * // WebSocket stream with graceful close
68
+ * const live = new Observable<string>(obs => {
69
+ * const ws = new WebSocket("wss://example.com");
70
+ * ws.onmessage = e => obs.next(e.data);
71
+ * ws.onerror = e => obs.error(e);
72
+ * ws.onclose = () => obs.complete();
73
+ * return () => ws.close();
74
+ * });
75
+ * ```
76
+ *
77
+ * @example Basic subscription:
78
+ * ```ts
79
+ * import { Observable } from './observable.ts';
80
+ *
81
+ * // Emit 1,2,3 then complete
82
+ * const subscription = Observable.of(1, 2, 3).subscribe({
83
+ * start(sub) { console.log('Subscribed'); },
84
+ * next(val) { console.log('Value:', val); },
85
+ * complete() { console.log('Complete'); }
86
+ * });
87
+ *
88
+ * // Cancel manually if needed
89
+ * subscription.unsubscribe();
90
+ * ```
91
+ *
92
+ * @example Resource-safe usage with `using` statement:
93
+ * ```ts
94
+ * import { Observable } from './observable.ts';
95
+ *
96
+ * {
97
+ * using subscription = Observable.of(1, 2, 3).subscribe({
98
+ * next(val) { console.log('Value:', val); }
99
+ * });
100
+ *
101
+ * // Code that uses the subscription
102
+ * doSomething();
103
+ *
104
+ * } // Subscription automatically unsubscribed at block end
105
+ * ```
106
+ *
107
+ * @example Simple async iteration:
108
+ * ```ts
109
+ * import { Observable } from './observable.ts';
110
+ *
111
+ * (async () => {
112
+ * for await (const x of Observable.of('a', 'b', 'c')) {
113
+ * console.log(x);
114
+ * }
115
+ * })();
116
+ * ```
117
+ *
118
+ * @example Pull with backpressure:
119
+ * ```ts
120
+ * import { Observable } from './observable.ts';
121
+ *
122
+ * const nums = Observable.from([1,2,3,4,5]);
123
+ * (async () => {
124
+ * for await (const n of nums.pull({ strategy: { highWaterMark: 2 } })) {
125
+ * console.log('Pulled:', n);
126
+ * await new Promise(r => setTimeout(r, 1000)); // Slow consumer
127
+ * }
128
+ * })();
129
+ * ```
130
+ *
131
+ * ## Spec Compliance & Notable Deviations
132
+ * | Area | Proposal Behaviour | This Library |
133
+ * |----------------------------|----------------------------------------|-----------------------------------------------------------------------------------------|
134
+ * | `subscribe` parameters | Only **observer object** | Adds `(next, error?, complete?)` triple‑param overload. |
135
+ * | Teardown shape | Function or `{ unsubscribe() }` | Also honours `[Symbol.dispose]` **and** `[Symbol.asyncDispose]`. |
136
+ * | Pull‑mode iteration | *Not in spec* | `pull()` helper returns an `AsyncGenerator` with `ReadableStream`‑backed back‑pressure. |
137
+ * | Error propagation in pull | Stream **error** ends iteration | Error encoded as `ObservableError` value so buffered items drain first. |
138
+ * | `Symbol.toStringTag` | Optional | Provided for `Observable` and `SubscriptionObserver`. |
139
+ *
140
+ * Anything not listed above matches the TC39 draft (**May 2025**).
141
+ *
142
+ * ## Lifecycle State Machine
143
+ * ```text
144
+ * (inactive) --subscribe()--> [ active ]
145
+ * ^ | next()
146
+ * | unsubscribe()/error() | complete()
147
+ * |<------------------------| (closed)
148
+ * ```
149
+ * *Teardown executes exactly once on the leftward arrow.*
150
+ *
151
+ * @example Type‑Parameter Primer
152
+ * ```ts
153
+ * Observable<number> // counter
154
+ * Observable<Response> // fetch responses
155
+ * Observable<{x:number;y:number}> // mouse coords
156
+ * Observable<never> // signal‑only (no payload)
157
+ * Observable<string | ErrorPayload> // unions are fine
158
+ * ```
159
+ *
160
+ * @example Interop Cheat‑Sheet
161
+ * ```ts
162
+ * // Promise → Observable (single value then complete)
163
+ * Observable.from(fetch("/api"));
164
+ *
165
+ * // Observable → async iterator (back‑pressure aware)
166
+ * for await (const chunk of obs) {
167
+ * processChunk(chunk);
168
+ * }
169
+ *
170
+ * // Observable → Promise (first value only)
171
+ * const first = (await obs.pull().next()).value;
172
+ * ```
173
+ *
174
+ * ## Performance Cookbook (pull())
175
+ * | Producer speed | Consumer speed | Suggested `highWaterMark` | Notes |
176
+ * |---------------:|---------------:|--------------------------:|-----------------------------------------|
177
+ * | 🔥 Very fast | 🐢 Slow | 1‑8 | Minimal RAM; heavy throttling. |
178
+ * | ⚡ Fast | 🚶 Moderate | 16‑64 (default 64) | Good balance for most apps. |
179
+ * | 🚀 Bursty | 🚀 Bursty | 128‑512 | Smooths spikes at the cost of memory. |
180
+ *
181
+ * ➜ If RSS climbs steadily, halve `highWaterMark`; if you’re dropping messages
182
+ * under load, raise it (RAM permitting).
183
+ *
184
+ * ## Memory Management
185
+ *
186
+ * **Critical**: Infinite Observables need manual cleanup via `unsubscribe()` or `using` blocks
187
+ * to prevent memory leaks. Finite Observables auto-cleanup on complete/error.
188
+ *
189
+ * @example Quick start - DOM events
190
+ * ```ts
191
+ * const clicks = new Observable(observer => {
192
+ * const handler = e => observer.next(e);
193
+ * button.addEventListener('click', handler);
194
+ * return () => button.removeEventListener('click', handler);
195
+ * });
196
+ *
197
+ * using subscription = clicks.subscribe(event => console.log('Clicked!'));
198
+ * // Auto-cleanup when leaving scope
199
+ * ```
200
+ *
201
+ * @example Network with backpressure
202
+ * ```ts
203
+ * const dataStream = new Observable(observer => {
204
+ * const ws = new WebSocket('ws://api.com/live');
205
+ * ws.onmessage = e => observer.next(JSON.parse(e.data));
206
+ * ws.onerror = e => observer.error(e);
207
+ * return () => ws.close();
208
+ * });
209
+ *
210
+ * // Consume at controlled pace
211
+ * for await (const data of dataStream.pull({ strategy: { highWaterMark: 10 } })) {
212
+ * await processSlowly(data); // Producer pauses when buffer fills
213
+ * }
214
+ * ```
215
+ *
216
+ * @example Testing & Debugging Tips
217
+ * ```ts
218
+ * import { expect, test } from "jsr:@libs/testing@^5";
219
+ *
220
+ * test("emits three ticks then completes", async () => {
221
+ * const ticks = Observable.of(1, 2, 3);
222
+ * const out: number[] = [];
223
+ * for await (const n of ticks) out.push(n);
224
+ * expect(out).toEqual([1, 2, 3]);
225
+ * });
226
+ *
227
+ * // Quick console probe
228
+ * obs.subscribe(v => console.log("[OBS]", v));
229
+ * ```
230
+ *
231
+ * ## FAQ
232
+ * - **Why does my network request fire twice?** Cold observables run once per
233
+ * subscribe. Reuse a single subscription or share the source.
234
+ * - **Why does `next()` throw after `complete()`?** The stream is closed; calls
235
+ * are ignored by design.
236
+ * - **Memory leak on interval** , Infinite streams require `unsubscribe()` or
237
+ * `using`.
238
+ *
239
+ * @module
240
+ */
241
+ import "./_dnt.polyfills.js";
242
+ import type { ObservableProtocol, SpecObservable, SpecSubscription } from "./_spec.js";
243
+ import type { Observer, Subscription } from "./_types.js";
244
+ import { ObservableError } from "./error.js";
245
+ import { Symbol } from "./symbol.js";
246
+ /**
247
+ * Teardown function returned by the *subscriber* when it needs to release
248
+ * resources (DOM handlers, sockets…).
249
+ *
250
+ * A *teardown* function or object returned from the subscriber to release
251
+ * resources when a subscription terminates.
252
+ *
253
+ * - `() => void` – plain cleanup callback.
254
+ * - `{ unsubscribe() }` – imperative cancel method.
255
+ * - `{ [Symbol.dispose](): void }` – synchronous disposable.
256
+ * - `{ [Symbol.asyncDispose](): Promise<void> }` – async disposable.
257
+ * - `undefined | null` – nothing to clean up.
258
+ *
259
+ * **Timing Note**: Cleanup is captured and called **even if** `observer.error()` or
260
+ * `observer.complete()` is called synchronously before your subscriber returns.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * new Observable(observer => {
265
+ * const timer = setInterval(() => observer.next(Date.now()), 1000);
266
+ * // Return teardown function
267
+ * return () => clearInterval(timer);
268
+ * });
269
+ * ```
270
+ *
271
+ * @example Multi-resource cleanup
272
+ * ```ts
273
+ * new Observable(observer => {
274
+ * const timer = setInterval(tick, 1000);
275
+ * const ws = new WebSocket(url);
276
+ * const sub = other.subscribe(observer);
277
+ *
278
+ * return () => {
279
+ * clearInterval(timer);
280
+ * ws.close();
281
+ * sub.unsubscribe();
282
+ * };
283
+ * });
284
+ */
285
+ export type Teardown = (() => void) | SpecSubscription | AsyncDisposable | Disposable | null | undefined | void;
286
+ /**
287
+ * Internal state associated with each Subscription.
288
+ * Using a dedicated state object stored in a WeakMap gives us:
289
+ * 1. A single source of truth for subscription state
290
+ * 2. No circular references that might leak memory
291
+ * 3. Clean separation between public interface and internal state
292
+ */
293
+ export interface StateMap<T> {
294
+ /** True once subscription is closed via unsubscribe, error, or complete */
295
+ closed: boolean;
296
+ /** Reference to the observer; nulled on closure to prevent memory leaks */
297
+ observer: Observer<T> | null;
298
+ /** Function or object returned by subscriber; used for resource cleanup */
299
+ cleanup: Teardown;
300
+ /** AbortSignal's abort event handler */
301
+ removeAbortHandler?: (() => void) | null;
302
+ }
303
+ /**
304
+ * Central registry of subscription state.
305
+ *
306
+ * Using a WeakMap allows us to:
307
+ * 1. Associate state with subscription objects without extending them
308
+ * 2. Let the garbage collector automatically clean up entries when subscriptions are no longer referenced
309
+ * 3. Hide implementation details from users
310
+ */
311
+ export declare const SubscriptionStateMap: WeakMap<Subscription, StateMap<unknown>>;
312
+ /**
313
+ * Creates a new Subscription object with properly initialized state.
314
+ *
315
+ * We validate observer methods early, ensuring type errors are caught
316
+ * at subscription time rather than during event emission.
317
+ *
318
+ * The returned Subscription includes support for:
319
+ * - Manual cancellation via `unsubscribe()`
320
+ * - Automatic cleanup via `using` blocks (Symbol.dispose)
321
+ * - Async cleanup contexts (Symbol.asyncDispose)
322
+ *
323
+ * @throws TypeError if observer methods are present but not functions
324
+ * @internal
325
+ */
326
+ export declare function createSubscription<T>(observer: Observer<T>, opts?: {
327
+ signal?: AbortSignal;
328
+ } | null): Subscription;
329
+ /**
330
+ * Mark subscription as closed and return observer reference.
331
+ * Does NOT perform cleanup - that happens later.
332
+ */
333
+ export declare function markSubscriptionClosed<T>(state: StateMap<T> | undefined | null, returnObserver: true): Observer<T> | null;
334
+ /**
335
+ * Marks a subscription as closed when the caller does not need the observer.
336
+ */
337
+ export declare function markSubscriptionClosed<T>(state: StateMap<T> | undefined | null, returnObserver: false): undefined | null;
338
+ /**
339
+ * Marks a subscription as closed and optionally returns the detached observer.
340
+ */
341
+ export declare function markSubscriptionClosed<T>(state: StateMap<T> | undefined | null, returnObserver?: boolean): Observer<T> | undefined | null;
342
+ /**
343
+ * Perform cleanup if available. Safe to call multiple times.
344
+ */
345
+ export declare function performSubscriptionCleanup(subscription: Subscription, state?: StateMap<unknown> | null): void;
346
+ /**
347
+ * Marks a subscription as closed and schedules necessary cleanup.
348
+ *
349
+ * This is the centralized implementation for all subscription termination paths:
350
+ * - Manual unsubscribe()
351
+ * - Observer.error()
352
+ * - Observer.complete()
353
+ *
354
+ * The function ensures:
355
+ * 1. Idempotency (safe to call multiple times)
356
+ * 2. Cleanup happens exactly once
357
+ * 3. State is properly cleared to prevent memory leaks
358
+ * 4. WeakMap entry is removed to aid garbage collection
359
+ *
360
+ * @param subscription - The subscription to close
361
+ * @internal
362
+ */
363
+ export declare function closeSubscription(subscription: Subscription, stateMap?: StateMap<unknown> | undefined | null): Observer<unknown> | null | void;
364
+ /**
365
+ * Wraps an observer with key guarantees required by the Observable specification.
366
+ *
367
+ * SubscriptionObserver is a critical component that ensures:
368
+ *
369
+ * 1. The observer contract is honored correctly
370
+ * 2. Notifications stop after a subscription is closed
371
+ * 3. Error/complete notifications properly terminate the subscription
372
+ * 4. Observer methods are called with the correct `this` context
373
+ * 5. Errors are properly propagated according to spec
374
+ *
375
+ * This wrapper acts as the intermediary between the Observable producer
376
+ * and the consumer-provided Observer.
377
+ *
378
+ * @typeParam T - The type of values delivered by the parent Observable.
379
+ */
380
+ export declare class SubscriptionObserver<T> {
381
+ #private;
382
+ /**
383
+ * Returns whether this observer's subscription is closed.
384
+ *
385
+ * Uses the single source of truth for closed state from SubscriptionStateMap.
386
+ * This property is used by subscriber functions to check if they should
387
+ * continue delivering events.
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * const timer = new Observable(observer => {
392
+ * const id = setInterval(() => {
393
+ * if (!observer.closed) {
394
+ * observer.next(Date.now());
395
+ * }
396
+ * }, 1000);
397
+ * return () => clearInterval(id);
398
+ * });
399
+ * ```
400
+ */
401
+ get closed(): boolean;
402
+ /**
403
+ * Creates a new SubscriptionObserver attached to the given subscription.
404
+ *
405
+ * @param subscription - The subscription that created this observer
406
+ */
407
+ constructor(subscription?: Subscription | null);
408
+ /**
409
+ * Delivers the next value to the observer if the subscription is open.
410
+ *
411
+ * This is typically the "hot path" in an Observable implementation,
412
+ * as it's called for every emitted value. Key behaviors:
413
+ *
414
+ * 1. Silently returns if subscription is closed (no errors)
415
+ * 2. Properly preserves observer's `this` context
416
+ * 3. Catches and handles errors thrown from observer.next
417
+ * 4. Forwards errors to observer.error when available
418
+ *
419
+ * Performance Considerations:
420
+ * - Minimizes property access chains
421
+ * - Early returns for closed subscriptions
422
+ * - Type checking to avoid calling non-functions
423
+ *
424
+ * @param value - The value to deliver to the observer
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * // Inside a subscriber function:
429
+ * observer.next(42); // Delivers value to consumer
430
+ * ```
431
+ *
432
+ * > Note: Error-propagation policy
433
+ * > ─────────────────────────────
434
+ * > * If the *observer supplies its own `error()` handler*,
435
+ * > that handler is considered the “catch-block” for the stream.
436
+ * > ↳ Any exception that happens *inside* the user’s `next()` /
437
+ * > `complete()` callbacks is forwarded to `error(err)` **once**.
438
+ * > ↳ If `error()` itself throws, we still delegate to `HostReportErrors` (≈ “unhandled-promise rejection”)
439
+ * > (i.e. `queueMicrotask`), exactly as the proposal specifies.
440
+ * >
441
+ * > * If the observer does **not** implement `error()`, we fall back to the
442
+ * > spec’s `HostReportErrors` behaviour (queueMicrotask + throw) so the host
443
+ * > surfaces the error just like an uncaught Promise rejection.
444
+ * >
445
+ * > Rationale – Think of `error()` as the moral equivalent of a `.catch()`
446
+ * > on a Promise. Once a catch exists, the host no longer warns about
447
+ * > “unhandled” rejections; we mirror that mental model here.
448
+ * >
449
+ * > Spec reference – This diverges slightly from stage-1, which still
450
+ * > invokes HostReportErrors if the *error handler itself* throws. We
451
+ * > intentionally suppress that extra surfacing for the reasons above.
452
+ */
453
+ next(value: T): void;
454
+ /**
455
+ * Delivers an error notification to the observer, then closes the subscription.
456
+ *
457
+ * Error is a terminal operation - after calling it:
458
+ * 1. The subscription is immediately marked as closed
459
+ * 2. Resources are released via unsubscribe()
460
+ * 3. No further notifications will be delivered
461
+ *
462
+ * Error Handling:
463
+ * - If observer.error exists, the error is delivered there
464
+ * - If observer.error throws, the error is reported asynchronously
465
+ * - If no error handler exists, the error is reported asynchronously
466
+ *
467
+ * > Note: Even for "silent" errors (no error handler), we still close
468
+ * the subscription and report the error to the host.
469
+ *
470
+ * ##
471
+ *
472
+ * @example Important Timing Consideration
473
+ * When this method is called during the subscriber function execution (before it returns),
474
+ * there's a potential race condition with cleanup functions.
475
+ *
476
+ * Consider:
477
+ * ```ts
478
+ * new Observable(observer => {
479
+ * observer.error(new Error()); // Triggers unsubscribe here
480
+ * return () => cleanupResources(); // But this hasn't been returned yet!
481
+ * });
482
+ * ```
483
+ *
484
+ * Our implementation handles this by:
485
+ * 1. Marking the subscription as closed immediately
486
+ * 2. Scheduling actual cleanup in a microtask to ensure the teardown function
487
+ * has time to be captured and stored
488
+ *
489
+ * This ensures resources are properly cleaned up even when error/complete
490
+ * is called synchronously during subscription setup.
491
+ *
492
+ * @param err - The error to deliver
493
+ *
494
+ * @example
495
+ * ```ts
496
+ * // Inside a subscriber function:
497
+ * try {
498
+ * doRiskyOperation();
499
+ * } catch (err) {
500
+ * observer.error(err); // Terminates the subscription with error
501
+ * }
502
+ * ```
503
+ *
504
+ * > Note: {@link SubscriptionObserver.next | Review the error propagation policy in `next()` on how errors propagate, the behaviour is not obvious on first glance.}
505
+ */
506
+ error(err: unknown): void;
507
+ /**
508
+ * Signals successful completion of the observable sequence.
509
+ *
510
+ * Complete is a terminal operation - after calling it:
511
+ * 1. The subscription is immediately marked as closed
512
+ * 2. Resources are released via unsubscribe()
513
+ * 3. No further notifications will be delivered
514
+ *
515
+ * If observer.complete throws an error:
516
+ * - The error is forwarded to observer.error if available
517
+ * - Otherwise, it's reported asynchronously to the host
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * // Inside a subscriber function:
522
+ * observer.next(1);
523
+ * observer.next(2);
524
+ * observer.complete(); // Terminates the subscription normally
525
+ * ```
526
+ *
527
+ * > Note: {@link SubscriptionObserver.next | Review the error propagation policy in `next()` on how errors propagate, the behaviour is not obvious on first glance.}
528
+ */
529
+ complete(): void;
530
+ /**
531
+ * Returns a standard string tag for the object.
532
+ * Used by Object.prototype.toString.
533
+ */
534
+ get [Symbol.toStringTag](): "Subscription Observer";
535
+ }
536
+ /**
537
+ * Observale - A push-based stream for handling async data over time.
538
+ *
539
+ * **What it is**: Like a "smart Promise" that can emit multiple values and provides
540
+ * unified patterns for resource management, error handling, and subscription lifecycle.
541
+ *
542
+ * Observable is the central type in this library, representing a push-based
543
+ * source of values that can be subscribed to. It delivers values to observers
544
+ * and provides lifecycle guarantees around subscription and cleanup.
545
+ *
546
+ * Key guarantees:
547
+ * 1. Lazy execution - nothing happens until `subscribe()` is called
548
+ * 2. Multiple independent subscriptions to the same Observable
549
+ * 3. Each subscriber executes and cleans up independently.
550
+ * 4. Cleanups are deterministic one‑time resource disposal, the occur when subscriptions are cancelled, error or complete
551
+ *
552
+ * Extensions beyond the TC39 proposal:
553
+ * - Pull API via AsyncIterable interface
554
+ * - Using/await using support via Symbol.dispose/asyncDispose
555
+ *
556
+ * Gotchas:
557
+ * - Two subscribers → two side‑effects on a cold stream.
558
+ * - Remember to cancel infinite observables.
559
+ * - Calling `next()` after `complete()` is a no‑op.
560
+ * - Errors in observer callbacks go to error handler if provided, else global reporting.
561
+ * - Synchronous completion during subscribe still captures cleanup functions.
562
+ *
563
+ * @typeParam T - Type of values emitted by this Observable
564
+ */
565
+ export declare class Observable<T> implements AsyncIterable<T>, SpecObservable<T>, ObservableProtocol<T> {
566
+ #private;
567
+ /**
568
+ * Creates a new Observable with the given subscriber function.
569
+ *
570
+ * **Important**: This just stores your function - nothing executes until `subscribe()` is called.
571
+ * Think of it like writing a recipe vs actually cooking.
572
+ *
573
+ * The subscriber function is the heart of an Observable. It:
574
+ * 1. Is called once per subscription (not at Observable creation time)
575
+ * 2. Receives a SubscriptionObserver to send values through
576
+ * 3. Can optionally return a cleanup function or subscription
577
+ *
578
+ * Nothing happens when an Observable is created - execution only
579
+ * begins when subscribe() is called.
580
+ *
581
+ * @param subscribeFn - Function that implements the Observable's behavior
582
+ *
583
+ * Your subscriber function receives a `SubscriptionObserver` to:
584
+ * - `observer.next(value)` - Emit a value
585
+ * - `observer.error(err)` - Emit error (terminates)
586
+ * - `observer.complete()` - Signal completion (terminates)
587
+ * - `observer.closed` - Check if subscription is still active
588
+ *
589
+ * @throws TypeError if subscribeFn is not a function
590
+ * @throws TypeError if Observable is called without "new"
591
+ *
592
+ * @example Timer with cleanup
593
+ * ```ts
594
+ * // Timer that emits the current timestamp every second
595
+ * const timer = new Observable(observer => {
596
+ * console.log('Subscription started!');
597
+ * const id = setInterval(() => {
598
+ * observer.next(Date.now());
599
+ * }, 1000);
600
+ *
601
+ * // Return cleanup function
602
+ * return () => {
603
+ * console.log('Cleaning up timer');
604
+ * clearInterval(id);
605
+ * };
606
+ * });
607
+ * ```
608
+ *
609
+ * @example Async operation with error handling
610
+ * ```ts
611
+ * const fetch = new Observable(observer => {
612
+ * const controller = new AbortController();
613
+ *
614
+ * fetch('/api/data', { signal: controller.signal })
615
+ * .then(res => res.json())
616
+ * .then(data => {
617
+ * observer.next(data);
618
+ * observer.complete();
619
+ * })
620
+ * .catch(err => observer.error(err));
621
+ *
622
+ * return () => controller.abort(); // Cleanup
623
+ * });
624
+ * ```
625
+ */
626
+ constructor(subscribeFn: (obs: SubscriptionObserver<T>) => Teardown);
627
+ /**
628
+ * Returns this Observable (required for interoperability).
629
+ *
630
+ * This method implements the TC39 Symbol.observable protocol,
631
+ * which allows foreign Observable implementations to recognize
632
+ * and interoperate with this implementation.
633
+ *
634
+ * @returns This Observable instance
635
+ */
636
+ [Symbol.observable](): Observable<T>;
637
+ /**
638
+ * Subscribes to this Observable with an observer object.
639
+ *
640
+ * This method creates a subscription that:
641
+ * 1. Executes the subscriber function to begin producing values
642
+ * 2. Delivers those values to the observer's callbacks
643
+ * 3. Returns a subscription object for cancellation
644
+ *
645
+ * **What happens**: Creates subscription → calls observer.start() → executes subscriber function →
646
+ * stores cleanup → returns subscription for cancellation.
647
+ *
648
+ * Subscription Lifecycle:
649
+ * - Starts immediately and synchronously
650
+ * - Continues until explicitly cancelled or completed/errored
651
+ * - Guarantees proper resource cleanup on termination
652
+ *
653
+ * **Error handling**: If you provide an error callback, it catches all stream errors.
654
+ * If not, errors become unhandled Promise rejections.
655
+ *
656
+ * **Memory warning**: Infinite Observables need manual `unsubscribe()` or `using` blocks.
657
+ * If the Observable never calls `complete()` or `error()`,
658
+ * resources will not be automatically released unless you call
659
+ * `unsubscribe()` manually.
660
+ *
661
+ * For long-lived subscriptions, consider:
662
+ * 1. Using a `using` block with this subscription
663
+ * 2. Setting up a timeout or take-until condition
664
+ * 3. Explicitly calling `unsubscribe()` when no longer needed
665
+ *
666
+ * @param observer - Object with next/error/complete callbacks
667
+ * @param opts.signal - Optional AbortSignal to close subscription
668
+ * @returns Subscription object that can be used to cancel the subscription
669
+ *
670
+ * @example Observer object
671
+ * ```ts
672
+ * const subscription = observable.subscribe({
673
+ * next(value) { console.log('Received:', value) },
674
+ * error(err) { console.error('Error:', err) },
675
+ * complete() { console.log('Done!') }
676
+ * });
677
+ *
678
+ * // Later, to cancel:
679
+ * subscription.unsubscribe();
680
+ * ```
681
+ *
682
+ * @example Two ways to subscribe
683
+ * ```ts
684
+ * // Observer object (recommended)
685
+ * obs.subscribe({
686
+ * start(sub) { console.log('Started, can call sub.unsubscribe()'); },
687
+ * next(val) { console.log('Value:', val); },
688
+ * error(err) { console.error('Error:', err); },
689
+ * complete() { console.log('Done'); }
690
+ * });
691
+ *
692
+ * // Separate functions
693
+ * obs.subscribe(
694
+ * val => console.log(val),
695
+ * err => console.error(err),
696
+ * () => console.log('done')
697
+ * );
698
+ * ```
699
+ */
700
+ subscribe(observer: Observer<T>, opts?: {
701
+ signal?: AbortSignal;
702
+ }): Subscription;
703
+ /**
704
+ * Subscribes to this Observable with callback functions.
705
+ *
706
+ * Convenience overload that wraps the callbacks in an Observer object.
707
+ * See the documentation for the observer-based overload for details
708
+ * on subscription behavior.
709
+ *
710
+ * This method creates a subscription that:
711
+ * 1. Executes the subscriber function to begin producing values
712
+ * 2. Delivers those values to the observer's callbacks
713
+ * 3. Returns a subscription object for cancellation
714
+ *
715
+ * **What happens**: Creates subscription → calls observer.start() → executes subscriber function →
716
+ * stores cleanup → returns subscription for cancellation.
717
+ *
718
+ * Subscription Lifecycle:
719
+ * - Starts immediately and synchronously
720
+ * - Continues until explicitly cancelled or completed/errored
721
+ * - Guarantees proper resource cleanup on termination
722
+ *
723
+ * **Error handling**: If you provide an error callback, it catches all stream errors.
724
+ * If not, errors become unhandled Promise rejections.
725
+ *
726
+ * **Memory warning**: Infinite Observables need manual `unsubscribe()` or `using` blocks.
727
+ * If the Observable never calls `complete()` or `error()`,
728
+ * resources will not be automatically released unless you call
729
+ * `unsubscribe()` manually.
730
+ *
731
+ * For long-lived subscriptions, consider:
732
+ * 1. Using a `using` block with this subscription
733
+ * 2. Setting up a timeout or take-until condition
734
+ * 3. Explicitly calling `unsubscribe()` when no longer needed
735
+ *
736
+ * @param next - Function to handle each emitted value
737
+ * @param error - Optional function to handle errors
738
+ * @param complete - Optional function to handle completion
739
+ * @param opts.signal - Optional AbortSignal to close subscription
740
+ * @returns Subscription object that can be used to cancel the subscription
741
+ *
742
+ * @example
743
+ * ```ts
744
+ * const subscription = observable.subscribe(
745
+ * value => console.log('Received:', value),
746
+ * err => console.error('Error:', err),
747
+ * () => console.log('Done!')
748
+ * );
749
+ * ```
750
+ *
751
+ * @example Two ways to subscribe
752
+ * ```ts
753
+ * // Observer object (recommended)
754
+ * obs.subscribe({
755
+ * start(sub) { console.log('Started, can call sub.unsubscribe()'); },
756
+ * next(val) { console.log('Value:', val); },
757
+ * error(err) { console.error('Error:', err); },
758
+ * complete() { console.log('Done'); }
759
+ * });
760
+ *
761
+ * // Separate functions
762
+ * obs.subscribe(
763
+ * val => console.log(val),
764
+ * err => console.error(err),
765
+ * () => console.log('done')
766
+ * );
767
+ * ```
768
+ */
769
+ subscribe(next: (value: T) => void, error?: (e: unknown) => void, complete?: () => void, opts?: {
770
+ signal?: AbortSignal;
771
+ }): Subscription;
772
+ /**
773
+ * Enables `for await ... of observable` syntax for direct async iteration.
774
+ *
775
+ * This method allows Observables to be used in any context that accepts an AsyncIterable,
776
+ * implementing the "pull" mode of consuming an Observable.
777
+ *
778
+ * Uses default buffer size of 64 items.
779
+ *
780
+ * The implementation delegates to the `pull()` function which:
781
+ * 1. Converts push-based events to pull-based async iteration
782
+ * 2. Applies backpressure with ReadableStream
783
+ * 3. Handles proper cleanup on early termination
784
+ *
785
+ * @returns An AsyncIterator that yields values from this Observable
786
+ *
787
+ * @example
788
+ * ```ts
789
+ * const observable = Observable.of(1, 2, 3);
790
+ *
791
+ * // Using for-await-of directly on an Observable
792
+ * for await (const value of observable) {
793
+ * console.log(value); // Logs 1, 2, 3
794
+ * }
795
+ * ```
796
+ */
797
+ [Symbol.asyncIterator](): AsyncIterator<T>;
798
+ /**
799
+ * Converts this Observable into an AsyncGenerator with backpressure control.
800
+ *
801
+ * **Why use this**: Control buffer size to prevent memory issues when producer is faster than consumer.
802
+ * Uses ReadableStream internally for efficient buffering.
803
+ *
804
+ * This method provides more control over async iteration than the default
805
+ * Symbol.asyncIterator implementation, allowing consumers to:
806
+ *
807
+ * 1. Specify a queuing strategy with a custom highWaterMark
808
+ * 2. Control buffering behavior when the producer is faster than the consumer
809
+ * 3. Apply backpressure to prevent memory issues with fast producers
810
+ *
811
+ * The implementation uses ReadableStream internally to manage buffering
812
+ * and backpressure, pausing the producer when the buffer fills up.
813
+ *
814
+ * **Buffer sizing**:
815
+ * - Small (1-10): Memory-constrained environments, large data items
816
+ * - Medium (10-100): Most applications, good balance
817
+ * - Large (100+): High-throughput scenarios, small items
818
+ *
819
+ * **Error handling**: Errors are sent through value channel (not stream errors) to ensure
820
+ * all buffered values are processed before error is thrown.
821
+ *
822
+ * @param options - Configuration options for the pull operation
823
+ * @param options.strategy.highWaterMark - Max items to buffer before applying backpressure (default: 64)
824
+ * @returns Async generator that yields values and slows the producer when the
825
+ * buffer is full.
826
+ *
827
+ * @example Memory-efficient processing
828
+ * ```ts
829
+ * // Buffer up to 5 items before applying backpressure
830
+ * for await (const value of observable.pull({
831
+ * strategy: { highWaterMark: 5 }
832
+ * })) {
833
+ * console.log(value);
834
+ * // Slow consumer - producer will pause when buffer fills
835
+ * await new Promise(r => setTimeout(r, 1000));
836
+ * }
837
+ *
838
+ * // Large items, tiny buffer
839
+ * for await (const item of largeDataStream.pull({ strategy: { highWaterMark: 1 } })) {
840
+ * await processLargeItem(item); // Producer pauses when buffer full
841
+ * }
842
+ *
843
+ * // High throughput, large buffer
844
+ * for await (const event of fastStream.pull({ strategy: { highWaterMark: 1000 } })) {
845
+ * await processFast(event);
846
+ * }
847
+ * ```
848
+ */
849
+ pull(opts?: Parameters<typeof pull>[1] & {
850
+ ignoreError?: true;
851
+ }): AsyncGenerator<T>;
852
+ /**
853
+ * Returns pulled values while preserving `ObservableError` objects in the
854
+ * yielded value channel.
855
+ */
856
+ pull(opts?: Parameters<typeof pull>[1] & {
857
+ ignoreError?: false;
858
+ }): AsyncGenerator<T | ObservableError>;
859
+ /**
860
+ * Converts Promise, an iterable, async iterable, or Observable-like object to an Observable.
861
+ *
862
+ * This static method is a key part of the Observable interoperability mechanism,
863
+ * handling multiple input types in a consistent way.
864
+ *
865
+ * **Handles**:
866
+ * - Arrays, Sets, Maps → sync emission
867
+ * - Async generators → values over time
868
+ * - Symbol.observable objects → delegates to their implementation
869
+ *
870
+ * Behavior depends on the input type:
871
+ * 1. Objects with Symbol.observable - Delegates to their implementation
872
+ * 2. Synchronous iterables - Emits all values then completes
873
+ * 3. Asynchronous iterables - Emits values as they arrive then completes
874
+ * 4. Promise - Emits a single value (the resovled value) then completes
875
+ *
876
+ * Unlike Promise.resolve, Observable.from will not return the input unchanged
877
+ * if it's already an Observable, unless it's an instance of the exact same
878
+ * constructor. This ensures consistent behavior across different Observable
879
+ * implementations.
880
+ *
881
+ * @param input - The object to convert to an Observable
882
+ * @returns A new Observable that emits values from the input
883
+ *
884
+ * @example
885
+ * ```ts
886
+ * // From an array
887
+ * Observable.from([1, 2, 3]).subscribe({
888
+ * next: val => console.log(val) // 1, 2, 3
889
+ * });
890
+ *
891
+ * // From a Promise
892
+ * Observable.from(Promise.resolve("result")).subscribe({
893
+ * next: val => console.log(val) // "result"
894
+ * });
895
+ *
896
+ * // From another Observable-like object
897
+ * const foreign = {
898
+ * [Symbol.observable]() {
899
+ * return new Observable(obs => {
900
+ * obs.next("hello");
901
+ * obs.complete();
902
+ * });
903
+ * }
904
+ * };
905
+ * Observable.from(foreign).subscribe({
906
+ * next: val => console.log(val) // "hello"
907
+ * });
908
+ * ```
909
+ */
910
+ static readonly from: typeof from;
911
+ /**
912
+ * Creates an Observable that synchronously emits the given values then completes.
913
+ *
914
+ * This is a convenience method for creating simple Observables that:
915
+ * 1. Emit a fixed set of values synchronously
916
+ * 2. Complete immediately after emitting all values
917
+ * 3. Never error
918
+ *
919
+ * It's the Observable equivalent of `Promise.resolve()` for single values
920
+ * or `[].values()` for multiple values.
921
+ *
922
+ * @param items - Values to emit
923
+ * @returns A new Observable that emits the given values then completes
924
+ *
925
+ * @example
926
+ * ```ts
927
+ * // Create and subscribe
928
+ * Observable.of(1, 2, 3).subscribe({
929
+ * next: val => console.log(val), // Logs 1, 2, 3
930
+ * complete: () => console.log('Done!')
931
+ * });
932
+ *
933
+ * // Output:
934
+ * // 1
935
+ * // 2
936
+ * // 3
937
+ * // Done!
938
+ * ```
939
+ */
940
+ static readonly of: typeof of;
941
+ /**
942
+ * Converts a Observable into an AsyncGenerator with backpressure control.
943
+ *
944
+ * This method provides more control over async iteration than the default
945
+ * Symbol.asyncIterator implementation, allowing consumers to:
946
+ *
947
+ * 1. Specify a queuing strategy with a custom highWaterMark
948
+ * 2. Control buffering behavior when the producer is faster than the consumer
949
+ * 3. Apply backpressure to prevent memory issues with fast producers
950
+ *
951
+ * The implementation uses ReadableStream internally to manage buffering
952
+ * and backpressure, pausing the producer when the buffer fills up.
953
+ *
954
+ * @param options - Configuration options for the pull operation
955
+ * @returns An AsyncGenerator that yields values from this Observable
956
+ *
957
+ * @example
958
+ * ```ts
959
+ * // Buffer up to 5 items before applying backpressure
960
+ * for await (const value of observable.pull({
961
+ * strategy: { highWaterMark: 5 }
962
+ * })) {
963
+ * console.log(value);
964
+ * // Slow consumer - producer will pause when buffer fills
965
+ * await new Promise(r => setTimeout(r, 1000));
966
+ * }
967
+ * ```
968
+ */
969
+ static readonly pull: typeof pull;
970
+ /**
971
+ * Standard string tag for the object.
972
+ * Used by Object.prototype.toString.
973
+ */
974
+ get [Symbol.toStringTag](): "Observable";
975
+ }
976
+ /**
977
+ * Creates an Observable that synchronously emits the given values then completes.
978
+ *
979
+ * This standalone function implements the Observable.of static method while
980
+ * properly supporting subclassing. It's the Observable equivalent of:
981
+ * - `Array.of()` for collections
982
+ * - `Promise.resolve()` for single values
983
+ *
984
+ * Key behaviors:
985
+ * 1. Emits values synchronously when subscribed
986
+ * 2. Completes immediately after all values are emitted
987
+ * 3. Never errors
988
+ * 4. Respects the constructor it was called on for subclassing
989
+ *
990
+ * @param items - Values to emit
991
+ * @returns A new Observable that emits the given values then completes
992
+ *
993
+ * @example
994
+ * ```ts
995
+ * // Basic usage
996
+ * of(1, 2, 3).subscribe({
997
+ * next: x => console.log(x),
998
+ * complete: () => console.log('Done!')
999
+ * });
1000
+ * // Output: 1, 2, 3, Done!
1001
+ *
1002
+ * // Subclassing support
1003
+ * class MyObservable extends Observable<number> {
1004
+ * // Custom methods...
1005
+ * }
1006
+ *
1007
+ * // Creates a MyObservable instance
1008
+ * const mine = MyObservable.of(1, 2, 3);
1009
+ * ```
1010
+ */
1011
+ export declare function of<T>(this: unknown, ...items: T[]): Observable<T>;
1012
+ /**
1013
+ * Converts an Observable-like, sync iterable, or async iterable into an Observable.
1014
+ *
1015
+ * This is the standalone implementation of Observable.from, supporting:
1016
+ * - Objects with Symbol.observable (Observable-like)
1017
+ * - Regular iterables (arrays, Maps, Sets, generators)
1018
+ * - Async iterables (async generators, ReadableStreams)
1019
+ *
1020
+ * Conversion follows these rules:
1021
+ * 1. For Symbol.observable objects: delegates to their implementation
1022
+ * 2. For Promises: resolves and emits the promise's value
1023
+ * 3. For iterables: synchronously emits all values, then completes
1024
+ * 4. For async iterables: emits values as they arrive, then completes
1025
+ *
1026
+ * This function properly supports subclassing, preserving the constructor
1027
+ * it was called on.
1028
+ *
1029
+ * @throws TypeError if input is null, undefined, or not convertible
1030
+ *
1031
+ * @example
1032
+ * ```ts
1033
+ * // From array
1034
+ * from([1, 2, 3]).subscribe(x => console.log(x));
1035
+ * // Output: 1, 2, 3
1036
+ *
1037
+ * // From Promise
1038
+ * from(Promise.resolve('done')).subscribe(x => console.log(x));
1039
+ * // Output: 'done'
1040
+ *
1041
+ * // From Map
1042
+ * from(new Map([['a', 1], ['b', 2]])).subscribe(x => console.log(x));
1043
+ * // Output: ['a', 1], ['b', 2]
1044
+ *
1045
+ * // From another Observable implementation
1046
+ * const foreign = {
1047
+ * [Symbol.observable]() {
1048
+ * return { subscribe: observer => {
1049
+ * observer.next('hello');
1050
+ * observer.complete();
1051
+ * return { unsubscribe() {} };
1052
+ * }};
1053
+ * }
1054
+ * };
1055
+ * from(foreign).subscribe(x => console.log(x));
1056
+ * // Output: 'hello'
1057
+ * ```
1058
+ */
1059
+ export declare function from<T>(this: unknown, input: SpecObservable<T> | Iterable<T> | AsyncIterable<T> | PromiseLike<T> | ArrayLike<T>, { throwError }?: {
1060
+ throwError?: boolean | undefined;
1061
+ }): Observable<T>;
1062
+ /**
1063
+ * Converts an Observable into an AsyncGenerator with backpressure control.
1064
+ *
1065
+ * This function bridges the gap between push-based Observables and
1066
+ * pull-based async iteration, allowing consumers to:
1067
+ * 1. Process values at their own pace
1068
+ * 2. Use standard async iteration patterns (for-await-of)
1069
+ * 3. Control buffering behavior to prevent memory issues
1070
+ *
1071
+ * ## How It Works
1072
+ * Observable → ReadableStream (for buffering) → AsyncGenerator
1073
+ *
1074
+ * **Key features**:
1075
+ * - Automatic backpressure when consumer slower than producer
1076
+ * - Configurable buffer size via highWaterMark
1077
+ * - Proper cleanup on early termination
1078
+ * - Errors sent through value channel to preserve buffered items
1079
+ *
1080
+ * Implementation details:
1081
+ * - Uses ReadableStream as the backpressure mechanism
1082
+ * - Connects the Observable to the stream as a source
1083
+ * - Returns an AsyncGenerator that yields values from the stream
1084
+ * - Handles proper cleanup on early termination
1085
+ *
1086
+ * Instead of using ReadableStream's error mechanism, this implementation uses a special
1087
+ * approach to error handling: errors are wrapped in `ObservableError` objects and
1088
+ * sent through the normal value channel. This ensures all values emitted before an error
1089
+ * are properly processed in order before the error is thrown.
1090
+ *
1091
+ * ## Key Benefits
1092
+ *
1093
+ * 1. **Controlled Processing**: Process values at your own pace rather than being overwhelmed
1094
+ * 2. **Proper Backpressure**: When your consumer is slow, the producer automatically slows down
1095
+ * 3. **Complete Error Handling**: Errors don't cause queued values to be lost
1096
+ * 4. **Resource Safety**: Automatically cleans up subscriptions, even with early termination
1097
+ * 5. **Memory Efficiency**: Controls buffer size to prevent memory issues with fast producers
1098
+ * 6. **Iterator Integration**: Natural integration with other async iteration tools
1099
+ *
1100
+ * @param observable - Source Observable to pull values from
1101
+ * @param options - Configuration options for the ReadableStream
1102
+ * @param options.strategy - Queuing strategy that controls how backpressure is applied
1103
+ * @param options.strategy.highWaterMark - Buffer size before backpressure (default: 64)
1104
+ *
1105
+ * @returns An AsyncGenerator that yields values from the Observable at the consumer's pace
1106
+ *
1107
+ * @example Basic usage with for-await-of loop:
1108
+ * ```ts
1109
+ * const numbers = Observable.of(1, 2, 3, 4, 5);
1110
+ *
1111
+ * // Process each value at your own pace
1112
+ * for await (const num of pull(numbers)) {
1113
+ * console.log(`Processing ${num}`);
1114
+ * await someTimeConsumingOperation(num);
1115
+ * }
1116
+ * // Output:
1117
+ * // Processing 1
1118
+ * // Processing 2
1119
+ * // Processing 3
1120
+ * // Processing 4
1121
+ * // Processing 5
1122
+ *
1123
+ * const fast = new Observable(obs => {
1124
+ * let count = 0;
1125
+ * const id = setInterval(() => obs.next(count++), 10); // 100/sec
1126
+ * return () => clearInterval(id);
1127
+ * });
1128
+ *
1129
+ * // Slow consumer with small buffer
1130
+ * for await (const n of pull(fast, { strategy: { highWaterMark: 5 } })) {
1131
+ * console.log(n);
1132
+ * await new Promise(r => setTimeout(r, 1000)); // 1/sec - producer slows down
1133
+ * }
1134
+ * ```
1135
+ *
1136
+ * @example Handling errors while ensuring all prior values are processed:
1137
+ * ```ts
1138
+ * // Observable that emits values then errors
1139
+ * const source = new Observable(observer => {
1140
+ * observer.next(1);
1141
+ * observer.next(2);
1142
+ * observer.error(new Error("Something went wrong"));
1143
+ * // Even though an error occurred, both 1 and 2 will be processed
1144
+ * });
1145
+ *
1146
+ * try {
1147
+ * for await (const value of pull(source)) {
1148
+ * console.log(`Got value: ${value}`);
1149
+ * }
1150
+ * } catch (err) {
1151
+ * console.error(`Error caught: ${err.message}`);
1152
+ * }
1153
+ *
1154
+ * // Output:
1155
+ * // Got value: 1
1156
+ * // Got value: 2
1157
+ * // Error caught: Something went wrong
1158
+ * ```
1159
+ *
1160
+ * @example Controlling buffer size for memory efficiency:
1161
+ * ```ts
1162
+ * // Create a producer that emits values rapidly
1163
+ * const fastProducer = new Observable(observer => {
1164
+ * let count = 0;
1165
+ * const interval = setInterval(() => {
1166
+ * observer.next(count++);
1167
+ * if (count > 1000) {
1168
+ * clearInterval(interval);
1169
+ * observer.complete();
1170
+ * }
1171
+ * }, 1);
1172
+ * return () => clearInterval(interval);
1173
+ * });
1174
+ *
1175
+ * // Limit buffer to just 5 items to prevent memory issues
1176
+ * for await (const num of pull(fastProducer, {
1177
+ * strategy: { highWaterMark: 5 }
1178
+ * })) {
1179
+ * console.log(`Processing ${num}`);
1180
+ * // Slow consumer - producer will pause when buffer fills
1181
+ * await new Promise(r => setTimeout(r, 100));
1182
+ * }
1183
+ * ```
1184
+ */
1185
+ export declare function pull<T>(this: unknown, observable: SpecObservable<T>, opts?: {
1186
+ strategy?: QueuingStrategy<T | ObservableError>;
1187
+ throwError?: true;
1188
+ }): AsyncGenerator<T>;
1189
+ /**
1190
+ * Converts an Observable into an AsyncGenerator that yields both values and
1191
+ * wrapped `ObservableError` objects when `throwError` is disabled.
1192
+ */
1193
+ export declare function pull<T>(this: unknown, observable: SpecObservable<T>, opts?: {
1194
+ strategy?: QueuingStrategy<T | ObservableError>;
1195
+ throwError?: false;
1196
+ }): AsyncGenerator<T | ObservableError>;
1197
+ /**
1198
+ * Checks if a value is an Observable instance from this library.
1199
+ *
1200
+ * When working with different Observable implementations or mixed data types, you often need
1201
+ * to verify what kind of object you're dealing with. This function provides a reliable way to
1202
+ * check if something is specifically an instance of our Observable class, which is helpful
1203
+ * for type safety and ensuring you can use all the methods available on our implementation.
1204
+ *
1205
+ * **Why This Function Exists**:
1206
+ *
1207
+ * In JavaScript ecosystems, you might encounter different Observable implementations - RxJS,
1208
+ * this library, custom implementations, or objects that just happen to have a `subscribe` method.
1209
+ * Without a proper way to distinguish between them, you'd have to either:
1210
+ * - Risk calling methods that don't exist (crashes your app)
1211
+ * - Write defensive code with lots of property checks (clutters your logic)
1212
+ * - Use duck typing that might give false positives (unreliable)
1213
+ *
1214
+ * This function eliminates those problems by giving you a definitive answer: "Is this an
1215
+ * Observable from our library?" If yes, you know exactly what methods and properties are
1216
+ * available.
1217
+ *
1218
+ * **How It Relates to Other Checks**:
1219
+ *
1220
+ * Think of this as the strict cousin of `isSpecObservable()`. While `isSpecObservable()`
1221
+ * asks "can I subscribe to this?" this function asks "is this specifically our Observable?"
1222
+ *
1223
+ * Use `isObservable()` when you need to ensure you're working with our exact implementation,
1224
+ * and `isSpecObservable()` when you just need something subscribable.
1225
+ *
1226
+ * **Performance Story**:
1227
+ *
1228
+ * This function uses `instanceof`, which modern JavaScript engines optimize very well. It's
1229
+ * essentially a pointer comparison under the hood, making it extremely fast and suitable for
1230
+ * use in performance-critical code paths.
1231
+ *
1232
+ * The performance characteristics are:
1233
+ * - Single `instanceof` check (optimized by JavaScript engines)
1234
+ * - No method calls or property access required
1235
+ * - Safe to use in tight loops or frequently called functions
1236
+ * - Memory efficient (no allocations, just a boolean return)
1237
+ *
1238
+ * **Common Ways to Use This Function**:
1239
+ *
1240
+ * ```typescript
1241
+ * // Scenario 1: Type-safe method access
1242
+ * function processObservable(input: unknown) {
1243
+ * if (isObservable(input)) {
1244
+ * // TypeScript now knows input is Observable<unknown>
1245
+ * const generator = input.pull({ strategy: { highWaterMark: 10 } });
1246
+ * return generator; // Can safely use our specific methods
1247
+ * }
1248
+ *
1249
+ * throw new Error('Expected an Observable from this library');
1250
+ * }
1251
+ *
1252
+ * // Scenario 2: Library interoperability
1253
+ * function convertToOurObservable(source: unknown): Observable<any> {
1254
+ * if (isObservable(source)) {
1255
+ * return source; // Already our type, no conversion needed
1256
+ * }
1257
+ *
1258
+ * if (isSpecObservable(source)) {
1259
+ * return Observable.from(source); // Convert from other implementation
1260
+ * }
1261
+ *
1262
+ * throw new Error('Cannot convert to Observable');
1263
+ * }
1264
+ *
1265
+ * // Scenario 3: Filtering mixed arrays
1266
+ * const mixedSources = [rxjsObservable, ourObservable, promise, array];
1267
+ * const ourObservables = mixedSources.filter(isObservable);
1268
+ * // ourObservables is now Observable[] with full type safety
1269
+ *
1270
+ * // Scenario 4: Defensive programming
1271
+ * function subscribeToSource(source: unknown) {
1272
+ * if (isObservable(source)) {
1273
+ * // We know exactly what methods are available
1274
+ * return source.subscribe({ next: console.log });
1275
+ * } else if (isSpecObservable(source)) {
1276
+ * // Different Observable implementation, but still subscribable
1277
+ * return source.subscribe({ next: console.log });
1278
+ * } else {
1279
+ * throw new Error('Source is not observable');
1280
+ * }
1281
+ * }
1282
+ * ```
1283
+ *
1284
+ * **What Makes This Function Reliable**:
1285
+ *
1286
+ * Unlike duck typing (checking for the presence of methods), this function is precise:
1287
+ * - Returns true only for actual instances of our Observable class
1288
+ * - Handles inheritance correctly (subclasses return true)
1289
+ * - Never gives false positives from look-alike objects
1290
+ * - Works correctly across different module loading scenarios
1291
+ *
1292
+ * **Edge Cases Handled**:
1293
+ * - `null` and `undefined` → false (not Observables)
1294
+ * - Objects with `subscribe` methods → false (unless they're actually our Observable)
1295
+ * - Subclasses of Observable → true (proper inheritance support)
1296
+ * - Cross-frame instances → true (same constructor reference)
1297
+ *
1298
+ * **When to Use This vs Other Options**:
1299
+ *
1300
+ * Choose `isObservable()` when:
1301
+ * - You need to access methods specific to our Observable implementation
1302
+ * - You're building type guards for strict type checking
1303
+ * - You need to distinguish between different Observable libraries
1304
+ * - You're doing performance-critical filtering of mixed object types
1305
+ *
1306
+ * Choose `isSpecObservable()` instead when:
1307
+ * - You just need something that can be subscribed to
1308
+ * - You want maximum compatibility with other Observable implementations
1309
+ * - You're building generic utilities that work with any Observable-like object
1310
+ *
1311
+ * @template T - The expected type for the Observable's emitted values
1312
+ * @param value - Any value that might or might not be our Observable
1313
+ * @returns true if the value is an instance of our Observable class, false otherwise
1314
+ *
1315
+ * @example Simple type checking
1316
+ * ```typescript
1317
+ * const maybeObservable: unknown = getDataSource();
1318
+ *
1319
+ * if (isObservable(maybeObservable)) {
1320
+ * // TypeScript knows maybeObservable is Observable<unknown>
1321
+ * const subscription = maybeObservable.subscribe(console.log);
1322
+ *
1323
+ * // Can also use our specific methods
1324
+ * for await (const value of maybeObservable.pull()) {
1325
+ * console.log('Pulled:', value);
1326
+ * }
1327
+ * } else {
1328
+ * console.log('Not our Observable implementation');
1329
+ * }
1330
+ * ```
1331
+ *
1332
+ * @example Building a conversion utility
1333
+ * ```typescript
1334
+ * function ensureOurObservable<T>(source: unknown): Observable<T> {
1335
+ * if (isObservable<T>(source)) {
1336
+ * return source; // Already the right type
1337
+ * }
1338
+ *
1339
+ * if (isSpecObservable<T>(source)) {
1340
+ * // Convert from another Observable implementation
1341
+ * return new Observable<T>(observer => {
1342
+ * const sub = source.subscribe(observer);
1343
+ * return () => sub.unsubscribe();
1344
+ * });
1345
+ * }
1346
+ *
1347
+ * // Try to convert from other types
1348
+ * return Observable.from(source as any);
1349
+ * }
1350
+ * ```
1351
+ *
1352
+ * @example Library integration
1353
+ * ```typescript
1354
+ * // Function that works with any Observable but optimizes for ours
1355
+ * function processStream<T>(stream: unknown): AsyncGenerator<T> {
1356
+ * if (isObservable<T>(stream)) {
1357
+ * // Use our optimized pull method
1358
+ * return stream.pull();
1359
+ * } else if (isSpecObservable<T>(stream)) {
1360
+ * // Convert and then use our method
1361
+ * return Observable.from(stream).pull();
1362
+ * } else {
1363
+ * throw new Error('Expected an Observable-like object');
1364
+ * }
1365
+ * }
1366
+ * ```
1367
+ */
1368
+ export declare function isObservable<T = unknown>(value: unknown): value is Observable<T>;
1369
+ /**
1370
+ * Checks if a value conforms to the Observable specification protocol.
1371
+ *
1372
+ * When building applications that work with multiple Observable implementations, you need a way
1373
+ * to identify objects that can be subscribed to, regardless of which specific library created
1374
+ * them. This function provides that capability by checking for the core Observable protocol
1375
+ * rather than specific implementation details.
1376
+ *
1377
+ * **Why This Function Exists**:
1378
+ *
1379
+ * The Observable ecosystem includes many implementations - RxJS, this library, Zen Observable,
1380
+ * and others. Each has its own class structure, but they all follow the same basic protocol:
1381
+ * having a `[Symbol.observable]()` method that returns an object with a `subscribe()` method.
1382
+ *
1383
+ * Without this function, you'd need to write complex checks to determine if something is
1384
+ * subscribable, leading to:
1385
+ * - Fragile duck typing that breaks with edge cases
1386
+ * - Verbose property checking that clutters your code
1387
+ * - Missing compatibility with new Observable implementations
1388
+ * - Inconsistent behavior across different parts of your application
1389
+ *
1390
+ * This function solves those problems by implementing the official Observable protocol check.
1391
+ *
1392
+ * **How It Relates to Other Checks**:
1393
+ *
1394
+ * Think of this as the diplomatic cousin of `isObservable()`. While `isObservable()` checks
1395
+ * for our specific implementation, this function asks "do you speak the Observable protocol?"
1396
+ * It's designed for interoperability and maximum compatibility.
1397
+ *
1398
+ * The relationship between these functions is:
1399
+ * - `isObservable()` → "Are you our exact Observable class?"
1400
+ * - `isSpecObservable()` → "Can I subscribe to you using the standard protocol?"
1401
+ *
1402
+ * **Performance Story**:
1403
+ *
1404
+ * This function is more complex than `isObservable()` because it needs to check multiple
1405
+ * properties and call a method. However, it's still quite efficient:
1406
+ *
1407
+ * - Fast property access for Symbol.observable
1408
+ * - Single method call to get the subscribable object
1409
+ * - Type checking for the subscribe method
1410
+ * - Early returns for non-objects to avoid unnecessary work
1411
+ *
1412
+ * While not as fast as `instanceof`, it's still suitable for most use cases. If you're in a
1413
+ * performance-critical path and know you're only dealing with our Observable implementation,
1414
+ * prefer `isObservable()`.
1415
+ *
1416
+ * **Common Ways to Use This Function**:
1417
+ *
1418
+ * ```typescript
1419
+ * // Scenario 1: Cross-library compatibility
1420
+ * import { Observable as RxObservable } from 'rxjs';
1421
+ * import { Observable as OurObservable } from './observable.ts';
1422
+ *
1423
+ * function processAnyObservable<T>(source: unknown): Promise<T[]> {
1424
+ * if (isSpecObservable<T>(source)) {
1425
+ * // Works with RxJS, our Observable, or any other spec-compliant implementation
1426
+ * const results: T[] = [];
1427
+ *
1428
+ * return new Promise((resolve, reject) => {
1429
+ * source.subscribe({
1430
+ * next: value => results.push(value),
1431
+ * error: reject,
1432
+ * complete: () => resolve(results)
1433
+ * });
1434
+ * });
1435
+ * }
1436
+ *
1437
+ * throw new Error('Source must be Observable-like');
1438
+ * }
1439
+ *
1440
+ * // Scenario 2: Building generic utilities
1441
+ * function toArray<T>(source: unknown): Promise<T[]> {
1442
+ * if (isSpecObservable<T>(source)) {
1443
+ * return new Promise((resolve, reject) => {
1444
+ * const items: T[] = [];
1445
+ * source.subscribe({
1446
+ * next: item => items.push(item),
1447
+ * error: reject,
1448
+ * complete: () => resolve(items)
1449
+ * });
1450
+ * });
1451
+ * }
1452
+ *
1453
+ * // Fallback for other iterable types
1454
+ * if (Array.isArray(source)) return Promise.resolve([...source]);
1455
+ *
1456
+ * throw new Error('Cannot convert to array');
1457
+ * }
1458
+ *
1459
+ * // Scenario 3: Input validation in APIs
1460
+ * function subscribeToStream<T>(
1461
+ * stream: unknown,
1462
+ * handler: (value: T) => void
1463
+ * ): () => void {
1464
+ * if (!isSpecObservable<T>(stream)) {
1465
+ * throw new TypeError('Expected an Observable-like object');
1466
+ * }
1467
+ *
1468
+ * const subscription = stream.subscribe({ next: handler });
1469
+ * return () => subscription.unsubscribe();
1470
+ * }
1471
+ *
1472
+ * // Scenario 4: Filtering and type narrowing
1473
+ * const mixedSources: unknown[] = [
1474
+ * rxjsObservable,
1475
+ * ourObservable,
1476
+ * { subscribe() { return { unsubscribe() {} }; } }, // Custom implementation
1477
+ * "not observable",
1478
+ * 42
1479
+ * ];
1480
+ *
1481
+ * const observableSources = mixedSources.filter(isSpecObservable);
1482
+ * // observableSources is now Array<{ subscribe: Function, [Symbol.observable]: Function }>
1483
+ * ```
1484
+ *
1485
+ * **What Makes This Function Robust**:
1486
+ *
1487
+ * This function implements the official Observable protocol checking:
1488
+ * 1. Verifies the object has `Symbol.observable` method
1489
+ * 2. Calls that method to get the subscribable object
1490
+ * 3. Ensures the result has a working `subscribe` method
1491
+ * 4. Handles errors gracefully (returns false rather than throwing)
1492
+ *
1493
+ * **Edge Cases Handled**:
1494
+ * - `null` and `undefined` → false (not objects)
1495
+ * - Objects without `Symbol.observable` → false (not Observable protocol)
1496
+ * - `Symbol.observable` that throws → false (graceful error handling)
1497
+ * - `Symbol.observable` returning non-objects → false (invalid protocol)
1498
+ * - Objects with `subscribe` but no `Symbol.observable` → false (incomplete protocol)
1499
+ *
1500
+ * **When to Use This vs Other Options**:
1501
+ *
1502
+ * Choose `isSpecObservable()` when:
1503
+ * - Building libraries that should work with any Observable implementation
1504
+ * - You need maximum compatibility across the Observable ecosystem
1505
+ * - You're creating utilities for consuming streams regardless of their origin
1506
+ * - You want to follow the official Observable specification strictly
1507
+ *
1508
+ * Choose `isObservable()` instead when:
1509
+ * - You need methods specific to our Observable implementation
1510
+ * - Performance is critical and you know the expected types
1511
+ * - You're working within a single Observable implementation ecosystem
1512
+ * - You need compile-time guarantees about available methods
1513
+ *
1514
+ * @template T - The expected type for values emitted by the Observable
1515
+ * @param value - Any value that might conform to the Observable protocol
1516
+ * @returns true if the value implements the Observable specification, false otherwise
1517
+ *
1518
+ * @example Cross-library compatibility
1519
+ * ```typescript
1520
+ * import { Observable as RxObservable } from 'rxjs';
1521
+ * import { Observable as OurObservable } from './observable.ts';
1522
+ *
1523
+ * const sources = [
1524
+ * new RxObservable(sub => sub.next(1)),
1525
+ * new OurObservable(obs => obs.next(2)),
1526
+ * { subscribe() { return { unsubscribe() {} }; } } // Custom
1527
+ * ];
1528
+ *
1529
+ * // Process any Observable-like object
1530
+ * sources.forEach(source => {
1531
+ * if (isSpecObservable(source)) {
1532
+ * console.log('Can subscribe to this source');
1533
+ * source.subscribe({ next: console.log });
1534
+ * }
1535
+ * });
1536
+ * ```
1537
+ *
1538
+ * @example Building a universal Observable utility
1539
+ * ```typescript
1540
+ * function first<T>(source: unknown): Promise<T> {
1541
+ * if (!isSpecObservable<T>(source)) {
1542
+ * return Promise.reject(new Error('Source must be Observable'));
1543
+ * }
1544
+ *
1545
+ * return new Promise((resolve, reject) => {
1546
+ * const subscription = source.subscribe({
1547
+ * next: value => {
1548
+ * subscription.unsubscribe();
1549
+ * resolve(value);
1550
+ * },
1551
+ * error: reject,
1552
+ * complete: () => reject(new Error('Observable completed without emitting'))
1553
+ * });
1554
+ * });
1555
+ * }
1556
+ *
1557
+ * // Works with any Observable implementation
1558
+ * const result1 = await first(rxjsObservable);
1559
+ * const result2 = await first(ourObservable);
1560
+ * ```
1561
+ *
1562
+ * @example Input validation for APIs
1563
+ * ```typescript
1564
+ * interface StreamProcessor<T> {
1565
+ * process(stream: unknown): AsyncGenerator<T>;
1566
+ * }
1567
+ *
1568
+ * class UniversalProcessor<T> implements StreamProcessor<T> {
1569
+ * async* process(stream: unknown): AsyncGenerator<T> {
1570
+ * if (isSpecObservable<T>(stream)) {
1571
+ * // Convert any Observable to async generator
1572
+ * const observable = stream[Symbol.observable]();
1573
+ *
1574
+ * let resolve: (value: IteratorResult<T>) => void;
1575
+ * let reject: (error: any) => void;
1576
+ * let promise = new Promise<IteratorResult<T>>((res, rej) => {
1577
+ * resolve = res;
1578
+ * reject = rej;
1579
+ * });
1580
+ *
1581
+ * const subscription = observable.subscribe({
1582
+ * next: value => {
1583
+ * resolve({ value, done: false });
1584
+ * promise = new Promise<IteratorResult<T>>((res, rej) => {
1585
+ * resolve = res;
1586
+ * reject = rej;
1587
+ * });
1588
+ * },
1589
+ * error: reject,
1590
+ * complete: () => resolve({ value: undefined as any, done: true })
1591
+ * });
1592
+ *
1593
+ * try {
1594
+ * while (true) {
1595
+ * const result = await promise;
1596
+ * if (result.done) break;
1597
+ * yield result.value;
1598
+ * }
1599
+ * } finally {
1600
+ * subscription.unsubscribe();
1601
+ * }
1602
+ * } else {
1603
+ * throw new Error('Input must implement Observable protocol');
1604
+ * }
1605
+ * }
1606
+ * }
1607
+ * ```
1608
+ */
1609
+ export declare function isSpecObservable<T = unknown>(value: unknown): value is SpecObservable<T>;
1610
+ //# sourceMappingURL=observable.d.ts.map