ReviewOS

also looking at this

craft-native/craft

fix(ios): preserve busy errors through dispatch

#270
Merged glennmichael123 wants to merge codex/ios-busy-dispatch into main
3 files +33 -13

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/zig/src/bridge_error.zigmodified+27-0
Changes to packages/zig/src/bridge_error.zig
@@ -190,6 +190,25 @@ pub fn errorMessage(err: BridgeError) []const u8 {
190190 };
191191}
192192
193/// Narrow the wider error set inferred for a bridge handler to the failures
194/// the dispatcher exposes. Errors the protocol cannot name become the honest
195/// catch-all instead of leaving the page without a reply.
196pub fn fromHandlerError(err: anyerror) BridgeError {
197 return switch (err) {
198 error.AllocationFailed, error.OutOfMemory => BridgeError.AllocationFailed,
199 error.UnknownAction => BridgeError.UnknownAction,
200 error.MissingData => BridgeError.MissingData,
201 error.InvalidJSON => BridgeError.InvalidJSON,
202 error.InvalidParameter => BridgeError.InvalidParameter,
203 error.Busy => BridgeError.Busy,
204 error.PlatformNotSupported, error.UnsupportedPlatform => BridgeError.PlatformNotSupported,
205 error.NotFound => BridgeError.NotFound,
206 error.PermissionDenied => BridgeError.PermissionDenied,
207 error.Timeout => BridgeError.Timeout,
208 else => BridgeError.NativeCallFailed,
209 };
210}
211
193212/// Send error to JavaScript via eval
194213pub fn sendErrorToJS(allocator: std.mem.Allocator, action: []const u8, err: BridgeError) void {
195214 var ctx = ErrorContext.init(err, action, errorMessage(err));
@@ -358,6 +377,14 @@ test "errorCodeString returns correct codes" {
358377 try testing.expectEqualStrings("BUSY", errorCodeString(BridgeError.Busy));
359378}
360379
380test "handler error narrowing preserves protocol errors" {
381 const testing = std.testing;
382 try testing.expectEqual(BridgeError.Busy, fromHandlerError(error.Busy));
383 try testing.expectEqual(BridgeError.PlatformNotSupported, fromHandlerError(error.UnsupportedPlatform));
384 try testing.expectEqual(BridgeError.AllocationFailed, fromHandlerError(error.OutOfMemory));
385 try testing.expectEqual(BridgeError.NativeCallFailed, fromHandlerError(error.NotInTheProtocol));
386}
387
361388test "ErrorContext.toJSON produces valid JSON" {
362389 const testing = std.testing;
363390 const allocator = testing.allocator;
packages/zig/src/bridge_mobile_imagepicker.zigmodified+1-1
Changes to packages/zig/src/bridge_mobile_imagepicker.zig
@@ -1434,7 +1434,7 @@ test "a delegate callback with no call recorded touches no slot" {
14341434test "the pool refusal is an error the caller sees, not a dropped call" {
14351435 try testing.expectEqual(
14361436 bridge_error.BridgeError.Busy,
1437 poolFull(A.open_camera),
1437 bridge_error.fromHandlerError(poolFull(A.open_camera)),
14381438 );
14391439}
14401440
packages/zig/src/ios_dispatch.zigmodified+5-12
Changes to packages/zig/src/ios_dispatch.zig
@@ -418,18 +418,7 @@ fn hostServesItself(action: []const u8) bool {
418418/// settle. `NativeCallFailed` is the honest catch-all for "it broke in a way
419419/// the protocol has no word for".
420420fn asBridgeError(err: anyerror) bridge_error.BridgeError {
421 return switch (err) {
422 error.AllocationFailed, error.OutOfMemory => bridge_error.BridgeError.AllocationFailed,
423 error.InvalidJSON => bridge_error.BridgeError.InvalidJSON,
424 error.InvalidParameter => bridge_error.BridgeError.InvalidParameter,
425 error.MissingData => bridge_error.BridgeError.MissingData,
426 error.NotFound => bridge_error.BridgeError.NotFound,
427 error.PermissionDenied => bridge_error.BridgeError.PermissionDenied,
428 error.PlatformNotSupported, error.UnsupportedPlatform => bridge_error.BridgeError.PlatformNotSupported,
429 error.Timeout => bridge_error.BridgeError.Timeout,
430 error.UnknownAction => bridge_error.BridgeError.UnknownAction,
431 else => bridge_error.BridgeError.NativeCallFailed,
432 };
421 return bridge_error.fromHandlerError(err);
433422}
434423
435424/// Hand an action to the host shim. Returns false when there is no shim.
@@ -790,6 +779,10 @@ test "a handler error reaches the page as something the protocol can say" {
790779 bridge_error.BridgeError.PlatformNotSupported,
791780 asBridgeError(error.UnsupportedPlatform),
792781 );
782 try testing.expectEqual(
783 bridge_error.BridgeError.Busy,
784 asBridgeError(error.Busy),
785 );
793786 // The catch-all. An error the protocol has no word for still gets one.
794787 try testing.expectEqual(
795788 bridge_error.BridgeError.NativeCallFailed,