@mikrojs/native 0.6.0-pr-70.gd2fdc0d → 0.6.0-pr-72.g464c29c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt CHANGED
@@ -50,7 +50,6 @@ set(MIKROJS_CORE_SOURCES
50
50
  src/mik_repl.cpp
51
51
  src/mik_app_config.cpp
52
52
  src/mik_udp.cpp
53
- src/mik_observable.cpp
54
53
  )
55
54
 
56
55
  add_library(mikrojs STATIC
@@ -81,7 +80,7 @@ endif()
81
80
  include(cmake/mikrojs_bytecode.cmake)
82
81
  mikrojs_generate_bytecode(
83
82
  RUNTIME_DIR "${CMAKE_CURRENT_SOURCE_DIR}/runtime"
84
- MODULES cbor env result schema fs http/helpers http/request i2c kv/nvs kv/rtc kv/shared neopixel observable observable/operators pin pwm reader sleep spi sntp stdio stream sys test uart udp wifi
83
+ MODULES cbor env result schema fs http/helpers http/request i2c kv/nvs kv/rtc kv/shared neopixel pin pwm reader sleep spi sntp stdio stream sys test uart udp wifi
85
84
  MODULE_PREFIX "mikrojs"
86
85
  SYMBOL_PREFIX "mikrojs"
87
86
  TARGET gen_bytecode
@@ -162,7 +161,6 @@ if(BUILD_TESTING)
162
161
  test/stream_test.cpp
163
162
  test/runtime_recycle_test.cpp
164
163
  test/udp_test.cpp
165
- test/observable_test.cpp
166
164
  )
167
165
 
168
166
  target_link_libraries(mikrojs_tests PRIVATE mikrojs)
@@ -215,9 +215,6 @@ JSModuleDef* mik__result_init(JSContext* ctx);
215
215
  /* UDP module (mik_udp.cpp) */
216
216
  JSModuleDef* mik__udp_init(JSContext* ctx);
217
217
 
218
- /* Observable module (mik_observable.cpp) */
219
- JSModuleDef* mik__observable_init(JSContext* ctx);
220
-
221
218
  bool mik__repl_is_evaluating(void);
222
219
 
223
220
  /* REPL protocol mode (mik_repl.cpp) — used by mik_console.cpp, mik_stdio.cpp */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/native",
3
- "version": "0.6.0-pr-70.gd2fdc0d",
3
+ "version": "0.6.0-pr-72.g464c29c",
4
4
  "description": "Mikro.js C++ runtime library and Node.js native addon",
5
5
  "keywords": [
6
6
  "esp32",
@@ -56,8 +56,6 @@
56
56
  "./runtime/kv/shared": "./runtime/kv/shared.ts",
57
57
  "./runtime/kv/types": "./runtime/kv/types.ts",
58
58
  "./runtime/neopixel/types": "./runtime/neopixel/types.ts",
59
- "./runtime/observable/operators": "./runtime/observable/operators.ts",
60
- "./runtime/observable/types": "./runtime/observable/types.ts",
61
59
  "./runtime/pin/types": "./runtime/pin/types.ts",
62
60
  "./runtime/pwm/types": "./runtime/pwm/types.ts",
63
61
  "./runtime/reader/types": "./runtime/reader/types.ts",
@@ -78,7 +76,7 @@
78
76
  "cmake-js": "^8.0.0",
79
77
  "node-addon-api": "^8.7.0",
80
78
  "node-gyp-build": "^4.8.4",
81
- "@mikrojs/quickjs": "0.6.0-pr-70.gd2fdc0d"
79
+ "@mikrojs/quickjs": "0.6.0-pr-72.g464c29c"
82
80
  },
83
81
  "devDependencies": {
84
82
  "@swc/core": "^1.15.30",
@@ -1,8 +1,6 @@
1
- import {Observable} from 'mikrojs/observable'
2
1
  import {err, ok} from 'mikrojs/result'
3
2
  import {Ble as NativeBle} from 'native:ble'
4
3
 
5
- import type {Subscriber} from '../observable/types.js'
6
4
  import type {Result} from '../result/types.js'
7
5
  import type {
8
6
  AdvertiseHandle,
@@ -11,9 +9,8 @@ import type {
11
9
  BleError,
12
10
  Characteristic,
13
11
  CharacteristicProperty,
14
- ConnectionInfo,
15
- MtuInfo,
16
12
  Peripheral,
13
+ PeripheralEventMap,
17
14
  Service,
18
15
  } from './types.js'
19
16
  import {parseUuid} from './uuid.js'
@@ -143,32 +140,6 @@ function normalizeServices(services: Service[]) {
143
140
 
144
141
  const native = new NativeBle()
145
142
 
146
- /* Lazy-attach: native.on runs only after the first JS subscriber and is
147
- * removed when the last one leaves. Same pattern as wifi.ts; see the note
148
- * there for the mbedTLS internal-RAM rationale. */
149
- function lazyEvent<T>(eventName: string): Observable<T> {
150
- const subscribers: Subscriber<T>[] = []
151
- function handler(value: unknown): void {
152
- const snapshot = subscribers.slice()
153
- for (const s of snapshot) {
154
- if (!s.closed) s.next(value as T)
155
- }
156
- }
157
- return new Observable<T>((sub) => {
158
- subscribers.push(sub)
159
- if (subscribers.length === 1) {
160
- native.on(eventName, handler)
161
- }
162
- sub.addTeardown(() => {
163
- const i = subscribers.indexOf(sub)
164
- if (i >= 0) subscribers.splice(i, 1)
165
- if (subscribers.length === 0) {
166
- native.off(eventName, handler)
167
- }
168
- })
169
- })
170
- }
171
-
172
143
  const ble: Ble = {
173
144
  get name(): string {
174
145
  return native.getName()
@@ -248,9 +219,13 @@ const peripheral: Peripheral = {
248
219
  return ok(handle)
249
220
  },
250
221
 
251
- onConnect: lazyEvent<ConnectionInfo>('connect'),
252
- onDisconnect: lazyEvent<ConnectionInfo>('disconnect'),
253
- onMtu: lazyEvent<MtuInfo>('mtu'),
222
+ on<K extends keyof PeripheralEventMap>(event: K, listener: PeripheralEventMap[K]) {
223
+ native.on(event, listener as (...args: unknown[]) => void)
224
+ },
225
+
226
+ off<K extends keyof PeripheralEventMap>(event: K, listener: PeripheralEventMap[K]) {
227
+ native.off(event, listener as (...args: unknown[]) => void)
228
+ },
254
229
  }
255
230
 
256
231
  export {ble, peripheral}
@@ -1,4 +1,3 @@
1
- import type {Observable} from '../observable/types.js'
2
1
  import type {Result} from '../result/types.js'
3
2
 
4
3
  /** Advertising interval range in milliseconds. */
@@ -172,15 +171,23 @@ export interface MtuInfo {
172
171
  mtu: number
173
172
  }
174
173
 
174
+ export interface PeripheralEventMap {
175
+ connect: (info: ConnectionInfo) => void
176
+ disconnect: (info: ConnectionInfo) => void
177
+ mtu: (info: MtuInfo) => void
178
+ }
179
+
175
180
  export interface Peripheral {
176
181
  /** Start advertising. Returns a handle whose `stop()` ends this session. */
177
182
  advertise(options?: AdvertiseOptions): Promise<Result<AdvertiseHandle, BleError>>
178
- /** Emits when a central connects. Subscribers are dispatched on the JS loop thread. */
179
- readonly onConnect: Observable<ConnectionInfo>
180
- /** Emits when a central disconnects. */
181
- readonly onDisconnect: Observable<ConnectionInfo>
182
- /** Emits when a connected central renegotiates its ATT MTU. */
183
- readonly onMtu: Observable<MtuInfo>
183
+ /**
184
+ * Register a listener for a peripheral lifecycle event. Listeners are
185
+ * called on the JS loop thread in registration order. Safe to register
186
+ * before calling `advertise()`.
187
+ */
188
+ on<K extends keyof PeripheralEventMap>(event: K, listener: PeripheralEventMap[K]): void
189
+ /** Remove a previously-registered listener. */
190
+ off<K extends keyof PeripheralEventMap>(event: K, listener: PeripheralEventMap[K]): void
184
191
  }
185
192
 
186
193
  export declare const ble: Ble
@@ -25,10 +25,6 @@ declare module 'native:result' {
25
25
  export function err<E>(error: E): ErrResult<E>
26
26
  }
27
27
 
28
- declare module 'native:observable' {
29
- export {Observable} from '@mikrojs/native/runtime/observable/types'
30
- }
31
-
32
28
  declare module 'native:sys' {
33
29
  import type {JsMemoryUsage} from './sys/types.js'
34
30
  export function evalScript(code: string): Promise<{value: unknown}>
@@ -1,4 +1,3 @@
1
- import type {Observable} from '../observable/types.js'
2
1
  import type {Result} from '../result/types.js'
3
2
 
4
3
  export type UdpFamily = 'ipv4' | 'ipv6'
@@ -32,31 +31,19 @@ export type UdpError =
32
31
  | {name: 'Closed'}
33
32
  | {name: 'NotBound'}
34
33
 
35
- /** Inbound datagram delivered via `socket.onMessage`. */
36
- export interface UdpMessage {
37
- msg: Uint8Array
38
- from: PeerAddress
39
- }
40
-
41
34
  export interface UdpSocket {
42
35
  readonly port: number
43
36
  readonly family: UdpFamily | 'dual'
44
37
  /**
45
38
  * Counter incremented each time an inbound datagram is dropped:
46
39
  * - the per-socket queue is full,
47
- * - no subscriber is attached to `onMessage` when a packet arrives,
40
+ * - no `onMessage` handler is set when a packet arrives,
48
41
  * - or the datagram is larger than the 1500-byte receive buffer
49
42
  * (typical Ethernet MTU; covers CoAP / mDNS / SNTP / DNS).
50
43
  */
51
44
  dropped: number
52
45
 
53
- /**
54
- * Stream of inbound datagrams. Subscribing attaches the native dispatch
55
- * once the first subscriber is added; the last `unsubscribe()` detaches
56
- * it again. Multiple subscribers fan out from one native registration.
57
- * Completes when `close()` is called.
58
- */
59
- readonly onMessage: Observable<UdpMessage>
46
+ onMessage: ((msg: Uint8Array, from: PeerAddress) => void) | null
60
47
 
61
48
  send(data: Uint8Array | string, to: PeerAddress): Promise<Result<void, UdpError>>
62
49
  joinMulticastGroup(group: MulticastGroup): Result<void, UdpError>
@@ -1,10 +1,8 @@
1
- import {Observable} from 'mikrojs/observable'
2
1
  import {ok} from 'mikrojs/result'
3
2
  import {bind as nativeBind, type NativeUdpSocket} from 'native:udp'
4
3
 
5
- import type {Subscriber} from '../observable/types.js'
6
4
  import type {Result} from '../result/types.js'
7
- import type {BindOptions, MulticastGroup, UdpError, UdpMessage, UdpSocket} from './types.js'
5
+ import type {BindOptions, MulticastGroup, PeerAddress, UdpError, UdpSocket} from './types.js'
8
6
 
9
7
  const utf8 = new TextEncoder()
10
8
 
@@ -13,37 +11,7 @@ function toBytes(data: Uint8Array | string): Uint8Array {
13
11
  }
14
12
 
15
13
  function makeSocket(handle: NativeUdpSocket): UdpSocket {
16
- /* Lazy native attach: keep handle.setOnMessage in sync with whether any
17
- * JS subscriber is listening. With zero subscribers, the native side
18
- * skips JS dispatch and increments the dropped counter for inbound
19
- * packets — preserving the pre-Observable behavior of unset onMessage
20
- * silently discarding traffic. */
21
- const subscribers: Subscriber<UdpMessage>[] = []
22
- let closed = false
23
-
24
- const onMessage = new Observable<UdpMessage>((sub) => {
25
- if (closed) {
26
- sub.complete()
27
- return
28
- }
29
- subscribers.push(sub)
30
- if (subscribers.length === 1) {
31
- handle.setOnMessage((msg, from) => {
32
- /* Snapshot so unsubscribes during dispatch don't shift indices. */
33
- const snapshot = subscribers.slice()
34
- for (const s of snapshot) {
35
- if (!s.closed) s.next({msg, from})
36
- }
37
- })
38
- }
39
- sub.addTeardown(() => {
40
- const i = subscribers.indexOf(sub)
41
- if (i >= 0) subscribers.splice(i, 1)
42
- if (subscribers.length === 0 && !closed) {
43
- handle.setOnMessage(null)
44
- }
45
- })
46
- })
14
+ let onMessage: ((msg: Uint8Array, from: PeerAddress) => void) | null = null
47
15
 
48
16
  const sock: UdpSocket = {
49
17
  get port() {
@@ -58,7 +26,13 @@ function makeSocket(handle: NativeUdpSocket): UdpSocket {
58
26
  set dropped(value: number) {
59
27
  handle.dropped = value
60
28
  },
61
- onMessage,
29
+ get onMessage() {
30
+ return onMessage
31
+ },
32
+ set onMessage(fn) {
33
+ onMessage = fn
34
+ handle.setOnMessage(fn)
35
+ },
62
36
  send(data, to) {
63
37
  return handle.send(toBytes(data), to)
64
38
  },
@@ -69,16 +43,6 @@ function makeSocket(handle: NativeUdpSocket): UdpSocket {
69
43
  return handle.leaveMulticastGroup(group.address)
70
44
  },
71
45
  close() {
72
- if (closed) return
73
- closed = true
74
- handle.setOnMessage(null)
75
- /* Complete any active subscriptions. Walk a snapshot in case a
76
- * complete handler unsubscribes a sibling. */
77
- const snapshot = subscribers.slice()
78
- subscribers.length = 0
79
- for (const s of snapshot) {
80
- if (!s.closed) s.complete()
81
- }
82
46
  handle.close()
83
47
  },
84
48
  }
@@ -1,4 +1,3 @@
1
- import type {Observable} from '../observable/types.js'
2
1
  import type {Result} from '../result/types.js'
3
2
 
4
3
  export type WifiStatus =
@@ -88,6 +87,17 @@ export interface ApStartOptions {
88
87
  maxConnections?: number
89
88
  }
90
89
 
90
+ export interface WifiEventMap {
91
+ connect: (info: WifiConnectionInfo) => void
92
+ disconnect: (reason: WifiDisconnectReason) => void
93
+ 'rssi-low': (rssi: number) => void
94
+ }
95
+
96
+ export interface ApEventMap {
97
+ 'station-connect': (info: ApStationInfo) => void
98
+ 'station-disconnect': (info: ApStationInfo) => void
99
+ }
100
+
91
101
  export type WifiError =
92
102
  | {name: 'InitFailed'; message: string}
93
103
  | {name: 'CountryNotSet'}
@@ -112,8 +122,8 @@ export interface WifiAp {
112
122
  readonly stations: ApStationInfo[]
113
123
  deauthStation(mac: string): Result<void, WifiError>
114
124
  inactiveTimeout: number
115
- readonly onStationConnect: Observable<ApStationInfo>
116
- readonly onStationDisconnect: Observable<ApStationInfo>
125
+ on<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]): void
126
+ off<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]): void
117
127
  }
118
128
 
119
129
  export interface Wifi {
@@ -125,9 +135,8 @@ export interface Wifi {
125
135
  isConnected: boolean
126
136
  scan(opts?: ScanOptions): Promise<Result<ScanResult[], WifiError>>
127
137
 
128
- readonly onConnect: Observable<WifiConnectionInfo>
129
- readonly onDisconnect: Observable<WifiDisconnectReason>
130
- readonly onRssiLow: Observable<number>
138
+ on<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]): void
139
+ off<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]): void
131
140
 
132
141
  readonly mac: string
133
142
  hostname: string | undefined
@@ -1,10 +1,9 @@
1
- import {Observable} from 'mikrojs/observable'
2
1
  import {err, ok} from 'mikrojs/result'
3
2
  import {Wifi as NativeWifi} from 'native:wifi'
4
3
 
5
- import type {Subscriber} from '../observable/types.js'
6
4
  import type {Result} from '../result/types.js'
7
5
  import type {
6
+ ApEventMap,
8
7
  ApStartOptions,
9
8
  ApStationInfo,
10
9
  IpConfig,
@@ -16,8 +15,8 @@ import type {
16
15
  WifiAp,
17
16
  WifiConnectionInfo,
18
17
  WifiCountryCode,
19
- WifiDisconnectReason,
20
18
  WifiError,
19
+ WifiEventMap,
21
20
  WifiStatus,
22
21
  } from './types.js'
23
22
 
@@ -38,36 +37,6 @@ const StatusFromCode = new Map<number, WifiStatus>(
38
37
 
39
38
  const native = new NativeWifi()
40
39
 
41
- /* Lazy-attach Observable: native.on is only called once a JS subscriber
42
- * appears, and native.off runs when the last one unsubscribes. Code paths
43
- * that import wifi but don't subscribe (e.g. a fetch-only flow) take the
44
- * exact same internal-RAM path as before observables existed — important
45
- * because mbedTLS handshake needs ~16 KB contiguous internal SRAM and
46
- * each eager closure pinned at module load chips into that headroom. */
47
- function lazyEvent<T>(eventName: string): Observable<T> {
48
- const subscribers: Subscriber<T>[] = []
49
- function handler(value: unknown): void {
50
- /* Snapshot so unsubscribes during dispatch don't shift indices. */
51
- const snapshot = subscribers.slice()
52
- for (const s of snapshot) {
53
- if (!s.closed) s.next(value as T)
54
- }
55
- }
56
- return new Observable<T>((sub) => {
57
- subscribers.push(sub)
58
- if (subscribers.length === 1) {
59
- native.on(eventName, handler)
60
- }
61
- sub.addTeardown(() => {
62
- const i = subscribers.indexOf(sub)
63
- if (i >= 0) subscribers.splice(i, 1)
64
- if (subscribers.length === 0) {
65
- native.off(eventName, handler)
66
- }
67
- })
68
- })
69
- }
70
-
71
40
  const ap: WifiAp = {
72
41
  start(options: ApStartOptions): Result<void, WifiError> {
73
42
  return native.apStart(options as Parameters<typeof native.apStart>[0])
@@ -102,8 +71,13 @@ const ap: WifiAp = {
102
71
  native.apSetInactiveTimeout(seconds)
103
72
  },
104
73
 
105
- onStationConnect: lazyEvent<ApStationInfo>('station-connect'),
106
- onStationDisconnect: lazyEvent<ApStationInfo>('station-disconnect'),
74
+ on<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]) {
75
+ native.on(event, listener as (...args: unknown[]) => void)
76
+ },
77
+
78
+ off<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]) {
79
+ native.off(event, listener as (...args: unknown[]) => void)
80
+ },
107
81
  }
108
82
 
109
83
  const MAX_CONNECT_RETRIES = 5
@@ -161,9 +135,13 @@ const wifi: Wifi = {
161
135
  return ok(asyncResult.value as ScanResult[])
162
136
  },
163
137
 
164
- onConnect: lazyEvent<WifiConnectionInfo>('connect'),
165
- onDisconnect: lazyEvent<WifiDisconnectReason>('disconnect'),
166
- onRssiLow: lazyEvent<number>('rssi-low'),
138
+ on<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]) {
139
+ native.on(event, listener as (...args: unknown[]) => void)
140
+ },
141
+
142
+ off<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]) {
143
+ native.off(event, listener as (...args: unknown[]) => void)
144
+ },
167
145
 
