@mikrojs/native 0.6.0-pr-72.g464c29c → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +3 -1
- package/include/mikrojs/mikrojs.h +2 -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 +6 -8
- package/runtime/ble/types.ts +7 -14
- package/runtime/internal.d.ts +8 -2
- package/runtime/observable/lazy.ts +39 -0
- package/runtime/observable/native-observable.node-shim.ts +220 -0
- package/runtime/observable/observable.ts +1 -0
- package/runtime/observable/operators.ts +144 -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 +8 -17
- package/runtime/wifi/wifi.ts +7 -28
- package/src/mik_app_config.cpp +4 -1
- package/src/mik_observable.cpp +950 -0
- package/src/mik_repl.cpp +9 -4
- package/src/mikrojs.cpp +1 -0
package/runtime/udp/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 UdpFamily = 'ipv4' | 'ipv6'
|
|
@@ -31,19 +32,31 @@ export type UdpError =
|
|
|
31
32
|
| {name: 'Closed'}
|
|
32
33
|
| {name: 'NotBound'}
|
|
33
34
|
|
|
35
|
+
/** Inbound datagram delivered via `socket.onMessage`. */
|
|
36
|
+
export interface UdpMessage {
|
|
37
|
+
msg: Uint8Array
|
|
38
|
+
from: PeerAddress
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
export interface UdpSocket {
|
|
35
42
|
readonly port: number
|
|
36
43
|
readonly family: UdpFamily | 'dual'
|
|
37
44
|
/**
|
|
38
45
|
* Counter incremented each time an inbound datagram is dropped:
|
|
39
46
|
* - the per-socket queue is full,
|
|
40
|
-
* - no `onMessage`
|
|
47
|
+
* - no subscriber is attached to `onMessage` when a packet arrives,
|
|
41
48
|
* - or the datagram is larger than the 1500-byte receive buffer
|
|
42
49
|
* (typical Ethernet MTU; covers CoAP / mDNS / SNTP / DNS).
|
|
43
50
|
*/
|
|
44
51
|
dropped: number
|
|
45
52
|
|
|
46
|
-
|
|
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>
|
|
47
60
|
|
|
48
61
|
send(data: Uint8Array | string, to: PeerAddress): Promise<Result<void, UdpError>>
|
|
49
62
|
joinMulticastGroup(group: MulticastGroup): Result<void, UdpError>
|
package/runtime/udp/udp.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {ok} from 'mikrojs/result'
|
|
2
|
+
import {Observable} from 'native:observable'
|
|
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,11 +125,12 @@ 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
|
-
hostname: string | undefined
|
|
133
|
+
readonly hostname: string | undefined
|
|
143
134
|
ipConfig(): Result<IpConfig | undefined, WifiError>
|
|
144
135
|
ipConfig(opts: StaticIpConfig): Result<void, WifiError>
|
|
145
136
|
ipConfig(opts?: StaticIpConfig): Result<IpConfig | undefined, WifiError> | Result<void, WifiError>
|
|
@@ -150,7 +141,7 @@ export interface Wifi {
|
|
|
150
141
|
rssiThreshold: number
|
|
151
142
|
|
|
152
143
|
powerSave: PowerSaveMode
|
|
153
|
-
country: WifiCountryCode | undefined
|
|
144
|
+
readonly country: WifiCountryCode | undefined
|
|
154
145
|
}
|
|
155
146
|
|
|
156
147
|
export declare const wifi: Wifi
|
package/runtime/wifi/wifi.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import {lazyEvent} from 'mikrojs/observable/lazy'
|
|
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
|
|
|
@@ -71,13 +71,8 @@ const ap: WifiAp = {
|
|
|
71
71
|
native.apSetInactiveTimeout(seconds)
|
|
72
72
|
},
|
|
73
73
|
|
|
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
|
-
},
|
|
74
|
+
onStationConnect: lazyEvent<ApStationInfo>(native, 'station-connect'),
|
|
75
|
+
onStationDisconnect: lazyEvent<ApStationInfo>(native, 'station-disconnect'),
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
const MAX_CONNECT_RETRIES = 5
|
|
@@ -135,13 +130,9 @@ const wifi: Wifi = {
|
|
|
135
130
|
return ok(asyncResult.value as ScanResult[])
|
|
136
131
|
},
|
|
137
132
|
|
|
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
|
-
},
|
|
133
|
+
onConnect: lazyEvent<WifiConnectionInfo>(native, 'connect'),
|
|
134
|
+
onDisconnect: lazyEvent<WifiDisconnectReason>(native, 'disconnect'),
|
|
135
|
+
onRssiLow: lazyEvent<number>(native, 'rssi-low'),
|
|
145
136
|
|
|
146
137
|
get mac(): string {
|
|
147
138
|
const result = native.mac()
|
|
@@ -152,12 +143,6 @@ const wifi: Wifi = {
|
|
|
152
143
|
return native.getHostname()
|
|
153
144
|
},
|
|
154
145
|
|
|
155
|
-
set hostname(value: string | undefined) {
|
|
156
|
-
if (value !== undefined) {
|
|
157
|
-
native.setHostname(value)
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
|
|
161
146
|
ipConfig: ((
|
|
162
147
|
opts?: StaticIpConfig,
|
|
163
148
|
): Result<IpConfig | undefined, WifiError> | Result<void, WifiError> => {
|
|
@@ -197,12 +182,6 @@ const wifi: Wifi = {
|
|
|
197
182
|
get country(): WifiCountryCode | undefined {
|
|
198
183
|
return native.getCountry() as WifiCountryCode | undefined
|
|
199
184
|
},
|
|
200
|
-
|
|
201
|
-
set country(cc: WifiCountryCode | undefined) {
|
|
202
|
-
if (cc !== undefined) {
|
|
203
|
-
native.setCountry(cc)
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
185
|
}
|
|
207
186
|
|
|
208
187
|
export {wifi}
|
package/src/mik_app_config.cpp
CHANGED
|
@@ -19,6 +19,7 @@ void MIK_DefaultConfig(MIKConfig* config) {
|
|
|
19
19
|
config->fs_read_max = 0; /* 0 = runtime default (65536) */
|
|
20
20
|
config->entry_point[0] = '\0';
|
|
21
21
|
config->wifi_country[0] = '\0';
|
|
22
|
+
config->wifi_hostname[0] = '\0';
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/* Minimal JSON parser for config file — avoids cJSON dependency.
|
|
@@ -343,8 +344,10 @@ int MIK_LoadConfig(const char* base_path, MIKConfig* config) {
|
|
|
343
344
|
if (mik__json_get_number(buf, "fsReadMax", &num_val)) {
|
|
344
345
|
config->fs_read_max = (uint32_t)num_val;
|
|
345
346
|
}
|
|
346
|
-
mik__json_get_string(buf, "
|
|
347
|
+
mik__json_get_string(buf, "wifi.country", config->wifi_country,
|
|
347
348
|
sizeof(config->wifi_country));
|
|
349
|
+
mik__json_get_string(buf, "wifi.hostname", config->wifi_hostname,
|
|
350
|
+
sizeof(config->wifi_hostname));
|
|
348
351
|
|
|
349
352
|
platform->log(MIK_LOG_INFO, TAG,
|
|
350
353
|
"Loaded config: restart=%d delay=%dms stack=%u reserved=%lu",
|