proto: give the generated C++ Dispatcher a real error channel (P1, 1.4.0)
DAEMON's daemon/docs/proto-requests-m1.md P1: velox::proto::Dispatcher's
on_* methods returned Result<T> = expected<T, ParseError>, and dispatch()
mapped every handler error to -32603 InternalError. A handler had no way to
return -32010 (download.get not-found), -32011 (download.add invalid-path)
or -32013 (probe-failed) with their data payloads -- three error fixtures a
conformant server must satisfy were unreachable, blocking DAEMON's
"conformance as a server" M1 DoD.
Two error channels now, kept separate on purpose:
- parse: Result<T> / ParseError -- dispatch() failing to turn the wire into
typed params. Always -32602, always structural.
- handler: HandlerResult<T> / HandlerError -- a handler deciding the request
can't be fulfilled. Carries any ErrorCode + message + free-form data.
struct HandlerError {
ErrorCode code{ErrorCode::InternalError}; // bare {} is a valid -32603
std::string message;
nlohmann::json data = nullptr; // straight into the error's data
};
template <class T> using HandlerResult = std::expected<T, HandlerError>;
dispatch()'s handler branch is now
make_error(id, r.error().code, r.error().message, r.error().data)
instead of a hard-coded InternalError. -32001/-32002/-32003 stay the server
layer's to raise around dispatch(), as DAEMON already does.
Verified end to end against the real dispatch() path: a handler returning
TaskNotFound/InvalidPath/ProbeFailed produces -32010/-32011/-32013 with the
data object intact, and a bare HandlerError{} still yields a clean -32603
with no data field. The `= nullptr` on the member (not `{nullptr}`) matters:
brace-init of nlohmann::json from nullptr is the array [null], not JSON null.
FixtureDispatcher regenerated to HandlerResult; conformance_main.cpp only
inspects dispatch()'s JSON and needed no change. TS side is untouched beyond
the version string -- no server Dispatcher is generated there.
P2 also handled: session.hello.version-mismatch's data.expected was a stale
"1.0.0"; now $any, with a note that the error-fixture compare is on `code`
only so a server echoing kProtocolVersion there is fine.
Version: minor, 1.3.0 -> 1.4.0. Wire is byte-identical (no schema, fixture,
or OpenRPC change) but every Dispatcher implementer must swap Result ->
HandlerResult on regen, and the bump is how lanes are told to. Not an ADR:
one lane consumes this binding, it's the one that asked, and the shape is
the one they proposed. Answered in contracts/proto-answers-daemon-m1.md.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Source: contracts/schema/**
|
||||
// Generator: contracts/codegen/gen_cpp.py
|
||||
// Contract: v1.3.0
|
||||
// Contract: v1.4.0
|
||||
//
|
||||
// Hand-editing this file is a merge blocker. Fix the schema and regenerate:
|
||||
// python3 contracts/codegen/gen_cpp.py
|
||||
@@ -7245,8 +7245,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_capture_getRules(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7257,8 +7256,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_capture_offer(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7269,8 +7267,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_category_list(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7281,8 +7278,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_category_remove(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7293,8 +7289,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_category_upsert(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7305,8 +7300,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_add(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7317,8 +7311,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_addBatch(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7329,8 +7322,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_cancel(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7341,8 +7333,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_get(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7353,8 +7344,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_list(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7365,8 +7355,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_pause(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7377,8 +7366,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_probe(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7389,8 +7377,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_provideAuth(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7401,8 +7388,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_refreshUrl(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7413,8 +7399,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_remove(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7425,8 +7410,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_resume(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7437,8 +7421,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_start(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7449,8 +7432,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_download_update(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7461,8 +7443,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_grabber_harvest(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7473,8 +7454,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_grabber_start(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7485,8 +7465,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_grabber_status(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7497,8 +7476,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_limiter_get(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7509,8 +7487,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_limiter_set(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7521,8 +7498,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_media_addVariant(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7533,8 +7509,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_media_listVariants(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7545,8 +7520,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_queue_list(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7557,8 +7531,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_queue_reorder(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7569,8 +7542,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_queue_start(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7581,8 +7553,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_queue_stop(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7593,8 +7564,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_queue_upsert(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7605,8 +7575,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_rules_list(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7617,8 +7586,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_rules_upsert(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7629,8 +7597,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_schedule_get(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7641,8 +7608,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_schedule_set(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7653,8 +7619,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_session_hello(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7665,8 +7630,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_session_pair(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7677,8 +7641,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_session_subscribe(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7689,8 +7652,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_settings_get(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
@@ -7701,8 +7663,7 @@ nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann
|
||||
nlohmann::json{{"path", p.error().path}});
|
||||
auto r = handler.on_settings_set(*p);
|
||||
if (!r)
|
||||
return make_error(id, ErrorCode::InternalError, r.error().message,
|
||||
nlohmann::json{{"path", r.error().path}});
|
||||
return make_error(id, r.error().code, r.error().message, r.error().data);
|
||||
nlohmann::json out = *r;
|
||||
return make_result(id, std::move(out));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Source: contracts/schema/**
|
||||
// Generator: contracts/codegen/gen_cpp.py
|
||||
// Contract: v1.3.0
|
||||
// Contract: v1.4.0
|
||||
//
|
||||
// Hand-editing this file is a merge blocker. Fix the schema and regenerate:
|
||||
// python3 contracts/codegen/gen_cpp.py
|
||||
@@ -27,7 +27,7 @@
|
||||
// docs/adr/0009-generated-protocol-library.md.
|
||||
namespace velox::proto {
|
||||
|
||||
inline constexpr std::string_view kProtocolVersion = "1.3.0";
|
||||
inline constexpr std::string_view kProtocolVersion = "1.4.0";
|
||||
|
||||
/// Why a payload could not be turned into a typed value. `path` is a JSON Pointer
|
||||
/// into the offending document, so a conformance failure names the exact field.
|
||||
@@ -1785,9 +1785,30 @@ nlohmann::json make_error(const nlohmann::json& id, ErrorCode code, std::string_
|
||||
nlohmann::json make_result(const nlohmann::json& id, nlohmann::json result);
|
||||
nlohmann::json make_notification(Event e, nlohmann::json params);
|
||||
|
||||
/// A handler's own failure — as opposed to ParseError, which is the wire failing
|
||||
/// to become typed params. Carries any contract error code, a message, and a
|
||||
/// free-form `data` object that goes straight into the JSON-RPC error's `data`
|
||||
/// field: `{"taskId": ...}` for TaskNotFound, `{"path": ...}` for InvalidPath,
|
||||
/// `{"httpStatus": ...}` for ProbeFailed. `code` defaults to InternalError so a
|
||||
/// handler that sets only a message still produces a valid error response.
|
||||
///
|
||||
/// -32001/-32002/-32003 are the server layer's to raise around dispatch(), not a
|
||||
/// handler's: they are decided before or without reference to method params.
|
||||
struct HandlerError {
|
||||
ErrorCode code{ErrorCode::InternalError};
|
||||
std::string message;
|
||||
// `= nullptr`, not `{nullptr}`: brace-init of nlohmann::json from nullptr
|
||||
// yields the array [null], not JSON null. make_error() drops a null data.
|
||||
nlohmann::json data = nullptr;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using HandlerResult = std::expected<T, HandlerError>;
|
||||
|
||||
/// One virtual per method. The daemon implements this; `dispatch` below does the
|
||||
/// envelope handling, the transport check and the parameter parsing, so a handler
|
||||
/// only ever sees a validated, typed params struct.
|
||||
/// only ever sees a validated, typed params struct. Return `std::unexpected(
|
||||
/// HandlerError{...})` to answer with a specific error code and data.
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() = default;
|
||||
@@ -1795,61 +1816,61 @@ public:
|
||||
/// The daemon's capture policy, so the extension's shouldCapture decision cannot drift from the
|
||||
/// daemon's. Fetched on connect and whenever event.settings.changed names a capture.* key. If
|
||||
/// this call fails the extension keeps its last known rules and stays fail-open.
|
||||
virtual Result<CaptureRules> on_capture_getRules(const CaptureGetRulesParams& params) = 0;
|
||||
virtual HandlerResult<CaptureRules> on_capture_getRules(const CaptureGetRulesParams& params) = 0;
|
||||
|
||||
/// Firefox offers an intercepted response to the daemon. The daemon MUST reply within 750 ms;
|
||||
/// the extension abandons the offer and lets Firefox download normally on timeout. This
|
||||
/// deadline is the whole reason capture fails open, and it is conformance-tested: a daemon that
|
||||
/// is slow, down, or erroring must never cost the user a download.
|
||||
virtual Result<CaptureOfferResult> on_capture_offer(const CaptureOfferParams& params) = 0;
|
||||
virtual HandlerResult<CaptureOfferResult> on_capture_offer(const CaptureOfferParams& params) = 0;
|
||||
|
||||
/// Every category with its folder and extension list. The extension calls this to populate its
|
||||
/// default-category picker, which is why it is not privileged; it is read-only and exposes only
|
||||
/// paths the user already configured.
|
||||
virtual Result<CategoryListResult> on_category_list(const CategoryListParams& params) = 0;
|
||||
virtual HandlerResult<CategoryListResult> on_category_list(const CategoryListParams& params) = 0;
|
||||
|
||||
/// Delete a user-created category. Built-in categories are refused with -32602. Tasks filed
|
||||
/// under it are reassigned to reassignTo, or to the default category when that is null; no task
|
||||
/// is ever orphaned.
|
||||
virtual Result<CategoryRemoveResult> on_category_remove(const CategoryRemoveParams& params) = 0;
|
||||
virtual HandlerResult<CategoryRemoveResult> on_category_remove(const CategoryRemoveParams& params) = 0;
|
||||
|
||||
/// Create or replace a category. Omit categoryId to create; supply it to replace. Changing
|
||||
/// saveDir does not move existing files — the GUI asks separately and issues download.update
|
||||
/// per task, so a re-point is never a surprise mass file move.
|
||||
virtual Result<CategoryUpsertResult> on_category_upsert(const CategoryUpsertParams& params) = 0;
|
||||
virtual HandlerResult<CategoryUpsertResult> on_category_upsert(const CategoryUpsertParams& params) = 0;
|
||||
|
||||
/// Create one task. saveDir is canonicalized and checked against saveTo.allowedRoots before
|
||||
/// anything is written; a path that escapes them is refused with -32011 and no file is created.
|
||||
virtual Result<DownloadAddResult> on_download_add(const DownloadSpec& params) = 0;
|
||||
virtual HandlerResult<DownloadAddResult> on_download_add(const DownloadSpec& params) = 0;
|
||||
|
||||
/// Create many tasks in one call: the clipboard blob, the wildcard expander, and the
|
||||
/// extension's 'Download all links'. Partial success is normal and is reported per item rather
|
||||
/// than failing the whole batch.
|
||||
virtual Result<DownloadAddBatchResult> on_download_addBatch(const DownloadAddBatchParams& params) = 0;
|
||||
virtual HandlerResult<DownloadAddBatchResult> on_download_addBatch(const DownloadAddBatchParams& params) = 0;
|
||||
|
||||
/// Stop the given tasks and mark them cancelled. The .veloxpart file is kept so the user can
|
||||
/// still resume from the list; download.remove is what deletes bytes.
|
||||
virtual Result<BulkTaskResult> on_download_cancel(const DownloadCancelParams& params) = 0;
|
||||
virtual HandlerResult<BulkTaskResult> on_download_cancel(const DownloadCancelParams& params) = 0;
|
||||
|
||||
/// Full detail for one task, including per-segment state. Backs the progress dialog. Poll it no
|
||||
/// faster than the progress dialog repaints; the table must use events instead.
|
||||
virtual Result<TaskDetail> on_download_get(const DownloadGetParams& params) = 0;
|
||||
virtual HandlerResult<TaskDetail> on_download_get(const DownloadGetParams& params) = 0;
|
||||
|
||||
/// The main table. Filtering, sorting and paging all happen in the daemon so the GUI never
|
||||
/// materializes 100k rows to show 40. Called once on connect; after that the table is
|
||||
/// maintained from events, never re-fetched on a progress tick.
|
||||
virtual Result<DownloadListResult> on_download_list(const DownloadListParams& params) = 0;
|
||||
virtual HandlerResult<DownloadListResult> on_download_list(const DownloadListParams& params) = 0;
|
||||
|
||||
/// Suspend transfers and flush every segment's progress to the .veloxpart.meta file, so a pause
|
||||
/// is indistinguishable from a crash as far as resume is concerned. Never loses bytes already
|
||||
/// written.
|
||||
virtual Result<BulkTaskResult> on_download_pause(const DownloadPauseParams& params) = 0;
|
||||
virtual HandlerResult<BulkTaskResult> on_download_pause(const DownloadPauseParams& params) = 0;
|
||||
|
||||
/// Ask what is at a URL without creating a task. Populates the File Info dialog. Runs a HEAD,
|
||||
/// falling back to a ranged GET when HEAD is refused, which is also how resumability is
|
||||
/// established. Never blocks the RPC loop; the dialog opens immediately and fills in when this
|
||||
/// lands.
|
||||
virtual Result<DownloadProbeResult> on_download_probe(const DownloadProbeParams& params) = 0;
|
||||
virtual HandlerResult<DownloadProbeResult> on_download_probe(const DownloadProbeParams& params) = 0;
|
||||
|
||||
/// Answer an event.auth.required challenge. The task sits in retry_wait until this arrives; on
|
||||
/// success the daemon retries with the credentials attached and the task resumes on its own —
|
||||
@@ -1859,137 +1880,137 @@ public:
|
||||
/// into a log') — this is the other half of that promise. Credentials are handed to the Secret
|
||||
/// Service, never to SQLite and never logged; save only tells the daemon whether to persist
|
||||
/// them there for next time, or use them for this attempt alone.
|
||||
virtual Result<DownloadProvideAuthResult> on_download_provideAuth(const DownloadProvideAuthParams& params) = 0;
|
||||
virtual HandlerResult<DownloadProvideAuthResult> on_download_provideAuth(const DownloadProvideAuthParams& params) = 0;
|
||||
|
||||
/// IDM's 'Refresh Download Address'. Point an existing task at a freshly-issued URL when a
|
||||
/// signed link has expired, keeping every byte already on disk. The daemon re-probes and
|
||||
/// compares size and validator: if they still match, the transfer resumes from where it
|
||||
/// stopped; if they do not, it says so rather than silently restarting.
|
||||
virtual Result<DownloadRefreshUrlResult> on_download_refreshUrl(const DownloadRefreshUrlParams& params) = 0;
|
||||
virtual HandlerResult<DownloadRefreshUrlResult> on_download_refreshUrl(const DownloadRefreshUrlParams& params) = 0;
|
||||
|
||||
/// Drop tasks from the list, optionally deleting the bytes on disk. Privileged: this is the
|
||||
/// only method that destroys user data, and the extension is never allowed to reach it. The
|
||||
/// daemon deletes the .veloxpart and .veloxpart.meta pair, and the finished file only when
|
||||
/// deleteFile is true.
|
||||
virtual Result<DownloadRemoveResult> on_download_remove(const DownloadRemoveParams& params) = 0;
|
||||
virtual HandlerResult<DownloadRemoveResult> on_download_remove(const DownloadRemoveParams& params) = 0;
|
||||
|
||||
/// Continue paused tasks. Resumption is revalidated with If-Range against the stored ETag or
|
||||
/// Last-Modified; a 200 where 206 was expected means the file changed on the server, and the
|
||||
/// task moves to failed with a clear error rather than corrupting the part file.
|
||||
virtual Result<BulkTaskResult> on_download_resume(const DownloadResumeParams& params) = 0;
|
||||
virtual HandlerResult<BulkTaskResult> on_download_resume(const DownloadResumeParams& params) = 0;
|
||||
|
||||
/// Begin or restart the given tasks. A task in 'queued' jumps its queue; a task already
|
||||
/// downloading is a no-op reported as changed false.
|
||||
virtual Result<BulkTaskResult> on_download_start(const DownloadStartParams& params) = 0;
|
||||
virtual HandlerResult<BulkTaskResult> on_download_start(const DownloadStartParams& params) = 0;
|
||||
|
||||
/// Change a task's mutable fields. Moving saveDir or filename moves the file on disk in the
|
||||
/// same operation, which is what makes dragging a row onto a category work as one RPC.
|
||||
/// Privileged: it can name a destination path.
|
||||
virtual Result<TaskSummary> on_download_update(const DownloadUpdateParams& params) = 0;
|
||||
virtual HandlerResult<TaskSummary> on_download_update(const DownloadUpdateParams& params) = 0;
|
||||
|
||||
/// Turn selected crawl results into tasks. This is the only grabber call that creates
|
||||
/// downloads, and it names exactly the files the user ticked — a crawl never starts a download
|
||||
/// on its own.
|
||||
virtual Result<GrabberHarvestResult> on_grabber_harvest(const GrabberHarvestParams& params) = 0;
|
||||
virtual HandlerResult<GrabberHarvestResult> on_grabber_harvest(const GrabberHarvestParams& params) = 0;
|
||||
|
||||
/// Start a depth-limited crawl. Nothing is downloaded by this call: it only walks pages and
|
||||
/// collects candidate links, which the wizard then shows for selection. Privileged because an
|
||||
/// unbounded crawl is a resource commitment the browser must not be able to make on the user's
|
||||
/// behalf.
|
||||
virtual Result<GrabberStartResult> on_grabber_start(const GrabberStartParams& params) = 0;
|
||||
virtual HandlerResult<GrabberStartResult> on_grabber_start(const GrabberStartParams& params) = 0;
|
||||
|
||||
/// Poll one crawl. Also delivered as event.grabber.progress; the poll exists so the wizard can
|
||||
/// be reopened on a job it did not start and still catch up.
|
||||
virtual Result<GrabberStatusResult> on_grabber_status(const GrabberStatusParams& params) = 0;
|
||||
virtual HandlerResult<GrabberStatusResult> on_grabber_status(const GrabberStatusParams& params) = 0;
|
||||
|
||||
/// Current global speed limit. Privileged: changing or reading the limiter belongs to the GUI
|
||||
/// and CLI; the extension shows throughput from event.speed.global instead.
|
||||
virtual Result<Limiter> on_limiter_get(const LimiterGetParams& params) = 0;
|
||||
virtual HandlerResult<Limiter> on_limiter_get(const LimiterGetParams& params) = 0;
|
||||
|
||||
/// Set the global token-bucket limit. With applyToRunning true the change re-tunes transfers
|
||||
/// already in flight instead of taking effect only on the next task — the Speed Limiter
|
||||
/// window's 'apply now' button.
|
||||
virtual Result<Limiter> on_limiter_set(const Limiter& params) = 0;
|
||||
virtual HandlerResult<Limiter> on_limiter_set(const Limiter& params) = 0;
|
||||
|
||||
/// Turn one enumerated variant into a task. The daemon fetches the segments in parallel and
|
||||
/// muxes them with ffmpeg; the result is an ordinary task that appears in the list like any
|
||||
/// other download. Refused with -32602 when the variant is DRM-protected.
|
||||
virtual Result<MediaAddVariantResult> on_media_addVariant(const MediaAddVariantParams& params) = 0;
|
||||
virtual HandlerResult<MediaAddVariantResult> on_media_addVariant(const MediaAddVariantParams& params) = 0;
|
||||
|
||||
/// Parse an HLS or DASH manifest in the daemon and enumerate its renditions. The extension
|
||||
/// never parses a manifest — that logic lives in one language, in one place. Variants with drm
|
||||
/// true are reported so the UI can grey them out; DRM-protected streams are refused, not
|
||||
/// attempted.
|
||||
virtual Result<MediaListVariantsResult> on_media_listVariants(const MediaListVariantsParams& params) = 0;
|
||||
virtual HandlerResult<MediaListVariantsResult> on_media_listVariants(const MediaListVariantsParams& params) = 0;
|
||||
|
||||
/// Every queue with its run state and ordering. Not privileged: the extension's 'Add to Queue'
|
||||
/// picker needs it.
|
||||
virtual Result<QueueListResult> on_queue_list(const QueueListParams& params) = 0;
|
||||
virtual HandlerResult<QueueListResult> on_queue_list(const QueueListParams& params) = 0;
|
||||
|
||||
/// Rewrite a queue's run order. taskIds must be a permutation of the queue's current
|
||||
/// membership; anything else is -32602 rather than a partial reorder, so a stale drag from an
|
||||
/// out-of-date view cannot quietly reshuffle the queue.
|
||||
virtual Result<QueueReorderResult> on_queue_reorder(const QueueReorderParams& params) = 0;
|
||||
virtual HandlerResult<QueueReorderResult> on_queue_reorder(const QueueReorderParams& params) = 0;
|
||||
|
||||
/// Start a queue running. The scheduler then admits up to maxConcurrent tasks from it, in
|
||||
/// order, and keeps that many running until the queue drains or is stopped.
|
||||
virtual Result<QueueStartResult> on_queue_start(const QueueStartParams& params) = 0;
|
||||
virtual HandlerResult<QueueStartResult> on_queue_start(const QueueStartParams& params) = 0;
|
||||
|
||||
/// Stop admitting new tasks from a queue. Tasks already running are paused when pauseRunning is
|
||||
/// true, and otherwise allowed to finish — the difference between 'stop the queue' and 'stop
|
||||
/// everything', which IDM conflates and users trip over.
|
||||
virtual Result<QueueStopResult> on_queue_stop(const QueueStopParams& params) = 0;
|
||||
virtual HandlerResult<QueueStopResult> on_queue_stop(const QueueStopParams& params) = 0;
|
||||
|
||||
/// Create or replace a queue, including its schedule and concurrency cap. Omit queueId to
|
||||
/// create. taskIds in the payload is ignored — membership changes through download.update and
|
||||
/// queue.reorder so that two clients editing at once cannot silently drop a task.
|
||||
virtual Result<QueueUpsertResult> on_queue_upsert(const QueueUpsertParams& params) = 0;
|
||||
virtual HandlerResult<QueueUpsertResult> on_queue_upsert(const QueueUpsertParams& params) = 0;
|
||||
|
||||
/// The rules engine's table, in priority order. Privileged: these are the daemon's routing
|
||||
/// policy. The extension gets its own narrowed view through capture.getRules instead.
|
||||
virtual Result<RulesListResult> on_rules_list(const RulesListParams& params) = 0;
|
||||
virtual HandlerResult<RulesListResult> on_rules_list(const RulesListParams& params) = 0;
|
||||
|
||||
/// Create, replace, or delete rules in one atomic write. 'upsert' carries the rules to store
|
||||
/// and 'remove' the ruleIds to drop; applying both at once means a reprioritisation never
|
||||
/// leaves the table in a half-valid state.
|
||||
virtual Result<RulesUpsertResult> on_rules_upsert(const RulesUpsertParams& params) = 0;
|
||||
virtual HandlerResult<RulesUpsertResult> on_rules_upsert(const RulesUpsertParams& params) = 0;
|
||||
|
||||
/// The schedule for one queue, or every schedule when queueId is null. Backs the Scheduler
|
||||
/// window.
|
||||
virtual Result<ScheduleGetResult> on_schedule_get(const ScheduleGetParams& params) = 0;
|
||||
virtual HandlerResult<ScheduleGetResult> on_schedule_get(const ScheduleGetParams& params) = 0;
|
||||
|
||||
/// Set or clear a queue's schedule. A null schedule clears it and leaves the queue under manual
|
||||
/// control. Times are local wall-clock and are re-evaluated on a DST change rather than being
|
||||
/// resolved to absolute instants at set time.
|
||||
virtual Result<ScheduleSetResult> on_schedule_set(const ScheduleSetParams& params) = 0;
|
||||
virtual HandlerResult<ScheduleSetResult> on_schedule_set(const ScheduleSetParams& params) = 0;
|
||||
|
||||
/// First call on every connection, on every transport. The daemon compares protocolVersion
|
||||
/// majors and refuses a mismatch with -32001 so a stale GUI or extension fails loudly on
|
||||
/// connect instead of subtly at the tenth field. On the WebSocket transport a valid token is
|
||||
/// required unless the client is about to call session.pair.
|
||||
virtual Result<SessionHelloResult> on_session_hello(const SessionHelloParams& params) = 0;
|
||||
virtual HandlerResult<SessionHelloResult> on_session_hello(const SessionHelloParams& params) = 0;
|
||||
|
||||
/// WebSocket transport only. Triggers a GUI or desktop-notification prompt showing a four-digit
|
||||
/// code; the user must approve before a token is issued. Failed attempts are rate-limited to
|
||||
/// 5/min followed by a 60 s lockout (-32014) so a token cannot be brute-forced by another local
|
||||
/// process. The daemon stores only a hash of the token.
|
||||
virtual Result<SessionPairResult> on_session_pair(const SessionPairParams& params) = 0;
|
||||
virtual HandlerResult<SessionPairResult> on_session_pair(const SessionPairParams& params) = 0;
|
||||
|
||||
/// Choose which notifications this connection receives. Subscribing replaces the previous
|
||||
/// selection rather than adding to it, so a client can narrow its firehose without
|
||||
/// reconnecting. Nothing is delivered until this is called.
|
||||
virtual Result<SessionSubscribeResult> on_session_subscribe(const SessionSubscribeParams& params) = 0;
|
||||
virtual HandlerResult<SessionSubscribeResult> on_session_subscribe(const SessionSubscribeParams& params) = 0;
|
||||
|
||||
/// Read settings. keys null means everything. Privileged: the settings bag names local
|
||||
/// filesystem paths and the allowed write roots, which the extension has no business
|
||||
/// enumerating — it gets capture.getRules instead.
|
||||
virtual Result<SettingsGetResult> on_settings_get(const SettingsGetParams& params) = 0;
|
||||
virtual HandlerResult<SettingsGetResult> on_settings_get(const SettingsGetParams& params) = 0;
|
||||
|
||||
/// Write settings. Only the keys present in values change. Rejected with -32602 if a key is
|
||||
/// unknown or a value fails the Settings schema, and with -32011 if a directory key names a
|
||||
/// path that cannot be written. Emits event.settings.changed with exactly the keys that took
|
||||
/// effect.
|
||||
virtual Result<SettingsSetResult> on_settings_set(const SettingsSetParams& params) = 0;
|
||||
virtual HandlerResult<SettingsSetResult> on_settings_set(const SettingsSetParams& params) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user