168
146
  get mac(): string {
169
147
  const result = native.mac()
package/src/mikrojs.cpp CHANGED
@@ -317,7 +317,6 @@ MIKRuntime* MIK_NewRuntimeInternal(MIKRunOptions* options) {
317
317
  mik__result_init(ctx);
318
318
  mik__cbor_init(ctx);
319
319
  mik__udp_init(ctx);
320
- mik__observable_init(ctx);
321
320
 
322
321
  /* Native mikrojs modules (replace bytecode builtins) */
323
322
  mik__inspect_register(ctx);
@@ -1,220 +0,0 @@
1
- // Host-side shim for `native:observable`, used only in vitest (Node) where
2
- // the mikrojs C runtime isn't available. Keep in sync with mik_observable.cpp.
3
- //
4
- // Mirrors the locked design in .claude/plans/observable.md:
5
- // - subscribe() returns a Subscription with unsubscribe() (no AbortSignal)
6
- // - no error channel — throws inside dispatch or teardown are caught at the
7
- // boundary, isolated to the offending subscriber, and re-thrown
8
- // asynchronously via setTimeout(0) so the synchronous producer keeps
9
- // running but the bug eventually surfaces (and on device, halts the
10
- // runtime via the existing unhandled-rejection path)
11
- // - sync emission allowed
12
- // - pipe-only composition (operators live in operators.ts)
13
- // - withEmitters() factory: {observable, next, complete}
14
-
15
- /* Catch a thrown error and re-throw it on the next tick. The synchronous
16
- * caller keeps going (other subscribers receive the value, remaining
17
- * teardowns run); the error eventually surfaces as an uncaught exception. */
18
- function panicAsync(err: unknown): void {
19
- setTimeout(() => {
20
- throw err
21
- }, 0)
22
- }
23
-
24
- class Subscriber<T> {
25
- closed = false
26
- private next_fn: ((v: T) => void) | undefined
27
- private complete_fn: (() => void) | undefined
28
- private teardowns: Array<() => void> = []
29
-
30
- constructor(observer: unknown) {
31
- if (observer == null) return
32
- if (typeof observer === 'function') {
33
- this.next_fn = observer as (v: T) => void
34
- } else if (typeof observer === 'object') {
35
- const o = observer as {next?: (v: T) => void; complete?: () => void}
36
- if (typeof o.next === 'function') this.next_fn = o.next
37
- if (typeof o.complete === 'function') this.complete_fn = o.complete
38
- } else {
39
- throw new TypeError('subscribe: observer must be a function, object, undefined, or null')
40
- }
41
- }
42
-
43
- next(value: T): void {
44
- if (this.closed) return
45
- if (this.next_fn) {
46
- try {
47
- this.next_fn(value)
48
- } catch (err) {
49
- panicAsync(err)
50
- }
51
- }
52
- }
53
-
54
- complete(): void {
55
- if (this.closed) return
56
- this.closed = true
57
- if (this.complete_fn) {
58
- try {
59
- this.complete_fn()
60
- } catch (err) {
61
- panicAsync(err)
62
- }
63
- }
64
- this.runTeardowns()
65
- }
66
-
67
- addTeardown(fn: () => void): void {
68
- if (typeof fn !== 'function') {
69
- throw new TypeError('addTeardown: argument must be a function')
70
- }
71
- if (this.closed) {
72
- try {
73
- fn()
74
- } catch (err) {
75
- panicAsync(err)
76
- }
77
- return
78
- }
79
- this.teardowns.push(fn)
80
- }
81
-
82
- // Used by Subscription.unsubscribe — silent (no observer.complete call).
83
- closeSilently(): void {
84
- if (this.closed) return
85
- this.closed = true
86
- this.runTeardowns()
87
- }
88
-
89
- private runTeardowns(): void {
90
- const list = this.teardowns
91
- this.teardowns = []
92
- for (let i = list.length - 1; i >= 0; i--) {
93
- try {
94
- list[i]!()
95
- } catch (err) {
96
- panicAsync(err)
97
- }
98
- }
99
- }
100
- }
101
-
102
- class Subscription {
103
- constructor(private subscriber: Subscriber<unknown>) {}
104
- unsubscribe(): void {
105
- this.subscriber.closeSilently()
106
- }
107
- }
108
-
109
- type SubscribeCallback<T> = (sub: Subscriber<T>) => void
110
-
111
- export class Observable<Ok, Err = never> {
112
- // The shim doesn't actually use `_phantom` at runtime; the type parameters are
113
- // purely for type-level alignment with the public interface.
114
- declare readonly _phantom: [Ok, Err]
115
- #cb: SubscribeCallback<unknown>
116
-
117
- constructor(cb: SubscribeCallback<unknown>) {
118
- if (typeof cb !== 'function') {
119
- throw new TypeError('Observable: constructor requires a function argument')
120
- }
121
- this.#cb = cb
122
- }
123
-
124
- subscribe(observer?: unknown): Subscription {
125
- const sub = new Subscriber<unknown>(observer)
126
- try {
127
- this.#cb(sub)
128
- } catch (err) {
129
- sub.closeSilently()
130
- throw err
131
- }
132
- return new Subscription(sub)
133
- }
134
-
135
- pipe(...ops: Array<(o: Observable<unknown, unknown>) => Observable<unknown, unknown>>) {
136
- let current: Observable<unknown, unknown> = this as unknown as Observable<unknown, unknown>
137
- for (const op of ops) {
138
- if (typeof op !== 'function') {
139
- throw new TypeError('pipe: arguments must be operator functions')
140
- }
141
- current = op(current)
142
- }
143
- return current
144
- }
145
-
146
- static from(src: unknown): Observable<unknown, unknown> {
147
- if (src instanceof Observable) return src
148
- if (
149
- src != null &&
150
- (typeof src === 'object' || typeof src === 'function') &&
151
- typeof (src as {then?: unknown}).then === 'function'
152
- ) {
153
- // Promise-shaped
154
- return new Observable<unknown>((sub) => {
155
- ;(src as PromiseLike<unknown>).then((value) => {
156
- if (sub.closed) return
157
- sub.next(value)
158
- if (sub.closed) return
159
- sub.complete()
160
- })
161
- })
162
- }
163
- if (
164
- src != null &&
165
- (typeof src === 'object' || typeof src === 'string') &&
166
- typeof (src as {[Symbol.iterator]?: unknown})[Symbol.iterator] === 'function'
167
- ) {
168
- return new Observable<unknown>((sub) => {
169
- for (const value of src as Iterable<unknown>) {
170
- if (sub.closed) return
171
- sub.next(value)
172
- }
173
- if (!sub.closed) sub.complete()
174
- })
175
- }
176
- throw new TypeError('Observable.from: source must be a Promise, Iterable, or Observable')
177
- }
178
-
179
- static withEmitters<Ok, Err = never>(): {
180
- observable: Observable<Ok, Err>
181
- next: (value: unknown) => void
182
- complete: () => void
183
- } {
184
- const subs: Array<Subscriber<unknown>> = []
185
- let completed = false
186
-
187
- const observable = new Observable<unknown>((sub) => {
188
- if (completed) {
189
- sub.complete()
190
- return
191
- }
192
- subs.push(sub)
193
- sub.addTeardown(() => {
194
- const i = subs.indexOf(sub)
195
- if (i >= 0) subs.splice(i, 1)
196
- })
197
- })
198
-
199
- const next = (value: unknown) => {
200
- if (completed) return
201
- // Snapshot to be resilient against mid-dispatch unsubscribes.
202
- const snapshot = subs.slice()
203
- for (const s of snapshot) {
204
- if (!s.closed) s.next(value)
205
- }
206
- }
207
-
208
- const complete = () => {
209
- if (completed) return
210
- completed = true
211
- const snapshot = subs.slice()
212
- subs.length = 0
213
- for (const s of snapshot) {
214
- if (!s.closed) s.complete()
215
- }
216
- }
217
-
218
- return {observable: observable as unknown as Observable<Ok, Err>, next, complete}
219
- }
220
- }
@@ -1 +0,0 @@
1
- export {Observable} from 'native:observable'