@mikrojs/native 0.20.1 → 0.21.0-next.20260913195055

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 (56) hide show
  1. package/CMakeLists.txt +3 -1
  2. package/cmake/mikrojs_bytecode.cmake +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/runtime/result/native-result.node-shim.js.map +1 -1
  6. package/dist/runtime/result/types.d.ts +5 -5
  7. package/dist/runtime/result/types.d.ts.map +1 -1
  8. package/dist/types.d.ts +1 -1
  9. package/dist/types.d.ts.map +1 -1
  10. package/include/mikrojs/mikrojs.h +26 -2
  11. package/include/mikrojs/ota_env.h +8 -6
  12. package/include/mikrojs/ota_policy.h +16 -0
  13. package/include/mikrojs/private.h +13 -0
  14. package/include/mikrojs/utils.h +24 -0
  15. package/package.json +10 -10
  16. package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
  17. package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
  18. package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
  19. package/runtime/gpio/types.ts +116 -0
  20. package/runtime/i2c/types.ts +25 -31
  21. package/runtime/i2s/types.ts +23 -12
  22. package/runtime/internal.d.ts +1 -174
  23. package/runtime/kv/shared.ts +13 -11
  24. package/runtime/neopixel/types.ts +24 -6
  25. package/runtime/observable/native-observable.node-shim.ts +73 -37
  26. package/runtime/observable/native-operators.node-shim.ts +417 -0
  27. package/runtime/observable/observable.ts +76 -1
  28. package/runtime/observable/operators.ts +156 -89
  29. package/runtime/observable/types.ts +61 -6
  30. package/runtime/pwm/types.ts +31 -11
  31. package/runtime/result/native-result.node-shim.ts +1 -1
  32. package/runtime/result/types.ts +5 -5
  33. package/runtime/sleep/types.ts +10 -8
  34. package/runtime/spi/types.ts +16 -14
  35. package/runtime/uart/types.ts +27 -15
  36. package/src/gpio_claim.cpp +63 -0
  37. package/src/mik_abort.cpp +1 -2
  38. package/src/mik_console.cpp +192 -115
  39. package/src/mik_http_client.cpp +3 -14
  40. package/src/mik_inspect.cpp +86 -9
  41. package/src/mik_observable.cpp +1028 -80
  42. package/src/mik_ota_client.cpp +5 -2
  43. package/src/mik_ota_policy.cpp +65 -18
  44. package/src/mik_result.cpp +10 -9
  45. package/src/mikrojs.cpp +26 -13
  46. package/src/modules.cpp +17 -6
  47. package/src/timers.cpp +6 -2
  48. package/src/utils.cpp +106 -0
  49. package/runtime/i2c/i2c.ts +0 -35
  50. package/runtime/i2s/i2s.ts +0 -46
  51. package/runtime/neopixel/neopixel.ts +0 -38
  52. package/runtime/pin/pin.ts +0 -51
  53. package/runtime/pin/types.ts +0 -49
  54. package/runtime/pwm/pwm.ts +0 -32
  55. package/runtime/spi/spi.ts +0 -31
  56. package/runtime/uart/uart.ts +0 -52
@@ -62,7 +62,7 @@ declare module 'native:mikro/result' {
62
62
  }
63
63
 
64
64
  declare module 'native:mikro/observable' {
65
- export {Observable} from '@mikrojs/native/runtime/observable/types'
65
+ export {from, Observable, of} from '@mikrojs/native/runtime/observable/types'
66
66
  }
67
67
 
68
68
  declare module 'native:mikro/sys' {
@@ -158,18 +158,6 @@ declare module 'native:mikro/stdio' {
158
158
  }
159
159
  }
160
160
 
161
- declare module 'native:mikro/pin' {
162
- import type {PinError} from '@mikrojs/native/runtime/pin/types'
163
- import type {Result} from 'mikro/result'
164
- export type PinMode = 0x01 | 0x03 | 0x05
165
- export function pinMode(pin: number, value: PinMode): Result<void, PinError>
166
- export function digitalWrite(pin: number, value: 0 | 1): Result<void, PinError>
167
- export function digitalRead(pin: number): number
168
- // attenuation: 0=0dB, 1=2.5dB, 2=6dB, 3=11dB
169
- export function analogRead(pin: number, attenuation: number): Result<number, PinError>
170
- export function analogReadMillivolts(pin: number, attenuation: number): Result<number, PinError>
171
- }
172
-
173
161
  declare module 'native:mikro/watchdog' {
174
162
  export function feed(): void
175
163
  }
