@mikrojs/native 0.18.3-next.20260829153835 → 0.19.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 +24 -2
- package/include/mikrojs/private.h +28 -0
- package/package.json +4 -5
- 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/http/helpers.ts +5 -0
- package/runtime/internal.d.ts +28 -0
- package/runtime/ota/types.ts +3 -3
- package/runtime/schema/schema.ts +16 -3
- package/runtime/schema/types.ts +1 -1
- package/runtime/test/test.ts +6 -2
- package/runtime/wifi/types.ts +3 -0
- package/scripts/gen-schema-fixtures.js +317 -0
- package/src/mik_app_config.cpp +9 -1
- package/src/mik_http_client.cpp +1357 -0
- package/src/mik_repl.cpp +44 -5
- package/src/mik_schema.cpp +892 -0
- package/src/mik_wifi_client.cpp +742 -0
- package/src/mikrojs.cpp +34 -8
- package/src/modules.cpp +66 -0
- package/dist/runtime/schema/core.d.ts +0 -163
- package/dist/runtime/schema/core.d.ts.map +0 -1
- package/dist/runtime/schema/core.js +0 -306
- package/dist/runtime/schema/core.js.map +0 -1
- package/dist/runtime/schema/shared.d.ts +0 -79
- package/dist/runtime/schema/shared.d.ts.map +0 -1
- package/dist/runtime/schema/shared.js +0 -1005
- package/dist/runtime/schema/shared.js.map +0 -1
- package/runtime/http/native.ts +0 -160
- package/runtime/http/request.ts +0 -25
- package/runtime/schema/core.ts +0 -842
- package/runtime/schema/shared.ts +0 -1040
- package/runtime/wifi/wifi.ts +0 -191
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Native implementation of the `mikro/wifi` builtin (formerly TypeScript
|
|
3
|
+
* bundled to bytecode; ~22 KB heap per import). The transport stays behind
|
|
4
|
+
* `native:mikro/wifi` and is resolved through the module loader, so
|
|
5
|
+
* virtual-module overrides (simulator stubs, host test fakes) keep working.
|
|
6
|
+
* Behavior is pinned by test/wifi_client_test.cpp; runtime/wifi/types.ts
|
|
7
|
+
* remains as the type surface.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include <mikrojs/mikrojs.h>
|
|
11
|
+
#include <mikrojs/platform.h>
|
|
12
|
+
#include <mikrojs/private.h>
|
|
13
|
+
#include <mikrojs/utils.h>
|
|
14
|
+
|
|
15
|
+
#include <stdio.h>
|
|
16
|
+
#include <string.h>
|
|
17
|
+
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
#include "quickjs.h"
|
|
21
|
+
|
|
22
|
+
namespace {
|
|
23
|
+
|
|
24
|
+
constexpr int kMaxConnectRetries = 5;
|
|
25
|
+
constexpr int64_t kRetryDelayMs = 2000;
|
|
26
|
+
|
|
27
|
+
/* obj.method(args) with atom lifecycle handled. Borrows obj and args. */
|
|
28
|
+
JSValue wc_invoke(JSContext* ctx, JSValue obj, const char* method, int argc, JSValue* argv) {
|
|
29
|
+
JSAtom atom = JS_NewAtom(ctx, method);
|
|
30
|
+
JSValue r = JS_Invoke(ctx, obj, atom, argc, argv);
|
|
31
|
+
JS_FreeAtom(ctx, atom);
|
|
32
|
+
return r;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Call settle function `fn` (borrowed) with `arg` (consumed). */
|
|
36
|
+
void wc_settle(JSContext* ctx, JSValue fn, JSValue arg) {
|
|
37
|
+
JSValue r = JS_Call(ctx, fn, JS_UNDEFINED, 1, &arg);
|
|
38
|
+
JS_FreeValue(ctx, r);
|
|
39
|
+
JS_FreeValue(ctx, arg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Settle the function stored at st[key] with `arg` (consumed). */
|
|
43
|
+
void wc_settle_prop(JSContext* ctx, JSValue st, const char* key, JSValue arg) {
|
|
44
|
+
JSValue fn = JS_GetPropertyStr(ctx, st, key);
|
|
45
|
+
wc_settle(ctx, fn, arg);
|
|
46
|
+
JS_FreeValue(ctx, fn);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Convert the pending exception into a rejected promise (async parity). */
|
|
50
|
+
JSValue wc_reject_pending_exception(JSContext* ctx) {
|
|
51
|
+
JSValue exc = JS_GetException(ctx);
|
|
52
|
+
return MIK_NewRejectedPromise(ctx, 1, &exc);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Promise.resolve(v) — normalizes thenables and plain values. Consumes `v`. */
|
|
56
|
+
JSValue wc_promise_resolve(JSContext* ctx, JSValue v) {
|
|
57
|
+
if (JS_IsException(v)) return v;
|
|
58
|
+
JSValue g = JS_GetGlobalObject(ctx);
|
|
59
|
+
JSValue promise_ctor = JS_GetPropertyStr(ctx, g, "Promise");
|
|
60
|
+
JS_FreeValue(ctx, g);
|
|
61
|
+
JSValue p = wc_invoke(ctx, promise_ctor, "resolve", 1, &v);
|
|
62
|
+
JS_FreeValue(ctx, promise_ctor);
|
|
63
|
+
JS_FreeValue(ctx, v);
|
|
64
|
+
return p;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* console.warn(msg) — the retry notice stays on the console stream (captured
|
|
68
|
+
* by sim/test output hooks), not the platform log. Failures are swallowed. */
|
|
69
|
+
void wc_console_warn(JSContext* ctx, const char* msg) {
|
|
70
|
+
JSValue g = JS_GetGlobalObject(ctx);
|
|
71
|
+
JSValue console = JS_GetPropertyStr(ctx, g, "console");
|
|
72
|
+
JS_FreeValue(ctx, g);
|
|
73
|
+
if (JS_IsException(console)) {
|
|
74
|
+
JS_FreeValue(ctx, JS_GetException(ctx));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (JS_IsObject(console)) {
|
|
78
|
+
JSValue arg = JS_NewString(ctx, msg);
|
|
79
|
+
JSValue r = wc_invoke(ctx, console, "warn", 1, &arg);
|
|
80
|
+
JS_FreeValue(ctx, arg);
|
|
81
|
+
if (JS_IsException(r)) JS_FreeValue(ctx, JS_GetException(ctx));
|
|
82
|
+
JS_FreeValue(ctx, r);
|
|
83
|
+
}
|
|
84
|
+
JS_FreeValue(ctx, console);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* p.then(on_fulfilled, on_rejected). Consumes both handlers; borrows `p`. */
|
|
88
|
+
void wc_then2(JSContext* ctx, JSValue p, JSValue on_fulfilled, JSValue on_rejected) {
|
|
89
|
+
JSValue args[2] = {on_fulfilled, on_rejected};
|
|
90
|
+
JSValue derived = wc_invoke(ctx, p, "then", 2, args);
|
|
91
|
+
JS_FreeValue(ctx, derived);
|
|
92
|
+
JS_FreeValue(ctx, on_fulfilled);
|
|
93
|
+
JS_FreeValue(ctx, on_rejected);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Read obj[name] as a boolean. Caller data can be a Proxy or a throwing
|
|
97
|
+
* getter; *threw reports that with the exception left pending (JS_ToBool
|
|
98
|
+
* maps JS_EXCEPTION to -1 without arming anything new). */
|
|
99
|
+
bool wc_get_flag(JSContext* ctx, JSValue obj, const char* name, bool* threw) {
|
|
100
|
+
JSValue v = JS_GetPropertyStr(ctx, obj, name);
|
|
101
|
+
if (JS_IsException(v)) {
|
|
102
|
+
*threw = true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
int b = JS_ToBool(ctx, v);
|
|
106
|
+
JS_FreeValue(ctx, v);
|
|
107
|
+
*threw = b < 0;
|
|
108
|
+
return b > 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Strict-number status code, or -1 when the value is not a number
|
|
112
|
+
* (TS used Map.get / === on numbers, so a string code never matches). */
|
|
113
|
+
int wc_status_code(JSContext* ctx, JSValue v) {
|
|
114
|
+
if (!JS_IsNumber(v)) return -1;
|
|
115
|
+
double d = 0;
|
|
116
|
+
JS_ToFloat64(ctx, &d, v);
|
|
117
|
+
int code = static_cast<int>(d);
|
|
118
|
+
return static_cast<double>(code) == d ? code : -1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* ── Passthrough / swallow / setter method tables ───────────────────────── */
|
|
122
|
+
|
|
123
|
+
/* Methods and getters that forward straight to the native instance,
|
|
124
|
+
* returning the native result verbatim. Second field: forward argv[0]. */
|
|
125
|
+
struct WcForward {
|
|
126
|
+
const char* method;
|
|
127
|
+
bool takes_arg;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
constexpr WcForward kForward[] = {
|
|
131
|
+
{"rssi", false}, // 0: wifi.rssi()
|
|
132
|
+
{"getHostname", false}, // 1: wifi.hostname
|
|
133
|
+
{"getCountry", false}, // 2: wifi.country
|
|
134
|
+
{"getRssiThreshold", false}, // 3: wifi.rssiThreshold
|
|
135
|
+
{"getPowerSave", false}, // 4: wifi.powerSave
|
|
136
|
+
{"apStart", true}, // 5: wifi.ap.start(options)
|
|
137
|
+
{"apStop", false}, // 6: wifi.ap.stop()
|
|
138
|
+
{"apDeauthStation", true}, // 7: wifi.ap.deauthStation(mac)
|
|
139
|
+
{"apIsActive", false}, // 8: wifi.ap.isActive
|
|
140
|
+
{"apIp", false}, // 9: wifi.ap.ip
|
|
141
|
+
{"apStations", false}, // 10: wifi.ap.stations
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
JSValue wc_forward_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
145
|
+
JSValue* func_data) {
|
|
146
|
+
(void)this_val;
|
|
147
|
+
const WcForward& f = kForward[magic];
|
|
148
|
+
JSValue arg = f.takes_arg ? (argc > 0 ? argv[0] : JS_UNDEFINED) : JS_UNDEFINED;
|
|
149
|
+
return wc_invoke(ctx, func_data[0], f.method, f.takes_arg ? 1 : 0, &arg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Setters: forward argv[0], discard the native Result. */
|
|
153
|
+
constexpr const char* kSetter[] = {
|
|
154
|
+
"setRssiThreshold", // 0: wifi.rssiThreshold =
|
|
155
|
+
"setPowerSave", // 1: wifi.powerSave =
|
|
156
|
+
"apSetInactiveTimeout", // 2: wifi.ap.inactiveTimeout =
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
JSValue wc_setter_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
160
|
+
JSValue* func_data) {
|
|
161
|
+
(void)this_val;
|
|
162
|
+
JSValue arg = argc > 0 ? argv[0] : JS_UNDEFINED;
|
|
163
|
+
JSValue r = wc_invoke(ctx, func_data[0], kSetter[magic], 1, &arg);
|
|
164
|
+
if (JS_IsException(r)) return r;
|
|
165
|
+
JS_FreeValue(ctx, r);
|
|
166
|
+
return JS_UNDEFINED;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Getters that unwrap an ok Result and fall back to a default on err. */
|
|
170
|
+
enum { SWALLOW_MAC, SWALLOW_TX_POWER, SWALLOW_AP_INACTIVE_TIMEOUT };
|
|
171
|
+
|
|
172
|
+
constexpr const char* kSwallowMethod[] = {"mac", "getTxPower", "apGetInactiveTimeout"};
|
|
173
|
+
|
|
174
|
+
JSValue wc_swallow_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
175
|
+
JSValue* func_data) {
|
|
176
|
+
(void)this_val;
|
|
177
|
+
(void)argc;
|
|
178
|
+
(void)argv;
|
|
179
|
+
JSValue r = wc_invoke(ctx, func_data[0], kSwallowMethod[magic], 0, nullptr);
|
|
180
|
+
if (JS_IsException(r)) return r;
|
|
181
|
+
bool threw = false;
|
|
182
|
+
bool is_ok = wc_get_flag(ctx, r, "ok", &threw);
|
|
183
|
+
if (threw) {
|
|
184
|
+
JS_FreeValue(ctx, r);
|
|
185
|
+
return JS_EXCEPTION;
|
|
186
|
+
}
|
|
187
|
+
if (is_ok) {
|
|
188
|
+
JSValue v = JS_GetPropertyStr(ctx, r, "value");
|
|
189
|
+
JS_FreeValue(ctx, r);
|
|
190
|
+
return v;
|
|
191
|
+
}
|
|
192
|
+
JS_FreeValue(ctx, r);
|
|
193
|
+
return magic == SWALLOW_MAC ? JS_NewString(ctx, "") : JS_NewInt32(ctx, 0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ── status / isConnected / ip / disconnect / ipConfig ──────────────────── */
|
|
197
|
+
|
|
198
|
+
const char* wc_status_name(int code) {
|
|
199
|
+
switch (code) {
|
|
200
|
+
case 254:
|
|
201
|
+
return "STOPPED";
|
|
202
|
+
case 0:
|
|
203
|
+
return "IDLE";
|
|
204
|
+
case 1:
|
|
205
|
+
return "NO_SSID_AVAIL";
|
|
206
|
+
case 2:
|
|
207
|
+
return "SCAN_COMPLETED";
|
|
208
|
+
case 3:
|
|
209
|
+
return "CONNECTED";
|
|
210
|
+
case 4:
|
|
211
|
+
return "CONNECT_FAILED";
|
|
212
|
+
case 5:
|
|
213
|
+
return "CONNECTION_LOST";
|
|
214
|
+
default:
|
|
215
|
+
return "DISCONNECTED";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
JSValue wc_status_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
220
|
+
JSValue* func_data) {
|
|
221
|
+
(void)this_val;
|
|
222
|
+
(void)argc;
|
|
223
|
+
(void)argv;
|
|
224
|
+
(void)magic;
|
|
225
|
+
JSValue v = wc_invoke(ctx, func_data[0], "status", 0, nullptr);
|
|
226
|
+
if (JS_IsException(v)) return v;
|
|
227
|
+
int code = wc_status_code(ctx, v);
|
|
228
|
+
JS_FreeValue(ctx, v);
|
|
229
|
+
return JS_NewString(ctx, wc_status_name(code));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
JSValue wc_is_connected_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
233
|
+
JSValue* func_data) {
|
|
234
|
+
(void)this_val;
|
|
235
|
+
(void)argc;
|
|
236
|
+
(void)argv;
|
|
237
|
+
(void)magic;
|
|
238
|
+
JSValue v = wc_invoke(ctx, func_data[0], "status", 0, nullptr);
|
|
239
|
+
if (JS_IsException(v)) return v;
|
|
240
|
+
bool connected = wc_status_code(ctx, v) == 3;
|
|
241
|
+
JS_FreeValue(ctx, v);
|
|
242
|
+
return JS_NewBool(ctx, connected);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
JSValue wc_ip_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
246
|
+
JSValue* func_data) {
|
|
247
|
+
(void)this_val;
|
|
248
|
+
(void)argc;
|
|
249
|
+
(void)argv;
|
|
250
|
+
(void)magic;
|
|
251
|
+
JSValue v = wc_invoke(ctx, func_data[0], "ip", 0, nullptr);
|
|
252
|
+
if (JS_IsException(v) || !JS_IsString(v)) return v;
|
|
253
|
+
const char* s = JS_ToCString(ctx, v);
|
|
254
|
+
bool unset = s && strcmp(s, "0.0.0.0") == 0;
|
|
255
|
+
JS_FreeCString(ctx, s);
|
|
256
|
+
if (!unset) return v;
|
|
257
|
+
JS_FreeValue(ctx, v);
|
|
258
|
+
return JS_UNDEFINED;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
JSValue wc_disconnect_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
262
|
+
JSValue* func_data) {
|
|
263
|
+
(void)this_val;
|
|
264
|
+
(void)magic;
|
|
265
|
+
/* options?.shutdown — nullish options collapse to undefined. */
|
|
266
|
+
JSValue shutdown = JS_UNDEFINED;
|
|
267
|
+
if (argc > 0 && JS_IsObject(argv[0])) {
|
|
268
|
+
shutdown = JS_GetPropertyStr(ctx, argv[0], "shutdown");
|
|
269
|
+
if (JS_IsException(shutdown)) return shutdown;
|
|
270
|
+
}
|
|
271
|
+
JSValue r = wc_invoke(ctx, func_data[0], "disconnect", 1, &shutdown);
|
|
272
|
+
JS_FreeValue(ctx, shutdown);
|
|
273
|
+
return r;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
JSValue wc_ip_config_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
277
|
+
JSValue* func_data) {
|
|
278
|
+
(void)this_val;
|
|
279
|
+
(void)magic;
|
|
280
|
+
/* Bare-undefined results from getIpConfig pass through untouched (pinned
|
|
281
|
+
* quirk: the native side returns no Result when the netif is missing). */
|
|
282
|
+
if (argc == 0 || JS_IsUndefined(argv[0])) {
|
|
283
|
+
return wc_invoke(ctx, func_data[0], "getIpConfig", 0, nullptr);
|
|
284
|
+
}
|
|
285
|
+
return wc_invoke(ctx, func_data[0], "setIpConfig", 1, &argv[0]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* ── scan ───────────────────────────────────────────────────────────────── */
|
|
289
|
+
|
|
290
|
+
/* func_data: [0]=resolve, [1]=reject. */
|
|
291
|
+
JSValue wc_scan_on_result(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
292
|
+
JSValue* func_data) {
|
|
293
|
+
(void)this_val;
|
|
294
|
+
(void)magic;
|
|
295
|
+
JSValue ar = argc > 0 ? argv[0] : JS_UNDEFINED;
|
|
296
|
+
bool threw = false;
|
|
297
|
+
bool is_ok = wc_get_flag(ctx, ar, "ok", &threw);
|
|
298
|
+
if (threw) {
|
|
299
|
+
wc_settle(ctx, func_data[1], JS_GetException(ctx));
|
|
300
|
+
return JS_UNDEFINED;
|
|
301
|
+
}
|
|
302
|
+
if (!is_ok) {
|
|
303
|
+
wc_settle(ctx, func_data[0], JS_DupValue(ctx, ar));
|
|
304
|
+
return JS_UNDEFINED;
|
|
305
|
+
}
|
|
306
|
+
JSValue v = JS_GetPropertyStr(ctx, ar, "value");
|
|
307
|
+
if (JS_IsException(v)) {
|
|
308
|
+
wc_settle(ctx, func_data[1], JS_GetException(ctx));
|
|
309
|
+
return JS_UNDEFINED;
|
|
310
|
+
}
|
|
311
|
+
wc_settle(ctx, func_data[0], mik__result_ok(ctx, v));
|
|
312
|
+
return JS_UNDEFINED;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
JSValue wc_on_rejected(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
316
|
+
JSValue* func_data) {
|
|
317
|
+
(void)this_val;
|
|
318
|
+
(void)magic;
|
|
319
|
+
wc_settle(ctx, func_data[1], JS_DupValue(ctx, argc > 0 ? argv[0] : JS_UNDEFINED));
|
|
320
|
+
return JS_UNDEFINED;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
JSValue wc_scan_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
324
|
+
JSValue* func_data) {
|
|
325
|
+
(void)this_val;
|
|
326
|
+
(void)magic;
|
|
327
|
+
JSValue opts = argc > 0 ? argv[0] : JS_UNDEFINED;
|
|
328
|
+
JSValue start = wc_invoke(ctx, func_data[0], "scan", 1, &opts);
|
|
329
|
+
if (JS_IsException(start)) return wc_reject_pending_exception(ctx);
|
|
330
|
+
bool threw = false;
|
|
331
|
+
bool is_ok = wc_get_flag(ctx, start, "ok", &threw);
|
|
332
|
+
if (threw) {
|
|
333
|
+
JS_FreeValue(ctx, start);
|
|
334
|
+
return wc_reject_pending_exception(ctx);
|
|
335
|
+
}
|
|
336
|
+
if (!is_ok) {
|
|
337
|
+
return MIK_NewResolvedPromise(ctx, 1, &start);
|
|
338
|
+
}
|
|
339
|
+
JSValue inner = JS_GetPropertyStr(ctx, start, "value");
|
|
340
|
+
JS_FreeValue(ctx, start);
|
|
341
|
+
JSValue wrapped = wc_promise_resolve(ctx, inner);
|
|
342
|
+
if (JS_IsException(wrapped)) return wc_reject_pending_exception(ctx);
|
|
343
|
+
|
|
344
|
+
JSValue rfuncs[2];
|
|
345
|
+
JSValue promise = JS_NewPromiseCapability(ctx, rfuncs);
|
|
346
|
+
if (JS_IsException(promise)) {
|
|
347
|
+
JS_FreeValue(ctx, wrapped);
|
|
348
|
+
return promise;
|
|
349
|
+
}
|
|
350
|
+
JSValue on_ok = JS_NewCFunctionData(ctx, wc_scan_on_result, 1, 0, 2, rfuncs);
|
|
351
|
+
JSValue on_err = JS_NewCFunctionData(ctx, wc_on_rejected, 1, 0, 2, rfuncs);
|
|
352
|
+
JS_FreeValue(ctx, rfuncs[0]);
|
|
353
|
+
JS_FreeValue(ctx, rfuncs[1]);
|
|
354
|
+
wc_then2(ctx, wrapped, on_ok, on_err);
|
|
355
|
+
JS_FreeValue(ctx, wrapped);
|
|
356
|
+
return promise;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* ── connect (C-driven retry loop) ──────────────────────────────────────── */
|
|
360
|
+
|
|
361
|
+
/* Connect state object props: nat (native instance), ssid, pass, att (int),
|
|
362
|
+
* res/rej (capability settle functions). */
|
|
363
|
+
|
|
364
|
+
void wc_connect_attempt(JSContext* ctx, JSValue st);
|
|
365
|
+
|
|
366
|
+
JSValue wc_connect_retry_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
367
|
+
JSValue* func_data) {
|
|
368
|
+
(void)this_val;
|
|
369
|
+
(void)argc;
|
|
370
|
+
(void)argv;
|
|
371
|
+
(void)magic;
|
|
372
|
+
wc_connect_attempt(ctx, func_data[0]);
|
|
373
|
+
return JS_UNDEFINED;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
void wc_connect_reject_pending(JSContext* ctx, JSValue st) {
|
|
377
|
+
JSValue exc = JS_GetException(ctx);
|
|
378
|
+
wc_settle_prop(ctx, st, "rej", exc);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* Settled value of one native connect attempt. func_data: [0]=state. */
|
|
382
|
+
JSValue wc_connect_on_result(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
383
|
+
JSValue* func_data) {
|
|
384
|
+
(void)this_val;
|
|
385
|
+
(void)magic;
|
|
386
|
+
JSValue st = func_data[0];
|
|
387
|
+
JSValue ar = argc > 0 ? argv[0] : JS_UNDEFINED;
|
|
388
|
+
|
|
389
|
+
bool threw = false;
|
|
390
|
+
bool is_ok = wc_get_flag(ctx, ar, "ok", &threw);
|
|
391
|
+
if (threw) {
|
|
392
|
+
wc_connect_reject_pending(ctx, st);
|
|
393
|
+
return JS_UNDEFINED;
|
|
394
|
+
}
|
|
395
|
+
if (is_ok) {
|
|
396
|
+
JSValue v = JS_GetPropertyStr(ctx, ar, "value");
|
|
397
|
+
if (JS_IsException(v)) {
|
|
398
|
+
wc_connect_reject_pending(ctx, st);
|
|
399
|
+
return JS_UNDEFINED;
|
|
400
|
+
}
|
|
401
|
+
wc_settle_prop(ctx, st, "res", mik__result_ok(ctx, v));
|
|
402
|
+
return JS_UNDEFINED;
|
|
403
|
+
}
|
|
404
|
+
JSValue att_v = JS_GetPropertyStr(ctx, st, "att");
|
|
405
|
+
if (JS_IsException(att_v)) {
|
|
406
|
+
wc_connect_reject_pending(ctx, st);
|
|
407
|
+
return JS_UNDEFINED;
|
|
408
|
+
}
|
|
409
|
+
int32_t attempt = 0;
|
|
410
|
+
JS_ToInt32(ctx, &attempt, att_v);
|
|
411
|
+
JS_FreeValue(ctx, att_v);
|
|
412
|
+
if (attempt >= kMaxConnectRetries) {
|
|
413
|
+
/* Exhaustion returns the last native error verbatim (pinned). */
|
|
414
|
+
wc_settle_prop(ctx, st, "res", JS_DupValue(ctx, ar));
|
|
415
|
+
return JS_UNDEFINED;
|
|
416
|
+
}
|
|
417
|
+
JS_SetPropertyStr(ctx, st, "att", JS_NewInt32(ctx, attempt + 1));
|
|
418
|
+
char warn_msg[64];
|
|
419
|
+
snprintf(warn_msg, sizeof(warn_msg), "WiFi connect failed, retrying in %dms…",
|
|
420
|
+
static_cast<int>(kRetryDelayMs));
|
|
421
|
+
wc_console_warn(ctx, warn_msg);
|
|
422
|
+
|
|
423
|
+
/* Keep the radio up between retries so the next attempt reconnects fast. */
|
|
424
|
+
JSValue nat = JS_GetPropertyStr(ctx, st, "nat");
|
|
425
|
+
JSValue keep_up = JS_FALSE;
|
|
426
|
+
JSValue r = wc_invoke(ctx, nat, "disconnect", 1, &keep_up);
|
|
427
|
+
JS_FreeValue(ctx, nat);
|
|
428
|
+
if (JS_IsException(r)) {
|
|
429
|
+
wc_connect_reject_pending(ctx, st);
|
|
430
|
+
return JS_UNDEFINED;
|
|
431
|
+
}
|
|
432
|
+
JS_FreeValue(ctx, r);
|
|
433
|
+
|
|
434
|
+
JSValue retry_fn = JS_NewCFunctionData(ctx, wc_connect_retry_cf, 0, 0, 1, &st);
|
|
435
|
+
MIKRuntime* mik_rt = MIK_GetRuntime(ctx);
|
|
436
|
+
/* MIK_FreeRuntime nulls timers before JS_FreeContext; skip scheduling in
|
|
437
|
+
* that window (the connect promise stays pending, matching http). */
|
|
438
|
+
if (mik_rt->timers) {
|
|
439
|
+
MIK_Timer_Schedule(mik_rt->timers, ctx, retry_fn, 0, nullptr, kRetryDelayMs * 1000, false,
|
|
440
|
+
MIK_GetPlatform()->get_boot_us());
|
|
441
|
+
}
|
|
442
|
+
JS_FreeValue(ctx, retry_fn);
|
|
443
|
+
return JS_UNDEFINED;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
JSValue wc_connect_on_reject(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
447
|
+
JSValue* func_data) {
|
|
448
|
+
(void)this_val;
|
|
449
|
+
(void)magic;
|
|
450
|
+
wc_settle_prop(ctx, func_data[0], "rej",
|
|
451
|
+
JS_DupValue(ctx, argc > 0 ? argv[0] : JS_UNDEFINED));
|
|
452
|
+
return JS_UNDEFINED;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
void wc_connect_attempt(JSContext* ctx, JSValue st) {
|
|
456
|
+
JSValue nat = JS_GetPropertyStr(ctx, st, "nat");
|
|
457
|
+
JSValue args[2] = {JS_GetPropertyStr(ctx, st, "ssid"), JS_GetPropertyStr(ctx, st, "pass")};
|
|
458
|
+
JSValue start = wc_invoke(ctx, nat, "connect", 2, args);
|
|
459
|
+
JS_FreeValue(ctx, args[0]);
|
|
460
|
+
JS_FreeValue(ctx, args[1]);
|
|
461
|
+
JS_FreeValue(ctx, nat);
|
|
462
|
+
if (JS_IsException(start)) {
|
|
463
|
+
wc_connect_reject_pending(ctx, st);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
bool threw = false;
|
|
467
|
+
bool is_ok = wc_get_flag(ctx, start, "ok", &threw);
|
|
468
|
+
if (threw) {
|
|
469
|
+
JS_FreeValue(ctx, start);
|
|
470
|
+
wc_connect_reject_pending(ctx, st);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (!is_ok) {
|
|
474
|
+
/* Start error is returned verbatim without retrying (pinned). */
|
|
475
|
+
wc_settle_prop(ctx, st, "res", start);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
JSValue inner = JS_GetPropertyStr(ctx, start, "value");
|
|
479
|
+
JS_FreeValue(ctx, start);
|
|
480
|
+
JSValue wrapped = wc_promise_resolve(ctx, inner);
|
|
481
|
+
if (JS_IsException(wrapped)) {
|
|
482
|
+
wc_connect_reject_pending(ctx, st);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
JSValue on_ok = JS_NewCFunctionData(ctx, wc_connect_on_result, 1, 0, 1, &st);
|
|
487
|
+
JSValue on_err = JS_NewCFunctionData(ctx, wc_connect_on_reject, 1, 0, 1, &st);
|
|
488
|
+
wc_then2(ctx, wrapped, on_ok, on_err);
|
|
489
|
+
JS_FreeValue(ctx, wrapped);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
JSValue wc_connect_cf(JSContext* ctx, JSValue this_val, int argc, JSValue* argv, int magic,
|
|
493
|
+
JSValue* func_data) {
|
|
494
|
+
(void)this_val;
|
|
495
|
+
(void)magic;
|
|
496
|
+
JSValue options = argc > 0 ? argv[0] : JS_UNDEFINED;
|
|
497
|
+
|
|
498
|
+
/* TS destructured `options` up front; a nullish argument throws there
|
|
499
|
+
* and rejects (async parity). */
|
|
500
|
+
JSValue ssid = JS_GetPropertyStr(ctx, options, "ssid");
|
|
501
|
+
if (JS_IsException(ssid)) return wc_reject_pending_exception(ctx);
|
|
502
|
+
JSValue pass = JS_GetPropertyStr(ctx, options, "passphrase");
|
|
503
|
+
if (JS_IsException(pass)) {
|
|
504
|
+
JS_FreeValue(ctx, ssid);
|
|
505
|
+
return wc_reject_pending_exception(ctx);
|
|
506
|
+
}
|
|
507
|
+
JSValue tx_power = JS_GetPropertyStr(ctx, options, "txPower");
|
|
508
|
+
if (JS_IsException(tx_power)) {
|
|
509
|
+
JS_FreeValue(ctx, ssid);
|
|
510
|
+
JS_FreeValue(ctx, pass);
|
|
511
|
+
return wc_reject_pending_exception(ctx);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!JS_IsUndefined(tx_power)) {
|
|
515
|
+
JSValue r = wc_invoke(ctx, func_data[0], "setTxPower", 1, &tx_power);
|
|
516
|
+
JS_FreeValue(ctx, tx_power);
|
|
517
|
+
if (JS_IsException(r)) {
|
|
518
|
+
JS_FreeValue(ctx, ssid);
|
|
519
|
+
JS_FreeValue(ctx, pass);
|
|
520
|
+
return wc_reject_pending_exception(ctx);
|
|
521
|
+
}
|
|
522
|
+
bool threw = false;
|
|
523
|
+
bool tx_ok = wc_get_flag(ctx, r, "ok", &threw);
|
|
524
|
+
if (threw) {
|
|
525
|
+
JS_FreeValue(ctx, r);
|
|
526
|
+
JS_FreeValue(ctx, ssid);
|
|
527
|
+
JS_FreeValue(ctx, pass);
|
|
528
|
+
return wc_reject_pending_exception(ctx);
|
|
529
|
+
}
|
|
530
|
+
if (!tx_ok) {
|
|
531
|
+
/* setTxPower error short-circuits before any connect (pinned). */
|
|
532
|
+
JS_FreeValue(ctx, ssid);
|
|
533
|
+
JS_FreeValue(ctx, pass);
|
|
534
|
+
return MIK_NewResolvedPromise(ctx, 1, &r);
|
|
535
|
+
}
|
|
536
|
+
JS_FreeValue(ctx, r);
|
|
537
|
+
} else {
|
|
538
|
+
JS_FreeValue(ctx, tx_power);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
JSValue rfuncs[2];
|
|
542
|
+
JSValue promise = JS_NewPromiseCapability(ctx, rfuncs);
|
|
543
|
+
if (JS_IsException(promise)) {
|
|
544
|
+
JS_FreeValue(ctx, ssid);
|
|
545
|
+
JS_FreeValue(ctx, pass);
|
|
546
|
+
return promise;
|
|
547
|
+
}
|
|
548
|
+
JSValue st = JS_NewObjectProto(ctx, JS_NULL);
|
|
549
|
+
JS_SetPropertyStr(ctx, st, "nat", JS_DupValue(ctx, func_data[0]));
|
|
550
|
+
JS_SetPropertyStr(ctx, st, "ssid", ssid);
|
|
551
|
+
JS_SetPropertyStr(ctx, st, "pass", pass);
|
|
552
|
+
JS_SetPropertyStr(ctx, st, "att", JS_NewInt32(ctx, 0));
|
|
553
|
+
JS_SetPropertyStr(ctx, st, "res", rfuncs[0]);
|
|
554
|
+
JS_SetPropertyStr(ctx, st, "rej", rfuncs[1]);
|
|
555
|
+
wc_connect_attempt(ctx, st);
|
|
556
|
+
JS_FreeValue(ctx, st);
|
|
557
|
+
return promise;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/* ── Module wiring ──────────────────────────────────────────────────────── */
|
|
561
|
+
|
|
562
|
+
/* lazyEvent(native, event) via mikro/observable/lazy. Returns the Observable
|
|
563
|
+
* (owned) or an exception. Borrows all arguments. */
|
|
564
|
+
JSValue wc_lazy_event(JSContext* ctx, JSValue lazy_fn, JSValue nat, const char* event) {
|
|
565
|
+
JSValue args[2] = {nat, JS_NewString(ctx, event)};
|
|
566
|
+
JSValue obs = JS_Call(ctx, lazy_fn, JS_UNDEFINED, 2, args);
|
|
567
|
+
JS_FreeValue(ctx, args[1]);
|
|
568
|
+
return obs;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* Define an accessor property on obj. Consumes getter and setter. */
|
|
572
|
+
int wc_define_accessor(JSContext* ctx, JSValue obj, const char* name, JSValue getter,
|
|
573
|
+
JSValue setter) {
|
|
574
|
+
JSAtom atom = JS_NewAtom(ctx, name);
|
|
575
|
+
int r = JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter,
|
|
576
|
+
JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE);
|
|
577
|
+
JS_FreeAtom(ctx, atom);
|
|
578
|
+
return r;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* JS_NewCFunctionData leaves functions anonymous; name the public methods so
|
|
582
|
+
* stacks and inspect output stay readable. Returns fn. */
|
|
583
|
+
JSValue wc_named(JSContext* ctx, JSValue fn, const char* name) {
|
|
584
|
+
JS_DefinePropertyValueStr(ctx, fn, "name", JS_NewString(ctx, name), JS_PROP_CONFIGURABLE);
|
|
585
|
+
return fn;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
JSValue wc_forward_fn(JSContext* ctx, JSValue nat, int magic, int length) {
|
|
589
|
+
return JS_NewCFunctionData(ctx, wc_forward_cf, length, magic, 1, &nat);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/* Build the `ap` namespace object. Borrows nat and lazy_fn. */
|
|
593
|
+
JSValue wc_make_ap(JSContext* ctx, JSValue nat, JSValue lazy_fn) {
|
|
594
|
+
JSValue ap = JS_NewObject(ctx);
|
|
595
|
+
JS_DefinePropertyValueStr(ctx, ap, "start", wc_named(ctx, wc_forward_fn(ctx, nat, 5, 1), "start"),
|
|
596
|
+
JS_PROP_C_W_E);
|
|
597
|
+
JS_DefinePropertyValueStr(ctx, ap, "stop", wc_named(ctx, wc_forward_fn(ctx, nat, 6, 0), "stop"),
|
|
598
|
+
JS_PROP_C_W_E);
|
|
599
|
+
wc_define_accessor(ctx, ap, "isActive", wc_forward_fn(ctx, nat, 8, 0), JS_UNDEFINED);
|
|
600
|
+
wc_define_accessor(ctx, ap, "ip", wc_forward_fn(ctx, nat, 9, 0), JS_UNDEFINED);
|
|
601
|
+
wc_define_accessor(ctx, ap, "stations", wc_forward_fn(ctx, nat, 10, 0), JS_UNDEFINED);
|
|
602
|
+
JS_DefinePropertyValueStr(ctx, ap, "deauthStation",
|
|
603
|
+
wc_named(ctx, wc_forward_fn(ctx, nat, 7, 1), "deauthStation"),
|
|
604
|
+
JS_PROP_C_W_E);
|
|
605
|
+
wc_define_accessor(
|
|
606
|
+
ctx, ap, "inactiveTimeout",
|
|
607
|
+
JS_NewCFunctionData(ctx, wc_swallow_cf, 0, SWALLOW_AP_INACTIVE_TIMEOUT, 1, &nat),
|
|
608
|
+
JS_NewCFunctionData(ctx, wc_setter_cf, 1, 2, 1, &nat));
|
|
609
|
+
|
|
610
|
+
static const struct {
|
|
611
|
+
const char* prop;
|
|
612
|
+
const char* event;
|
|
613
|
+
} kApEvents[] = {
|
|
614
|
+
{"onStationConnect", "station-connect"},
|
|
615
|
+
{"onStationDisconnect", "station-disconnect"},
|
|
616
|
+
};
|
|
617
|
+
for (const auto& e : kApEvents) {
|
|
618
|
+
JSValue obs = wc_lazy_event(ctx, lazy_fn, nat, e.event);
|
|
619
|
+
if (JS_IsException(obs)) {
|
|
620
|
+
JS_FreeValue(ctx, ap);
|
|
621
|
+
return obs;
|
|
622
|
+
}
|
|
623
|
+
JS_DefinePropertyValueStr(ctx, ap, e.prop, obs, JS_PROP_C_W_E);
|
|
624
|
+
}
|
|
625
|
+
return ap;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
int wc_module_init(JSContext* ctx, JSModuleDef* m) {
|
|
629
|
+
/* Wifi (a class export) is constructed right here, and lazyEvent is
|
|
630
|
+
* invoked below — both need the target module fully evaluated. */
|
|
631
|
+
JSValue ns = mik__load_module_ns(ctx, "mikro/wifi", "native:mikro/wifi", true);
|
|
632
|
+
if (JS_IsException(ns)) return -1;
|
|
633
|
+
JSValue wifi_ctor = JS_GetPropertyStr(ctx, ns, "Wifi");
|
|
634
|
+
JS_FreeValue(ctx, ns);
|
|
635
|
+
if (JS_IsException(wifi_ctor)) return -1;
|
|
636
|
+
|
|
637
|
+
/* One native instance per runtime, constructed at import (TS parity:
|
|
638
|
+
* `new NativeWifi()` ran at module scope and its throw failed the import). */
|
|
639
|
+
JSValue nat = JS_CallConstructor(ctx, wifi_ctor, 0, nullptr);
|
|
640
|
+
JS_FreeValue(ctx, wifi_ctor);
|
|
641
|
+
if (JS_IsException(nat)) return -1;
|
|
642
|
+
|
|
643
|
+
JSValue lazy_ns = mik__load_module_ns(ctx, "mikro/wifi", "mikro/observable/lazy", true);
|
|
644
|
+
if (JS_IsException(lazy_ns)) {
|
|
645
|
+
JS_FreeValue(ctx, nat);
|
|
646
|
+
return -1;
|
|
647
|
+
}
|
|
648
|
+
JSValue lazy_fn = JS_GetPropertyStr(ctx, lazy_ns, "lazyEvent");
|
|
649
|
+
JS_FreeValue(ctx, lazy_ns);
|
|
650
|
+
if (JS_IsException(lazy_fn)) {
|
|
651
|
+
JS_FreeValue(ctx, nat);
|
|
652
|
+
return -1;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/* Property definition order mirrors the TS object literal so
|
|
656
|
+
* Object.keys(wifi) snapshots stay identical across the port. */
|
|
657
|
+
JSValue wifi = JS_NewObject(ctx);
|
|
658
|
+
JS_DefinePropertyValueStr(
|
|
659
|
+
ctx, wifi, "connect",
|
|
660
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_connect_cf, 1, 0, 1, &nat), "connect"),
|
|
661
|
+
JS_PROP_C_W_E);
|
|
662
|
+
JS_DefinePropertyValueStr(
|
|
663
|
+
ctx, wifi, "disconnect",
|
|
664
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_disconnect_cf, 0, 0, 1, &nat), "disconnect"),
|
|
665
|
+
JS_PROP_C_W_E);
|
|
666
|
+
JS_DefinePropertyValueStr(ctx, wifi, "rssi", wc_named(ctx, wc_forward_fn(ctx, nat, 0, 0), "rssi"),
|
|
667
|
+
JS_PROP_C_W_E);
|
|
668
|
+
JS_DefinePropertyValueStr(ctx, wifi, "ip",
|
|
669
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_ip_cf, 0, 0, 1, &nat), "ip"),
|
|
670
|
+
JS_PROP_C_W_E);
|
|
671
|
+
JS_DefinePropertyValueStr(
|
|
672
|
+
ctx, wifi, "status",
|
|
673
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_status_cf, 0, 0, 1, &nat), "status"),
|
|
674
|
+
JS_PROP_C_W_E);
|
|
675
|
+
wc_define_accessor(ctx, wifi, "isConnected",
|
|
676
|
+
JS_NewCFunctionData(ctx, wc_is_connected_cf, 0, 0, 1, &nat), JS_UNDEFINED);
|
|
677
|
+
JS_DefinePropertyValueStr(
|
|
678
|
+
ctx, wifi, "scan",
|
|
679
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_scan_cf, 0, 0, 1, &nat), "scan"), JS_PROP_C_W_E);
|
|
680
|
+
|
|
681
|
+
static const struct {
|
|
682
|
+
const char* prop;
|
|
683
|
+
const char* event;
|
|
684
|
+
} kEvents[] = {
|
|
685
|
+
{"onConnect", "connect"},
|
|
686
|
+
{"onDisconnect", "disconnect"},
|
|
687
|
+
{"onRssiLow", "rssi-low"},
|
|
688
|
+
};
|
|
689
|
+
for (const auto& e : kEvents) {
|
|
690
|
+
JSValue obs = wc_lazy_event(ctx, lazy_fn, nat, e.event);
|
|
691
|
+
if (JS_IsException(obs)) {
|
|
692
|
+
JS_FreeValue(ctx, wifi);
|
|
693
|
+
JS_FreeValue(ctx, lazy_fn);
|
|
694
|
+
JS_FreeValue(ctx, nat);
|
|
695
|
+
return -1;
|
|
696
|
+
}
|
|
697
|
+
JS_DefinePropertyValueStr(ctx, wifi, e.prop, obs, JS_PROP_C_W_E);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
wc_define_accessor(ctx, wifi, "mac",
|
|
701
|
+
JS_NewCFunctionData(ctx, wc_swallow_cf, 0, SWALLOW_MAC, 1, &nat),
|
|
702
|
+
JS_UNDEFINED);
|
|
703
|
+
wc_define_accessor(ctx, wifi, "hostname", wc_forward_fn(ctx, nat, 1, 0), JS_UNDEFINED);
|
|
704
|
+
JS_DefinePropertyValueStr(
|
|
705
|
+
ctx, wifi, "ipConfig",
|
|
706
|
+
wc_named(ctx, JS_NewCFunctionData(ctx, wc_ip_config_cf, 0, 0, 1, &nat), "ipConfig"),
|
|
707
|
+
JS_PROP_C_W_E);
|
|
708
|
+
|
|
709
|
+
JSValue ap = wc_make_ap(ctx, nat, lazy_fn);
|
|
710
|
+
JS_FreeValue(ctx, lazy_fn);
|
|
711
|
+
if (JS_IsException(ap)) {
|
|
712
|
+
JS_FreeValue(ctx, wifi);
|
|
713
|
+
JS_FreeValue(ctx, nat);
|
|
714
|
+
return -1;
|
|
715
|
+
}
|
|
716
|
+
JS_DefinePropertyValueStr(ctx, wifi, "ap", ap, JS_PROP_C_W_E);
|
|
717
|
+
|
|
718
|
+
wc_define_accessor(ctx, wifi, "txPower",
|
|
719
|
+
JS_NewCFunctionData(ctx, wc_swallow_cf, 0, SWALLOW_TX_POWER, 1, &nat),
|
|
720
|
+
JS_UNDEFINED);
|
|
721
|
+
wc_define_accessor(ctx, wifi, "rssiThreshold", wc_forward_fn(ctx, nat, 3, 0),
|
|
722
|
+
JS_NewCFunctionData(ctx, wc_setter_cf, 1, 0, 1, &nat));
|
|
723
|
+
wc_define_accessor(ctx, wifi, "powerSave", wc_forward_fn(ctx, nat, 4, 0),
|
|
724
|
+
JS_NewCFunctionData(ctx, wc_setter_cf, 1, 1, 1, &nat));
|
|
725
|
+
wc_define_accessor(ctx, wifi, "country", wc_forward_fn(ctx, nat, 2, 0), JS_UNDEFINED);
|
|
726
|
+
JS_FreeValue(ctx, nat);
|
|
727
|
+
|
|
728
|
+
JS_SetModuleExport(ctx, m, "wifi", wifi);
|
|
729
|
+
return 0;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
} // namespace
|
|
733
|
+
|
|
734
|
+
/* Loader hook (see the C-module table in modules.cpp). Created lazily on
|
|
735
|
+
* first import so MIK_RegisterVirtualModule keeps precedence for this name
|
|
736
|
+
* and runtimes that never import wifi pay nothing. */
|
|
737
|
+
JSModuleDef* mik__wifi_client_load(JSContext* ctx) {
|
|
738
|
+
JSModuleDef* m = JS_NewCModule(ctx, "mikro/wifi", wc_module_init);
|
|
739
|
+
if (!m) return nullptr;
|
|
740
|
+
JS_AddModuleExport(ctx, m, "wifi");
|
|
741
|
+
return m;
|
|
742
|
+
}
|