@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,60 @@
|
|
|
1
|
+
export declare class PanicError extends Error {
|
|
2
|
+
constructor(message: string, options?: {cause?: unknown})
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface OkResult<T> {
|
|
6
|
+
readonly ok: true
|
|
7
|
+
readonly value: T
|
|
8
|
+
readonly error?: never
|
|
9
|
+
map<U>(fn: (value: T) => U): OkResult<U>
|
|
10
|
+
mapErr<F>(fn: (error: never) => F): OkResult<T>
|
|
11
|
+
andThen<U, F>(fn: (value: T) => Result<U, F>): Result<U, F>
|
|
12
|
+
match<A, B>(handlers: {ok: (value: T) => A; err: (error: never) => B}): A
|
|
13
|
+
/** Get the value, or return the default if this is an Err. */
|
|
14
|
+
orDefault<D>(defaultValue: D): T | D
|
|
15
|
+
/** Get the value, or panic with the given message if this is an Err. */
|
|
16
|
+
orPanic(message: string): T
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ErrResult<E> {
|
|
20
|
+
readonly ok: false
|
|
21
|
+
readonly value?: never
|
|
22
|
+
readonly error: E
|
|
23
|
+
map<U>(fn: (value: never) => U): ErrResult<E>
|
|
24
|
+
mapErr<F>(fn: (error: E) => F): ErrResult<F>
|
|
25
|
+
andThen<U, F>(fn: (value: never) => Result<U, F>): ErrResult<E>
|
|
26
|
+
match<A, B>(handlers: {ok: (value: never) => A; err: (error: E) => B}): B
|
|
27
|
+
/** Return the default value since this is an Err. */
|
|
28
|
+
orDefault<D>(defaultValue: D): D
|
|
29
|
+
/** Always panics with the given message since this is an Err. The error is included as cause. */
|
|
30
|
+
orPanic(message: string): never
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type Result<T, E> = OkResult<T> | ErrResult<E>
|
|
34
|
+
|
|
35
|
+
export declare function ok(): OkResult<void>
|
|
36
|
+
export declare function ok<T>(value: T): OkResult<T>
|
|
37
|
+
|
|
38
|
+
export declare function err<E>(error: E): ErrResult<E>
|
|
39
|
+
|
|
40
|
+
type VariantDef = Record<string, (...args: any[]) => Record<string, unknown>>
|
|
41
|
+
|
|
42
|
+
type ErrorConstructors<D extends VariantDef> = {
|
|
43
|
+
[K in keyof D & string]: (...args: Parameters<D[K]>) => {name: K} & ReturnType<D[K]>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ErrorOf<T extends Record<string, (...args: any[]) => any>> = {
|
|
47
|
+
[K in keyof T & string]: ReturnType<T[K]>
|
|
48
|
+
}[keyof T & string]
|
|
49
|
+
|
|
50
|
+
export declare function defineError<D extends VariantDef>(
|
|
51
|
+
name: string,
|
|
52
|
+
variants: D,
|
|
53
|
+
): ErrorConstructors<D>
|
|
54
|
+
|
|
55
|
+
/** Raw error shape returned by native mik__result_err(). */
|
|
56
|
+
export interface NativeError {
|
|
57
|
+
readonly code: number
|
|
58
|
+
readonly message: string
|
|
59
|
+
readonly errno?: number
|
|
60
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import {defineError, err, ok} from 'mikrojs/result'
|
|
2
|
+
|
|
3
|
+
import type {Result} from '../result/types.js'
|
|
4
|
+
|
|
5
|
+
export const SchemaError = defineError('SchemaError', {
|
|
6
|
+
ValidationFailed: (message: string, path: string) => ({message, path}),
|
|
7
|
+
})
|
|
8
|
+
export type SchemaError = ReturnType<typeof SchemaError.ValidationFailed>
|
|
9
|
+
|
|
10
|
+
// ── Schema types ────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
type Primitive = string | number | boolean
|
|
13
|
+
|
|
14
|
+
export interface StringSchema {
|
|
15
|
+
readonly kind: 'string'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface NumberSchema {
|
|
19
|
+
readonly kind: 'number'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BooleanSchema {
|
|
23
|
+
readonly kind: 'boolean'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UnknownSchema {
|
|
27
|
+
readonly kind: 'unknown'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface LiteralSchema<T extends Primitive = Primitive> {
|
|
31
|
+
readonly kind: 'literal'
|
|
32
|
+
readonly value: T
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ArraySchema<S extends Schema = Schema> {
|
|
36
|
+
readonly kind: 'array'
|
|
37
|
+
readonly element: S
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ObjectSchema<Shape extends Record<string, Schema> = Record<string, Schema>> {
|
|
41
|
+
readonly kind: 'object'
|
|
42
|
+
readonly shape: Shape
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface OptionalSchema<S extends Schema = Schema> {
|
|
46
|
+
readonly kind: 'optional'
|
|
47
|
+
readonly inner: S
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TupleSchema<Elements extends readonly Schema[] = readonly Schema[]> {
|
|
51
|
+
readonly kind: 'tuple'
|
|
52
|
+
readonly elements: Elements
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UnionSchema<Members extends readonly Schema[] = readonly Schema[]> {
|
|
56
|
+
readonly kind: 'union'
|
|
57
|
+
readonly members: Members
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface TaggedUnionSchema<
|
|
61
|
+
Key extends string = string,
|
|
62
|
+
Branches extends Record<string, ObjectSchema> = Record<string, ObjectSchema>,
|
|
63
|
+
> {
|
|
64
|
+
readonly kind: 'taggedUnion'
|
|
65
|
+
readonly key: Key
|
|
66
|
+
readonly branches: Branches
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type Schema =
|
|
70
|
+
| StringSchema
|
|
71
|
+
| NumberSchema
|
|
72
|
+
| BooleanSchema
|
|
73
|
+
| UnknownSchema
|
|
74
|
+
| LiteralSchema
|
|
75
|
+
| ArraySchema
|
|
76
|
+
| ObjectSchema
|
|
77
|
+
| OptionalSchema
|
|
78
|
+
| TupleSchema
|
|
79
|
+
| UnionSchema
|
|
80
|
+
| TaggedUnionSchema
|
|
81
|
+
|
|
82
|
+
// ── Type inference ──────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
type Simplify<T> = {[K in keyof T]: T[K]} & {}
|
|
85
|
+
|
|
86
|
+
export type Infer<S> = S extends StringSchema
|
|
87
|
+
? string
|
|
88
|
+
: S extends NumberSchema
|
|
89
|
+
? number
|
|
90
|
+
: S extends BooleanSchema
|
|
91
|
+
? boolean
|
|
92
|
+
: S extends UnknownSchema
|
|
93
|
+
? unknown
|
|
94
|
+
: S extends LiteralSchema<infer T>
|
|
95
|
+
? T
|
|
96
|
+
: S extends ArraySchema<infer E>
|
|
97
|
+
? Infer<E>[]
|
|
98
|
+
: S extends ObjectSchema<infer Shape>
|
|
99
|
+
? Simplify<InferObject<Shape>>
|
|
100
|
+
: S extends TupleSchema<infer Elements>
|
|
101
|
+
? InferTuple<Elements>
|
|
102
|
+
: S extends OptionalSchema<infer Inner>
|
|
103
|
+
? Infer<Inner> | undefined
|
|
104
|
+
: S extends UnionSchema<infer Members>
|
|
105
|
+
? InferUnion<Members>
|
|
106
|
+
: S extends TaggedUnionSchema<infer Key, infer Branches>
|
|
107
|
+
? InferTaggedUnion<Key, Branches>
|
|
108
|
+
: never
|
|
109
|
+
|
|
110
|
+
type InferObject<Shape> = {
|
|
111
|
+
[K in keyof Shape as Shape[K] extends OptionalSchema ? never : K]: Infer<Shape[K]>
|
|
112
|
+
} & {
|
|
113
|
+
[K in keyof Shape as Shape[K] extends OptionalSchema ? K : never]?: Infer<Shape[K]>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type InferTuple<Elements> = Elements extends readonly [infer Head, ...infer Tail]
|
|
117
|
+
? [Infer<Head>, ...InferTuple<Tail>]
|
|
118
|
+
: []
|
|
119
|
+
|
|
120
|
+
type InferUnion<Members> = Members extends readonly [infer Head, ...infer Tail]
|
|
121
|
+
? Infer<Head> | InferUnion<Tail>
|
|
122
|
+
: never
|
|
123
|
+
|
|
124
|
+
type InferTaggedUnion<Key extends string, Branches> = {
|
|
125
|
+
[Tag in keyof Branches & string]: {[K in Key]: Tag} & Infer<Branches[Tag]>
|
|
126
|
+
}[keyof Branches & string]
|
|
127
|
+
|
|
128
|
+
// ── Schema constructors ─────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
export function string(): StringSchema {
|
|
131
|
+
return {kind: 'string'}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function number(): NumberSchema {
|
|
135
|
+
return {kind: 'number'}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function boolean(): BooleanSchema {
|
|
139
|
+
return {kind: 'boolean'}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function unknown(): UnknownSchema {
|
|
143
|
+
return {kind: 'unknown'}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function literal<T extends Primitive>(value: T): LiteralSchema<T> {
|
|
147
|
+
return {kind: 'literal', value}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function array<S extends Schema>(element: S): ArraySchema<S> {
|
|
151
|
+
return {kind: 'array', element}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function object<Shape extends Record<string, Schema>>(shape: Shape): ObjectSchema<Shape> {
|
|
155
|
+
return {kind: 'object', shape}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function tuple<Elements extends readonly Schema[]>(
|
|
159
|
+
elements: [...Elements],
|
|
160
|
+
): TupleSchema<Elements> {
|
|
161
|
+
return {kind: 'tuple', elements}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function optional<S extends Schema>(inner: S): OptionalSchema<S> {
|
|
165
|
+
return {kind: 'optional', inner}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function union<Members extends readonly Schema[]>(
|
|
169
|
+
members: [...Members],
|
|
170
|
+
): UnionSchema<Members> {
|
|
171
|
+
return {kind: 'union', members}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function taggedUnion<Key extends string, Branches extends Record<string, ObjectSchema>>(
|
|
175
|
+
key: Key,
|
|
176
|
+
branches: Branches,
|
|
177
|
+
): TaggedUnionSchema<Key, Branches> {
|
|
178
|
+
return {kind: 'taggedUnion', key, branches}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Parse ───────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
export function parse<S extends Schema>(schema: S, value: unknown): Result<Infer<S>, SchemaError> {
|
|
184
|
+
const result = validate(schema, value, '')
|
|
185
|
+
if (result !== null) return result as Result<Infer<S>, SchemaError>
|
|
186
|
+
return ok(value as Infer<S>)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function typeOf(value: unknown): string {
|
|
190
|
+
if (value === null) return 'null'
|
|
191
|
+
if (Array.isArray(value)) return 'array'
|
|
192
|
+
return typeof value
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function validate(
|
|
196
|
+
schema: Schema,
|
|
197
|
+
value: unknown,
|
|
198
|
+
path: string,
|
|
199
|
+
): ReturnType<typeof err<SchemaError>> | null {
|
|
200
|
+
switch (schema.kind) {
|
|
201
|
+
case 'string':
|
|
202
|
+
if (typeof value !== 'string')
|
|
203
|
+
return err(SchemaError.ValidationFailed(`expected string, got ${typeOf(value)}`, path))
|
|
204
|
+
return null
|
|
205
|
+
|
|
206
|
+
case 'number':
|
|
207
|
+
if (typeof value !== 'number' || Number.isNaN(value))
|
|
208
|
+
return err(SchemaError.ValidationFailed(`expected number, got ${typeOf(value)}`, path))
|
|
209
|
+
return null
|
|
210
|
+
|
|
211
|
+
case 'boolean':
|
|
212
|
+
if (typeof value !== 'boolean')
|
|
213
|
+
return err(SchemaError.ValidationFailed(`expected boolean, got ${typeOf(value)}`, path))
|
|
214
|
+
return null
|
|
215
|
+
|
|
216
|
+
case 'unknown':
|
|
217
|
+
return null
|
|
218
|
+
|
|
219
|
+
case 'literal':
|
|
220
|
+
if (value !== schema.value)
|
|
221
|
+
return err(
|
|
222
|
+
SchemaError.ValidationFailed(
|
|
223
|
+
`expected ${JSON.stringify(schema.value)}, got ${JSON.stringify(value)}`,
|
|
224
|
+
path,
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
return null
|
|
228
|
+
|
|
229
|
+
case 'array': {
|
|
230
|
+
if (!Array.isArray(value))
|
|
231
|
+
return err(SchemaError.ValidationFailed(`expected array, got ${typeOf(value)}`, path))
|
|
232
|
+
for (let i = 0; i < value.length; i++) {
|
|
233
|
+
const result = validate(schema.element, value[i], `${path}[${i}]`)
|
|
234
|
+
if (result !== null) return result
|
|
235
|
+
}
|
|
236
|
+
return null
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case 'object': {
|
|
240
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
241
|
+
return err(SchemaError.ValidationFailed(`expected object, got ${typeOf(value)}`, path))
|
|
242
|
+
const obj = value as Record<string, unknown>
|
|
243
|
+
const keys = Object.keys(schema.shape)
|
|
244
|
+
for (let i = 0; i < keys.length; i++) {
|
|
245
|
+
const key = keys[i]!
|
|
246
|
+
const fieldSchema = schema.shape[key]!
|
|
247
|
+
const fieldPath = `${path}.${key}`
|
|
248
|
+
if (fieldSchema.kind === 'optional') {
|
|
249
|
+
if (key in obj) {
|
|
250
|
+
const result = validate(fieldSchema, obj[key], fieldPath)
|
|
251
|
+
if (result !== null) return result
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
if (!(key in obj))
|
|
255
|
+
return err(SchemaError.ValidationFailed(`missing required field`, fieldPath))
|
|
256
|
+
const result = validate(fieldSchema, obj[key], fieldPath)
|
|
257
|
+
if (result !== null) return result
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return null
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case 'tuple': {
|
|
264
|
+
if (!Array.isArray(value))
|
|
265
|
+
return err(SchemaError.ValidationFailed(`expected array, got ${typeOf(value)}`, path))
|
|
266
|
+
if (value.length !== schema.elements.length)
|
|
267
|
+
return err(
|
|
268
|
+
SchemaError.ValidationFailed(
|
|
269
|
+
`expected ${schema.elements.length} elements, got ${value.length}`,
|
|
270
|
+
path,
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
for (let i = 0; i < schema.elements.length; i++) {
|
|
274
|
+
const result = validate(schema.elements[i]!, value[i], `${path}[${i}]`)
|
|
275
|
+
if (result !== null) return result
|
|
276
|
+
}
|
|
277
|
+
return null
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
case 'optional': {
|
|
281
|
+
if (value === undefined) return null
|
|
282
|
+
return validate(schema.inner, value, path)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
case 'union': {
|
|
286
|
+
for (let i = 0; i < schema.members.length; i++) {
|
|
287
|
+
const result = validate(schema.members[i]!, value, path)
|
|
288
|
+
if (result === null) return null
|
|
289
|
+
}
|
|
290
|
+
return err(SchemaError.ValidationFailed(`value did not match any union member`, path))
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
case 'taggedUnion': {
|
|
294
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
295
|
+
return err(SchemaError.ValidationFailed(`expected object, got ${typeOf(value)}`, path))
|
|
296
|
+
const obj = value as Record<string, unknown>
|
|
297
|
+
const tag = obj[schema.key]
|
|
298
|
+
if (tag === undefined)
|
|
299
|
+
return err(
|
|
300
|
+
SchemaError.ValidationFailed(`missing discriminator field`, `${path}.${schema.key}`),
|
|
301
|
+
)
|
|
302
|
+
if (typeof tag !== 'string' && typeof tag !== 'number' && typeof tag !== 'boolean')
|
|
303
|
+
return err(
|
|
304
|
+
SchemaError.ValidationFailed(
|
|
305
|
+
`expected primitive discriminator, got ${typeOf(tag)}`,
|
|
306
|
+
`${path}.${schema.key}`,
|
|
307
|
+
),
|
|
308
|
+
)
|
|
309
|
+
const branch = schema.branches[tag as string]
|
|
310
|
+
if (branch === undefined)
|
|
311
|
+
return err(
|
|
312
|
+
SchemaError.ValidationFailed(
|
|
313
|
+
`unknown tag ${JSON.stringify(tag)}`,
|
|
314
|
+
`${path}.${schema.key}`,
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
return validate(branch, value, path)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return err(SchemaError.ValidationFailed(`unknown schema kind: ${(schema as any).kind}`, path))
|
|
321
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
type Primitive = string | number | boolean
|
|
4
|
+
|
|
5
|
+
export interface StringSchema {
|
|
6
|
+
readonly kind: 'string'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface NumberSchema {
|
|
10
|
+
readonly kind: 'number'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BooleanSchema {
|
|
14
|
+
readonly kind: 'boolean'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UnknownSchema {
|
|
18
|
+
readonly kind: 'unknown'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LiteralSchema<T extends Primitive = Primitive> {
|
|
22
|
+
readonly kind: 'literal'
|
|
23
|
+
readonly value: T
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ArraySchema<S extends Schema = Schema> {
|
|
27
|
+
readonly kind: 'array'
|
|
28
|
+
readonly element: S
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ObjectSchema<Shape extends Record<string, Schema> = Record<string, Schema>> {
|
|
32
|
+
readonly kind: 'object'
|
|
33
|
+
readonly shape: Shape
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OptionalSchema<S extends Schema = Schema> {
|
|
37
|
+
readonly kind: 'optional'
|
|
38
|
+
readonly inner: S
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TupleSchema<Elements extends readonly Schema[] = readonly Schema[]> {
|
|
42
|
+
readonly kind: 'tuple'
|
|
43
|
+
readonly elements: Elements
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UnionSchema<Members extends readonly Schema[] = readonly Schema[]> {
|
|
47
|
+
readonly kind: 'union'
|
|
48
|
+
readonly members: Members
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface TaggedUnionSchema<
|
|
52
|
+
Key extends string = string,
|
|
53
|
+
Branches extends Record<string, ObjectSchema> = Record<string, ObjectSchema>,
|
|
54
|
+
> {
|
|
55
|
+
readonly kind: 'taggedUnion'
|
|
56
|
+
readonly key: Key
|
|
57
|
+
readonly branches: Branches
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type Schema =
|
|
61
|
+
| StringSchema
|
|
62
|
+
| NumberSchema
|
|
63
|
+
| BooleanSchema
|
|
64
|
+
| UnknownSchema
|
|
65
|
+
| LiteralSchema
|
|
66
|
+
| ArraySchema
|
|
67
|
+
| ObjectSchema
|
|
68
|
+
| OptionalSchema
|
|
69
|
+
| TupleSchema
|
|
70
|
+
| UnionSchema
|
|
71
|
+
| TaggedUnionSchema
|
|
72
|
+
|
|
73
|
+
type Simplify<T> = {[K in keyof T]: T[K]} & {}
|
|
74
|
+
|
|
75
|
+
export type Infer<S> = S extends StringSchema
|
|
76
|
+
? string
|
|
77
|
+
: S extends NumberSchema
|
|
78
|
+
? number
|
|
79
|
+
: S extends BooleanSchema
|
|
80
|
+
? boolean
|
|
81
|
+
: S extends UnknownSchema
|
|
82
|
+
? unknown
|
|
83
|
+
: S extends LiteralSchema<infer T>
|
|
84
|
+
? T
|
|
85
|
+
: S extends ArraySchema<infer E>
|
|
86
|
+
? ArraySchema extends S
|
|
87
|
+
? []
|
|
88
|
+
: Infer<E>[]
|
|
89
|
+
: S extends ObjectSchema<infer Shape>
|
|
90
|
+
? ObjectSchema extends S
|
|
91
|
+
? object
|
|
92
|
+
: Simplify<InferObject<Shape>>
|
|
93
|
+
: S extends TupleSchema<infer Elements>
|
|
94
|
+
? InferTuple<Elements>
|
|
95
|
+
: S extends OptionalSchema<infer Inner>
|
|
96
|
+
? OptionalSchema extends S
|
|
97
|
+
? OptionalSchema
|
|
98
|
+
: Infer<Inner> | undefined
|
|
99
|
+
: S extends UnionSchema<infer Members>
|
|
100
|
+
? InferUnion<Members>
|
|
101
|
+
: S extends TaggedUnionSchema<infer Key, infer Branches>
|
|
102
|
+
? InferTaggedUnion<Key, Branches>
|
|
103
|
+
: never
|
|
104
|
+
|
|
105
|
+
type InferObject<Shape> = {
|
|
106
|
+
[K in keyof Shape as Shape[K] extends OptionalSchema ? never : K]: Infer<Shape[K]>
|
|
107
|
+
} & {
|
|
108
|
+
[K in keyof Shape as Shape[K] extends OptionalSchema ? K : never]?: Infer<Shape[K]>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type InferTuple<Elements> = Elements extends readonly [infer Head, ...infer Tail]
|
|
112
|
+
? [Infer<Head>, ...InferTuple<Tail>]
|
|
113
|
+
: []
|
|
114
|
+
|
|
115
|
+
type InferUnion<Members> = Members extends readonly [infer Head, ...infer Tail]
|
|
116
|
+
? Infer<Head> | InferUnion<Tail>
|
|
117
|
+
: never
|
|
118
|
+
|
|
119
|
+
type InferTaggedUnion<Key extends string, Branches> = {
|
|
120
|
+
[Tag in keyof Branches & string]: {[K in Key]: Tag} & Infer<Branches[Tag]>
|
|
121
|
+
}[keyof Branches & string]
|
|
122
|
+
|
|
123
|
+
export type SchemaError = {name: 'ValidationFailed'; message: string; path: string}
|
|
124
|
+
|
|
125
|
+
export declare function string(): StringSchema
|
|
126
|
+
export declare function number(): NumberSchema
|
|
127
|
+
export declare function boolean(): BooleanSchema
|
|
128
|
+
export declare function unknown(): UnknownSchema
|
|
129
|
+
export declare function literal<T extends Primitive>(value: T): LiteralSchema<T>
|
|
130
|
+
export declare function array<S extends Schema>(element: S): ArraySchema<S>
|
|
131
|
+
export declare function object<Shape extends Record<string, Schema>>(
|
|
132
|
+
shape: Shape,
|
|
133
|
+
): ObjectSchema<Shape>
|
|
134
|
+
export declare function tuple<Elements extends readonly Schema[]>(
|
|
135
|
+
elements: [...Elements],
|
|
136
|
+
): TupleSchema<Elements>
|
|
137
|
+
export declare function optional<S extends Schema>(inner: S): OptionalSchema<S>
|
|
138
|
+
export declare function union<Members extends readonly Schema[]>(
|
|
139
|
+
members: [...Members],
|
|
140
|
+
): UnionSchema<Members>
|
|
141
|
+
export declare function taggedUnion<
|
|
142
|
+
Key extends string,
|
|
143
|
+
Branches extends Record<string, ObjectSchema>,
|
|
144
|
+
>(key: Key, branches: Branches): TaggedUnionSchema<Key, Branches>
|
|
145
|
+
export declare function parse<S extends Schema>(
|
|
146
|
+
schema: S,
|
|
147
|
+
value: unknown,
|
|
148
|
+
): Result<Infer<S>, SchemaError>
|
|
149
|
+
|
|
150
|
+
export declare const SchemaError: {
|
|
151
|
+
ValidationFailed: (message: string, path: string) => SchemaError
|
|
152
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
deepSleep,
|
|
3
|
+
disableWakeupSource,
|
|
4
|
+
enableExt0Wakeup,
|
|
5
|
+
enableExt1Wakeup,
|
|
6
|
+
enableGpioWakeup,
|
|
7
|
+
enableTimerWakeup,
|
|
8
|
+
getWakeupCause,
|
|
9
|
+
lightSleep,
|
|
10
|
+
} from 'native:sleep'
|
|
11
|
+
|
|
12
|
+
export function sleep(ms: number): Promise<void> {
|
|
13
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
export type SleepError =
|
|
4
|
+
| {name: 'WakeupConfigFailed'; message: string}
|
|
5
|
+
| {name: 'LightSleepFailed'; message: string}
|
|
6
|
+
| {name: 'DisableWakeupFailed'; message: string}
|
|
7
|
+
|
|
8
|
+
export declare function deepSleep(time: number): never
|
|
9
|
+
|
|
10
|
+
export declare function lightSleep(time: number): Result<void, SleepError>
|
|
11
|
+
|
|
12
|
+
export declare function getWakeupCause(): string
|
|
13
|
+
|
|
14
|
+
/** Configure timer wakeup source (microseconds). */
|
|
15
|
+
export declare function enableTimerWakeup(us: number): Result<void, SleepError>
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Configure GPIO wakeup for light sleep.
|
|
19
|
+
* @param pin GPIO pin number
|
|
20
|
+
* @param level GPIO interrupt type (e.g. 4 = low, 5 = high)
|
|
21
|
+
*/
|
|
22
|
+
export declare function enableGpioWakeup(pin: number, level: number): Result<void, SleepError>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Configure ext0 wakeup for deep sleep (ESP32, ESP32-S2, ESP32-S3 only).
|
|
26
|
+
* @param pin RTC GPIO pin number
|
|
27
|
+
* @param level 0 = low, 1 = high
|
|
28
|
+
*/
|
|
29
|
+
export declare function enableExt0Wakeup(pin: number, level: number): Result<void, SleepError>
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configure ext1 wakeup for deep sleep.
|
|
33
|
+
* @param pinMask Bitmask of RTC GPIO pins
|
|
34
|
+
* @param mode 0 = ESP_EXT1_WAKEUP_ALL_LOW, 1 = ESP_EXT1_WAKEUP_ANY_HIGH
|
|
35
|
+
*/
|
|
36
|
+
export declare function enableExt1Wakeup(pinMask: number, mode: number): Result<void, SleepError>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Disable a wakeup source. If no source is specified, all sources are disabled.
|
|
40
|
+
* @param source Optional: "timer", "ext0", "ext1", "gpio", "touchpad", "ulp"
|
|
41
|
+
*/
|
|
42
|
+
export declare function disableWakeupSource(source?: string): Result<void, SleepError>
|
|
43
|
+
|
|
44
|
+
export declare function sleep(ms: number): Promise<void>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {err, ok} from 'mikrojs/result'
|
|
2
|
+
import {setTimezone, stop as nativeStop, sync as nativeSync} from 'native:sntp'
|
|
3
|
+
|
|
4
|
+
import type {Result} from '../result/types.js'
|
|
5
|
+
import type {Sntp, SntpError, SntpOptions, SntpSyncResult, SntpSyncResultWithStop} from './types.js'
|
|
6
|
+
|
|
7
|
+
// native:sntp emits `InitFailed` / `Cancelled` directly; `Timeout` is a
|
|
8
|
+
// JS-side variant (the Promise racer below owns the clock).
|
|
9
|
+
|
|
10
|
+
const DEFAULT_SERVERS = ['pool.ntp.org']
|
|
11
|
+
const DEFAULT_TIMEZONE = 'UTC0'
|
|
12
|
+
const DEFAULT_TIMEOUT = 60_000
|
|
13
|
+
|
|
14
|
+
function sync(
|
|
15
|
+
options: SntpOptions & {background: true},
|
|
16
|
+
): Promise<Result<SntpSyncResultWithStop, SntpError>>
|
|
17
|
+
function sync(
|
|
18
|
+
options?: SntpOptions & {background?: false},
|
|
19
|
+
): Promise<Result<SntpSyncResult, SntpError>>
|
|
20
|
+
function sync(
|
|
21
|
+
options?: SntpOptions & {background?: boolean},
|
|
22
|
+
): Promise<Result<SntpSyncResult | SntpSyncResultWithStop, SntpError>> {
|
|
23
|
+
const servers = options?.servers ?? DEFAULT_SERVERS
|
|
24
|
+
const timezone = options?.timezone ?? DEFAULT_TIMEZONE
|
|
25
|
+
const background = options?.background ?? false
|
|
26
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT
|
|
27
|
+
|
|
28
|
+
const startResult = nativeSync(servers, timezone, background)
|
|
29
|
+
if (!startResult.ok) return Promise.resolve(startResult)
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
nativeStop()
|
|
34
|
+
resolve(err({name: 'Timeout' as const, ms: timeout}))
|
|
35
|
+
}, timeout)
|
|
36
|
+
|
|
37
|
+
void startResult.value.then((asyncResult) => {
|
|
38
|
+
clearTimeout(timer)
|
|
39
|
+
if (!asyncResult.ok) {
|
|
40
|
+
resolve(asyncResult)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
const result: SntpSyncResult = {time: new Date(asyncResult.value.time)}
|
|
44
|
+
if (background) {
|
|
45
|
+
;(result as SntpSyncResultWithStop).stop = () => nativeStop()
|
|
46
|
+
}
|
|
47
|
+
resolve(ok(result))
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const sntp: Sntp = {sync, setTimezone}
|
|
53
|
+
|
|
54
|
+
export {sntp}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {Result} from '../result/types.js'
|
|
2
|
+
|
|
3
|
+
export interface SntpOptions {
|
|
4
|
+
/** NTP server(s). Default: ['pool.ntp.org'] */
|
|
5
|
+
servers?: string[]
|
|
6
|
+
/** POSIX TZ string, e.g. 'CET-1CEST,M3.5.0,M10.5.0/3'. Default: 'UTC0' */
|
|
7
|
+
timezone?: string
|
|
8
|
+
/** Milliseconds before timeout. Default: 60000 */
|
|
9
|
+
timeout?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SntpSyncResult {
|
|
13
|
+
time: Date
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SntpSyncResultWithStop extends SntpSyncResult {
|
|
17
|
+
stop: () => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type SntpError =
|
|
21
|
+
| {name: 'InitFailed'; message: string}
|
|
22
|
+
| {name: 'Timeout'; ms: number}
|
|
23
|
+
| {name: 'Cancelled'}
|
|
24
|
+
|
|
25
|
+
export interface Sntp {
|
|
26
|
+
sync(
|
|
27
|
+
options: SntpOptions & {background: true},
|
|
28
|
+
): Promise<Result<SntpSyncResultWithStop, SntpError>>
|
|
29
|
+
sync(options?: SntpOptions & {background?: false}): Promise<Result<SntpSyncResult, SntpError>>
|
|
30
|
+
sync(
|
|
31
|
+
options?: SntpOptions & {background?: boolean},
|
|
32
|
+
): Promise<Result<SntpSyncResult | SntpSyncResultWithStop, SntpError>>
|
|
33
|
+
|
|
34
|
+
/** Set the POSIX timezone string independently of sync. */
|
|
35
|
+
setTimezone(tz: string): void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export declare const sntp: Sntp
|