@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.
Files changed (109) hide show
  1. package/CMakeLists.txt +198 -0
  2. package/LICENSE +21 -0
  3. package/README.md +49 -0
  4. package/cmake/mikrojs_bytecode.cmake +146 -0
  5. package/cmake.js +22 -0
  6. package/dist/index.d.ts +52 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +132 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/types.d.ts +43 -0
  11. package/dist/types.d.ts.map +1 -0
  12. package/dist/types.js +2 -0
  13. package/dist/types.js.map +1 -0
  14. package/include/byteorder_apple.h +11 -0
  15. package/include/byteorder_windows.h +12 -0
  16. package/include/mikrojs/cbor_helpers.h +24 -0
  17. package/include/mikrojs/cutils_wrap.h +59 -0
  18. package/include/mikrojs/errors.h +144 -0
  19. package/include/mikrojs/mem.h +11 -0
  20. package/include/mikrojs/mik_color.h +32 -0
  21. package/include/mikrojs/mikrojs.h +331 -0
  22. package/include/mikrojs/platform.h +82 -0
  23. package/include/mikrojs/private.h +281 -0
  24. package/include/mikrojs/utils.h +125 -0
  25. package/package.json +100 -0
  26. package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
  27. package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
  28. package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
  29. package/runtime/ble/ble.ts +231 -0
  30. package/runtime/ble/types.ts +194 -0
  31. package/runtime/ble/uuid.ts +89 -0
  32. package/runtime/ble/validators.ts +61 -0
  33. package/runtime/cbor/cbor.ts +1 -0
  34. package/runtime/cbor/types.ts +8 -0
  35. package/runtime/console/types.ts +50 -0
  36. package/runtime/env/env.ts +17 -0
  37. package/runtime/env/types.ts +12 -0
  38. package/runtime/format/types.ts +4 -0
  39. package/runtime/fs/fs.ts +93 -0
  40. package/runtime/fs/types.ts +92 -0
  41. package/runtime/globals.d.ts +87 -0
  42. package/runtime/http/helpers.ts +222 -0
  43. package/runtime/http/native.ts +151 -0
  44. package/runtime/http/request.ts +25 -0
  45. package/runtime/i2c/i2c.ts +35 -0
  46. package/runtime/i2c/types.ts +55 -0
  47. package/runtime/inspect/types.ts +10 -0
  48. package/runtime/internal.d.ts +456 -0
  49. package/runtime/kv/nvs.ts +17 -0
  50. package/runtime/kv/rtc.ts +17 -0
  51. package/runtime/kv/shared.ts +107 -0
  52. package/runtime/kv/types.ts +150 -0
  53. package/runtime/neopixel/neopixel.ts +38 -0
  54. package/runtime/neopixel/types.ts +27 -0
  55. package/runtime/pin/pin.ts +51 -0
  56. package/runtime/pin/types.ts +49 -0
  57. package/runtime/pwm/pwm.ts +32 -0
  58. package/runtime/pwm/types.ts +29 -0
  59. package/runtime/reader/reader.ts +167 -0
  60. package/runtime/reader/types.ts +34 -0
  61. package/runtime/result/native-result.node-shim.ts +44 -0
  62. package/runtime/result/result.ts +26 -0
  63. package/runtime/result/types.ts +60 -0
  64. package/runtime/schema/schema.ts +321 -0
  65. package/runtime/schema/types.ts +152 -0
  66. package/runtime/sleep/sleep.ts +14 -0
  67. package/runtime/sleep/types.ts +44 -0
  68. package/runtime/sntp/sntp.ts +54 -0
  69. package/runtime/sntp/types.ts +38 -0
  70. package/runtime/spi/spi.ts +31 -0
  71. package/runtime/spi/types.ts +42 -0
  72. package/runtime/stdio/stdio.ts +44 -0
  73. package/runtime/stdio/types.ts +22 -0
  74. package/runtime/stream/stream.ts +150 -0
  75. package/runtime/stream/types.ts +47 -0
  76. package/runtime/sys/sys.ts +90 -0
  77. package/runtime/sys/types.ts +131 -0
  78. package/runtime/test/test.ts +595 -0
  79. package/runtime/test/types.ts +97 -0
  80. package/runtime/uart/types.ts +75 -0
  81. package/runtime/uart/uart.ts +51 -0
  82. package/runtime/wifi/types.ts +156 -0
  83. package/runtime/wifi/wifi.ts +208 -0
  84. package/scripts/bundle-runtime.js +149 -0
  85. package/scripts/compare-minifiers.js +189 -0
  86. package/scripts/compile-bytecode.sh +38 -0
  87. package/scripts/copy-prebuild.js +20 -0
  88. package/scripts/generate-symbol-map.js +146 -0
  89. package/src/builtins.cpp +82 -0
  90. package/src/cutils_compat.c +38 -0
  91. package/src/eval_bytecode.cpp +42 -0
  92. package/src/fs.cpp +878 -0
  93. package/src/mem.cpp +63 -0
  94. package/src/mik_abort.cpp +160 -0
  95. package/src/mik_app_config.cpp +358 -0
  96. package/src/mik_cbor.cpp +334 -0
  97. package/src/mik_color.cpp +46 -0
  98. package/src/mik_console.cpp +422 -0
  99. package/src/mik_inspect.cpp +850 -0
  100. package/src/mik_repl.cpp +1122 -0
  101. package/src/mik_result.cpp +344 -0
  102. package/src/mik_stdio.cpp +147 -0
  103. package/src/mik_sys.cpp +239 -0
  104. package/src/mik_text_encoding.cpp +443 -0
  105. package/src/mikrojs.cpp +942 -0
  106. package/src/modules.cpp +944 -0
  107. package/src/platform_posix.cpp +134 -0
  108. package/src/timers.cpp +208 -0
  109. 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
+ }