@mikrojs/native 0.5.1 → 0.6.0-pr-70.gc88e298
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 +3 -1
- package/include/mikrojs/private.h +3 -0
- package/package.json +4 -2
- package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
- package/runtime/ble/ble.ts +16 -8
- package/runtime/ble/types.ts +7 -14
- package/runtime/internal.d.ts +4 -0
- package/runtime/observable/native-observable.node-shim.ts +212 -0
- package/runtime/observable/observable.ts +1 -0
- package/runtime/observable/operators.ts +129 -0
- package/runtime/observable/types.ts +80 -0
- package/runtime/udp/types.ts +15 -2
- package/runtime/udp/udp.ts +45 -9
- package/runtime/wifi/types.ts +6 -15
- package/runtime/wifi/wifi.ts +24 -16
- package/src/mik_observable.cpp +882 -0
- package/src/mikrojs.cpp +1 -0
package/runtime/udp/udp.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import {Observable} from 'mikrojs/observable'
|
|
1
2
|
import {ok} from 'mikrojs/result'
|
|
2
3
|
import {bind as nativeBind, type NativeUdpSocket} from 'native:udp'
|
|
3
4
|
|
|
5
|
+
import type {Subscriber} from '../observable/types.js'
|
|
4
6
|
import type {Result} from '../result/types.js'
|
|
5
|
-
import type {BindOptions, MulticastGroup,
|
|
7
|
+
import type {BindOptions, MulticastGroup, UdpError, UdpMessage, UdpSocket} from './types.js'
|
|
6
8
|
|
|
7
9
|
const utf8 = new TextEncoder()
|
|
8
10
|
|
|
@@ -11,7 +13,37 @@ function toBytes(data: Uint8Array | string): Uint8Array {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
function makeSocket(handle: NativeUdpSocket): UdpSocket {
|
|
14
|
-
|
|
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
|
+
})
|
|
15
47
|
|
|
16
48
|
const sock: UdpSocket = {
|
|
17
49
|
get port() {
|
|
@@ -26,13 +58,7 @@ function makeSocket(handle: NativeUdpSocket): UdpSocket {
|
|
|
26
58
|
set dropped(value: number) {
|
|
27
59
|
handle.dropped = value
|
|
28
60
|
},
|
|
29
|
-
|
|
30
|
-
return onMessage
|
|
31
|
-
},
|
|
32
|
-
set onMessage(fn) {
|
|
33
|
-
onMessage = fn
|
|
34
|
-
handle.setOnMessage(fn)
|
|
35
|
-
},
|
|
61
|
+
onMessage,
|
|
36
62
|
send(data, to) {
|
|
37
63
|
return handle.send(toBytes(data), to)
|
|
38
64
|
},
|
|
@@ -43,6 +69,16 @@ function makeSocket(handle: NativeUdpSocket): UdpSocket {
|
|
|
43
69
|
return handle.leaveMulticastGroup(group.address)
|
|
44
70
|
},
|
|
45
71
|
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
|
+
}
|
|
46
82
|
handle.close()
|
|
47
83
|
},
|
|
48
84
|
}
|
package/runtime/wifi/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type {Observable} from '../observable/types.js'
|
|
1
2
|
import type {Result} from '../result/types.js'
|
|
2
3
|
|
|
3
4
|
export type WifiStatus =
|
|
@@ -87,17 +88,6 @@ export interface ApStartOptions {
|
|
|
87
88
|
maxConnections?: number
|
|
88
89
|
}
|
|
89
90
|
|
|
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
|
-
|
|
101
91
|
export type WifiError =
|
|
102
92
|
| {name: 'InitFailed'; message: string}
|
|
103
93
|
| {name: 'CountryNotSet'}
|
|
@@ -122,8 +112,8 @@ export interface WifiAp {
|
|
|
122
112
|
readonly stations: ApStationInfo[]
|
|
123
113
|
deauthStation(mac: string): Result<void, WifiError>
|
|
124
114
|
inactiveTimeout: number
|
|
125
|
-
|
|
126
|
-
|
|
115
|
+
readonly onStationConnect: Observable<ApStationInfo>
|
|
116
|
+
readonly onStationDisconnect: Observable<ApStationInfo>
|
|
127
117
|
}
|
|
128
118
|
|
|
129
119
|
export interface Wifi {
|
|
@@ -135,8 +125,9 @@ export interface Wifi {
|
|
|
135
125
|
isConnected: boolean
|
|
136
126
|
scan(opts?: ScanOptions): Promise<Result<ScanResult[], WifiError>>
|
|
137
127
|
|
|
138
|
-
|
|
139
|
-
|
|
128
|
+
readonly onConnect: Observable<WifiConnectionInfo>
|
|
129
|
+
readonly onDisconnect: Observable<WifiDisconnectReason>
|
|
130
|
+
readonly onRssiLow: Observable<number>
|
|
140
131
|
|
|
141
132
|
readonly mac: string
|
|
142
133
|
hostname: string | undefined
|
package/runtime/wifi/wifi.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import {Observable} from 'mikrojs/observable'
|
|
1
2
|
import {err, ok} from 'mikrojs/result'
|
|
2
3
|
import {Wifi as NativeWifi} from 'native:wifi'
|
|
3
4
|
|
|
4
5
|
import type {Result} from '../result/types.js'
|
|
5
6
|
import type {
|
|
6
|
-
ApEventMap,
|
|
7
7
|
ApStartOptions,
|
|
8
8
|
ApStationInfo,
|
|
9
9
|
IpConfig,
|
|
@@ -15,8 +15,8 @@ import type {
|
|
|
15
15
|
WifiAp,
|
|
16
16
|
WifiConnectionInfo,
|
|
17
17
|
WifiCountryCode,
|
|
18
|
+
WifiDisconnectReason,
|
|
18
19
|
WifiError,
|
|
19
|
-
WifiEventMap,
|
|
20
20
|
WifiStatus,
|
|
21
21
|
} from './types.js'
|
|
22
22
|
|
|
@@ -37,6 +37,23 @@ const StatusFromCode = new Map<number, WifiStatus>(
|
|
|
37
37
|
|
|
38
38
|
const native = new NativeWifi()
|
|
39
39
|
|
|
40
|
+
/* One Observable per event type, each backed by a single native.on
|
|
41
|
+
* registration. Subscribers share that registration via the multicast
|
|
42
|
+
* source from Observable.withEmitters() — registering the listener once
|
|
43
|
+
* keeps the C-level callback list at minimum size regardless of how many
|
|
44
|
+
* JS subscribers are attached. */
|
|
45
|
+
const _onConnect = Observable.withEmitters<WifiConnectionInfo>()
|
|
46
|
+
const _onDisconnect = Observable.withEmitters<WifiDisconnectReason>()
|
|
47
|
+
const _onRssiLow = Observable.withEmitters<number>()
|
|
48
|
+
const _onStationConnect = Observable.withEmitters<ApStationInfo>()
|
|
49
|
+
const _onStationDisconnect = Observable.withEmitters<ApStationInfo>()
|
|
50
|
+
|
|
51
|
+
native.on('connect', (info) => _onConnect.next(info as WifiConnectionInfo))
|
|
52
|
+
native.on('disconnect', (reason) => _onDisconnect.next(reason as WifiDisconnectReason))
|
|
53
|
+
native.on('rssi-low', (rssi) => _onRssiLow.next(rssi as number))
|
|
54
|
+
native.on('station-connect', (info) => _onStationConnect.next(info as ApStationInfo))
|
|
55
|
+
native.on('station-disconnect', (info) => _onStationDisconnect.next(info as ApStationInfo))
|
|
56
|
+
|
|
40
57
|
const ap: WifiAp = {
|
|
41
58
|
start(options: ApStartOptions): Result<void, WifiError> {
|
|
42
59
|
return native.apStart(options as Parameters<typeof native.apStart>[0])
|
|
@@ -71,13 +88,8 @@ const ap: WifiAp = {
|
|
|
71
88
|
native.apSetInactiveTimeout(seconds)
|
|
72
89
|
},
|
|
73
90
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
off<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]) {
|
|
79
|
-
native.off(event, listener as (...args: unknown[]) => void)
|
|
80
|
-
},
|
|
91
|
+
onStationConnect: _onStationConnect.observable,
|
|
92
|
+
onStationDisconnect: _onStationDisconnect.observable,
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
const MAX_CONNECT_RETRIES = 5
|
|
@@ -135,13 +147,9 @@ const wifi: Wifi = {
|
|
|
135
147
|
return ok(asyncResult.value as ScanResult[])
|
|
136
148
|
},
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
off<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]) {
|
|
143
|
-
native.off(event, listener as (...args: unknown[]) => void)
|
|
144
|
-
},
|
|
150
|
+
onConnect: _onConnect.observable,
|
|
151
|
+
onDisconnect: _onDisconnect.observable,
|
|
152
|
+
onRssiLow: _onRssiLow.observable,
|
|
145
153
|
|
|
146
154
|
get mac(): string {
|
|
147
155
|
const result = native.mac()
|