@@ -237,66 +225,6 @@ declare module 'native:mikro/http_server' {
237
225
  export function respondEnd(id: number): void
238
226
  export function getHeader(id: number, name: string): string | undefined
239
227
  }
240
- declare module 'native:mikro/i2c' {
241
- import type {I2cError} from '@mikrojs/native/runtime/i2c/types'
242
- import type {Result} from 'mikro/result'
243
-
244
- export interface I2cBaseOptions {
245
- freq?: number
246
- timeout?: number
247
- }
248
-
249
- export interface I2cOptionsWithPins extends I2cBaseOptions {
250
- sda: number
251
- scl: number
252
- }
253
-
254
- export type I2cOptions = I2cBaseOptions | I2cOptionsWithPins
255
- export declare const I2c: {
256
- prototype: I2c
257
- new (busNo: 0 | 1, options?: I2cOptions): I2c
258
- }
259
-
260
- export interface I2c {
261
- begin(): Result<void, I2cError>
262
-
263
- end(): Result<void, I2cError>
264
-
265
- read(address: number, bytes: number): Result<Uint8Array, I2cError>
266
-
267
- write(address: number, data: Uint8Array, stop?: boolean): Result<void, I2cError>
268
-
269
- scan(): Result<Uint8Array, I2cError>
270
- }
271
- }
272
- declare module 'native:mikro/spi' {
273
- import type {SpiError} from '@mikrojs/native/runtime/spi/types'
274
- import type {Result} from 'mikro/result'
275
-
276
- export interface SpiOptions {
277
- clk: number
278
- mosi: number
279
- miso?: number
280
- cs?: number
281
- freq?: number
282
- mode?: 0 | 1 | 2 | 3
283
- }
284
-
285
- export declare const Spi: {
286
- prototype: Spi
287
- new (hostNo: 1 | 2, options: SpiOptions): Spi
288
- }
289
-
290
- export interface Spi {
291
- begin(): Result<void, SpiError>
292
-
293
- end(): Result<void, SpiError>
294
-
295
- transfer(data: Uint8Array): Result<Uint8Array, SpiError>
296
-
297
- write(data: Uint8Array): Result<void, SpiError>
298
- }
299
- }
300
228
 
