Skip to content

Commit df8fcef

Browse files
refactor(native): split bridge modules and merge worker teardown fix (#255)
* Fix native worker-thread teardown stability * refactor(native): split bridge modules * fix(native): align engine guard semantics * test(node): gate prebuild native loader smoke * fix(native): harden config and debug numeric parsing
1 parent 19b2d9c commit df8fcef

File tree

10 files changed

+2462
-2361
lines changed

10 files changed

+2462
-2361
lines changed

packages/native/build.rs

Lines changed: 79 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,92 @@ use std::env;
22
use std::path::PathBuf;
33

44
fn main() {
5-
napi_build::setup();
5+
napi_build::setup();
66

7-
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
8-
let vendor = manifest_dir.join("vendor").join("zireael");
9-
let include_dir = vendor.join("include");
10-
let src_dir = vendor.join("src");
7+
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
8+
let vendor = manifest_dir.join("vendor").join("zireael");
9+
let include_dir = vendor.join("include");
10+
let src_dir = vendor.join("src");
1111

12-
let mut build = cc::Build::new();
13-
build.include(&include_dir);
14-
build.include(&src_dir);
15-
build.warnings(false);
12+
let mut build = cc::Build::new();
13+
build.include(&include_dir);
14+
build.include(&src_dir);
15+
build.warnings(false);
1616

17-
// The engine assumes a C99-or-newer compiler; C11 is required for atomics on MSVC.
18-
if build.get_compiler().is_like_msvc() {
19-
build.flag_if_supported("/std:c11");
20-
build.flag_if_supported("/experimental:c11atomics");
21-
} else {
22-
build.flag_if_supported("-std=c11");
23-
}
17+
// The engine assumes a C99-or-newer compiler; C11 is required for atomics on MSVC.
18+
if build.get_compiler().is_like_msvc() {
19+
build.flag_if_supported("/std:c11");
20+
build.flag_if_supported("/experimental:c11atomics");
21+
} else {
22+
build.flag_if_supported("-std=c11");
23+
}
2424

25-
// Core + unicode + util.
26-
build.file(src_dir.join("core").join("zr_engine.c"));
27-
build.file(src_dir.join("core").join("zr_framebuffer.c"));
28-
build.file(src_dir.join("core").join("zr_drawlist.c"));
29-
build.file(src_dir.join("core").join("zr_event_pack.c"));
30-
build.file(src_dir.join("core").join("zr_event_queue.c"));
31-
build.file(src_dir.join("core").join("zr_metrics.c"));
32-
build.file(src_dir.join("core").join("zr_input_parser.c"));
33-
build.file(src_dir.join("core").join("zr_damage.c"));
34-
build.file(src_dir.join("core").join("zr_config.c"));
35-
build.file(src_dir.join("core").join("zr_base64.c"));
36-
build.file(src_dir.join("core").join("zr_blit.c"));
37-
build.file(src_dir.join("core").join("zr_blit_ascii.c"));
38-
build.file(src_dir.join("core").join("zr_blit_braille.c"));
39-
build.file(src_dir.join("core").join("zr_blit_halfblock.c"));
40-
build.file(src_dir.join("core").join("zr_blit_quadrant.c"));
41-
build.file(src_dir.join("core").join("zr_blit_sextant.c"));
42-
build.file(src_dir.join("core").join("zr_debug_overlay.c"));
43-
build.file(src_dir.join("core").join("zr_debug_trace.c"));
44-
build.file(src_dir.join("core").join("zr_detect.c"));
45-
build.file(src_dir.join("core").join("zr_diff.c"));
46-
build.file(src_dir.join("core").join("zr_placeholder.c"));
47-
build.file(src_dir.join("core").join("zr_image.c"));
48-
build.file(src_dir.join("core").join("zr_image_iterm2.c"));
49-
build.file(src_dir.join("core").join("zr_image_kitty.c"));
50-
build.file(src_dir.join("core").join("zr_image_sixel.c"));
25+
// Core + unicode + util.
26+
build.file(src_dir.join("core").join("zr_engine.c"));
27+
build.file(src_dir.join("core").join("zr_framebuffer.c"));
28+
build.file(src_dir.join("core").join("zr_drawlist.c"));
29+
build.file(src_dir.join("core").join("zr_event_pack.c"));
30+
build.file(src_dir.join("core").join("zr_event_queue.c"));
31+
build.file(src_dir.join("core").join("zr_metrics.c"));
32+
build.file(src_dir.join("core").join("zr_input_parser.c"));
33+
build.file(src_dir.join("core").join("zr_damage.c"));
34+
build.file(src_dir.join("core").join("zr_config.c"));
35+
build.file(src_dir.join("core").join("zr_base64.c"));
36+
build.file(src_dir.join("core").join("zr_blit.c"));
37+
build.file(src_dir.join("core").join("zr_blit_ascii.c"));
38+
build.file(src_dir.join("core").join("zr_blit_braille.c"));
39+
build.file(src_dir.join("core").join("zr_blit_halfblock.c"));
40+
build.file(src_dir.join("core").join("zr_blit_quadrant.c"));
41+
build.file(src_dir.join("core").join("zr_blit_sextant.c"));
42+
build.file(src_dir.join("core").join("zr_debug_overlay.c"));
43+
build.file(src_dir.join("core").join("zr_debug_trace.c"));
44+
build.file(src_dir.join("core").join("zr_detect.c"));
45+
build.file(src_dir.join("core").join("zr_diff.c"));
46+
build.file(src_dir.join("core").join("zr_placeholder.c"));
47+
build.file(src_dir.join("core").join("zr_image.c"));
48+
build.file(src_dir.join("core").join("zr_image_iterm2.c"));
49+
build.file(src_dir.join("core").join("zr_image_kitty.c"));
50+
build.file(src_dir.join("core").join("zr_image_sixel.c"));
5151

52-
build.file(src_dir.join("unicode").join("zr_width.c"));
53-
build.file(src_dir.join("unicode").join("zr_unicode_data.c"));
54-
build.file(src_dir.join("unicode").join("zr_utf8.c"));
55-
build.file(src_dir.join("unicode").join("zr_grapheme.c"));
56-
build.file(src_dir.join("unicode").join("zr_wrap.c"));
52+
build.file(src_dir.join("unicode").join("zr_width.c"));
53+
build.file(src_dir.join("unicode").join("zr_unicode_data.c"));
54+
build.file(src_dir.join("unicode").join("zr_utf8.c"));
55+
build.file(src_dir.join("unicode").join("zr_grapheme.c"));
56+
build.file(src_dir.join("unicode").join("zr_wrap.c"));
5757

58-
build.file(src_dir.join("util").join("zr_arena.c"));
59-
build.file(src_dir.join("util").join("zr_caps.c"));
60-
build.file(src_dir.join("util").join("zr_ring.c"));
61-
build.file(src_dir.join("util").join("zr_log.c"));
62-
build.file(src_dir.join("util").join("zr_assert.c"));
63-
build.file(src_dir.join("util").join("zr_string_builder.c"));
64-
build.file(src_dir.join("util").join("zr_vec.c"));
58+
build.file(src_dir.join("util").join("zr_arena.c"));
59+
build.file(src_dir.join("util").join("zr_caps.c"));
60+
build.file(src_dir.join("util").join("zr_ring.c"));
61+
build.file(src_dir.join("util").join("zr_log.c"));
62+
build.file(src_dir.join("util").join("zr_assert.c"));
63+
build.file(src_dir.join("util").join("zr_string_builder.c"));
64+
build.file(src_dir.join("util").join("zr_vec.c"));
6565

66-
// Platform selection + backend.
67-
build.file(src_dir.join("platform").join("zr_platform_select.c"));
68-
if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
69-
build.file(src_dir.join("platform").join("win32").join("zr_plat_win32.c"));
70-
println!("cargo:rustc-link-lib=dylib=advapi32");
71-
println!("cargo:rustc-link-lib=dylib=kernel32");
72-
println!("cargo:rustc-link-lib=dylib=user32");
73-
} else {
74-
build.file(src_dir.join("platform").join("posix").join("zr_plat_posix.c"));
75-
}
66+
// Platform selection + backend.
67+
build.file(src_dir.join("platform").join("zr_platform_select.c"));
68+
if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
69+
build.file(
70+
src_dir
71+
.join("platform")
72+
.join("win32")
73+
.join("zr_plat_win32.c"),
74+
);
75+
println!("cargo:rustc-link-lib=dylib=advapi32");
76+
println!("cargo:rustc-link-lib=dylib=kernel32");
77+
println!("cargo:rustc-link-lib=dylib=user32");
78+
} else {
79+
build.file(
80+
src_dir
81+
.join("platform")
82+
.join("posix")
83+
.join("zr_plat_posix.c"),
84+
);
85+
}
7686

