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

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
@@ -81,7 +81,7 @@ endif()
81
81
  include(cmake/mikrojs_bytecode.cmake)
82
82
  mikrojs_generate_bytecode(
83
83
  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
84
+ MODULES cbor env result schema fs http/helpers http/request i2c kv/nvs kv/rtc kv/shared neopixel observable observable/lazy observable/operators pin pwm reader sleep spi sntp stdio stream sys test uart udp wifi
85
85
  MODULE_PREFIX "mikrojs"
86
86
  SYMBOL_PREFIX "mikrojs"
87
87
  TARGET gen_bytecode
@@ -30,7 +30,8 @@ typedef struct MIKConfig {
30
30
  uint32_t mem_reserved;
31
31
  uint32_t fs_read_max; /* 0 = keep runtime default (65536) */
32
32
  char entry_point[128];
33
- char wifi_country[3]; /* Two-letter country code + NUL, e.g. "NO" */
33
+ char wifi_country[3]; /* Two-letter country code + NUL, e.g. "NO" */
34
+ char wifi_hostname[64]; /* DHCP hostname; empty = use mikrojs-<device-id> default */
34
35
  } MIKConfig;
35
36
 
36
37
  void MIK_DefaultConfig(MIKConfig* config);
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",
4
4
  "description": "Mikro.js C++ runtime library and Node.js native addon",
5
5
  "keywords": [
6
6
  "esp32",
@@ -78,7 +78,7 @@
78
78
  "cmake-js": "^8.0.0",
79
79
  "node-addon-api": "^8.7.0",
80
80
  "node-gyp-build": "^4.8.4",
81
- "@mikrojs/quickjs": "0.6.0-pr-70.gd2fdc0d"
81
+ "@mikrojs/quickjs": "0.6.0"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@swc/core": "^1.15.30",
@@ -1,8 +1,7 @@
1
- import {Observable} from 'mikrojs/observable'
1
+ import {lazyEvent} from 'mikrojs/observable/lazy'
2
2
  import {err, ok} from 'mikrojs/result'
3
3
  import {Ble as NativeBle} from 'native:ble'
4
4
 
5
- import type {Subscriber} from '../observable/types.js'
6
5
  import type {Result} from '../result/types.js'
7
6
  import type {
8
7
  AdvertiseHandle,
@@ -143,32 +142,6 @@ function normalizeServices(services: Service[]) {
143
142
 
144
143
  const native = new NativeBle()
145
144
 
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
145
  const ble: Ble = {
173
146
  get name(): string {
174
147
  return native.getName()
@@ -248,9 +221,9 @@ const peripheral: Peripheral = {
248
221
  return ok(handle)
249
222
  },
250
223
 
251
- onConnect: lazyEvent<ConnectionInfo>('connect'),
252
- onDisconnect: lazyEvent<ConnectionInfo>('disconnect'),
253
- onMtu: lazyEvent<MtuInfo>('mtu'),
224
+ onConnect: lazyEvent<ConnectionInfo>(native, 'connect'),
225
+ onDisconnect: lazyEvent<ConnectionInfo>(native, 'disconnect'),
226
+ onMtu: lazyEvent<MtuInfo>(native, 'mtu'),
254
227
  }
255
228
 
256
229
  export {ble, peripheral}
@@ -11,6 +11,10 @@ declare module 'mikrojs/kv/shared' {
11
11
  export {KVError, makeCreateValue, type NativeKvFns} from './kv/shared.js'
12
12
  }
13
13
 
14
+ declare module 'mikrojs/observable/lazy' {
15
+ export {lazyEvent} from './observable/lazy.js'
16
+ }
17
+
14
18
  declare module 'native:cbor' {
15
19
  import type {CborError} from '@mikrojs/native/runtime/cbor/types'
16
20
  import type {Result} from 'mikrojs/result'
@@ -411,7 +415,6 @@ declare module 'native:wifi' {
411
415
  // Network config
412
416
  mac(): R<string>
413
417
  getHostname(): string | undefined
414
- setHostname(hostname: string): R<void>
415
418
  getIpConfig(): R<{ip: string; netmask: string; gateway: string; dns: string} | undefined>
416
419
  setIpConfig(opts: {
417
420
  ip?: string
@@ -450,7 +453,6 @@ declare module 'native:wifi' {
450
453
  getPowerSave(): string
451
454
  setPowerSave(mode: string): R<void>
452
455
  getCountry(): string | undefined
453
- setCountry(cc: string): R<void>
454
456
  }
455
457
 
456
458
  export declare const Wifi: {
@@ -0,0 +1,39 @@
1
+ import {Observable} from 'native:observable'
2
+
3
+ import type {Subscriber} from './types.js'
4
+
5
+ /* Lazy-attach Observable: native.on is only called once a JS subscriber
6
+ * appears, and native.off runs when the last one unsubscribes. Code paths
7
+ * that import the host module (wifi/ble/...) but don't subscribe take the
8
+ * exact same internal-RAM path as before observables existed — important
9
+ * because mbedTLS handshake needs ~16 KB contiguous internal SRAM and each
10
+ * eager closure pinned at module load chips into that headroom. */
11
+
12
+ interface NativeEventSource {
13
+ on(event: string, listener: (...args: unknown[]) => void): void
14
+ off(event: string, listener: (...args: unknown[]) => void): void
15
+ }
16
+
17
+ export function lazyEvent<T>(source: NativeEventSource, eventName: string): Observable<T> {
18
+ const subscribers: Subscriber<T>[] = []
19
+ function handler(value: unknown): void {
20
+ /* Snapshot so unsubscribes during dispatch don't shift indices. */
21
+ const snapshot = subscribers.slice()
22
+ for (const s of snapshot) {
23
+ if (!s.closed) s.next(value as T)
24
+ }
25
+ }
26
+ return new Observable<T>((sub) => {
27
+ subscribers.push(sub)
28
+ if (subscribers.length === 1) {
29
+ source.on(eventName, handler)
30
+ }
31
+ sub.addTeardown(() => {
32
+ const i = subscribers.indexOf(sub)
33
+ if (i >= 0) subscribers.splice(i, 1)
34
+ if (subscribers.length === 0) {
35
+ source.off(eventName, handler)
36
+ }
37
+ })
38
+ })
39
+ }
@@ -17,12 +17,16 @@
17
17
  * See `.claude/plans/observable.md` for the full design.
18
18
  */
19
19
 
20
- /* Importing from `mikrojs/observable` (not `native:observable`) so this
21
- * module typechecks from any package that already re-exports it. At runtime
22
- * `mikrojs/observable` resolves to the bytecode bundle which re-exports the
23
- * native class; at typecheck it resolves to the ambient `declare class` in
24
- * runtime/observable/types.ts. */
25
- import {Observable} from 'mikrojs/observable'
20
+ import {Observable as NativeObservable} from 'native:observable'
21
+
22
+ import type {Observable as ObservableT} from './types.js'
23
+
24
+ /* `native:observable` resolves only inside the runtime build; outside
25
+ * (twoslash, host typecheck without internal.d.ts) it falls back to `any`,
26
+ * which collapses pipe/operator inference at use sites. Pin the type to the
27
+ * declared class in `./types.ts` so consumers always see the typed shape. */
28
+ const Observable = NativeObservable as unknown as typeof ObservableT
29
+ type Observable<Ok, Err = never> = ObservableT<Ok, Err>
26
30
 
27
31
  /* Catch a thrown error and re-throw it on the next tick. The synchronous
28
32
  * caller keeps going; the error eventually surfaces as an uncaught
@@ -1,5 +1,5 @@
1
- import {Observable} from 'mikrojs/observable'
2
1
  import {ok} from 'mikrojs/result'
2
+ import {Observable} from 'native:observable'
3
3
  import {bind as nativeBind, type NativeUdpSocket} from 'native:udp'
4
4
 
5
5
  import type {Subscriber} from '../observable/types.js'
@@ -130,7 +130,7 @@ export interface Wifi {
130
130
  readonly onRssiLow: Observable<number>
131
131
 
132
132
  readonly mac: string
133
- hostname: string | undefined
133
+ readonly hostname: string | undefined
134
134
  ipConfig(): Result<IpConfig | undefined, WifiError>
135
135
  ipConfig(opts: StaticIpConfig): Result<void, WifiError>
136
136
  ipConfig(opts?: StaticIpConfig): Result<IpConfig | undefined, WifiError> | Result<void, WifiError>
@@ -141,7 +141,7 @@ export interface Wifi {
141
141
  rssiThreshold: number
142
142
 
143
143
  powerSave: PowerSaveMode
144
- country: WifiCountryCode | undefined
144
+ readonly country: WifiCountryCode | undefined
145
145
  }
146
146
 
147
147
  export declare const wifi: Wifi
@@ -1,8 +1,7 @@
1
- import {Observable} from 'mikrojs/observable'
1
+ import {lazyEvent} from 'mikrojs/observable/lazy'
2
2
  import {err, ok} from 'mikrojs/result'
3
3
  import {Wifi as NativeWifi} from 'native:wifi'
4
4
 
5
- import type {Subscriber} from '../observable/types.js'
6
5
  import type {Result} from '../result/types.js'
7
6
  import type {
8
7
  ApStartOptions,
@@ -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,8 @@ 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
+ onStationConnect: lazyEvent<ApStationInfo>(native, 'station-connect'),
75
+ onStationDisconnect: lazyEvent<ApStationInfo>(native, 'station-disconnect'),
107
76
  }
108
77
 
109
78
  const MAX_CONNECT_RETRIES = 5
@@ -161,9 +130,9 @@ const wifi: Wifi = {
161
130
  return ok(asyncResult.value as ScanResult[])
162
131
  },
163
132
 
164
- onConnect: lazyEvent<WifiConnectionInfo>('connect'),
165
- onDisconnect: lazyEvent<WifiDisconnectReason>('disconnect'),
166
- onRssiLow: lazyEvent<number>('rssi-low'),
133
+ onConnect: lazyEvent<WifiConnectionInfo>(native, 'connect'),
134
+ onDisconnect: lazyEvent<WifiDisconnectReason>(native, 'disconnect'),
135
+ onRssiLow: lazyEvent<number>(native, 'rssi-low'),
167
136
 
168
137
  get mac(): string {
169
138
  const result = native.mac()
@@ -174,12 +143,6 @@ const wifi: Wifi = {
174
143
  return native.getHostname()
175
144
  },
176
145
 
177
- set hostname(value: string | undefined) {
178
- if (value !== undefined) {
179
- native.setHostname(value)
180
- }
181
- },
182
-
183
146
  ipConfig: ((
184
147
  opts?: StaticIpConfig,
185
148
  ): Result<IpConfig | undefined, WifiError> | Result<void, WifiError> => {
@@ -219,12 +182,6 @@ const wifi: Wifi = {
219
182
  get country(): WifiCountryCode | undefined {
220
183
  return native.getCountry() as WifiCountryCode | undefined
221
184
  },
222
-
223
- set country(cc: WifiCountryCode | undefined) {
224
- if (cc !== undefined) {
225
- native.setCountry(cc)
226
- }
227
- },
228
185
  }
229
186
 
230
187
  export {wifi}
@@ -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, "wifiCountry", config->wifi_country,
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",