301
229
  declare module 'native:mikro/sntp' {
302
230
  import type {SntpError} from '@mikrojs/native/runtime/sntp/types'
@@ -338,73 +266,6 @@ declare module 'native:mikro/nvs_kv' {
338
266
  export function info(): {entries: number; used: number; total: number; free: number}
339
267
  }
340
268
 
341
- declare module 'native:mikro/pwm' {
342
- import type {PwmError} from '@mikrojs/native/runtime/pwm/types'
343
- import type {Result} from 'mikro/result'
344
-
345
- export declare const Pwm: {
346
- prototype: Pwm
347
- new (pin: number, freq: number, duty: number): Pwm
348
- }
349
-
350
- export interface Pwm {
351
- duty(value?: number): Result<number, PwmError>
352
- freq(value?: number): Result<number, PwmError>
353
- fade(target: number, durationMs: number): Result<Promise<void>, PwmError>
354
- end(): Result<void, PwmError>
355
- }
356
- }
357
-
358
- declare module 'native:mikro/neopixel' {
359
- import type {NeoPixelError} from '@mikrojs/native/runtime/neopixel/types'
360
- import type {Result} from 'mikro/result'
361
-
362
- export declare const NeoPixel: {
363
- prototype: NeoPixel
364
- new (pin: number, numLeds: number, bytesPerLed: number): NeoPixel
365
- }
366
-
367
- export interface NeoPixel {
368
- setPixel(index: number, r: number, g: number, b: number, w: number): Result<void, NeoPixelError>
369
- fill(r: number, g: number, b: number, w: number): Result<void, NeoPixelError>
370
- show(): Result<void, NeoPixelError>
371
- clear(): Result<void, NeoPixelError>
372
- end(): Result<void, NeoPixelError>
373
- }
374
- }
375
-
376
- declare module 'native:mikro/uart' {
377
- import type {UartError} from '@mikrojs/native/runtime/uart/types'
378
- import type {Result} from 'mikro/result'
379
-
380
- export interface UartOptions {
381
- tx?: number
382
- rx?: number
383
- baudRate: number
384
- }
385
-
386
- export declare const Uart: {
387
- prototype: Uart
388
- new (port: number, options: UartOptions): Uart
389
- }
390
-
391
- export interface Uart {
392
- begin(): Result<void, UartError>
393
-
394
- end(): Result<void, UartError>
395
-
396
- write(data: Uint8Array): Result<void, UartError>
397
-
398
- read(): Result<
399
- {
400
- next(): Promise<IteratorResult<Result<Uint8Array, UartError>>>
401
- return(): Promise<IteratorResult<Result<Uint8Array, UartError>>>
402
- },
403
- UartError
404
- >
405
- }
406
- }
407
-
408
269
  declare module 'native:mikro/ble' {
409
270
  import type {BleError} from '@mikrojs/native/runtime/ble/types'
410
271
  import type {Result} from 'mikro/result'
@@ -593,37 +454,3 @@ declare module 'native:mikro/ota_client' {
593
454
  export const bearer: Ota['bearer']
594
455
  export const registry: Ota['registry']
595
456
  }
596
-
597
- declare module 'native:mikro/i2s' {
598
- import type {I2sError, I2sSamples} from '@mikrojs/native/runtime/i2s/types'
599
- import type {Result} from 'mikro/result'
600
-
601
- export interface I2sNativeOptions {
602
- mode?: 'std' | 'pdm'
603
- sampleRate: number
604
- bitsPerSample?: 16 | 32
605
- channels?: 'mono' | 'stereo'
606
- bclk?: number
607
- ws?: number
608
- clk?: number
609
- dout?: number
610
- din?: number
611
- dmaFrames?: number
612
- dmaBuffers?: number
613
- }
614
-
615
- export declare const I2s: {
616
- prototype: I2s
617
- new (port: number, options: I2sNativeOptions): I2s
618
- }
619
-
620
- export interface I2s {
621
- begin(): Result<void, I2sError>
622
-
623
- end(): Result<void, I2sError>
624
-
625
- write(data: I2sSamples | Uint8Array): Promise<Result<void, I2sError>>
626
-
627
- capture(frames: number, options?: {gainBits?: number}): Result<Int16Array, I2sError>
628
- }
629
- }
@@ -58,18 +58,12 @@ export function makeCreateValue(native: NativeKvFns) {
58
58
  const onReadError = options?.onReadError ?? (() => undefined)
59
59
 
60
60
  function doGet(): unknown {
61
+ let v: unknown
62
+ // Only the native read is guarded: a throw from parse() or from the
63
+ // handler itself is a caller bug, and treating it as corruption would
64
+ // delete data that read back fine.
61
65
  try {
62
- const v = native.get(key)
63
- if (v === undefined) return hasInitialValue ? initialValue : undefined
64
- if (schema) {
65
- const result = parse(schema, v)
66
- if (!result.ok) {
67
- // Schema mismatch: data decoded fine, don't delete it
68
- return onReadError(result.error)
69
- }
70
- return result.value
71
- }
72
- return v
66
+ v = native.get(key)
73
67
  } catch (e) {
74
68
  // TypeError is the native's corruption marker (stored bytes that do
75
69
  // not decode): delete and heal. Anything else is a transient read
@@ -83,6 +77,14 @@ export function makeCreateValue(native: NativeKvFns) {
83
77
  }
84
78
  return onReadError(e)
85
79
  }
80
+ if (v === undefined) return hasInitialValue ? initialValue : undefined
81
+ if (schema) {
82
+ const result = parse(schema, v)
83
+ // Schema mismatch: data decoded fine, don't delete it
84
+ if (!result.ok) return onReadError(result.error)
85
+ return result.value
86
+ }
87
+ return v
86
88
  }
87
89
 
88
90
  function doSet(value: unknown) {
@@ -1,19 +1,27 @@
1
+ /* mikro/neopixel, declared here and implemented in C (mik_neopixel.cpp). */
2
+
3
+ import type {GpioInUse} from '../gpio/types.js'
1
4
  import type {Result} from '../result/types.js'
2
5
 
3
6
  export interface NeoPixelOptions {
4
- /** Number of LEDs in the strip */
7
+ /** Number of LEDs in the strip, 1 to 1024 */
5
8
  count: number
6
9
  /** Set to true for RGBW strips (SK6812). Defaults to false (RGB/WS2812). */
7
10
  rgbw?: boolean
8
11
  }
9
12
 
10
13
  export type NeoPixelError =
11
- | {name: 'NotActive'}
14
+ | GpioInUse
15
+ | {name: 'InvalidGpio'; message: string}
16
+ | {name: 'InvalidParam'; message: string}
17
+ | {name: 'ConfigFailed'; message: string}
12
18
  | {name: 'IndexOutOfRange'}
13
19
  | {name: 'ShowFailed'; message: string}
14
20
 
15
- export declare class NeoPixel {
16
- constructor(pin: number, options: NeoPixelOptions)
21
+ /**
22
+ * @public
23
+ */
24
+ export interface NeoPixel {
17
25
  /** Set a single pixel's color (0–255 per channel) */
18
26
  setPixel(index: number, r: number, g: number, b: number, w?: number): Result<void, NeoPixelError>
19
27
  /** Set all pixels to the same color */
@@ -22,6 +30,16 @@ export declare class NeoPixel {
22
30
  show(): Result<void, NeoPixelError>
23
31
  /** Turn off all pixels and transmit */
24
32
  clear(): Result<void, NeoPixelError>
25
- /** Release the RMT channel */
26
- end(): Result<void, NeoPixelError>
33
+ /** Releases the RMT channel and the GPIO pin. Calling it again does nothing. Afterwards the other
34
+ * methods do nothing and return `ok()`; the first such call prints a warning. */
35
+ end(): void
27
36
  }
37
+
38
+ /**
39
+ * Claims a GPIO pin as the data line of a WS2812 or SK6812 LED strip.
40
+ * @public
41
+ */
42
+ export declare function NeoPixel(
43
+ gpio: number,
44
+ options: NeoPixelOptions,
45
+ ): Result<NeoPixel, NeoPixelError>
@@ -167,7 +167,7 @@ class Subscription {
167
167
  }
168
168
  }
169
169
 
170
- type SubscribeCallback<T> = (sub: Subscriber<T>) => void
170
+ type SubscribeCallback<T> = (sub: Subscriber<T>) => void | (() => void)
171
171
 
172
172
  export class Observable<Ok, Err = never> {
173
173
  // The shim doesn't actually use `_phantom` at runtime; the type parameters are
@@ -183,15 +183,46 @@ export class Observable<Ok, Err = never> {
183
183
  }
184
184
 
185
185
  subscribe(observer?: unknown): Subscription {
186
- if (!dispatchActive) panicked = false
187
186
  const sub = new Subscriber<unknown>(observer)
187
+ this.start(sub)
188
+ return new Subscription(sub)
189
+ }
190
+
191
+ /* Run the producer against a subscriber built by the caller. */
192
+ private start(sub: Subscriber<unknown>): void {
193
+ if (!dispatchActive) panicked = false
194
+ let teardown: void | (() => void)
188
195
  try {
189
- this.#cb(sub)
196
+ teardown = this.#cb(sub)
190
197
  } catch (err) {
191
198
  sub.closeSilently()
192
199
  throw err
193
200
  }
194
- return new Subscription(sub)
201
+ /* A returned function is a teardown, as if passed to addTeardown() last. */
202
+ if (typeof teardown === 'function') sub.addTeardown(teardown)
203
+ }
204
+
205
+ /* For the operators shim, mirroring forward() in mik_observable.cpp: the
206
+ * upstream subscriber joins `down`'s teardowns before the producer runs, so
207
+ * a chain that completes during setup already closes it, and a closed
208
+ * `down` subscribes nothing. */
209
+ static forward(
210
+ source: Observable<unknown, unknown>,
211
+ down: {closed: boolean; addTeardown(fn: () => void): void},
212
+ observer: unknown,
213
+ ): void {
214
+ if (down.closed) return
215
+ const up = new Subscriber<unknown>(observer)
216
+ down.addTeardown(() => up.closeSilently())
217
+ source.start(up)
218
+ }
219
+
220
+ /* Subscribe and hand back a handle the caller closes itself (switchMap's
221
+ * inner stream, whose lifetime the operator manages). */
222
+ static start(source: Observable<unknown, unknown>, observer: unknown): {close(): void} {
223
+ const up = new Subscriber<unknown>(observer)
224
+ source.start(up)
225
+ return {close: () => up.closeSilently()}
195
226
  }
196
227
 
197
228
  pipe(...ops: Array<(o: Observable<unknown, unknown>) => Observable<unknown, unknown>>) {
@@ -205,39 +236,6 @@ export class Observable<Ok, Err = never> {
205
236
  return current
206
237
  }
207
238
 
208
- static from(src: unknown): Observable<unknown, unknown> {
209
- if (src instanceof Observable) return src
210
- if (
211
- src != null &&
212
- (typeof src === 'object' || typeof src === 'function') &&
213
- typeof (src as {then?: unknown}).then === 'function'
214
- ) {
215
- // Promise-shaped
216
- return new Observable<unknown>((sub) => {
217
- ;(src as PromiseLike<unknown>).then((value) => {
218
- if (sub.closed) return
219
- sub.next(value)
220
- if (sub.closed) return
221
- sub.complete()
222
- })
223
- })
224
- }
225
- if (
226
- src != null &&
227
- (typeof src === 'object' || typeof src === 'string') &&
228
- typeof (src as {[Symbol.iterator]?: unknown})[Symbol.iterator] === 'function'
229
- ) {
230
- return new Observable<unknown>((sub) => {
231
- for (const value of src as Iterable<unknown>) {
232
- if (sub.closed || panicked) return
233
- sub.next(value)
234
- }
235
- if (!sub.closed) sub.complete()
236
- })
237
- }
238
- throw new TypeError('Observable.from: source must be a Promise, Iterable, or Observable')
239
- }
240
-
241
239
  static withEmitters<Ok, Err = never>(): {
242
240
  observable: Observable<Ok, Err>
243
241
  next: (value: unknown) => void
@@ -282,3 +280,41 @@ export class Observable<Ok, Err = never> {
282
280
  return {observable: observable as unknown as Observable<Ok, Err>, next, complete}
283
281
  }
284
282
  }
283
+
284
+ /* Mirrors the C module exports: from(source) and of(...values). */
285
+ export function from(src: unknown): Observable<unknown, unknown> {
286
+ if (
287
+ src != null &&
288
+ (typeof src === 'object' || typeof src === 'function') &&
289
+ typeof (src as {then?: unknown}).then === 'function'
290
+ ) {
291
+ // Promise-shaped
292
+ return new Observable<unknown>((sub) => {
293
+ ;(src as PromiseLike<unknown>).then((value) => {
294
+ if (sub.closed) return
295
+ sub.next(value)
296
+ if (sub.closed) return
297
+ sub.complete()
298
+ })
299
+ })
300
+ }
301
+ if (
302
+ src != null &&
303
+ (typeof src === 'object' || typeof src === 'string') &&
304
+ typeof (src as {[Symbol.iterator]?: unknown})[Symbol.iterator] === 'function'
305
+ ) {
306
+ return new Observable<unknown>((sub) => {
307
+ for (const value of src as Iterable<unknown>) {
308
+ if (sub.closed || panicked) return
309
+ sub.next(value)
310
+ }
311
+ if (!sub.closed) sub.complete()
312
+ })
313
+ }
314
+ throw new TypeError('from: source must be a Promise or an Iterable')
315
+ }
316
+
317
+ /* of(...values): from() over the argument array. */
318
+ export function of(...values: unknown[]): Observable<unknown, unknown> {
319
+ return from(values)
320
+ }