@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,31 @@
|
|
|
1
|
+
import * as native from 'native:spi'
|
|
2
|
+
|
|
3
|
+
import type {Result} from '../result/types.js'
|
|
4
|
+
import type {Spi as PlatformSpi, SpiError, SpiOptions} from './types.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export class Spi implements PlatformSpi {
|
|
10
|
+
#native: native.Spi
|
|
11
|
+
|
|
12
|
+
constructor(hostNo: 1 | 2, options: SpiOptions) {
|
|
13
|
+
this.#native = new native.Spi(hostNo, options)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
begin(): Result<void, SpiError> {
|
|
17
|
+
return this.#native.begin()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
end(): Result<void, SpiError> {
|
|
21
|
+
return this.#native.end()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
transfer(data: Uint8Array): Result<Uint8Array, SpiError> {
|
|
25
|
+
return this.#native.transfer(data)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
write(data: Uint8Array): Result<void, SpiError> {
|
|
29
|
+
return this.#native.write(data)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export interface SpiOptions {
|
|
7
|
+
clk: number
|
|
8
|
+
mosi: number
|
|
9
|
+
miso?: number
|
|
10
|
+
cs?: number
|
|
11
|
+
freq?: number
|
|
12
|
+
mode?: 0 | 1 | 2 | 3
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type SpiError =
|
|
16
|
+
| {name: 'BusInitFailed'; message: string}
|
|
17
|
+
| {name: 'AddDeviceFailed'; message: string}
|
|
18
|
+
| {name: 'NotStarted'}
|
|
19
|
+
| {name: 'MissingPins'}
|
|
20
|
+
| {name: 'TransferFailed'; message: string}
|
|
21
|
+
| {name: 'WriteFailed'; message: string}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export declare const Spi: {
|
|
27
|
+
prototype: Spi
|
|
28
|
+
new (hostNo: 1 | 2, options: SpiOptions): Spi
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export interface Spi {
|
|
35
|
+
begin(): Result<void, SpiError>
|
|
36
|
+
|
|
37
|
+
end(): Result<void, SpiError>
|
|
38
|
+
|
|
39
|
+
transfer(data: Uint8Array): Result<Uint8Array, SpiError>
|
|
40
|
+
|
|
41
|
+
write(data: Uint8Array): Result<void, SpiError>
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as native from 'native:stdio'
|
|
2
|
+
|
|
3
|
+
import type {StdIn, StdOut} from './types.js'
|
|
4
|
+
|
|
5
|
+
function createStdIn(nativeStdin: typeof native.stdin): StdIn {
|
|
6
|
+
const listeners: ((buffer: Uint8Array) => void)[] = []
|
|
7
|
+
|
|
8
|
+
function onData(chunk: Uint8Array) {
|
|
9
|
+
listeners.forEach((listener) => listener(chunk))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
addListener(_event: 'data', callback: (buffer: Uint8Array) => void) {
|
|
14
|
+
if (!listeners.includes(callback)) {
|
|
15
|
+
listeners.push(callback)
|
|
16
|
+
if (listeners.length === 1) {
|
|
17
|
+
nativeStdin.setHandler(onData)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
removeListener(_event: 'data', callback: (buffer: Uint8Array) => void) {
|
|
22
|
+
const idx = listeners.indexOf(callback)
|
|
23
|
+
if (idx > -1) {
|
|
24
|
+
listeners.splice(idx, 1)
|
|
25
|
+
if (listeners.length === 0) {
|
|
26
|
+
nativeStdin.clearHandler()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
read: nativeStdin.read,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createStdOut(nativeStdout: typeof native.stdout): StdOut {
|
|
35
|
+
return {
|
|
36
|
+
write: nativeStdout.write,
|
|
37
|
+
flush: nativeStdout.flush,
|
|
38
|
+
isTTY: false,
|
|
39
|
+
getWindowSize: undefined,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const stdin = createStdIn(native.stdin)
|
|
44
|
+
export const stdout = createStdOut(native.stdout)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface StdIn {
|
|
2
|
+
addListener(event: 'data', callback: (chunk: Uint8Array) => any): void
|
|
3
|
+
|
|
4
|
+
removeListener(event: 'data', callback: (chunk: Uint8Array) => any): void
|
|
5
|
+
|
|
6
|
+
read(): Uint8Array | undefined
|
|
7
|
+
|
|
8
|
+
setRawMode?: (rawMode: boolean) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StdOut {
|
|
12
|
+
isTTY: boolean
|
|
13
|
+
|
|
14
|
+
write(output: string | Uint8Array): boolean
|
|
15
|
+
|
|
16
|
+
getWindowSize?: () => [width: number, height: number]
|
|
17
|
+
|
|
18
|
+
flush(): void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export declare const stdin: StdIn
|
|
22
|
+
export declare const stdout: StdOut
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal composable stream primitives for mikrojs.
|
|
3
|
+
*
|
|
4
|
+
* Streams are plain `AsyncIterable<T>` — no class hierarchy, no
|
|
5
|
+
* lockable readers, no BYOB, no queueing strategies. Transforms are
|
|
6
|
+
* async generator functions. Composition is function call.
|
|
7
|
+
*
|
|
8
|
+
* This is deliberately a subset of what Snell's "new-streams" proposal
|
|
9
|
+
* covers, adapted for the realities of QuickJS on a microcontroller:
|
|
10
|
+
* no fast-path promise optimizations, no sendfile-style native
|
|
11
|
+
* shortcuts, tight heap budget.
|
|
12
|
+
*
|
|
13
|
+
* V1 scope: enough to rewrite a modem AT channel as a one-file shim.
|
|
14
|
+
* Expand only when a second call site asks for it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Decode a stream of UTF-8 byte chunks into a stream of strings. */
|
|
18
|
+
export async function* decodeUtf8(source: AsyncIterable<Uint8Array>): AsyncIterable<string> {
|
|
19
|
+
const decoder = new TextDecoder()
|
|
20
|
+
for await (const chunk of source) yield decoder.decode(chunk, {stream: true})
|
|
21
|
+
yield decoder.decode()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Split a stream of text into a stream of lines.
|
|
26
|
+
*
|
|
27
|
+
* The delimiter is a string (default `\n`). Partial lines that don't
|
|
28
|
+
* end with the delimiter in a given input chunk are held in an
|
|
29
|
+
* internal buffer and prepended to the next chunk's content — so
|
|
30
|
+
* splitting happens correctly regardless of how the underlying source
|
|
31
|
+
* chunks its data. If the source closes before emitting a final
|
|
32
|
+
* delimiter, any trailing buffered content is emitted as a final line
|
|
33
|
+
* (matching how Node's readline handles EOF).
|
|
34
|
+
*
|
|
35
|
+
* Empty lines (consecutive delimiters) are preserved. This matters
|
|
36
|
+
* for protocols that use a blank line as a separator, e.g. HTTP
|
|
37
|
+
* headers vs. body.
|
|
38
|
+
*/
|
|
39
|
+
export async function* splitLines(
|
|
40
|
+
source: AsyncIterable<string>,
|
|
41
|
+
delimiter: string = '\n',
|
|
42
|
+
): AsyncIterable<string> {
|
|
43
|
+
// indexOf('') returns 0 unconditionally, which would make the inner
|
|
44
|
+
// loop spin forever yielding empty strings. Reject at entry instead
|
|
45
|
+
// of hanging the event loop on a user mistake.
|
|
46
|
+
if (delimiter.length === 0) {
|
|
47
|
+
throw new RangeError('splitLines: delimiter must be non-empty')
|
|
48
|
+
}
|
|
49
|
+
let buffer = ''
|
|
50
|
+
for await (const chunk of source) {
|
|
51
|
+
buffer += chunk
|
|
52
|
+
let idx = buffer.indexOf(delimiter)
|
|
53
|
+
while (idx !== -1) {
|
|
54
|
+
yield buffer.slice(0, idx)
|
|
55
|
+
buffer = buffer.slice(idx + delimiter.length)
|
|
56
|
+
idx = buffer.indexOf(delimiter)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (buffer.length > 0) yield buffer
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Consume a stream until an item matches `predicate`, then stop.
|
|
64
|
+
*
|
|
65
|
+
* Returns both the matching item and every item that came before it.
|
|
66
|
+
* The matching item is NOT included in `collected` — callers who need
|
|
67
|
+
* it have `matched` for that. This distinction matters for protocols
|
|
68
|
+
* like modem AT commands where the terminator (`OK`/`ERROR`) is
|
|
69
|
+
* semantically different from the response body lines in between.
|
|
70
|
+
*
|
|
71
|
+
* If the stream closes before any item matches, throws `StreamClosed`
|
|
72
|
+
* (a plain Error with a `.name`). Callers should wrap this in their
|
|
73
|
+
* own Result type if they want structured error handling.
|
|
74
|
+
*/
|
|
75
|
+
export async function collectUntil<T>(
|
|
76
|
+
source: AsyncIterable<T>,
|
|
77
|
+
predicate: (item: T) => boolean,
|
|
78
|
+
): Promise<{matched: T; collected: T[]}> {
|
|
79
|
+
const collected: T[] = []
|
|
80
|
+
for await (const item of source) {
|
|
81
|
+
if (predicate(item)) {
|
|
82
|
+
return {matched: item, collected}
|
|
83
|
+
}
|
|
84
|
+
collected.push(item)
|
|
85
|
+
}
|
|
86
|
+
const err = new Error('Stream closed before predicate matched')
|
|
87
|
+
err.name = 'StreamClosed'
|
|
88
|
+
throw err
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wrap an async iterable with a total-duration timeout.
|
|
93
|
+
*
|
|
94
|
+
* The deadline starts when this function is called (not per-item).
|
|
95
|
+
* If the next item doesn't arrive before the deadline, the returned
|
|
96
|
+
* iterable throws a `TimeoutError`, and cleans up the upstream source
|
|
97
|
+
* by calling its `return()` method. Callers using `for await` will see
|
|
98
|
+
* the error propagate out of the loop.
|
|
99
|
+
*
|
|
100
|
+
* Implementation notes:
|
|
101
|
+
* - Promise.race with setTimeout would leak orphaned timers on the
|
|
102
|
+
* fast path (source wins, setTimeout's callback still fires
|
|
103
|
+
* eventually). We track the timer id and clearTimeout() in a finally
|
|
104
|
+
* around the race so fast-path calls don't accumulate pending jobs.
|
|
105
|
+
* - The upstream iterator is explicitly .return()'d on exit (normal,
|
|
106
|
+
* timeout, or consumer break) so underlying resources (UART claims,
|
|
107
|
+
* socket handles, ring buffers) get released.
|
|
108
|
+
*/
|
|
109
|
+
export async function* withTimeout<T>(source: AsyncIterable<T>, ms: number): AsyncIterable<T> {
|
|
110
|
+
const deadline = Date.now() + ms
|
|
111
|
+
const iter = source[Symbol.asyncIterator]()
|
|
112
|
+
try {
|
|
113
|
+
while (true) {
|
|
114
|
+
const remaining = deadline - Date.now()
|
|
115
|
+
if (remaining <= 0) {
|
|
116
|
+
const err = new Error(`Stream did not complete within ${ms}ms`)
|
|
117
|
+
err.name = 'TimeoutError'
|
|
118
|
+
throw err
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let timerId: ReturnType<typeof setTimeout> | undefined
|
|
122
|
+
let result: IteratorResult<T, unknown>
|
|
123
|
+
try {
|
|
124
|
+
result = await Promise.race([
|
|
125
|
+
iter.next(),
|
|
126
|
+
new Promise<IteratorResult<T, unknown>>((_, reject) => {
|
|
127
|
+
timerId = setTimeout(() => {
|
|
128
|
+
const err = new Error(`Stream did not complete within ${ms}ms`)
|
|
129
|
+
err.name = 'TimeoutError'
|
|
130
|
+
reject(err)
|
|
131
|
+
}, remaining)
|
|
132
|
+
}),
|
|
133
|
+
])
|
|
134
|
+
} finally {
|
|
135
|
+
if (timerId !== undefined) clearTimeout(timerId)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (result.done) return
|
|
139
|
+
yield result.value as T
|
|
140
|
+
}
|
|
141
|
+
} finally {
|
|
142
|
+
if (typeof iter.return === 'function') {
|
|
143
|
+
try {
|
|
144
|
+
await iter.return(undefined)
|
|
145
|
+
} catch {
|
|
146
|
+
/* swallow — upstream cleanup errors shouldn't mask ours */
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for `mikrojs/stream`.
|
|
3
|
+
*
|
|
4
|
+
* The runtime implementation lives in `./stream.ts` and is compiled to
|
|
5
|
+
* bytecode at firmware build time. This file is the declaration-only
|
|
6
|
+
* surface consumed by the `mikrojs/stream` re-export in
|
|
7
|
+
* `packages/mikrojs` and by user apps via their tsconfig.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Decode a stream of UTF-8 byte chunks into a stream of strings. */
|
|
11
|
+
export declare function decodeUtf8(source: AsyncIterable<Uint8Array>): AsyncIterable<string>
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Split a stream of text into a stream of lines on the given delimiter
|
|
15
|
+
* (default `\n`). Partial lines spanning chunk boundaries are buffered
|
|
16
|
+
* and reassembled. Empty lines between consecutive delimiters are
|
|
17
|
+
* preserved. Throws `RangeError` if called with an empty delimiter.
|
|
18
|
+
*/
|
|
19
|
+
export declare function splitLines(
|
|
20
|
+
source: AsyncIterable<string>,
|
|
21
|
+
delimiter?: string,
|
|
22
|
+
): AsyncIterable<string>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Consume a stream until an item matches `predicate`, then stop.
|
|
26
|
+
*
|
|
27
|
+
* Returns `{matched, collected}` where `collected` is every item that
|
|
28
|
+
* came BEFORE the match (not including the matching item). Throws an
|
|
29
|
+
* Error with `name === 'StreamClosed'` if the stream ends before any
|
|
30
|
+
* item matches.
|
|
31
|
+
*/
|
|
32
|
+
export declare function collectUntil<T>(
|
|
33
|
+
source: AsyncIterable<T>,
|
|
34
|
+
predicate: (item: T) => boolean,
|
|
35
|
+
): Promise<{matched: T; collected: T[]}>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Wrap an async iterable with a total-duration deadline. Throws an
|
|
39
|
+
* Error with `name === 'TimeoutError'` if the next item doesn't
|
|
40
|
+
* arrive in time. Releases the upstream iterator via `return()` on
|
|
41
|
+
* timeout, error, or consumer break.
|
|
42
|
+
*/
|
|
43
|
+
export declare function withTimeout<T>(source: AsyncIterable<T>, ms: number): AsyncIterable<T>
|
|
44
|
+
|
|
45
|
+
/* BufferedReader lives in the sibling `mikrojs/reader` module — see
|
|
46
|
+
* `runtime/reader/` — so apps that only need byte-level framing can
|
|
47
|
+
* avoid loading the async-generator transforms above. */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {err, ok, PanicError, type Result} from 'mikrojs/result'
|
|
2
|
+
import * as native from 'native:sys'
|
|
3
|
+
|
|
4
|
+
export function uptime(): {boot: number; rtc: number} {
|
|
5
|
+
return native.uptime()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function setSystemTime(millisSinceEpoch: number): void
|
|
9
|
+
export function setSystemTime(date: Date): void
|
|
10
|
+
export function setSystemTime(dateOrMillisSinceEpoch: Date | number): void
|
|
11
|
+
export function setSystemTime(dateOrMillisSinceEpoch: Date | number): void {
|
|
12
|
+
return native.setTime(
|
|
13
|
+
dateOrMillisSinceEpoch instanceof Date
|
|
14
|
+
? dateOrMillisSinceEpoch.getTime()
|
|
15
|
+
: dateOrMillisSinceEpoch,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function memoryUsage() {
|
|
20
|
+
return native.memoryUsage()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function jsMemoryUsage() {
|
|
24
|
+
return native.jsMemoryUsage()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function gc() {
|
|
28
|
+
return native.gc()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const version: string = native.version
|
|
32
|
+
|
|
33
|
+
export const board = native.board
|
|
34
|
+
|
|
35
|
+
export const firmware = native.firmware
|
|
36
|
+
|
|
37
|
+
export const deviceId: string = native.deviceId
|
|
38
|
+
|
|
39
|
+
export function restart(): never {
|
|
40
|
+
return native.restart() as never
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function exit(_exitCode?: number): never {
|
|
44
|
+
return native.restart() as never
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function panic(message: string): never {
|
|
48
|
+
throw new PanicError(message)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const buildDate = new Date(native.firmware.date)
|
|
52
|
+
|
|
53
|
+
export class MonotonicTimestamp {
|
|
54
|
+
readonly uptimeMs: number
|
|
55
|
+
|
|
56
|
+
private constructor(uptimeMs: number) {
|
|
57
|
+
this.uptimeMs = uptimeMs
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static now(): MonotonicTimestamp {
|
|
61
|
+
return new MonotonicTimestamp(uptime().rtc)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Rehydrate a previously-serialized timestamp. `uptimeMs` must be a
|
|
65
|
+
* value that originated from `.uptimeMs` on a MonotonicTimestamp taken
|
|
66
|
+
* within the current RTC reset cycle; values from prior reset cycles
|
|
67
|
+
* will resolve to meaningless wall-clock times because the RTC counter
|
|
68
|
+
* reset between then and now. Callers must guard cross-reset-cycle
|
|
69
|
+
* misuse themselves (e.g. via a cold-boot session id). */
|
|
70
|
+
static fromRtcMs(uptimeMs: number): MonotonicTimestamp {
|
|
71
|
+
return new MonotonicTimestamp(uptimeMs)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Elapsed ms since another MonotonicTimestamp (NTP-independent) */
|
|
75
|
+
since(other: MonotonicTimestamp): number {
|
|
76
|
+
return this.uptimeMs - other.uptimeMs
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Resolve to wall-clock epoch ms */
|
|
80
|
+
private resolveMs(): number {
|
|
81
|
+
const elapsed = uptime().rtc - this.uptimeMs
|
|
82
|
+
return Date.now() - elapsed
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
wallDate(): Result<Date, 'ClockNotSynced'> {
|
|
86
|
+
const date = new Date(this.resolveMs())
|
|
87
|
+
if (date < buildDate) return err('ClockNotSynced' as const)
|
|
88
|
+
return ok(date)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
export interface MemoryUsage {
|
|
4
|
+
heapUsed: number
|
|
5
|
+
heapTotal: number
|
|
6
|
+
/** Free system heap in bytes (0 on host) */
|
|
7
|
+
systemFree: number
|
|
8
|
+
/** All-time minimum free system heap in bytes (0 on host) */
|
|
9
|
+
systemMinFree: number
|
|
10
|
+
/** Total system heap in bytes (0 on host) */
|
|
11
|
+
systemTotal: number
|
|
12
|
+
/** Largest contiguous free block in bytes (0 on host). Allocations
|
|
13
|
+
* larger than this fail even when `systemFree` is bigger — the gap
|
|
14
|
+
* between `systemLargestFree` and `systemFree` measures heap
|
|
15
|
+
* fragmentation. */
|
|
16
|
+
systemLargestFree: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Uptime {
|
|
20
|
+
/** Milliseconds since last boot (resets on deep sleep) */
|
|
21
|
+
readonly boot: number
|
|
22
|
+
/** Milliseconds from RTC clock (survives deep sleep) */
|
|
23
|
+
readonly rtc: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export declare function uptime(): Uptime
|
|
27
|
+
|
|
28
|
+
export declare function setSystemTime(timestamp: number): void
|
|
29
|
+
export declare function setSystemTime(date: Date): void
|
|
30
|
+
|
|
31
|
+
export declare function memoryUsage(): MemoryUsage
|
|
32
|
+
|
|
33
|
+
/** Curated breakdown of the QuickJS engine heap — "where is memory
|
|
34
|
+
* going?" Use this when `memoryUsage()` tells you the heap is full
|
|
35
|
+
* and you want to know *what kind* of allocation dominates. Values
|
|
36
|
+
* are in bytes unless the field name ends in `Count`. */
|
|
37
|
+
export interface JsMemoryUsage {
|
|
38
|
+
/** Number of live JS strings */
|
|
39
|
+
strCount: number
|
|
40
|
+
/** Bytes held by live JS strings */
|
|
41
|
+
strSize: number
|
|
42
|
+
/** Number of live JS objects */
|
|
43
|
+
objCount: number
|
|
44
|
+
/** Bytes held by live JS object headers */
|
|
45
|
+
objSize: number
|
|
46
|
+
/** Number of object properties across all live objects */
|
|
47
|
+
propCount: number
|
|
48
|
+
/** Bytes held by property slots */
|
|
49
|
+
propSize: number
|
|
50
|
+
/** Number of distinct object shapes (hidden classes) */
|
|
51
|
+
shapeCount: number
|
|
52
|
+
/** Bytes held by shape descriptors */
|
|
53
|
+
shapeSize: number
|
|
54
|
+
/** Number of compiled JS functions in memory */
|
|
55
|
+
jsFuncCount: number
|
|
56
|
+
/** Bytes of compiled JS bytecode in memory (usually the biggest
|
|
57
|
+
* contributor to QuickJS heap on an mikrojs app) */
|
|
58
|
+
jsFuncCodeSize: number
|
|
59
|
+
/** Number of live arrays (fast + slow) */
|
|
60
|
+
arrayCount: number
|
|
61
|
+
/** Total element slots across all fast arrays */
|
|
62
|
+
fastArrayElements: number
|
|
63
|
+
/** Bytes of backing storage for ArrayBuffer / TypedArray data */
|
|
64
|
+
binaryObjectSize: number
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export declare function jsMemoryUsage(): JsMemoryUsage
|
|
68
|
+
|
|
69
|
+
export declare function gc(): void
|
|
70
|
+
|
|
71
|
+
/** Firmware version string (e.g. "0.1.0") */
|
|
72
|
+
export declare const version: string
|
|
73
|
+
|
|
74
|
+
export interface BoardInfo {
|
|
75
|
+
/** Board name from the firmware build (e.g. "xiao-esp32c6") or "generic" */
|
|
76
|
+
name: string
|
|
77
|
+
/** Chip target (e.g. "esp32c6") or "host" */
|
|
78
|
+
chip: string
|
|
79
|
+
/** Number of CPU cores */
|
|
80
|
+
cores: number
|
|
81
|
+
/** Silicon revision (major * 100 + minor on ESP32, 0 on host) */
|
|
82
|
+
revision: number
|
|
83
|
+
/** Supported features (e.g. ["wifi", "ble"]) */
|
|
84
|
+
features: string[]
|
|
85
|
+
/** Flash size in bytes (0 on host) */
|
|
86
|
+
flash: number
|
|
87
|
+
/** PSRAM size in bytes (0 if unavailable) */
|
|
88
|
+
psram: number
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Board and hardware info */
|
|
92
|
+
export declare const board: BoardInfo
|
|
93
|
+
|
|
94
|
+
/** Unique device identifier encoded as Crockford's Base32 (10 lowercase
|
|
95
|
+
* characters, no special symbols). On ESP32, derived from the chip's
|
|
96
|
+
* base MAC address; the encoding is lossless so decoding the 10 chars
|
|
97
|
+
* yields the original 6 MAC bytes. On host/Node builds, derived from
|
|
98
|
+
* the hostname via FNV-1a hash (stable across restarts on the same
|
|
99
|
+
* machine). */
|
|
100
|
+
export declare const deviceId: string
|
|
101
|
+
|
|
102
|
+
/** Firmware build identifiers */
|
|
103
|
+
export declare const firmware: {
|
|
104
|
+
/** ELF SHA256 hash on ESP32, "dev" on host */
|
|
105
|
+
readonly hash: string
|
|
106
|
+
/** Build date and time */
|
|
107
|
+
readonly date: string
|
|
108
|
+
/** ESP-IDF version, undefined on host */
|
|
109
|
+
readonly idfVersion: string | undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export declare function restart(): never
|
|
113
|
+
|
|
114
|
+
export declare function exit(exitCode?: number): never
|
|
115
|
+
|
|
116
|
+
/** Immediately crash with an error message. Use for unrecoverable situations. */
|
|
117
|
+
export declare function panic(message: string): never
|
|
118
|
+
|
|
119
|
+
export declare class MonotonicTimestamp {
|
|
120
|
+
readonly uptimeMs: number
|
|
121
|
+
private constructor(uptimeMs: number)
|
|
122
|
+
static now(): MonotonicTimestamp
|
|
123
|
+
/** Rehydrate a previously-serialized timestamp. The `uptimeMs` value
|
|
124
|
+
* must come from the current RTC reset cycle; values from prior reset
|
|
125
|
+
* cycles will resolve to meaningless wall-clock times. */
|
|
126
|
+
static fromRtcMs(uptimeMs: number): MonotonicTimestamp
|
|
127
|
+
/** Elapsed ms since another MonotonicTimestamp (NTP-independent) */
|
|
128
|
+
since(other: MonotonicTimestamp): number
|
|
129
|
+
/** Resolve to a Date. Returns err if the system clock is clearly not synced. */
|
|
130
|
+
wallDate(): Result<Date, 'ClockNotSynced'>
|
|
131
|
+
}
|