@mikrojs/native 0.0.7
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 +198 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/cmake/mikrojs_bytecode.cmake +146 -0
- package/cmake.js +22 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/include/byteorder_apple.h +11 -0
- package/include/byteorder_windows.h +12 -0
- package/include/mikrojs/cbor_helpers.h +24 -0
- package/include/mikrojs/cutils_wrap.h +59 -0
- package/include/mikrojs/errors.h +144 -0
- package/include/mikrojs/mem.h +11 -0
- package/include/mikrojs/mik_color.h +32 -0
- package/include/mikrojs/mikrojs.h +331 -0
- package/include/mikrojs/platform.h +82 -0
- package/include/mikrojs/private.h +281 -0
- package/include/mikrojs/utils.h +125 -0
- package/package.json +100 -0
- 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 +231 -0
- package/runtime/ble/types.ts +194 -0
- package/runtime/ble/uuid.ts +89 -0
- package/runtime/ble/validators.ts +61 -0
- package/runtime/cbor/cbor.ts +1 -0
- package/runtime/cbor/types.ts +8 -0
- package/runtime/console/types.ts +50 -0
- package/runtime/env/env.ts +17 -0
- package/runtime/env/types.ts +12 -0
- package/runtime/format/types.ts +4 -0
- package/runtime/fs/fs.ts +93 -0
- package/runtime/fs/types.ts +92 -0
- package/runtime/globals.d.ts +87 -0
- package/runtime/http/helpers.ts +222 -0
- package/runtime/http/native.ts +151 -0
- package/runtime/http/request.ts +25 -0
- package/runtime/i2c/i2c.ts +35 -0
- package/runtime/i2c/types.ts +55 -0
- package/runtime/inspect/types.ts +10 -0
- package/runtime/internal.d.ts +456 -0
- package/runtime/kv/nvs.ts +17 -0
- package/runtime/kv/rtc.ts +17 -0
- package/runtime/kv/shared.ts +107 -0
- package/runtime/kv/types.ts +150 -0
- package/runtime/neopixel/neopixel.ts +38 -0
- package/runtime/neopixel/types.ts +27 -0
- package/runtime/pin/pin.ts +51 -0
- package/runtime/pin/types.ts +49 -0
- package/runtime/pwm/pwm.ts +32 -0
- package/runtime/pwm/types.ts +29 -0
- package/runtime/reader/reader.ts +167 -0
- package/runtime/reader/types.ts +34 -0
- package/runtime/result/native-result.node-shim.ts +44 -0
- package/runtime/result/result.ts +26 -0
- package/runtime/result/types.ts +60 -0
- package/runtime/schema/schema.ts +321 -0
- package/runtime/schema/types.ts +152 -0
- package/runtime/sleep/sleep.ts +14 -0
- package/runtime/sleep/types.ts +44 -0
- package/runtime/sntp/sntp.ts +54 -0
- package/runtime/sntp/types.ts +38 -0
- package/runtime/spi/spi.ts +31 -0
- package/runtime/spi/types.ts +42 -0
- package/runtime/stdio/stdio.ts +44 -0
- package/runtime/stdio/types.ts +22 -0
- package/runtime/stream/stream.ts +150 -0
- package/runtime/stream/types.ts +47 -0
- package/runtime/sys/sys.ts +90 -0
- package/runtime/sys/types.ts +131 -0
- package/runtime/test/test.ts +595 -0
- package/runtime/test/types.ts +97 -0
- package/runtime/uart/types.ts +75 -0
- package/runtime/uart/uart.ts +51 -0
- package/runtime/wifi/types.ts +156 -0
- package/runtime/wifi/wifi.ts +208 -0
- package/scripts/bundle-runtime.js +149 -0
- package/scripts/compare-minifiers.js +189 -0
- package/scripts/compile-bytecode.sh +38 -0
- package/scripts/copy-prebuild.js +20 -0
- package/scripts/generate-symbol-map.js +146 -0
- package/src/builtins.cpp +82 -0
- package/src/cutils_compat.c +38 -0
- package/src/eval_bytecode.cpp +42 -0
- package/src/fs.cpp +878 -0
- package/src/mem.cpp +63 -0
- package/src/mik_abort.cpp +160 -0
- package/src/mik_app_config.cpp +358 -0
- package/src/mik_cbor.cpp +334 -0
- package/src/mik_color.cpp +46 -0
- package/src/mik_console.cpp +422 -0
- package/src/mik_inspect.cpp +850 -0
- package/src/mik_repl.cpp +1122 -0
- package/src/mik_result.cpp +344 -0
- package/src/mik_stdio.cpp +147 -0
- package/src/mik_sys.cpp +239 -0
- package/src/mik_text_encoding.cpp +443 -0
- package/src/mikrojs.cpp +942 -0
- package/src/modules.cpp +944 -0
- package/src/platform_posix.cpp +134 -0
- package/src/timers.cpp +208 -0
- package/src/utils.cpp +173 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export interface UartBaseOptions {
|
|
7
|
+
baudRate: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface UartTxRxOptions extends UartBaseOptions {
|
|
14
|
+
tx: number
|
|
15
|
+
rx: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export interface UartTxOnlyOptions extends UartBaseOptions {
|
|
22
|
+
tx: number
|
|
23
|
+
rx?: undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export interface UartRxOnlyOptions extends UartBaseOptions {
|
|
30
|
+
tx?: undefined
|
|
31
|
+
rx: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type UartError =
|
|
35
|
+
| {name: 'DriverInstallFailed'; message: string}
|
|
36
|
+
| {name: 'SetPinFailed'; message: string}
|
|
37
|
+
| {name: 'InvalidParam'; message: string}
|
|
38
|
+
| {name: 'WriteFailed'; message: string}
|
|
39
|
+
| {name: 'ReadFailed'; message: string}
|
|
40
|
+
| {name: 'NotStarted'}
|
|
41
|
+
| {name: 'AlreadyReading'}
|
|
42
|
+
| {name: 'NoRxPin'}
|
|
43
|
+
| {name: 'NoTxPin'}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export interface UartTx {
|
|
49
|
+
write(data: Uint8Array): Result<void, UartError>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export interface UartRx {
|
|
56
|
+
read(): Result<AsyncIterable<Uint8Array>, UartError>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
export interface UartBase {
|
|
63
|
+
begin(): Result<void, UartError>
|
|
64
|
+
end(): Result<void, UartError>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export declare const Uart: {
|
|
71
|
+
prototype: UartBase
|
|
72
|
+
new (port: number, options: UartTxRxOptions): UartBase & UartTx & UartRx
|
|
73
|
+
new (port: number, options: UartTxOnlyOptions): UartBase & UartTx
|
|
74
|
+
new (port: number, options: UartRxOnlyOptions): UartBase & UartRx
|
|
75
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {ok} from 'mikrojs/result'
|
|
2
|
+
import * as native from 'native:uart'
|
|
3
|
+
|
|
4
|
+
import type {Result} from '../result/types.js'
|
|
5
|
+
import type {
|
|
6
|
+
UartBase,
|
|
7
|
+
UartBaseOptions,
|
|
8
|
+
UartError,
|
|
9
|
+
UartRxOnlyOptions,
|
|
10
|
+
UartTxOnlyOptions,
|
|
11
|
+
UartTxRxOptions,
|
|
12
|
+
} from './types.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export class Uart implements UartBase {
|
|
18
|
+
#native: native.Uart
|
|
19
|
+
|
|
20
|
+
constructor(port: number, options: UartTxRxOptions)
|
|
21
|
+
constructor(port: number, options: UartTxOnlyOptions)
|
|
22
|
+
constructor(port: number, options: UartRxOnlyOptions)
|
|
23
|
+
constructor(port: number, options: UartBaseOptions & {tx?: number; rx?: number}) {
|
|
24
|
+
this.#native = new native.Uart(port, options)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
begin(): Result<void, UartError> {
|
|
28
|
+
return this.#native.begin()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
end(): Result<void, UartError> {
|
|
32
|
+
return this.#native.end()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
write(data: Uint8Array): Result<void, UartError> {
|
|
36
|
+
return this.#native.write(data)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
read(): Result<AsyncIterable<Uint8Array>, UartError> {
|
|
40
|
+
const result = this.#native.read()
|
|
41
|
+
if (!result.ok) return result
|
|
42
|
+
const nativeIter = result.value
|
|
43
|
+
// Wrap the native iterator object (which has next/return) into a proper AsyncIterable
|
|
44
|
+
const iterable: AsyncIterable<Uint8Array> = {
|
|
45
|
+
[Symbol.asyncIterator]() {
|
|
46
|
+
return nativeIter as AsyncIterator<Uint8Array>
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
return ok(iterable)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
export type WifiStatus =
|
|
4
|
+
| 'STOPPED'
|
|
5
|
+
| 'IDLE'
|
|
6
|
+
| 'NO_SSID_AVAIL'
|
|
7
|
+
| 'SCAN_COMPLETED'
|
|
8
|
+
| 'CONNECTED'
|
|
9
|
+
| 'CONNECT_FAILED'
|
|
10
|
+
| 'CONNECTION_LOST'
|
|
11
|
+
| 'DISCONNECTED'
|
|
12
|
+
|
|
13
|
+
export type AuthMode = 'open' | 'wpa2-psk' | 'wpa3-psk' | 'wpa2-wpa3-psk' | 'unknown'
|
|
14
|
+
|
|
15
|
+
export type PowerSaveMode = 'none' | 'min' | 'max'
|
|
16
|
+
|
|
17
|
+
// prettier-ignore
|
|
18
|
+
// Based on esp_wifi_regulatory.c wifi_country_table
|
|
19
|
+
// https://github.com/espressif/esp-idf/blob/484e56869c6f6b8f777cc76d73fc02390587a7c5/components/esp_wifi/regulatory/esp_wifi_regulatory.c#L213
|
|
20
|
+
export type WifiCountryCode =
|
|
21
|
+
| "01" | "AD" | "AE" | "AF" | "AI" | "AL" | "AM" | "AN" | "AR" | "AS" | "AT"
|
|
22
|
+
| "AU" | "AW" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BL"
|
|
23
|
+
| "BM" | "BN" | "BO" | "BR" | "BS" | "BT" | "BY" | "BZ" | "CA" | "CF" | "CH"
|
|
24
|
+
| "CI" | "CL" | "CN" | "CO" | "CR" | "CU" | "CX" | "CY" | "CZ" | "DE" | "DK"
|
|
25
|
+
| "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "ES" | "ET" | "EU" | "FI" | "FM"
|
|
26
|
+
| "FR" | "GB" | "GD" | "GE" | "GF" | "GH" | "GL" | "GP" | "GR" | "GT" | "GU"
|
|
27
|
+
| "GY" | "HK" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IN" | "IR"
|
|
28
|
+
| "IS" | "IT" | "JM" | "JO" | "JP" | "KE" | "KH" | "KN" | "KP" | "KR" | "KW"
|
|
29
|
+
| "KY" | "KZ" | "LB" | "LC" | "LI" | "LK" | "LS" | "LT" | "LU" | "LV" | "MA"
|
|
30
|
+
| "MC" | "MD" | "ME" | "MF" | "MH" | "MK" | "MN" | "MO" | "MP" | "MQ" | "MR"
|
|
31
|
+
| "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "NA" | "NG" | "NI" | "NL" | "NO"
|
|
32
|
+
| "NP" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM"
|
|
33
|
+
| "PR" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA"
|
|
34
|
+
| "SE" | "SG" | "SI" | "SK" | "SN" | "SR" | "SV" | "SY" | "TC" | "TD" | "TG"
|
|
35
|
+
| "TH" | "TN" | "TR" | "TT" | "TW" | "TZ" | "UA" | "UG" | "US" | "UY" | "UZ"
|
|
36
|
+
| "VC" | "VE" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZW"
|
|
37
|
+
|
|
38
|
+
export interface ScanOptions {
|
|
39
|
+
ssid?: string
|
|
40
|
+
channel?: number
|
|
41
|
+
passive?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ScanResult {
|
|
45
|
+
ssid: string
|
|
46
|
+
bssid: string
|
|
47
|
+
channel: number
|
|
48
|
+
rssi: number
|
|
49
|
+
authMode: AuthMode
|
|
50
|
+
hidden: boolean
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface WifiConnectionInfo {
|
|
54
|
+
ip: string
|
|
55
|
+
netmask: string
|
|
56
|
+
gateway: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface IpConfig {
|
|
60
|
+
ip: string
|
|
61
|
+
netmask: string
|
|
62
|
+
gateway: string
|
|
63
|
+
dns: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface StaticIpConfig {
|
|
67
|
+
ip?: string
|
|
68
|
+
netmask?: string
|
|
69
|
+
gateway?: string
|
|
70
|
+
dns?: string
|
|
71
|
+
dhcp?: boolean
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type WifiDisconnectReason = 'no-ssid' | 'auth-failed' | 'connection-lost' | 'disconnected'
|
|
75
|
+
|
|
76
|
+
export interface ApStationInfo {
|
|
77
|
+
mac: string
|
|
78
|
+
rssi: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ApStartOptions {
|
|
82
|
+
ssid: string
|
|
83
|
+
passphrase?: string
|
|
84
|
+
authMode?: AuthMode
|
|
85
|
+
channel?: number
|
|
86
|
+
hidden?: boolean
|
|
87
|
+
maxConnections?: number
|
|
88
|
+
}
|
|
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
|
+
|
|
101
|
+
export type WifiError =
|
|
102
|
+
| {name: 'InitFailed'; message: string}
|
|
103
|
+
| {name: 'CountryNotSet'}
|
|
104
|
+
| {name: 'StartFailed'; message: string}
|
|
105
|
+
| {name: 'ConnectFailed'; message: string}
|
|
106
|
+
| {name: 'ConnectInProgress'}
|
|
107
|
+
| {name: 'DisconnectFailed'; message: string}
|
|
108
|
+
| {name: 'ScanFailed'; message: string}
|
|
109
|
+
| {name: 'ScanInProgress'}
|
|
110
|
+
| {name: 'NotInitialized'}
|
|
111
|
+
| {name: 'ConfigFailed'; message: string}
|
|
112
|
+
| {name: 'ApStartFailed'; message: string}
|
|
113
|
+
| {name: 'ApStopFailed'; message: string}
|
|
114
|
+
| {name: 'SetFailed'; message: string}
|
|
115
|
+
| {name: 'GetFailed'; message: string}
|
|
116
|
+
|
|
117
|
+
export interface WifiAp {
|
|
118
|
+
start(options: ApStartOptions): Result<void, WifiError>
|
|
119
|
+
stop(): Result<void, WifiError>
|
|
120
|
+
readonly isActive: boolean
|
|
121
|
+
readonly ip: string | undefined
|
|
122
|
+
readonly stations: ApStationInfo[]
|
|
123
|
+
deauthStation(mac: string): Result<void, WifiError>
|
|
124
|
+
inactiveTimeout: number
|
|
125
|
+
on<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]): void
|
|
126
|
+
off<K extends keyof ApEventMap>(event: K, listener: ApEventMap[K]): void
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface Wifi {
|
|
130
|
+
connect(ssid: string, passphrase: string): Promise<Result<WifiConnectionInfo, WifiError>>
|
|
131
|
+
disconnect(): Result<void, WifiError>
|
|
132
|
+
rssi(): Result<number, WifiError>
|
|
133
|
+
ip(): string | undefined
|
|
134
|
+
status(): WifiStatus
|
|
135
|
+
isConnected: boolean
|
|
136
|
+
scan(opts?: ScanOptions): Promise<Result<ScanResult[], WifiError>>
|
|
137
|
+
|
|
138
|
+
on<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]): void
|
|
139
|
+
off<K extends keyof WifiEventMap>(event: K, listener: WifiEventMap[K]): void
|
|
140
|
+
|
|
141
|
+
readonly mac: string
|
|
142
|
+
hostname: string | undefined
|
|
143
|
+
ipConfig(): Result<IpConfig | undefined, WifiError>
|
|
144
|
+
ipConfig(opts: StaticIpConfig): Result<void, WifiError>
|
|
145
|
+
ipConfig(opts?: StaticIpConfig): Result<IpConfig | undefined, WifiError> | Result<void, WifiError>
|
|
146
|
+
|
|
147
|
+
readonly ap: WifiAp
|
|
148
|
+
|
|
149
|
+
txPower: number
|
|
150
|
+
rssiThreshold: number
|
|
151
|
+
|
|
152
|
+
powerSave: PowerSaveMode
|
|
153
|
+
country: WifiCountryCode | undefined
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export declare const wifi: Wifi
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {err, ok} from 'mikrojs/result'
|
|
2
|
+
import {Wifi as NativeWifi} from 'native:wifi'
|
|
3
|
+
|
|
4
|
+
import type {Result} from '../result/types.js'
|
|
5
|
+
import type {
|
|
6
|
+
ApEventMap,
|
|
7
|
+
ApStartOptions,
|
|
8
|
+
ApStationInfo,
|
|
9
|
+
IpConfig,
|
|
10
|
+
PowerSaveMode,
|
|
11
|
+
ScanOptions,
|
|
12
|
+
ScanResult,
|
|
13
|
+
StaticIpConfig,
|
|
14
|
+
Wifi,
|
|
15
|
+
WifiAp,
|
|
16
|
+
WifiConnectionInfo,
|
|
17
|
+
WifiCountryCode,
|
|
18
|
+
WifiError,
|
|
19
|
+
WifiEventMap,
|
|
20
|
+
WifiStatus,
|
|
21
|
+
} from './types.js'
|
|
22
|
+
|
|
23
|
+
const WifiStatusCodes: Record<WifiStatus, number> = {
|
|
24
|
+
STOPPED: 254,
|
|
25
|
+
IDLE: 0,
|
|
26
|
+
NO_SSID_AVAIL: 1,
|
|
27
|
+
SCAN_COMPLETED: 2,
|
|
28
|
+
CONNECTED: 3,
|
|
29
|
+
CONNECT_FAILED: 4,
|
|
30
|
+
CONNECTION_LOST: 5,
|
|
31
|
+
DISCONNECTED: 6,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const StatusFromCode = new Map<number, WifiStatus>(
|
|
35
|
+
Object.entries(WifiStatusCodes).map(([k, v]) => [v, k as WifiStatus]),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const native = new NativeWifi()
|
|
39
|
+
|
|
40
|
+
const ap: WifiAp = {
|
|
41
|
+
start(options: ApStartOptions): Result<void, WifiError> {
|
|
42
|
+
return native.apStart(options as Parameters<typeof native.apStart>[0])
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
stop(): Result<void, WifiError> {
|
|
46
|
+
return native.apStop()
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
get isActive(): boolean {
|
|
50
|
+
return native.apIsActive()
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
get ip(): string | undefined {
|
|
54
|
+
return native.apIp()
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
get stations(): ApStationInfo[] {
|
|
58
|
+
return native.apStations()
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
deauthStation(mac: string): Result<void, WifiError> {
|
|
62
|
+
return native.apDeauthStation(mac)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
get inactiveTimeout(): number {
|
|
66
|
+
const result = native.apGetInactiveTimeout()
|
|
67
|
+
return result.ok ? result.value : 0
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
set inactiveTimeout(seconds: number) {
|
|
71
|
+
native.apSetInactiveTimeout(seconds)
|
|
72
|
+
},
|
|
73
|
+
|
|
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
|
+
},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const MAX_CONNECT_RETRIES = 5
|
|
84
|
+
const RETRY_DELAY_MS = 2000
|
|
85
|
+
|
|
86
|
+
const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))
|
|
87
|
+
|
|
88
|
+
const wifi: Wifi = {
|
|
89
|
+
async connect(ssid: string, passphrase: string): Promise<Result<WifiConnectionInfo, WifiError>> {
|
|
90
|
+
for (let attempt = 0; attempt <= MAX_CONNECT_RETRIES; attempt++) {
|
|
91
|
+
const startResult = native.connect(ssid, passphrase)
|
|
92
|
+
if (!startResult.ok) return startResult
|
|
93
|
+
|
|
94
|
+
const asyncResult = await startResult.value
|
|
95
|
+
if (asyncResult.ok) return ok(asyncResult.value as WifiConnectionInfo)
|
|
96
|
+
|
|
97
|
+
if (attempt === MAX_CONNECT_RETRIES) return asyncResult
|
|
98
|
+
|
|
99
|
+
// eslint-disable-next-line no-console
|
|
100
|
+
console.warn(`WiFi connect failed, retrying in ${RETRY_DELAY_MS}ms…`)
|
|
101
|
+
native.disconnect()
|
|
102
|
+
await sleep(RETRY_DELAY_MS)
|
|
103
|
+
}
|
|
104
|
+
return err({name: 'ConnectFailed' as const, message: 'max retries exceeded'})
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
disconnect(): Result<void, WifiError> {
|
|
108
|
+
return native.disconnect()
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
rssi(): Result<number, WifiError> {
|
|
112
|
+
return native.rssi()
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
ip(): string | undefined {
|
|
116
|
+
const ip = native.ip()
|
|
117
|
+
return ip === '0.0.0.0' ? undefined : ip
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
status(): WifiStatus {
|
|
121
|
+
const code = native.status()
|
|
122
|
+
return StatusFromCode.get(code) ?? 'DISCONNECTED'
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
get isConnected(): boolean {
|
|
126
|
+
return native.status() === WifiStatusCodes.CONNECTED
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
async scan(opts?: ScanOptions): Promise<Result<ScanResult[], WifiError>> {
|
|
130
|
+
const startResult = native.scan(opts)
|
|
131
|
+
if (!startResult.ok) return startResult
|
|
132
|
+
|
|
133
|
+
const asyncResult = await startResult.value
|
|
134
|
+
if (!asyncResult.ok) return asyncResult
|
|
135
|
+
return ok(asyncResult.value as ScanResult[])
|
|
136
|
+
},
|
|
137
|
+
|
|
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
|
+
},
|
|
145
|
+
|
|
146
|
+
get mac(): string {
|
|
147
|
+
const result = native.mac()
|
|
148
|
+
return result.ok ? result.value : ''
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
get hostname(): string | undefined {
|
|
152
|
+
return native.getHostname()
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
set hostname(value: string | undefined) {
|
|
156
|
+
if (value !== undefined) {
|
|
157
|
+
native.setHostname(value)
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
ipConfig: ((
|
|
162
|
+
opts?: StaticIpConfig,
|
|
163
|
+
): Result<IpConfig | undefined, WifiError> | Result<void, WifiError> => {
|
|
164
|
+
if (opts === undefined) {
|
|
165
|
+
return native.getIpConfig()
|
|
166
|
+
}
|
|
167
|
+
return native.setIpConfig(opts)
|
|
168
|
+
}) as Wifi['ipConfig'],
|
|
169
|
+
|
|
170
|
+
ap,
|
|
171
|
+
|
|
172
|
+
get txPower(): number {
|
|
173
|
+
const result = native.getTxPower()
|
|
174
|
+
return result.ok ? result.value : 0
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
set txPower(dbm: number) {
|
|
178
|
+
native.setTxPower(dbm)
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
get rssiThreshold(): number {
|
|
182
|
+
return native.getRssiThreshold()
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
set rssiThreshold(value: number) {
|
|
186
|
+
native.setRssiThreshold(value)
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
get powerSave(): PowerSaveMode {
|
|
190
|
+
return native.getPowerSave() as PowerSaveMode
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
set powerSave(mode: PowerSaveMode) {
|
|
194
|
+
native.setPowerSave(mode)
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
get country(): WifiCountryCode | undefined {
|
|
198
|
+
return native.getCountry() as WifiCountryCode | undefined
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
set country(cc: WifiCountryCode | undefined) {
|
|
202
|
+
if (cc !== undefined) {
|
|
203
|
+
native.setCountry(cc)
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export {wifi}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {existsSync, mkdirSync, writeFileSync} from 'node:fs'
|
|
2
|
+
import {dirname, join} from 'node:path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bundle runtime TypeScript modules to JavaScript using esbuild.
|
|
6
|
+
* Output goes to a directory where qjsc can compile them to bytecode.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node bundle-runtime.js <outdir> <runtime_dir> <mod1> <mod2> ... [--minifier=NAME] [--minify-level=LEVEL]
|
|
9
|
+
*/
|
|
10
|
+
import {build} from 'esbuild'
|
|
11
|
+
|
|
12
|
+
// Parse positional args and optional flags
|
|
13
|
+
const positionalArgs = []
|
|
14
|
+
let minifier = 'esbuild'
|
|
15
|
+
let minifyLevel = 'default'
|
|
16
|
+
for (const arg of process.argv.slice(2)) {
|
|
17
|
+
if (arg.startsWith('--minifier=')) {
|
|
18
|
+
minifier = arg.slice('--minifier='.length)
|
|
19
|
+
} else if (arg.startsWith('--minify-level=')) {
|
|
20
|
+
minifyLevel = arg.slice('--minify-level='.length)
|
|
21
|
+
} else {
|
|
22
|
+
positionalArgs.push(arg)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const outDir = positionalArgs[0]
|
|
27
|
+
const runtimeDir = positionalArgs[1]
|
|
28
|
+
const moduleNames = positionalArgs.slice(2)
|
|
29
|
+
|
|
30
|
+
if (!outDir || !runtimeDir || moduleNames.length === 0) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.error(
|
|
33
|
+
'Usage: node bundle-runtime.js <outdir> <runtime_dir> <mod1> <mod2> ... [--minifier=NAME] [--minify-level=LEVEL]',
|
|
34
|
+
)
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Dynamic import helper to avoid TypeScript module resolution at parse time
|
|
39
|
+
function importOptional(name) {
|
|
40
|
+
return import(name)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function minifyWithTerser(code) {
|
|
44
|
+
const terser = await importOptional('terser')
|
|
45
|
+
const options =
|
|
46
|
+
minifyLevel === 'max'
|
|
47
|
+
? {
|
|
48
|
+
module: true,
|
|
49
|
+
ecma: 2020,
|
|
50
|
+
compress: {
|
|
51
|
+
ecma: 2020,
|
|
52
|
+
module: true,
|
|
53
|
+
passes: 3,
|
|
54
|
+
toplevel: true,
|
|
55
|
+
pure_getters: true,
|
|
56
|
+
unsafe_arrows: true,
|
|
57
|
+
unsafe_math: true,
|
|
58
|
+
unsafe_methods: true,
|
|
59
|
+
},
|
|
60
|
+
mangle: {module: true, toplevel: true},
|
|
61
|
+
}
|
|
62
|
+
: {module: true}
|
|
63
|
+
const result = await terser.minify(code, options)
|
|
64
|
+
if (result.code === undefined) throw new Error('terser produced no output')
|
|
65
|
+
return result.code
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function minifyWithSwc(code) {
|
|
69
|
+
const swc = await importOptional('@swc/core')
|
|
70
|
+
const options =
|
|
71
|
+
minifyLevel === 'max'
|
|
72
|
+
? {
|
|
73
|
+
module: true,
|
|
74
|
+
compress: {
|
|
75
|
+
module: true,
|
|
76
|
+
passes: 3,
|
|
77
|
+
toplevel: true,
|
|
78
|
+
pure_getters: true,
|
|
79
|
+
unsafe_arrows: true,
|
|
80
|
+
unsafe_math: true,
|
|
81
|
+
unsafe_methods: true,
|
|
82
|
+
},
|
|
83
|
+
mangle: {toplevel: true},
|
|
84
|
+
}
|
|
85
|
+
: {module: true}
|
|
86
|
+
const result = await swc.minify(code, options)
|
|
87
|
+
return result.code
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
mkdirSync(outDir, {recursive: true})
|
|
91
|
+
|
|
92
|
+
for (const name of moduleNames) {
|
|
93
|
+
// Resolve entry point: {name}/{name}.ts (core modules) or {name}.ts (board/driver)
|
|
94
|
+
const nested = join(runtimeDir, name, `${name}.ts`)
|
|
95
|
+
const flat = join(runtimeDir, `${name}.ts`)
|
|
96
|
+
const entry = existsSync(nested) ? nested : flat
|
|
97
|
+
const useEsbuildMinify = minifier === 'esbuild'
|
|
98
|
+
const result = await build({
|
|
99
|
+
bundle: true,
|
|
100
|
+
entryPoints: [entry],
|
|
101
|
+
write: false,
|
|
102
|
+
minify: useEsbuildMinify,
|
|
103
|
+
treeShaking: true,
|
|
104
|
+
target: 'es2024',
|
|
105
|
+
platform: 'neutral',
|
|
106
|
+
format: 'esm',
|
|
107
|
+
external: ['mikrojs', 'mikrojs/*', '@mikrojs/*', 'native:*'],
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const output = result.outputFiles?.[0]
|
|
111
|
+
if (!output) {
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.error(`Bundling ${name} did not produce a file`)
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let source = new TextDecoder().decode(output.contents)
|
|
118
|
+
|
|
119
|
+
// Post-process with selected minifier if not esbuild
|
|
120
|
+
if (!useEsbuildMinify) {
|
|
121
|
+
if (minifier === 'terser') {
|
|
122
|
+
source = await minifyWithTerser(source)
|
|
123
|
+
} else if (minifier === 'swc') {
|
|
124
|
+
source = await minifyWithSwc(source)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Extract external imports so CMake can pass them as -M flags to qjsc
|
|
129
|
+
const externals = []
|
|
130
|
+
for (const match of source.matchAll(
|
|
131
|
+
/(?:from|import)\s*["']((?:native:|mikrojs\/|@mikrojs\/)[^"']+)["']/g,
|
|
132
|
+
)) {
|
|
133
|
+
externals.push(match[1])
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const outFile = join(outDir, `${name}.js`)
|
|
137
|
+
// Module names may contain slashes (e.g. 'kv/nvs') for nested subpath
|
|
138
|
+
// builtins like `mikrojs/kv/nvs`. Ensure the parent directory exists
|
|
139
|
+
// before writing — writeFileSync won't create it on its own.
|
|
140
|
+
mkdirSync(dirname(outFile), {recursive: true})
|
|
141
|
+
writeFileSync(outFile, source)
|
|
142
|
+
|
|
143
|
+
// Write externals list for CMake to read
|
|
144
|
+
const extFile = join(outDir, `${name}.externals`)
|
|
145
|
+
writeFileSync(extFile, externals.join('\n'))
|
|
146
|
+
|
|
147
|
+
// eslint-disable-next-line no-console
|
|
148
|
+
console.log(`Bundled ${name} (${Buffer.byteLength(source)} bytes, ${externals.length} externals)`)
|
|
149
|
+
}
|