77-
build.compile("zireael_core");
87+
build.compile("zireael_core");
7888

79-
// Keep rebuilds deterministic when vendored sources change.
80-
println!("cargo:rerun-if-changed=vendor/VENDOR_COMMIT.txt");
81-
println!("cargo:rerun-if-changed=vendor/zireael/include");
82-
println!("cargo:rerun-if-changed=vendor/zireael/src");
89+
// Keep rebuilds deterministic when vendored sources change.
90+
println!("cargo:rerun-if-changed=vendor/VENDOR_COMMIT.txt");
91+
println!("cargo:rerun-if-changed=vendor/zireael/include");
92+
println!("cargo:rerun-if-changed=vendor/zireael/src");
8393
}

packages/native/index.d.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,39 @@
33

44
/* auto-generated by NAPI-RS */
55

6+
export interface DebugStats {
7+
totalRecords: bigint;
8+
totalDropped: bigint;
9+
errorCount: number;
10+
warnCount: number;
11+
currentRingUsage: number;
12+
ringCapacity: number;
13+
}
14+
export interface DebugQueryResult {
15+
recordsReturned: number;
16+
recordsAvailable: number;
17+
oldestRecordId: bigint;
18+
newestRecordId: bigint;
19+
recordsDropped: number;
20+
}
21+
export declare function engineDebugEnable(
22+
engineId: number,
23+
config?: object | undefined | null,
24+
): number;
25+
export declare function engineDebugDisable(engineId: number): number;
26+
export declare function engineDebugQuery(
27+
engineId: number,
28+
query: object | undefined | null,
29+
outHeaders: Uint8Array,
30+
): DebugQueryResult;
31+
export declare function engineDebugGetPayload(
32+
engineId: number,
33+
recordId: bigint,
34+
outPayload: Uint8Array,
35+
): number;
36+
export declare function engineDebugGetStats(engineId: number): DebugStats;
37+
export declare function engineDebugExport(engineId: number, outBuf: Uint8Array): number;
38+
export declare function engineDebugReset(engineId: number): number;
639
export interface EngineMetrics {
740
structSize: number;
841
negotiatedEngineAbiMajor: number;
@@ -62,36 +95,3 @@ export declare function enginePostUserEvent(
6295
export declare function engineSetConfig(engineId: number, cfg?: object | undefined | null): number;
6396
export declare function engineGetMetrics(engineId: number): EngineMetrics;
6497
export declare function engineGetCaps(engineId: number): TerminalCaps;
65-
export interface DebugStats {
66-
totalRecords: bigint;
67-
totalDropped: bigint;
68-
errorCount: number;
69-
warnCount: number;
70-
currentRingUsage: number;
71-
ringCapacity: number;
72-
}
73-
export interface DebugQueryResult {
74-
recordsReturned: number;
75-
recordsAvailable: number;
76-
oldestRecordId: bigint;
77-
newestRecordId: bigint;
78-
recordsDropped: number;
79-
}
80-
export declare function engineDebugEnable(
81-
engineId: number,
82-
config?: object | undefined | null,
83-
): number;
84-
export declare function engineDebugDisable(engineId: number): number;
85-
export declare function engineDebugQuery(
86-
engineId: number,
87-
query: object | undefined | null,
88-
outHeaders: Uint8Array,
89-
): DebugQueryResult;
90-
export declare function engineDebugGetPayload(
91-
engineId: number,
92-
recordId: bigint,
93-
outPayload: Uint8Array,
94-
): number;
95-
export declare function engineDebugGetStats(engineId: number): DebugStats;
96-
export declare function engineDebugExport(engineId: number, outBuf: Uint8Array): number;
97-
export declare function engineDebugReset(engineId: number): number;

packages/native/scripts/smoke.mjs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ async function assertWorkerLoadExitCleanly() {
7777
workerData: { phase: "loadOnly" },
7878
type: "module",
7979
});
80+
const exitPromise = new Promise((resolve) => worker.once("exit", resolve));
8081

8182
const loadResult = await new Promise((resolve, reject) => {
8283
const onExit = (code) => reject(new Error(`load-only worker exited with ${code}`));
@@ -92,7 +93,7 @@ async function assertWorkerLoadExitCleanly() {
9293
});
9394

9495
assert(loadResult.phase === "loadOnly", "worker load-only phase must complete");
95-
const exitCode = await new Promise((resolve) => worker.once("exit", resolve));
96+
const exitCode = await exitPromise;
9697
assert(exitCode === 0, `load-only worker must exit cleanly, got: ${String(exitCode)}`);
9798
}
9899

@@ -167,6 +168,11 @@ assert(
167168
engineSetConfig(engineId, null) === ZR_ERR_INVALID_ARGUMENT,
168169
"engineSetConfig(null) must return ZR_ERR_INVALID_ARGUMENT",
169170
);
171+
assertThrows(
172+
() => engineSetConfig(engineId, { plat: 1 }),
173+
/plat must be an object/i,
174+
"engineSetConfig must reject non-object plat values",
175+
);
170176
assertThrows(
171177
() => engineSetConfig(engineId, { unknownKey: 1 }),
172178
/unknown key/i,
@@ -292,8 +298,8 @@ assert(
292298
`wrong-thread enginePresent must return ZR_ERR_INVALID_ARGUMENT, got: ${alive.present}`,
293299
);
294300
assert(
295-
alive.postUserEvent === ZR_OK,
296-
`enginePostUserEvent must succeed cross-thread while alive (ZR_OK), got: ${alive.postUserEvent}`,
301+
alive.postUserEvent === ZR_ERR_INVALID_ARGUMENT,
302+
`wrong-thread enginePostUserEvent must return ZR_ERR_INVALID_ARGUMENT, got: ${alive.postUserEvent}`,
297303
);
298304
assert(
299305
alive.setConfig === ZR_ERR_INVALID_ARGUMENT,

0 commit comments

Comments
 (0)