@zappdev/cli 0.1.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/README.md +55 -0
- package/dist/zapp-cli.js +9471 -0
- package/native/src/app/app.zc +490 -0
- package/native/src/event/event.zc +24 -0
- package/native/src/event/events.zc +70 -0
- package/native/src/platform/darwin/backend.zc +923 -0
- package/native/src/platform/darwin/backend_bootstrap.zc +9 -0
- package/native/src/platform/darwin/bootstrap.zc +9 -0
- package/native/src/platform/darwin/engine_jsc.zc +86 -0
- package/native/src/platform/darwin/engine_qjs.zc +92 -0
- package/native/src/platform/darwin/platform.zc +156 -0
- package/native/src/platform/darwin/webview.zc +550 -0
- package/native/src/platform/darwin/webview_bootstrap.zc +9 -0
- package/native/src/platform/darwin/window.zc +1223 -0
- package/native/src/platform/darwin/worker/common.zc +223 -0
- package/native/src/platform/darwin/worker/core/base64_core.zc +29 -0
- package/native/src/platform/darwin/worker/core/crypto_core.zc +19 -0
- package/native/src/platform/darwin/worker/core/encoding_core.zc +32 -0
- package/native/src/platform/darwin/worker/core/fetch_core.zc +145 -0
- package/native/src/platform/darwin/worker/core/url_core.zc +69 -0
- package/native/src/platform/darwin/worker/core/websocket_core.zc +179 -0
- package/native/src/platform/darwin/worker/dispatch.zc +55 -0
- package/native/src/platform/darwin/worker/jsc/base64_jsc.zc +39 -0
- package/native/src/platform/darwin/worker/jsc/crypto_jsc.zc +49 -0
- package/native/src/platform/darwin/worker/jsc/encoding_jsc.zc +86 -0
- package/native/src/platform/darwin/worker/jsc/fetch_jsc.zc +149 -0
- package/native/src/platform/darwin/worker/jsc/url_jsc.zc +54 -0
- package/native/src/platform/darwin/worker/jsc/websocket_jsc.zc +127 -0
- package/native/src/platform/darwin/worker/jsc.zc +670 -0
- package/native/src/platform/darwin/worker/mod.zc +30 -0
- package/native/src/platform/darwin/worker/qjs/fetch_qjs.zc +233 -0
- package/native/src/platform/darwin/worker/qjs/qjs_macros.zc +23 -0
- package/native/src/platform/darwin/worker/qjs/websocket_qjs.zc +223 -0
- package/native/src/platform/darwin/worker/qjs.zc +1053 -0
- package/native/src/platform/darwin/worker/timers.zc +149 -0
- package/native/src/platform/darwin/worker/timers_qjs.zc +209 -0
- package/native/src/platform/platform.zc +64 -0
- package/native/src/platform/shared/log.zc +156 -0
- package/native/src/platform/shared/worker/qjs/base64_qjs.zc +38 -0
- package/native/src/platform/shared/worker/qjs/crypto_qjs.zc +44 -0
- package/native/src/platform/shared/worker/qjs/encoding_qjs.zc +95 -0
- package/native/src/platform/shared/worker/qjs/url_qjs.zc +65 -0
- package/native/src/platform/shared/worker_registry.zc +206 -0
- package/native/src/platform/window.zc +446 -0
- package/native/src/platform/windows/backend.zc +452 -0
- package/native/src/platform/windows/backend_bootstrap.zc +9 -0
- package/native/src/platform/windows/bootstrap.zc +9 -0
- package/native/src/platform/windows/engine_qjs.zc +60 -0
- package/native/src/platform/windows/platform.zc +387 -0
- package/native/src/platform/windows/webview.zc +1175 -0
- package/native/src/platform/windows/webview_bootstrap.zc +9 -0
- package/native/src/platform/windows/window.zc +1271 -0
- package/native/src/platform/windows/worker/common.zc +409 -0
- package/native/src/platform/windows/worker/core/base64_core.zc +52 -0
- package/native/src/platform/windows/worker/core/crypto_core.zc +34 -0
- package/native/src/platform/windows/worker/core/encoding_core.zc +60 -0
- package/native/src/platform/windows/worker/core/fetch_core.zc +274 -0
- package/native/src/platform/windows/worker/core/url_core.zc +216 -0
- package/native/src/platform/windows/worker/core/websocket_core.zc +343 -0
- package/native/src/platform/windows/worker/dispatch.zc +34 -0
- package/native/src/platform/windows/worker/mod.zc +46 -0
- package/native/src/platform/windows/worker/qjs/fetch_qjs.zc +255 -0
- package/native/src/platform/windows/worker/qjs/websocket_qjs.zc +263 -0
- package/native/src/platform/windows/worker/qjs.zc +1049 -0
- package/native/src/platform/windows/worker/timers_qjs.zc +288 -0
- package/native/src/platform/worker.zc +8 -0
- package/native/src/service/service.zc +228 -0
- package/native/vendor/quickjs-ng/.gitattributes +4 -0
- package/native/vendor/quickjs-ng/.github/dependabot.yml +7 -0
- package/native/vendor/quickjs-ng/.github/workflows/ci.yml +812 -0
- package/native/vendor/quickjs-ng/.github/workflows/docs.yml +49 -0
- package/native/vendor/quickjs-ng/.github/workflows/release.yml +162 -0
- package/native/vendor/quickjs-ng/.github/workflows/test-docs.yml +23 -0
- package/native/vendor/quickjs-ng/.github/workflows/tsan.yml +32 -0
- package/native/vendor/quickjs-ng/.github/workflows/valgrind.yml +33 -0
- package/native/vendor/quickjs-ng/.gitmodules +5 -0
- package/native/vendor/quickjs-ng/CMakeLists.txt +553 -0
- package/native/vendor/quickjs-ng/LICENSE +24 -0
- package/native/vendor/quickjs-ng/Makefile +149 -0
- package/native/vendor/quickjs-ng/amalgam.js +53 -0
- package/native/vendor/quickjs-ng/api-test.c +927 -0
- package/native/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
- package/native/vendor/quickjs-ng/builtin-array-fromasync.js +36 -0
- package/native/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
- package/native/vendor/quickjs-ng/builtin-iterator-zip-keyed.js +194 -0
- package/native/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
- package/native/vendor/quickjs-ng/builtin-iterator-zip.js +210 -0
- package/native/vendor/quickjs-ng/ctest.c +17 -0
- package/native/vendor/quickjs-ng/cutils.h +2013 -0
- package/native/vendor/quickjs-ng/cxxtest.cc +2 -0
- package/native/vendor/quickjs-ng/dtoa.c +1619 -0
- package/native/vendor/quickjs-ng/dtoa.h +87 -0
- package/native/vendor/quickjs-ng/examples/fib.c +67 -0
- package/native/vendor/quickjs-ng/examples/fib_module.js +10 -0
- package/native/vendor/quickjs-ng/examples/hello.js +1 -0
- package/native/vendor/quickjs-ng/examples/hello_module.js +6 -0
- package/native/vendor/quickjs-ng/examples/meson.build +17 -0
- package/native/vendor/quickjs-ng/examples/pi_bigint.js +118 -0
- package/native/vendor/quickjs-ng/examples/point.c +154 -0
- package/native/vendor/quickjs-ng/examples/test_fib.js +8 -0
- package/native/vendor/quickjs-ng/examples/test_point.js +43 -0
- package/native/vendor/quickjs-ng/fuzz.c +51 -0
- package/native/vendor/quickjs-ng/gen/function_source.c +81 -0
- package/native/vendor/quickjs-ng/gen/hello.c +53 -0
- package/native/vendor/quickjs-ng/gen/hello_module.c +106 -0
- package/native/vendor/quickjs-ng/gen/repl.c +3053 -0
- package/native/vendor/quickjs-ng/gen/standalone.c +324 -0
- package/native/vendor/quickjs-ng/gen/test_fib.c +81 -0
- package/native/vendor/quickjs-ng/libregexp-opcode.h +58 -0
- package/native/vendor/quickjs-ng/libregexp.c +2687 -0
- package/native/vendor/quickjs-ng/libregexp.h +98 -0
- package/native/vendor/quickjs-ng/libunicode-table.h +4707 -0
- package/native/vendor/quickjs-ng/libunicode.c +1746 -0
- package/native/vendor/quickjs-ng/libunicode.h +126 -0
- package/native/vendor/quickjs-ng/list.h +107 -0
- package/native/vendor/quickjs-ng/lre-test.c +73 -0
- package/native/vendor/quickjs-ng/meson.build +684 -0
- package/native/vendor/quickjs-ng/meson_options.txt +6 -0
- package/native/vendor/quickjs-ng/qjs-wasi-reactor.c +208 -0
- package/native/vendor/quickjs-ng/qjs.c +748 -0
- package/native/vendor/quickjs-ng/qjsc.c +673 -0
- package/native/vendor/quickjs-ng/quickjs-atom.h +267 -0
- package/native/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
- package/native/vendor/quickjs-ng/quickjs-libc.c +4986 -0
- package/native/vendor/quickjs-ng/quickjs-libc.h +79 -0
- package/native/vendor/quickjs-ng/quickjs-opcode.h +369 -0
- package/native/vendor/quickjs-ng/quickjs.c +60259 -0
- package/native/vendor/quickjs-ng/quickjs.h +1419 -0
- package/native/vendor/quickjs-ng/repl.js +1927 -0
- package/native/vendor/quickjs-ng/run-test262.c +2417 -0
- package/native/vendor/quickjs-ng/standalone.js +129 -0
- package/native/vendor/quickjs-ng/tests/assert.js +49 -0
- package/native/vendor/quickjs-ng/tests/bug1221.js +16 -0
- package/native/vendor/quickjs-ng/tests/bug1296.js +12 -0
- package/native/vendor/quickjs-ng/tests/bug1297.js +22 -0
- package/native/vendor/quickjs-ng/tests/bug1301.js +21 -0
- package/native/vendor/quickjs-ng/tests/bug1302.js +24 -0
- package/native/vendor/quickjs-ng/tests/bug1305.js +26 -0
- package/native/vendor/quickjs-ng/tests/bug1318.js +54 -0
- package/native/vendor/quickjs-ng/tests/bug1352.js +8 -0
- package/native/vendor/quickjs-ng/tests/bug1354.js +6 -0
- package/native/vendor/quickjs-ng/tests/bug1355.js +58 -0
- package/native/vendor/quickjs-ng/tests/bug1368.js +9 -0
- package/native/vendor/quickjs-ng/tests/bug39/1.js +6 -0
- package/native/vendor/quickjs-ng/tests/bug39/2.js +6 -0
- package/native/vendor/quickjs-ng/tests/bug39/3.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug488-upstream.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug633/0.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug633/1.js +4 -0
- package/native/vendor/quickjs-ng/tests/bug633/2.js +4 -0
- package/native/vendor/quickjs-ng/tests/bug633/3.js +4 -0
- package/native/vendor/quickjs-ng/tests/bug645/0.js +4 -0
- package/native/vendor/quickjs-ng/tests/bug645/1.js +9 -0
- package/native/vendor/quickjs-ng/tests/bug645/2.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug648.js +13 -0
- package/native/vendor/quickjs-ng/tests/bug652.js +4 -0
- package/native/vendor/quickjs-ng/tests/bug741.js +19 -0
- package/native/vendor/quickjs-ng/tests/bug775.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug776.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug832.js +2 -0
- package/native/vendor/quickjs-ng/tests/bug858.js +26 -0
- package/native/vendor/quickjs-ng/tests/bug904.js +6 -0
- package/native/vendor/quickjs-ng/tests/bug988.js +7 -0
- package/native/vendor/quickjs-ng/tests/bug999.js +3 -0
- package/native/vendor/quickjs-ng/tests/destructured-export.js +8 -0
- package/native/vendor/quickjs-ng/tests/detect_module/0.js +1 -0
- package/native/vendor/quickjs-ng/tests/detect_module/1.js +2 -0
- package/native/vendor/quickjs-ng/tests/detect_module/2.js +1 -0
- package/native/vendor/quickjs-ng/tests/detect_module/3.js +8 -0
- package/native/vendor/quickjs-ng/tests/detect_module/4.js +3 -0
- package/native/vendor/quickjs-ng/tests/empty.js +0 -0
- package/native/vendor/quickjs-ng/tests/fixture_cyclic_import.js +2 -0
- package/native/vendor/quickjs-ng/tests/fixture_string_exports.js +12 -0
- package/native/vendor/quickjs-ng/tests/function_source.js +14 -0
- package/native/vendor/quickjs-ng/tests/microbench.js +1267 -0
- package/native/vendor/quickjs-ng/tests/null_or_undefined.js +38 -0
- package/native/vendor/quickjs-ng/tests/str-pad-leak.js +5 -0
- package/native/vendor/quickjs-ng/tests/test_bigint.js +107 -0
- package/native/vendor/quickjs-ng/tests/test_bjson.js +366 -0
- package/native/vendor/quickjs-ng/tests/test_builtin.js +1314 -0
- package/native/vendor/quickjs-ng/tests/test_closure.js +220 -0
- package/native/vendor/quickjs-ng/tests/test_cyclic_import.js +12 -0
- package/native/vendor/quickjs-ng/tests/test_domexception.js +35 -0
- package/native/vendor/quickjs-ng/tests/test_language.js +755 -0
- package/native/vendor/quickjs-ng/tests/test_loop.js +367 -0
- package/native/vendor/quickjs-ng/tests/test_queue_microtask.js +39 -0
- package/native/vendor/quickjs-ng/tests/test_std.js +340 -0
- package/native/vendor/quickjs-ng/tests/test_string_exports.js +25 -0
- package/native/vendor/quickjs-ng/tests/test_worker.js +43 -0
- package/native/vendor/quickjs-ng/tests/test_worker_module.js +30 -0
- package/native/vendor/quickjs-ng/tests.conf +14 -0
- package/native/vendor/quickjs-ng/unicode_download.sh +19 -0
- package/native/vendor/quickjs-ng/unicode_gen.c +3108 -0
- package/native/vendor/quickjs-ng/unicode_gen_def.h +310 -0
- package/native/vendor/quickjs-ng/update-version.sh +32 -0
- package/native/vendor/webview2/include/WebView2.h +60636 -0
- package/native/vendor/webview2/include/WebView2EnvironmentOptions.h +406 -0
- package/package.json +33 -0
- package/src/backend.ts +139 -0
- package/src/build-config.ts +87 -0
- package/src/build.ts +276 -0
- package/src/common.ts +195 -0
- package/src/config.ts +89 -0
- package/src/dev.ts +164 -0
- package/src/generate.ts +200 -0
- package/src/icons.ts +116 -0
- package/src/init.ts +190 -0
- package/src/package.ts +150 -0
- package/src/zapp-cli.ts +263 -0
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
import "../platform.zc";
|
|
2
|
+
import "./bootstrap.zc";
|
|
3
|
+
import "./backend_bootstrap.zc";
|
|
4
|
+
import "./worker/common.zc";
|
|
5
|
+
import "./worker/timers.zc";
|
|
6
|
+
import "./worker/core/encoding_core.zc";
|
|
7
|
+
import "./worker/core/url_core.zc";
|
|
8
|
+
import "./worker/core/base64_core.zc";
|
|
9
|
+
import "./worker/core/websocket_core.zc";
|
|
10
|
+
import "./worker/core/crypto_core.zc";
|
|
11
|
+
import "./worker/core/fetch_core.zc";
|
|
12
|
+
import "../shared/log.zc";
|
|
13
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
14
|
+
import "./worker/jsc/encoding_jsc.zc";
|
|
15
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
16
|
+
import "./worker/jsc/url_jsc.zc";
|
|
17
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
18
|
+
import "./worker/jsc/base64_jsc.zc";
|
|
19
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
20
|
+
import "./worker/jsc/websocket_jsc.zc";
|
|
21
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
22
|
+
import "./worker/jsc/crypto_jsc.zc";
|
|
23
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
24
|
+
import "./worker/jsc/fetch_jsc.zc";
|
|
25
|
+
@cfg(not(ZAPP_WORKER_ENGINE_QJS))
|
|
26
|
+
import "./engine_jsc.zc";
|
|
27
|
+
|
|
28
|
+
@cfg(ZAPP_WORKER_ENGINE_QJS)
|
|
29
|
+
import "./engine_qjs.zc";
|
|
30
|
+
@cfg(ZAPP_WORKER_ENGINE_QJS)
|
|
31
|
+
import "./worker/qjs.zc";
|
|
32
|
+
|
|
33
|
+
//> macos: framework: JavaScriptCore
|
|
34
|
+
raw {
|
|
35
|
+
#ifdef __APPLE__
|
|
36
|
+
#include <Cocoa/Cocoa.h>
|
|
37
|
+
#include <WebKit/WebKit.h>
|
|
38
|
+
#include <dispatch/dispatch.h>
|
|
39
|
+
#include <JavaScriptCore/JavaScriptCore.h>
|
|
40
|
+
|
|
41
|
+
// Log level constants (from shared/log.zc)
|
|
42
|
+
#ifndef ZAPP_LOG_ERROR
|
|
43
|
+
#define ZAPP_LOG_ERROR 0
|
|
44
|
+
#define ZAPP_LOG_WARN 1
|
|
45
|
+
#define ZAPP_LOG_INFO 2
|
|
46
|
+
#define ZAPP_LOG_DEBUG 3
|
|
47
|
+
#define ZAPP_LOG_TRACE 4
|
|
48
|
+
#endif
|
|
49
|
+
|
|
50
|
+
#if !defined(ZAPP_WORKER_ENGINE_QJS)
|
|
51
|
+
extern void* engine_jsc_create_ctx(void* vm, const char* name);
|
|
52
|
+
extern int engine_jsc_eval(void* ctx, const char* utf8, size_t len, const char* filename);
|
|
53
|
+
extern const char* engine_jsc_get_exception_message(void* ctx);
|
|
54
|
+
extern void engine_jsc_free_ctx(void* ctx, void* vm, const char* new_name);
|
|
55
|
+
|
|
56
|
+
extern void zapp_darwin_worker_js_sync(dispatch_block_t block);
|
|
57
|
+
extern void zapp_darwin_worker_timers_init(JSContext* ctx, NSString* workerId);
|
|
58
|
+
extern void zapp_darwin_worker_fetch_init(JSContext* ctx, NSString* workerId);
|
|
59
|
+
extern void zapp_darwin_worker_crypto_init(JSContext* ctx);
|
|
60
|
+
extern void zapp_darwin_worker_encoding_init(JSContext* ctx);
|
|
61
|
+
extern void zapp_darwin_worker_url_init(JSContext* ctx);
|
|
62
|
+
extern void zapp_darwin_worker_base64_init(JSContext* ctx);
|
|
63
|
+
extern void zapp_darwin_worker_websocket_init(JSContext* ctx);
|
|
64
|
+
extern void zapp_darwin_worker_timers_cancel(NSString* workerId);
|
|
65
|
+
extern void zapp_darwin_worker_fetch_cancel(NSString* workerId);
|
|
66
|
+
extern const char* zapp_darwin_worker_bootstrap_script(void);
|
|
67
|
+
extern const char* zapp_darwin_backend_bootstrap_script(void);
|
|
68
|
+
|
|
69
|
+
extern void* app_get_active(void);
|
|
70
|
+
extern void zapp_handle_message(void* app_ptr, char* msg_c);
|
|
71
|
+
extern NSString* zapp_json_stringify_obj(id obj);
|
|
72
|
+
extern void zapp_dispatch_worker_to_webviews(const char* kind, const char* worker_id, const char* payload_json);
|
|
73
|
+
extern void zapp_log_backend(int level, const char* message);
|
|
74
|
+
|
|
75
|
+
extern const char* zapp_build_initial_url(void);
|
|
76
|
+
|
|
77
|
+
static JSVirtualMachine* zapp_backend_vm = nil;
|
|
78
|
+
static JSContext* zapp_backend_ctx = nil;
|
|
79
|
+
static NSString* zapp_backend_id = @"__zapp_backend__";
|
|
80
|
+
|
|
81
|
+
static void zapp_backend_call_bridge(NSString* methodName, NSArray* args) {
|
|
82
|
+
if (zapp_backend_ctx == nil || methodName == nil) return;
|
|
83
|
+
JSValue* bridge = [zapp_backend_ctx evaluateScript:@"globalThis[Symbol.for('zapp.bridge')]"];
|
|
84
|
+
if (bridge == nil || bridge.isUndefined || ![bridge isObject]) return;
|
|
85
|
+
JSValue* fn = bridge[methodName];
|
|
86
|
+
if (fn == nil || fn.isUndefined) return;
|
|
87
|
+
@try {
|
|
88
|
+
[fn callWithArguments:args ?: @[]];
|
|
89
|
+
} @catch (NSException* e) {
|
|
90
|
+
char buf[512];
|
|
91
|
+
snprintf(buf, sizeof(buf), "bridge.%s exception: %s", [methodName UTF8String], [[e description] UTF8String]);
|
|
92
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void zapp_backend_deliver_event(const char* name, const char* payload) {
|
|
97
|
+
if (zapp_backend_ctx == nil) return;
|
|
98
|
+
if (name == NULL) return;
|
|
99
|
+
NSString* n = [NSString stringWithUTF8String:name];
|
|
100
|
+
NSString* p = payload ? [NSString stringWithUTF8String:payload] : @"{}";
|
|
101
|
+
zapp_darwin_worker_js_sync(^{
|
|
102
|
+
if (zapp_backend_ctx == nil) return;
|
|
103
|
+
zapp_backend_call_bridge(@"deliverEvent", @[n, p]);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void zapp_backend_dispatch_window_result(const char* payload_json) {
|
|
108
|
+
if (zapp_backend_ctx == nil || payload_json == NULL) return;
|
|
109
|
+
NSString* payload = [NSString stringWithUTF8String:payload_json];
|
|
110
|
+
zapp_darwin_worker_js_sync(^{
|
|
111
|
+
if (zapp_backend_ctx == nil) return;
|
|
112
|
+
zapp_backend_call_bridge(@"dispatchWindowResult", @[payload]);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
void zapp_backend_dispatch_window_ready(const char* window_id) {
|
|
117
|
+
if (zapp_backend_ctx == nil || window_id == NULL) return;
|
|
118
|
+
NSString* windowId = [NSString stringWithUTF8String:window_id];
|
|
119
|
+
NSString* eventJson = [NSString stringWithFormat:@"{\"windowId\":\"%@\"}", windowId];
|
|
120
|
+
zapp_darwin_worker_js_sync(^{
|
|
121
|
+
if (zapp_backend_ctx == nil) return;
|
|
122
|
+
zapp_backend_call_bridge(@"deliverEvent", @[@"window:ready", eventJson]);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
int32_t zapp_backend_context_create(void* app_ptr, char* script_source) {
|
|
127
|
+
if (app_ptr == NULL || script_source == NULL) return -1;
|
|
128
|
+
App* app = (App*)app_ptr;
|
|
129
|
+
|
|
130
|
+
zapp_darwin_worker_js_sync(^{
|
|
131
|
+
if (zapp_backend_vm == nil) {
|
|
132
|
+
zapp_backend_vm = [[JSVirtualMachine alloc] init];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void* opaque = engine_jsc_create_ctx((__bridge void*)zapp_backend_vm, "ZappBackend");
|
|
136
|
+
if (!opaque) {
|
|
137
|
+
zapp_log_backend(ZAPP_LOG_ERROR, "failed to create backend JSC context");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
JSContext* ctx = (__bridge JSContext*)opaque;
|
|
142
|
+
zapp_backend_ctx = ctx;
|
|
143
|
+
|
|
144
|
+
if (@available(macOS 13.3, *)) {
|
|
145
|
+
extern bool app_get_bootstrap_web_content_inspectable(void);
|
|
146
|
+
ctx.inspectable = app_get_bootstrap_web_content_inspectable() ? YES : NO;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
ctx.exceptionHandler = ^(JSContext* context, JSValue* exception) {
|
|
150
|
+
void (^previousHandler)(JSContext*, JSValue*) = context.exceptionHandler;
|
|
151
|
+
context.exceptionHandler = nil;
|
|
152
|
+
context.exception = nil;
|
|
153
|
+
NSString* msg = @"Backend error";
|
|
154
|
+
@try { msg = [exception toString] ?: msg; } @catch (NSException* e) {}
|
|
155
|
+
char buf[256];
|
|
156
|
+
snprintf(buf, sizeof(buf), "error: %s", [msg UTF8String]);
|
|
157
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
158
|
+
context.exceptionHandler = previousHandler;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
ctx[@"globalThis"] = ctx.globalObject;
|
|
162
|
+
ctx[@"self"] = ctx.globalObject;
|
|
163
|
+
ctx[@"__zappWorkerDispatchId"] = zapp_backend_id;
|
|
164
|
+
|
|
165
|
+
// Inject initial config so the bootstrap can read it
|
|
166
|
+
{
|
|
167
|
+
extern char* app_get_bootstrap_name(void);
|
|
168
|
+
extern bool app_get_bootstrap_application_should_terminate_after_last_window_closed(void);
|
|
169
|
+
extern bool app_get_bootstrap_web_content_inspectable(void);
|
|
170
|
+
extern int app_get_bootstrap_max_workers(void);
|
|
171
|
+
|
|
172
|
+
NSString* appName = [NSString stringWithUTF8String:app_get_bootstrap_name()];
|
|
173
|
+
appName = [appName stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
|
|
174
|
+
appName = [appName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
|
175
|
+
NSString* configScript = [NSString stringWithFormat:
|
|
176
|
+
@"globalThis.__zappConfig={name:'%@',applicationShouldTerminateAfterLastWindowClosed:%@,webContentInspectable:%@,maxWorkers:%d};",
|
|
177
|
+
appName,
|
|
178
|
+
app_get_bootstrap_application_should_terminate_after_last_window_closed() ? @"true" : @"false",
|
|
179
|
+
app_get_bootstrap_web_content_inspectable() ? @"true" : @"false",
|
|
180
|
+
app_get_bootstrap_max_workers()
|
|
181
|
+
];
|
|
182
|
+
[ctx evaluateScript:configScript];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
JSValue* bridge = [JSValue valueWithNewObjectInContext:ctx];
|
|
186
|
+
ctx[@"__zappBridge"] = bridge;
|
|
187
|
+
|
|
188
|
+
// Timers, fetch, crypto, encoding, URL, base64 (same as workers)
|
|
189
|
+
zapp_darwin_worker_timers_init(ctx, zapp_backend_id);
|
|
190
|
+
zapp_darwin_worker_fetch_init(ctx, zapp_backend_id);
|
|
191
|
+
zapp_darwin_worker_crypto_init(ctx);
|
|
192
|
+
zapp_darwin_worker_encoding_init(ctx);
|
|
193
|
+
zapp_darwin_worker_url_init(ctx);
|
|
194
|
+
zapp_darwin_worker_base64_init(ctx);
|
|
195
|
+
zapp_darwin_worker_websocket_init(ctx);
|
|
196
|
+
|
|
197
|
+
// Event emission -> routes through the app message handler
|
|
198
|
+
bridge[@"emitToHost"] = ^(JSValue* nameVal, JSValue* payloadVal) {
|
|
199
|
+
NSString* eventName = [nameVal toString];
|
|
200
|
+
if (![eventName isKindOfClass:[NSString class]] || [eventName length] == 0) return;
|
|
201
|
+
|
|
202
|
+
// Handle internal window/app action events from the backend bootstrap
|
|
203
|
+
if ([eventName isEqualToString:@"__zapp_window_create"] ||
|
|
204
|
+
[eventName isEqualToString:@"__zapp_window_action"] ||
|
|
205
|
+
[eventName isEqualToString:@"__zapp_app_action"]) {
|
|
206
|
+
NSString* payloadStr = [payloadVal toString];
|
|
207
|
+
if (![payloadStr isKindOfClass:[NSString class]]) return;
|
|
208
|
+
NSData* jsonData = [payloadStr dataUsingEncoding:NSUTF8StringEncoding];
|
|
209
|
+
if (!jsonData) return;
|
|
210
|
+
NSDictionary* body = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
211
|
+
if (![body isKindOfClass:[NSDictionary class]]) return;
|
|
212
|
+
|
|
213
|
+
if ([eventName isEqualToString:@"__zapp_window_create"]) {
|
|
214
|
+
NSString* wire = [NSString stringWithFormat:@"window\ncreate\n%@", payloadStr];
|
|
215
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
216
|
+
void* activeApp = app_get_active();
|
|
217
|
+
if (activeApp) { char* m = strdup([wire UTF8String]); zapp_handle_message(activeApp, m); free(m); }
|
|
218
|
+
});
|
|
219
|
+
} else if ([eventName isEqualToString:@"__zapp_window_action"]) {
|
|
220
|
+
NSString* action = body[@"action"];
|
|
221
|
+
if (![action isKindOfClass:[NSString class]]) return;
|
|
222
|
+
NSString* wire = [NSString stringWithFormat:@"window\n%@\n%@", action, payloadStr];
|
|
223
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
224
|
+
void* activeApp = app_get_active();
|
|
225
|
+
if (activeApp) { char* m = strdup([wire UTF8String]); zapp_handle_message(activeApp, m); free(m); }
|
|
226
|
+
});
|
|
227
|
+
} else if ([eventName isEqualToString:@"__zapp_app_action"]) {
|
|
228
|
+
NSString* action = body[@"action"];
|
|
229
|
+
if (![action isKindOfClass:[NSString class]]) return;
|
|
230
|
+
NSString* wire = [NSString stringWithFormat:@"app\n%@\n{}", action];
|
|
231
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
232
|
+
void* activeApp = app_get_active();
|
|
233
|
+
if (activeApp) { char* m = strdup([wire UTF8String]); zapp_handle_message(activeApp, m); free(m); }
|
|
234
|
+
});
|
|
235
|
+
} else if ([eventName isEqualToString:@"__zapp_app_configure"]) {
|
|
236
|
+
NSString* wire = [NSString stringWithFormat:@"app\nconfigure\n%@", payloadStr];
|
|
237
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
238
|
+
void* activeApp = app_get_active();
|
|
239
|
+
if (activeApp) { char* m = strdup([wire UTF8String]); zapp_handle_message(activeApp, m); free(m); }
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
id payloadObj = [payloadVal toObject];
|
|
246
|
+
NSString* payloadJson = zapp_json_stringify_obj(payloadObj);
|
|
247
|
+
if (payloadJson == nil) payloadJson = @"{}";
|
|
248
|
+
NSString* wire = [NSString stringWithFormat:@"emit\n%@\n%@", eventName, payloadJson];
|
|
249
|
+
void* activeApp = app_get_active();
|
|
250
|
+
if (activeApp) { char* m = strdup([wire UTF8String]); zapp_handle_message(activeApp, m); free(m); }
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
bridge[@"invokeToHost"] = ^(JSValue* methodVal, JSValue* payloadVal) {
|
|
254
|
+
NSString* methodName = [methodVal toString];
|
|
255
|
+
if (![methodName isKindOfClass:[NSString class]] || [methodName length] == 0) return;
|
|
256
|
+
id payloadObj = [payloadVal toObject];
|
|
257
|
+
NSString* payloadJson = zapp_json_stringify_obj(payloadObj);
|
|
258
|
+
if (payloadJson == nil) payloadJson = @"{}";
|
|
259
|
+
NSString* wire = [NSString stringWithFormat:@"invoke\n%@\n%@", methodName, payloadJson];
|
|
260
|
+
void* activeApp = app_get_active();
|
|
261
|
+
if (activeApp) zapp_handle_message(activeApp, (char*)[wire UTF8String]);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
bridge[@"reportError"] = ^(JSValue* errVal) {
|
|
265
|
+
NSString* msg = @"Backend runtime error";
|
|
266
|
+
if (errVal != nil && !errVal.isUndefined && !errVal.isNull) {
|
|
267
|
+
@try { msg = [errVal toString] ?: msg; } @catch (NSException* e) {}
|
|
268
|
+
}
|
|
269
|
+
zapp_log_backend(ZAPP_LOG_ERROR, [msg UTF8String]);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
bridge[@"consoleLog"] = ^(JSValue* levelVal, JSValue* msgVal) {
|
|
273
|
+
NSString* level = [levelVal toString] ?: @"log";
|
|
274
|
+
NSString* msg = @"";
|
|
275
|
+
if (msgVal != nil && !msgVal.isUndefined && !msgVal.isNull) {
|
|
276
|
+
@try { msg = [msgVal toString] ?: @""; } @catch (NSException* e) {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Convert level string to int
|
|
280
|
+
int level_int = ZAPP_LOG_INFO;
|
|
281
|
+
if ([level isEqualToString:@"error"]) level_int = ZAPP_LOG_ERROR;
|
|
282
|
+
else if ([level isEqualToString:@"warn"]) level_int = ZAPP_LOG_WARN;
|
|
283
|
+
else if ([level isEqualToString:@"debug"]) level_int = ZAPP_LOG_DEBUG;
|
|
284
|
+
else if ([level isEqualToString:@"trace"]) level_int = ZAPP_LOG_TRACE;
|
|
285
|
+
|
|
286
|
+
zapp_log_backend(level_int, [msg UTF8String]);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
bridge[@"postMessage"] = ^(JSValue* data, JSValue* optTargetId) {
|
|
290
|
+
(void)data; (void)optTargetId;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
bridge[@"closeWorker"] = ^() {
|
|
294
|
+
// no-op for backend context
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
bridge[@"syncWait"] = ^(JSValue* requestVal) {
|
|
298
|
+
id reqObj = [requestVal toObject];
|
|
299
|
+
NSString* payloadJson = @"{}";
|
|
300
|
+
if ([reqObj isKindOfClass:[NSDictionary class]]) {
|
|
301
|
+
NSMutableDictionary* payload = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)reqObj];
|
|
302
|
+
[payload setObject:zapp_backend_id forKey:@"targetWorkerId"];
|
|
303
|
+
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
|
|
304
|
+
payloadJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] ?: @"{}";
|
|
305
|
+
}
|
|
306
|
+
NSString* wire = [NSString stringWithFormat:@"sync\nwait\n%@", payloadJson];
|
|
307
|
+
void* activeApp = app_get_active();
|
|
308
|
+
if (activeApp) zapp_handle_message(activeApp, (char*)[wire UTF8String]);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
bridge[@"syncNotify"] = ^(JSValue* requestVal) {
|
|
312
|
+
id reqObj = [requestVal toObject];
|
|
313
|
+
NSString* payloadJson = @"{}";
|
|
314
|
+
if ([reqObj isKindOfClass:[NSDictionary class]]) {
|
|
315
|
+
NSMutableDictionary* payload = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)reqObj];
|
|
316
|
+
[payload setObject:zapp_backend_id forKey:@"targetWorkerId"];
|
|
317
|
+
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
|
|
318
|
+
payloadJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] ?: @"{}";
|
|
319
|
+
}
|
|
320
|
+
NSString* wire = [NSString stringWithFormat:@"sync\nnotify\n%@", payloadJson];
|
|
321
|
+
void* activeApp = app_get_active();
|
|
322
|
+
if (activeApp) zapp_handle_message(activeApp, (char*)[wire UTF8String]);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
bridge[@"syncCancel"] = ^(JSValue* requestVal) {
|
|
326
|
+
id reqObj = [requestVal toObject];
|
|
327
|
+
NSString* payloadJson = @"{}";
|
|
328
|
+
if ([reqObj isKindOfClass:[NSDictionary class]]) {
|
|
329
|
+
NSMutableDictionary* payload = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)reqObj];
|
|
330
|
+
[payload setObject:zapp_backend_id forKey:@"targetWorkerId"];
|
|
331
|
+
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
|
|
332
|
+
payloadJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] ?: @"{}";
|
|
333
|
+
}
|
|
334
|
+
NSString* wire = [NSString stringWithFormat:@"sync\ncancel\n%@", payloadJson];
|
|
335
|
+
void* activeApp = app_get_active();
|
|
336
|
+
if (activeApp) zapp_handle_message(activeApp, (char*)[wire UTF8String]);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Evaluate bootstrap
|
|
340
|
+
const char* bootstrap = zapp_darwin_backend_bootstrap_script();
|
|
341
|
+
size_t blen = strlen(bootstrap);
|
|
342
|
+
int err = engine_jsc_eval((__bridge void*)ctx, bootstrap, blen, "backend-bootstrap.js");
|
|
343
|
+
if (err != 0) {
|
|
344
|
+
const char* msg = engine_jsc_get_exception_message((__bridge void*)ctx);
|
|
345
|
+
char buf[256];
|
|
346
|
+
snprintf(buf, sizeof(buf), "bootstrap failed: %s", msg ?: "unknown error");
|
|
347
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
348
|
+
zapp_darwin_worker_timers_cancel(zapp_backend_id);
|
|
349
|
+
zapp_darwin_worker_fetch_cancel(zapp_backend_id);
|
|
350
|
+
engine_jsc_free_ctx((__bridge void*)ctx, (__bridge void*)zapp_backend_vm, "DeadBackend");
|
|
351
|
+
zapp_backend_ctx = nil;
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Evaluate user script in async IIFE with error catching
|
|
356
|
+
NSString* wrappedSource = [NSString stringWithFormat:
|
|
357
|
+
@"(async function(){\n%s\n})()"
|
|
358
|
+
@".catch(function(e){"
|
|
359
|
+
@"var b=globalThis[Symbol.for('zapp.bridge')];"
|
|
360
|
+
@"if(b&&b.reportError)b.reportError(e);"
|
|
361
|
+
@"else if(typeof console!=='undefined')console.error('[backend] unhandled:',e);"
|
|
362
|
+
@"});", script_source];
|
|
363
|
+
NSData* wrappedData = [wrappedSource dataUsingEncoding:NSUTF8StringEncoding];
|
|
364
|
+
if (wrappedData == nil || [wrappedData length] == 0) {
|
|
365
|
+
zapp_log_backend(ZAPP_LOG_ERROR, "script encoding failed");
|
|
366
|
+
zapp_darwin_worker_timers_cancel(zapp_backend_id);
|
|
367
|
+
zapp_darwin_worker_fetch_cancel(zapp_backend_id);
|
|
368
|
+
engine_jsc_free_ctx((__bridge void*)ctx, (__bridge void*)zapp_backend_vm, "DeadBackend");
|
|
369
|
+
zapp_backend_ctx = nil;
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const char* wrappedUtf8 = (const char*)[wrappedData bytes];
|
|
373
|
+
size_t wrappedLen = [wrappedData length];
|
|
374
|
+
err = engine_jsc_eval((__bridge void*)ctx, wrappedUtf8, wrappedLen, "backend.js");
|
|
375
|
+
if (err != 0) {
|
|
376
|
+
const char* msg = engine_jsc_get_exception_message((__bridge void*)ctx);
|
|
377
|
+
char buf[256];
|
|
378
|
+
snprintf(buf, sizeof(buf), "script error: %s", msg ?: "unknown error");
|
|
379
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
return (zapp_backend_ctx != nil) ? 0 : -1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
void zapp_backend_context_destroy(void) {
|
|
387
|
+
zapp_darwin_worker_js_sync(^{
|
|
388
|
+
if (zapp_backend_ctx != nil) {
|
|
389
|
+
zapp_darwin_worker_timers_cancel(zapp_backend_id);
|
|
390
|
+
zapp_darwin_worker_fetch_cancel(zapp_backend_id);
|
|
391
|
+
engine_jsc_free_ctx((__bridge void*)zapp_backend_ctx, (__bridge void*)zapp_backend_vm, "DeadBackend");
|
|
392
|
+
zapp_backend_ctx = nil;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
#elif defined(ZAPP_WORKER_ENGINE_QJS)
|
|
397
|
+
#define JSContext QJSContext
|
|
398
|
+
#define JSRuntime QJSRuntime
|
|
399
|
+
#define JSValue QJSValue
|
|
400
|
+
#define JSClassID QJSClassID
|
|
401
|
+
#define JSClassDef QJSClassDef
|
|
402
|
+
#define JSClassEx QJSClassEx
|
|
403
|
+
#define JSValueConst QJSValueConst
|
|
404
|
+
#define JSPropertyEnum QJSPropertyEnum
|
|
405
|
+
#define JSPropertyDescriptor QJSPropertyDescriptor
|
|
406
|
+
|
|
407
|
+
extern void* engine_qjs_create_ctx(void* rt);
|
|
408
|
+
extern int engine_qjs_eval(void* ctx, const char* utf8, size_t len, const char* filename);
|
|
409
|
+
extern const char* engine_qjs_get_exception_message(void* ctx);
|
|
410
|
+
extern void engine_qjs_free_ctx(void* ctx);
|
|
411
|
+
|
|
412
|
+
extern void zapp_handle_message(void* app, char* msg);
|
|
413
|
+
extern void* app_get_active(void);
|
|
414
|
+
extern const char* zapp_darwin_backend_bootstrap_script(void);
|
|
415
|
+
|
|
416
|
+
extern void zapp_darwin_qjs_ensure_init(void);
|
|
417
|
+
extern void* zapp_darwin_qjs_get_runtime(void);
|
|
418
|
+
extern dispatch_queue_t zapp_darwin_qjs_get_queue(void);
|
|
419
|
+
extern void zapp_darwin_qjs_register_context(NSString* ctxId, void* ctx_opaque);
|
|
420
|
+
extern void zapp_darwin_qjs_unregister_context(NSString* ctxId);
|
|
421
|
+
extern void zapp_darwin_qjs_drain_jobs(void* ctx, const char* label);
|
|
422
|
+
extern void zapp_darwin_qjs_timers_install(QJSContext* ctx, QJSValue bridge, const char* worker_id);
|
|
423
|
+
extern void zapp_darwin_qjs_fetch_install(QJSContext* ctx, QJSValue bridge, const char* worker_id);
|
|
424
|
+
extern void zapp_qjs_crypto_install(QJSContext* ctx, QJSValue bridge);
|
|
425
|
+
extern void zapp_qjs_encoding_install(QJSContext* ctx, QJSValue bridge);
|
|
426
|
+
extern void zapp_qjs_url_install(QJSContext* ctx, QJSValue bridge);
|
|
427
|
+
extern void zapp_qjs_base64_install(QJSContext* ctx, QJSValue bridge);
|
|
428
|
+
extern void zapp_darwin_qjs_websocket_install(QJSContext* ctx, QJSValue bridge, const char* worker_id);
|
|
429
|
+
extern void zapp_darwin_qjs_timers_cancel(NSString* workerId);
|
|
430
|
+
extern void zapp_darwin_qjs_fetch_cancel(NSString* workerId);
|
|
431
|
+
|
|
432
|
+
// Log functions from log.zc
|
|
433
|
+
extern void zapp_log_backend(int level, const char* message);
|
|
434
|
+
#ifndef ZAPP_LOG_ERROR
|
|
435
|
+
#define ZAPP_LOG_ERROR 0
|
|
436
|
+
#define ZAPP_LOG_WARN 1
|
|
437
|
+
#define ZAPP_LOG_INFO 2
|
|
438
|
+
#define ZAPP_LOG_DEBUG 3
|
|
439
|
+
#define ZAPP_LOG_TRACE 4
|
|
440
|
+
#endif
|
|
441
|
+
|
|
442
|
+
static QJSContext* qjs_be_ctx = NULL;
|
|
443
|
+
static NSString* qjs_be_id = @"__zapp_backend__";
|
|
444
|
+
|
|
445
|
+
static void qjs_be_drain(QJSContext* ctx, const char* label);
|
|
446
|
+
|
|
447
|
+
static void qjs_be_call_bridge(NSString* methodName, int argc, QJSValue* argv) {
|
|
448
|
+
if (qjs_be_ctx == NULL || methodName == nil) return;
|
|
449
|
+
|
|
450
|
+
QJSValue global = JS_GetGlobalObject(qjs_be_ctx);
|
|
451
|
+
QJSValue symbol = JS_GetPropertyStr(qjs_be_ctx, global, "Symbol");
|
|
452
|
+
QJSValue symbolFor = JS_GetPropertyStr(qjs_be_ctx, symbol, "for");
|
|
453
|
+
QJSValue arg = JS_NewString(qjs_be_ctx, "zapp.bridge");
|
|
454
|
+
QJSValue bridgeSym = JS_Call(qjs_be_ctx, symbolFor, symbol, 1, &arg);
|
|
455
|
+
JSAtom bridgeAtom = JS_ValueToAtom(qjs_be_ctx, bridgeSym);
|
|
456
|
+
QJSValue bridge = JS_GetProperty(qjs_be_ctx, global, bridgeAtom);
|
|
457
|
+
|
|
458
|
+
if (JS_IsObject(bridge)) {
|
|
459
|
+
QJSValue fn = JS_GetPropertyStr(qjs_be_ctx, bridge, [methodName UTF8String]);
|
|
460
|
+
if (JS_IsFunction(qjs_be_ctx, fn)) {
|
|
461
|
+
QJSValue ret = JS_Call(qjs_be_ctx, fn, bridge, argc, argv);
|
|
462
|
+
if (JS_IsException(ret)) {
|
|
463
|
+
QJSValue exc = JS_GetException(qjs_be_ctx);
|
|
464
|
+
const char* str = JS_ToCString(qjs_be_ctx, exc);
|
|
465
|
+
char buf[256];
|
|
466
|
+
snprintf(buf, sizeof(buf), "bridge.%s threw: %s", [methodName UTF8String], str ? str : "unknown");
|
|
467
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
468
|
+
if (str) JS_FreeCString(qjs_be_ctx, str);
|
|
469
|
+
JS_FreeValue(qjs_be_ctx, exc);
|
|
470
|
+
}
|
|
471
|
+
JS_FreeValue(qjs_be_ctx, ret);
|
|
472
|
+
qjs_be_drain(qjs_be_ctx, [methodName UTF8String]);
|
|
473
|
+
}
|
|
474
|
+
JS_FreeValue(qjs_be_ctx, fn);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
JS_FreeAtom(qjs_be_ctx, bridgeAtom);
|
|
478
|
+
JS_FreeValue(qjs_be_ctx, bridge);
|
|
479
|
+
JS_FreeValue(qjs_be_ctx, bridgeSym);
|
|
480
|
+
JS_FreeValue(qjs_be_ctx, arg);
|
|
481
|
+
JS_FreeValue(qjs_be_ctx, symbolFor);
|
|
482
|
+
JS_FreeValue(qjs_be_ctx, symbol);
|
|
483
|
+
JS_FreeValue(qjs_be_ctx, global);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
static void qjs_be_drain(QJSContext* ctx, const char* label) {
|
|
487
|
+
if (!ctx) return;
|
|
488
|
+
QJSContext* pctx = NULL;
|
|
489
|
+
for (;;) {
|
|
490
|
+
int err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &pctx);
|
|
491
|
+
if (err <= 0) {
|
|
492
|
+
if (err < 0 && pctx) {
|
|
493
|
+
QJSValue exc = JS_GetException(pctx);
|
|
494
|
+
const char* str = JS_ToCString(pctx, exc);
|
|
495
|
+
char buf[256];
|
|
496
|
+
snprintf(buf, sizeof(buf), "pending job error (%s): %s", label ? label : "?", str ? str : "unknown");
|
|
497
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
498
|
+
if (str) JS_FreeCString(pctx, str);
|
|
499
|
+
JS_FreeValue(pctx, exc);
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
static QJSValue qjs_be_console_log(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv, int magic, QJSValue *func_data) {
|
|
507
|
+
(void)this_val; (void)func_data;
|
|
508
|
+
char buf[4096];
|
|
509
|
+
size_t offset = 0;
|
|
510
|
+
for (int i = 0; i < argc && offset < sizeof(buf) - 1; i++) {
|
|
511
|
+
const char* str = JS_ToCString(ctx, argv[i]);
|
|
512
|
+
if (str) {
|
|
513
|
+
int n = snprintf(buf + offset, sizeof(buf) - offset, "%s%s", i > 0 ? " " : "", str);
|
|
514
|
+
if (n > 0) offset += (size_t)n;
|
|
515
|
+
JS_FreeCString(ctx, str);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
buf[offset] = '\0';
|
|
519
|
+
int level = (magic >= ZAPP_LOG_ERROR && magic <= ZAPP_LOG_TRACE) ? magic : ZAPP_LOG_INFO;
|
|
520
|
+
zapp_log_backend(level, buf);
|
|
521
|
+
return JS_UNDEFINED;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
static QJSValue qjs_be_bridge_consoleLog(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
525
|
+
const char* level = (argc >= 1) ? JS_ToCString(ctx, argv[0]) : NULL;
|
|
526
|
+
const char* msg = (argc >= 2) ? JS_ToCString(ctx, argv[1]) : NULL;
|
|
527
|
+
|
|
528
|
+
int level_int = ZAPP_LOG_INFO;
|
|
529
|
+
if (level) {
|
|
530
|
+
if (strcmp(level, "error") == 0) level_int = ZAPP_LOG_ERROR;
|
|
531
|
+
else if (strcmp(level, "warn") == 0) level_int = ZAPP_LOG_WARN;
|
|
532
|
+
else if (strcmp(level, "debug") == 0) level_int = ZAPP_LOG_DEBUG;
|
|
533
|
+
else if (strcmp(level, "trace") == 0) level_int = ZAPP_LOG_TRACE;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
zapp_log_backend(level_int, msg ? msg : "");
|
|
537
|
+
if (level) JS_FreeCString(ctx, level);
|
|
538
|
+
if (msg) JS_FreeCString(ctx, msg);
|
|
539
|
+
return JS_UNDEFINED;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
static char* qjs_be_json_stringify(QJSContext* ctx, QJSValue val) {
|
|
543
|
+
QJSValue global = JS_GetGlobalObject(ctx);
|
|
544
|
+
QJSValue JSON = JS_GetPropertyStr(ctx, global, "JSON");
|
|
545
|
+
QJSValue stringify = JS_GetPropertyStr(ctx, JSON, "stringify");
|
|
546
|
+
QJSValue result = JS_Call(ctx, stringify, JSON, 1, &val);
|
|
547
|
+
const char* str = JS_ToCString(ctx, result);
|
|
548
|
+
char* copy = str ? strdup(str) : NULL;
|
|
549
|
+
if (str) JS_FreeCString(ctx, str);
|
|
550
|
+
JS_FreeValue(ctx, result);
|
|
551
|
+
JS_FreeValue(ctx, stringify);
|
|
552
|
+
JS_FreeValue(ctx, JSON);
|
|
553
|
+
JS_FreeValue(ctx, global);
|
|
554
|
+
return copy;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
static QJSValue qjs_be_emitToHost(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
558
|
+
if (argc < 2) return JS_UNDEFINED;
|
|
559
|
+
|
|
560
|
+
const char* name = JS_ToCString(ctx, argv[0]);
|
|
561
|
+
if (!name) return JS_UNDEFINED;
|
|
562
|
+
|
|
563
|
+
NSString* eventName = [NSString stringWithUTF8String:name];
|
|
564
|
+
JS_FreeCString(ctx, name);
|
|
565
|
+
|
|
566
|
+
// Use JS_ToCString (equivalent to JSC's toString) to avoid double-encoding.
|
|
567
|
+
// The bootstrap already passes JSON.stringify'd strings.
|
|
568
|
+
const char* payloadCStr = JS_ToCString(ctx, argv[1]);
|
|
569
|
+
NSString* payload = payloadCStr ? [NSString stringWithUTF8String:payloadCStr] : @"{}";
|
|
570
|
+
if (payloadCStr) JS_FreeCString(ctx, payloadCStr);
|
|
571
|
+
|
|
572
|
+
if ([eventName isEqualToString:@"__zapp_window_create"] ||
|
|
573
|
+
[eventName isEqualToString:@"__zapp_window_action"] ||
|
|
574
|
+
[eventName isEqualToString:@"__zapp_app_action"] ||
|
|
575
|
+
[eventName isEqualToString:@"__zapp_app_configure"]) {
|
|
576
|
+
|
|
577
|
+
if ([eventName isEqualToString:@"__zapp_window_create"]) {
|
|
578
|
+
NSString* wire = [NSString stringWithFormat:@"window\ncreate\n%@", payload];
|
|
579
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
580
|
+
void* a = app_get_active();
|
|
581
|
+
if (a) { char* m = strdup([wire UTF8String]); zapp_handle_message(a, m); free(m); }
|
|
582
|
+
});
|
|
583
|
+
} else if ([eventName isEqualToString:@"__zapp_window_action"]) {
|
|
584
|
+
NSData* d = [payload dataUsingEncoding:NSUTF8StringEncoding];
|
|
585
|
+
NSDictionary* body = d ? [NSJSONSerialization JSONObjectWithData:d options:0 error:nil] : nil;
|
|
586
|
+
NSString* action = ([body isKindOfClass:[NSDictionary class]]) ? body[@"action"] : nil;
|
|
587
|
+
if ([action isKindOfClass:[NSString class]]) {
|
|
588
|
+
NSString* wire = [NSString stringWithFormat:@"window\n%@\n%@", action, payload];
|
|
589
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
590
|
+
void* a = app_get_active();
|
|
591
|
+
if (a) { char* m = strdup([wire UTF8String]); zapp_handle_message(a, m); free(m); }
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
} else if ([eventName isEqualToString:@"__zapp_app_action"]) {
|
|
595
|
+
NSData* d = [payload dataUsingEncoding:NSUTF8StringEncoding];
|
|
596
|
+
NSDictionary* body = d ? [NSJSONSerialization JSONObjectWithData:d options:0 error:nil] : nil;
|
|
597
|
+
NSString* action = ([body isKindOfClass:[NSDictionary class]]) ? body[@"action"] : nil;
|
|
598
|
+
if ([action isKindOfClass:[NSString class]]) {
|
|
599
|
+
NSString* wire = [NSString stringWithFormat:@"app\n%@\n{}", action];
|
|
600
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
601
|
+
void* a = app_get_active();
|
|
602
|
+
if (a) { char* m = strdup([wire UTF8String]); zapp_handle_message(a, m); free(m); }
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
} else if ([eventName isEqualToString:@"__zapp_app_configure"]) {
|
|
606
|
+
NSString* wire = [NSString stringWithFormat:@"app\nconfigure\n%@", payload];
|
|
607
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
608
|
+
void* a = app_get_active();
|
|
609
|
+
if (a) { char* m = strdup([wire UTF8String]); zapp_handle_message(a, m); free(m); }
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
return JS_UNDEFINED;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
NSString* wire = [NSString stringWithFormat:@"emit\n%@\n%@", eventName, payload];
|
|
616
|
+
void* a = app_get_active();
|
|
617
|
+
if (a) { char* m = strdup([wire UTF8String]); zapp_handle_message(a, m); free(m); }
|
|
618
|
+
return JS_UNDEFINED;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
static QJSValue qjs_be_invokeToHost(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
622
|
+
if (argc < 2) return JS_UNDEFINED;
|
|
623
|
+
const char* name = JS_ToCString(ctx, argv[0]);
|
|
624
|
+
if (!name) return JS_UNDEFINED;
|
|
625
|
+
|
|
626
|
+
char* payloadStr = qjs_be_json_stringify(ctx, argv[1]);
|
|
627
|
+
const char* p = payloadStr ? payloadStr : "{}";
|
|
628
|
+
|
|
629
|
+
void* a = app_get_active();
|
|
630
|
+
if (a) {
|
|
631
|
+
NSString* wire = [NSString stringWithFormat:@"invoke\n%s\n%s", name, p];
|
|
632
|
+
zapp_handle_message(a, (char*)[wire UTF8String]);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (payloadStr) free(payloadStr);
|
|
636
|
+
JS_FreeCString(ctx, name);
|
|
637
|
+
return JS_UNDEFINED;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
static QJSValue qjs_be_reportError(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
641
|
+
if (argc < 1) return JS_UNDEFINED;
|
|
642
|
+
const char* msg = JS_ToCString(ctx, argv[0]);
|
|
643
|
+
zapp_log_backend(ZAPP_LOG_ERROR, msg ? msg : "unknown");
|
|
644
|
+
if (msg) JS_FreeCString(ctx, msg);
|
|
645
|
+
return JS_UNDEFINED;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
static QJSValue qjs_be_noop(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
649
|
+
(void)ctx; (void)this_val; (void)argc; (void)argv;
|
|
650
|
+
return JS_UNDEFINED;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
static QJSValue qjs_be_sync_forward(QJSContext *ctx, const char* action, int argc, QJSValue *argv) {
|
|
654
|
+
if (argc < 1 || !action) return JS_UNDEFINED;
|
|
655
|
+
char* payloadStr = qjs_be_json_stringify(ctx, argv[0]);
|
|
656
|
+
NSString* payloadJson = payloadStr ? [NSString stringWithUTF8String:payloadStr] : @"{}";
|
|
657
|
+
if (payloadStr) free(payloadStr);
|
|
658
|
+
|
|
659
|
+
NSData* d = [payloadJson dataUsingEncoding:NSUTF8StringEncoding];
|
|
660
|
+
id rawObj = d ? [NSJSONSerialization JSONObjectWithData:d options:0 error:nil] : nil;
|
|
661
|
+
NSMutableDictionary* body = [NSMutableDictionary dictionary];
|
|
662
|
+
if ([rawObj isKindOfClass:[NSDictionary class]]) [body addEntriesFromDictionary:(NSDictionary*)rawObj];
|
|
663
|
+
[body setObject:qjs_be_id forKey:@"targetWorkerId"];
|
|
664
|
+
NSData* outData = [NSJSONSerialization dataWithJSONObject:body options:0 error:nil];
|
|
665
|
+
NSString* outJson = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding] ?: @"{}";
|
|
666
|
+
|
|
667
|
+
void* a = app_get_active();
|
|
668
|
+
if (a) {
|
|
669
|
+
NSString* wire = [NSString stringWithFormat:@"sync\n%s\n%@", action, outJson];
|
|
670
|
+
zapp_handle_message(a, (char*)[wire UTF8String]);
|
|
671
|
+
}
|
|
672
|
+
return JS_UNDEFINED;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
static QJSValue qjs_be_syncWait(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
676
|
+
return qjs_be_sync_forward(ctx, "wait", argc, argv);
|
|
677
|
+
}
|
|
678
|
+
static QJSValue qjs_be_syncNotify(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
679
|
+
return qjs_be_sync_forward(ctx, "notify", argc, argv);
|
|
680
|
+
}
|
|
681
|
+
static QJSValue qjs_be_syncCancel(QJSContext *ctx, QJSValue this_val, int argc, QJSValue *argv) {
|
|
682
|
+
return qjs_be_sync_forward(ctx, "cancel", argc, argv);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
void zapp_backend_deliver_event(const char* name, const char* payload) {
|
|
686
|
+
if (qjs_be_ctx == NULL || name == NULL) return;
|
|
687
|
+
NSString* n = [NSString stringWithUTF8String:name];
|
|
688
|
+
NSString* p = payload ? [NSString stringWithUTF8String:payload] : @"{}";
|
|
689
|
+
dispatch_queue_t q = zapp_darwin_qjs_get_queue();
|
|
690
|
+
if (!q) return;
|
|
691
|
+
dispatch_async(q, ^{
|
|
692
|
+
if (qjs_be_ctx == NULL) return;
|
|
693
|
+
QJSValue args[2];
|
|
694
|
+
args[0] = JS_NewString(qjs_be_ctx, [n UTF8String]);
|
|
695
|
+
args[1] = JS_NewString(qjs_be_ctx, [p UTF8String]);
|
|
696
|
+
qjs_be_call_bridge(@"deliverEvent", 2, args);
|
|
697
|
+
JS_FreeValue(qjs_be_ctx, args[0]);
|
|
698
|
+
JS_FreeValue(qjs_be_ctx, args[1]);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
void zapp_backend_dispatch_window_result(const char* payload_json) {
|
|
703
|
+
if (qjs_be_ctx == NULL || payload_json == NULL) return;
|
|
704
|
+
NSString* payload = [NSString stringWithUTF8String:payload_json];
|
|
705
|
+
dispatch_queue_t q = zapp_darwin_qjs_get_queue();
|
|
706
|
+
if (!q) return;
|
|
707
|
+
dispatch_async(q, ^{
|
|
708
|
+
if (qjs_be_ctx == NULL) return;
|
|
709
|
+
QJSValue args[1];
|
|
710
|
+
args[0] = JS_NewString(qjs_be_ctx, [payload UTF8String]);
|
|
711
|
+
qjs_be_call_bridge(@"dispatchWindowResult", 1, args);
|
|
712
|
+
JS_FreeValue(qjs_be_ctx, args[0]);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
void zapp_backend_dispatch_window_ready(const char* window_id) {
|
|
717
|
+
if (qjs_be_ctx == NULL || window_id == NULL) return;
|
|
718
|
+
NSString* windowId = [NSString stringWithUTF8String:window_id];
|
|
719
|
+
dispatch_queue_t q = zapp_darwin_qjs_get_queue();
|
|
720
|
+
if (!q) return;
|
|
721
|
+
dispatch_async(q, ^{
|
|
722
|
+
if (qjs_be_ctx == NULL) return;
|
|
723
|
+
// Deliver "window:ready" event to backend
|
|
724
|
+
NSString* eventJson = [NSString stringWithFormat:@"{\"windowId\":\"%@\"}", windowId];
|
|
725
|
+
NSString* js = [NSString stringWithFormat:
|
|
726
|
+
@"(function(){var b=globalThis[Symbol.for('zapp.bridge')];if(b&&typeof b.deliverEvent==='function'){b.deliverEvent('window:ready','%@');}})();",
|
|
727
|
+
[eventJson stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]
|
|
728
|
+
];
|
|
729
|
+
const char* jsUtf8 = [js UTF8String];
|
|
730
|
+
extern int engine_qjs_eval(void* ctx, const char* utf8, size_t len, const char* filename);
|
|
731
|
+
engine_qjs_eval(qjs_be_ctx, jsUtf8, strlen(jsUtf8), "<window:ready>");
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
int32_t zapp_backend_context_create(void* app_ptr, char* script_source) {
|
|
736
|
+
if (app_ptr == NULL || script_source == NULL) return -1;
|
|
737
|
+
|
|
738
|
+
zapp_darwin_qjs_ensure_init();
|
|
739
|
+
|
|
740
|
+
__block int32_t result = -1;
|
|
741
|
+
dispatch_queue_t q = zapp_darwin_qjs_get_queue();
|
|
742
|
+
if (!q) return -1;
|
|
743
|
+
|
|
744
|
+
dispatch_sync(q, ^{
|
|
745
|
+
void* rt = zapp_darwin_qjs_get_runtime();
|
|
746
|
+
if (!rt) { zapp_log_backend(ZAPP_LOG_ERROR, "no runtime"); return; }
|
|
747
|
+
|
|
748
|
+
void* ctx_opaque = engine_qjs_create_ctx(rt);
|
|
749
|
+
if (!ctx_opaque) { zapp_log_backend(ZAPP_LOG_ERROR, "failed to create context"); return; }
|
|
750
|
+
|
|
751
|
+
QJSContext* ctx = (QJSContext*)ctx_opaque;
|
|
752
|
+
qjs_be_ctx = ctx;
|
|
753
|
+
|
|
754
|
+
zapp_darwin_qjs_register_context(qjs_be_id, ctx_opaque);
|
|
755
|
+
|
|
756
|
+
QJSValue global = JS_GetGlobalObject(ctx);
|
|
757
|
+
|
|
758
|
+
// console — each method routes through zapp_log_backend with appropriate level
|
|
759
|
+
QJSValue consoleObj = JS_NewObject(ctx);
|
|
760
|
+
JS_SetPropertyStr(ctx, consoleObj, "log", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_INFO, 0, NULL));
|
|
761
|
+
JS_SetPropertyStr(ctx, consoleObj, "info", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_INFO, 0, NULL));
|
|
762
|
+
JS_SetPropertyStr(ctx, consoleObj, "warn", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_WARN, 0, NULL));
|
|
763
|
+
JS_SetPropertyStr(ctx, consoleObj, "error", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_ERROR, 0, NULL));
|
|
764
|
+
JS_SetPropertyStr(ctx, consoleObj, "debug", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_DEBUG, 0, NULL));
|
|
765
|
+
JS_SetPropertyStr(ctx, consoleObj, "trace", JS_NewCFunctionData(ctx, qjs_be_console_log, 1, ZAPP_LOG_TRACE, 0, NULL));
|
|
766
|
+
JS_SetPropertyStr(ctx, global, "console", consoleObj);
|
|
767
|
+
|
|
768
|
+
// bridge
|
|
769
|
+
QJSValue bridge = JS_NewObject(ctx);
|
|
770
|
+
|
|
771
|
+
JS_SetPropertyStr(ctx, bridge, "emitToHost", JS_NewCFunction(ctx, qjs_be_emitToHost, "emitToHost", 2));
|
|
772
|
+
JS_SetPropertyStr(ctx, bridge, "invokeToHost", JS_NewCFunction(ctx, qjs_be_invokeToHost, "invokeToHost", 2));
|
|
773
|
+
JS_SetPropertyStr(ctx, bridge, "reportError", JS_NewCFunction(ctx, qjs_be_reportError, "reportError", 1));
|
|
774
|
+
JS_SetPropertyStr(ctx, bridge, "consoleLog", JS_NewCFunction(ctx, qjs_be_bridge_consoleLog, "consoleLog", 2));
|
|
775
|
+
JS_SetPropertyStr(ctx, bridge, "syncWait", JS_NewCFunction(ctx, qjs_be_syncWait, "syncWait", 1));
|
|
776
|
+
JS_SetPropertyStr(ctx, bridge, "syncNotify", JS_NewCFunction(ctx, qjs_be_syncNotify, "syncNotify", 1));
|
|
777
|
+
JS_SetPropertyStr(ctx, bridge, "syncCancel", JS_NewCFunction(ctx, qjs_be_syncCancel, "syncCancel", 1));
|
|
778
|
+
|
|
779
|
+
// postMessage / closeWorker no-ops for backend
|
|
780
|
+
QJSValue noop = JS_NewCFunction(ctx, qjs_be_noop, "noop", 0);
|
|
781
|
+
JS_SetPropertyStr(ctx, bridge, "postMessage", JS_DupValue(ctx, noop));
|
|
782
|
+
JS_SetPropertyStr(ctx, bridge, "closeWorker", noop);
|
|
783
|
+
|
|
784
|
+
zapp_darwin_qjs_timers_install(ctx, bridge, [qjs_be_id UTF8String]);
|
|
785
|
+
zapp_darwin_qjs_fetch_install(ctx, bridge, [qjs_be_id UTF8String]);
|
|
786
|
+
zapp_qjs_crypto_install(ctx, bridge);
|
|
787
|
+
zapp_qjs_encoding_install(ctx, bridge);
|
|
788
|
+
zapp_qjs_url_install(ctx, bridge);
|
|
789
|
+
zapp_qjs_base64_install(ctx, bridge);
|
|
790
|
+
zapp_darwin_qjs_websocket_install(ctx, bridge, [qjs_be_id UTF8String]);
|
|
791
|
+
|
|
792
|
+
JS_SetPropertyStr(ctx, global, "globalThis", JS_DupValue(ctx, global));
|
|
793
|
+
JS_SetPropertyStr(ctx, global, "self", JS_DupValue(ctx, global));
|
|
794
|
+
JS_SetPropertyStr(ctx, global, "__zappWorkerDispatchId", JS_NewString(ctx, [qjs_be_id UTF8String]));
|
|
795
|
+
JS_SetPropertyStr(ctx, global, "__zappBridge", bridge);
|
|
796
|
+
|
|
797
|
+
// Inject initial config
|
|
798
|
+
{
|
|
799
|
+
extern char* app_get_bootstrap_name(void);
|
|
800
|
+
extern bool app_get_bootstrap_application_should_terminate_after_last_window_closed(void);
|
|
801
|
+
extern bool app_get_bootstrap_web_content_inspectable(void);
|
|
802
|
+
extern int app_get_bootstrap_max_workers(void);
|
|
803
|
+
|
|
804
|
+
char configScript[1024];
|
|
805
|
+
const char* appName = app_get_bootstrap_name();
|
|
806
|
+
snprintf(configScript, sizeof(configScript),
|
|
807
|
+
"globalThis.__zappConfig={name:'%s',"
|
|
808
|
+
"applicationShouldTerminateAfterLastWindowClosed:%s,"
|
|
809
|
+
"webContentInspectable:%s,"
|
|
810
|
+
"maxWorkers:%d};",
|
|
811
|
+
appName ? appName : "Zapp",
|
|
812
|
+
app_get_bootstrap_application_should_terminate_after_last_window_closed() ? "true" : "false",
|
|
813
|
+
app_get_bootstrap_web_content_inspectable() ? "true" : "false",
|
|
814
|
+
app_get_bootstrap_max_workers()
|
|
815
|
+
);
|
|
816
|
+
engine_qjs_eval(ctx, configScript, strlen(configScript), "<config>");
|
|
817
|
+
qjs_be_drain(ctx, "config");
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Evaluate bootstrap
|
|
821
|
+
const char* bootstrap = zapp_darwin_backend_bootstrap_script();
|
|
822
|
+
size_t blen = strlen(bootstrap);
|
|
823
|
+
int err = engine_qjs_eval(ctx, bootstrap, blen, "backend-bootstrap.js");
|
|
824
|
+
if (err != 0) {
|
|
825
|
+
const char* msg = engine_qjs_get_exception_message(ctx);
|
|
826
|
+
char buf[256];
|
|
827
|
+
snprintf(buf, sizeof(buf), "bootstrap failed: %s", msg ? msg : "unknown");
|
|
828
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
829
|
+
zapp_darwin_qjs_timers_cancel(qjs_be_id);
|
|
830
|
+
zapp_darwin_qjs_fetch_cancel(qjs_be_id);
|
|
831
|
+
zapp_darwin_qjs_unregister_context(qjs_be_id);
|
|
832
|
+
engine_qjs_free_ctx(ctx);
|
|
833
|
+
qjs_be_ctx = NULL;
|
|
834
|
+
JS_FreeValue(ctx, global);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
qjs_be_drain(ctx, "bootstrap");
|
|
838
|
+
|
|
839
|
+
// Evaluate user script in async IIFE with error catching
|
|
840
|
+
size_t srcLen = strlen(script_source);
|
|
841
|
+
size_t totalLen = srcLen + 256;
|
|
842
|
+
char* wrapped = (char*)malloc(totalLen);
|
|
843
|
+
if (!wrapped) {
|
|
844
|
+
zapp_log_backend(ZAPP_LOG_ERROR, "out of memory");
|
|
845
|
+
zapp_darwin_qjs_timers_cancel(qjs_be_id);
|
|
846
|
+
zapp_darwin_qjs_fetch_cancel(qjs_be_id);
|
|
847
|
+
zapp_darwin_qjs_unregister_context(qjs_be_id);
|
|
848
|
+
engine_qjs_free_ctx(ctx);
|
|
849
|
+
qjs_be_ctx = NULL;
|
|
850
|
+
JS_FreeValue(ctx, global);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
snprintf(wrapped, totalLen,
|
|
854
|
+
"(async function(){\n%s\n})()"
|
|
855
|
+
".catch(function(e){"
|
|
856
|
+
"var b=globalThis[Symbol.for('zapp.bridge')];"
|
|
857
|
+
"if(b&&b.reportError)b.reportError(String(e));"
|
|
858
|
+
"else console.error('[backend] unhandled:',e);"
|
|
859
|
+
"});", script_source);
|
|
860
|
+
err = engine_qjs_eval(ctx, wrapped, strlen(wrapped), "backend.js");
|
|
861
|
+
free(wrapped);
|
|
862
|
+
if (err != 0) {
|
|
863
|
+
const char* msg = engine_qjs_get_exception_message(ctx);
|
|
864
|
+
char buf[256];
|
|
865
|
+
snprintf(buf, sizeof(buf), "script error: %s", msg ? msg : "unknown");
|
|
866
|
+
zapp_log_backend(ZAPP_LOG_ERROR, buf);
|
|
867
|
+
}
|
|
868
|
+
qjs_be_drain(ctx, "backend-script");
|
|
869
|
+
|
|
870
|
+
JS_FreeValue(ctx, global);
|
|
871
|
+
result = 0;
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
return result;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
void zapp_backend_context_destroy(void) {
|
|
878
|
+
dispatch_queue_t q = zapp_darwin_qjs_get_queue();
|
|
879
|
+
if (!q || qjs_be_ctx == NULL) return;
|
|
880
|
+
dispatch_sync(q, ^{
|
|
881
|
+
if (qjs_be_ctx != NULL) {
|
|
882
|
+
zapp_darwin_qjs_timers_cancel(qjs_be_id);
|
|
883
|
+
zapp_darwin_qjs_fetch_cancel(qjs_be_id);
|
|
884
|
+
zapp_darwin_qjs_unregister_context(qjs_be_id);
|
|
885
|
+
engine_qjs_free_ctx(qjs_be_ctx);
|
|
886
|
+
qjs_be_ctx = NULL;
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
#undef JSContext
|
|
892
|
+
#undef JSRuntime
|
|
893
|
+
#undef JSValue
|
|
894
|
+
#undef JSClassID
|
|
895
|
+
#undef JSClassDef
|
|
896
|
+
#undef JSClassEx
|
|
897
|
+
#undef JSValueConst
|
|
898
|
+
#undef JSPropertyEnum
|
|
899
|
+
#undef JSPropertyDescriptor
|
|
900
|
+
#else
|
|
901
|
+
void zapp_backend_deliver_event(const char* name, const char* payload) {
|
|
902
|
+
(void)name; (void)payload;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
void zapp_backend_dispatch_window_result(const char* payload_json) {
|
|
906
|
+
(void)payload_json;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
void zapp_backend_dispatch_window_ready(const char* window_id) {
|
|
910
|
+
(void)window_id;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
int zapp_backend_context_create(void* app_ptr, char* script_source) {
|
|
914
|
+
(void)app_ptr; (void)script_source;
|
|
915
|
+
return -1;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
void zapp_backend_context_destroy(void) {}
|
|
919
|
+
#endif
|
|
920
|
+
#endif /* __APPLE__ */
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
struct DarwinBackend {}
|