feat(workout): include host result contract in mobile kit
Record share/haptics and the denied/timeout/unavailable/error host result kinds in Android and iOS release metadata, and make mobile verification require them. req: examples/006 req: host/002
This commit is contained in:
@@ -55,6 +55,9 @@ The generated manifest records:
|
|||||||
- cache policy: release-scoped HTML/CSS/runtime assets only;
|
- cache policy: release-scoped HTML/CSS/runtime assets only;
|
||||||
- offline truth policy: app-owned command/event/projection records, never DOM
|
- offline truth policy: app-owned command/event/projection records, never DOM
|
||||||
patches or UI effect payloads;
|
patches or UI effect payloads;
|
||||||
|
- host capability policy: Android and iOS shell metadata declare share/haptics and
|
||||||
|
the same denied, timeout, unavailable, and error result kinds handled by app
|
||||||
|
code before UI effects;
|
||||||
- environment/secrets boundary: public shell config in the kit, signing secrets
|
- environment/secrets boundary: public shell config in the kit, signing secrets
|
||||||
outside the repo;
|
outside the repo;
|
||||||
- rollback: redeploy the previous server binary and rebuild store artifacts from
|
- rollback: redeploy the previous server binary and rebuild store artifacts from
|
||||||
@@ -76,4 +79,5 @@ reviewed without guessing what is still external. req: examples/006
|
|||||||
This is not a `hemx-mobile` framework, sync layer, client database, or native UI
|
This is not a `hemx-mobile` framework, sync layer, client database, or native UI
|
||||||
runtime. The mobile shells load the production Workout web app and route host
|
runtime. The mobile shells load the production Workout web app and route host
|
||||||
capabilities such as share/haptics through the typed host boundary before UI
|
capabilities such as share/haptics through the typed host boundary before UI
|
||||||
effects are produced. req: host/002 req: local/002
|
effects are produced; denied, timeout, unavailable, and error cases share the
|
||||||
|
same host result shape. req: host/002 req: local/002
|
||||||
|
|||||||
+24
-3
@@ -291,6 +291,10 @@ fn verify_workout_mobile_release(
|
|||||||
"rollback",
|
"rollback",
|
||||||
"android/twa-release.json",
|
"android/twa-release.json",
|
||||||
"ios/webview-release.json",
|
"ios/webview-release.json",
|
||||||
|
"denied",
|
||||||
|
"timeout",
|
||||||
|
"unavailable",
|
||||||
|
"error",
|
||||||
],
|
],
|
||||||
&mut failures,
|
&mut failures,
|
||||||
);
|
);
|
||||||
@@ -301,6 +305,12 @@ fn verify_workout_mobile_release(
|
|||||||
&config.app_name,
|
&config.app_name,
|
||||||
origin_host(&config.origin),
|
origin_host(&config.origin),
|
||||||
"external Android keystore",
|
"external Android keystore",
|
||||||
|
"share",
|
||||||
|
"haptics",
|
||||||
|
"denied",
|
||||||
|
"timeout",
|
||||||
|
"unavailable",
|
||||||
|
"error",
|
||||||
],
|
],
|
||||||
&mut failures,
|
&mut failures,
|
||||||
);
|
);
|
||||||
@@ -312,6 +322,10 @@ fn verify_workout_mobile_release(
|
|||||||
"share",
|
"share",
|
||||||
"haptics",
|
"haptics",
|
||||||
"external Apple team",
|
"external Apple team",
|
||||||
|
"denied",
|
||||||
|
"timeout",
|
||||||
|
"unavailable",
|
||||||
|
"error",
|
||||||
],
|
],
|
||||||
&mut failures,
|
&mut failures,
|
||||||
);
|
);
|
||||||
@@ -331,7 +345,7 @@ fn check_file_contains(path: &Path, needles: &[&str], failures: &mut Vec<String>
|
|||||||
|
|
||||||
fn workout_mobile_manifest(config: &WorkoutMobileConfig, blockers: &[String]) -> String {
|
fn workout_mobile_manifest(config: &WorkoutMobileConfig, blockers: &[String]) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{{\n \"app_id\": \"{}\",\n \"name\": \"{}\",\n \"version\": \"{}\",\n \"origin\": \"{}\",\n \"server_binary\": \"target/release/hemx-workout-example\",\n \"runtime_asset_path\": \"served by hemx_axum::runtime_js_path() from the same release\",\n \"cache_policy\": \"cache only release-scoped HTML/CSS/runtime assets; never store DOM patches or UI effects as truth\",\n \"state_policy\": \"app-owned command/event/projection records\",\n \"environment_boundary\": \"public mobile shell config lives here; secrets and signing credentials stay outside the repo\",\n \"rollback\": \"redeploy the previous server binary and matching mobile shell metadata; rebuild store artifacts with the previous version/signing inputs\",\n \"android\": \"android/twa-release.json\",\n \"ios\": \"ios/webview-release.json\",\n \"external_blockers\": [{}]\n}}\n",
|
"{{\n \"app_id\": \"{}\",\n \"name\": \"{}\",\n \"version\": \"{}\",\n \"origin\": \"{}\",\n \"server_binary\": \"target/release/hemx-workout-example\",\n \"runtime_asset_path\": \"served by hemx_axum::runtime_js_path() from the same release\",\n \"cache_policy\": \"cache only release-scoped HTML/CSS/runtime assets; never store DOM patches or UI effects as truth\",\n \"state_policy\": \"app-owned command/event/projection records\",\n \"host_result_kinds\": [\"denied\", \"timeout\", \"unavailable\", \"error\"],\n \"environment_boundary\": \"public mobile shell config lives here; secrets and signing credentials stay outside the repo\",\n \"rollback\": \"redeploy the previous server binary and matching mobile shell metadata; rebuild store artifacts with the previous version/signing inputs\",\n \"android\": \"android/twa-release.json\",\n \"ios\": \"ios/webview-release.json\",\n \"external_blockers\": [{}]\n}}\n",
|
||||||
json_escape(&config.app_id),
|
json_escape(&config.app_id),
|
||||||
json_escape(&config.app_name),
|
json_escape(&config.app_name),
|
||||||
json_escape(&config.version),
|
json_escape(&config.version),
|
||||||
@@ -342,7 +356,7 @@ fn workout_mobile_manifest(config: &WorkoutMobileConfig, blockers: &[String]) ->
|
|||||||
|
|
||||||
fn android_twa_release_json(config: &WorkoutMobileConfig) -> String {
|
fn android_twa_release_json(config: &WorkoutMobileConfig) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{{\n \"package\": \"{}\",\n \"name\": \"{}\",\n \"start_url\": \"{}/\",\n \"host\": \"{}\",\n \"version\": \"{}\",\n \"signing\": \"external Android keystore; never commit credentials\"\n}}\n",
|
"{{\n \"package\": \"{}\",\n \"name\": \"{}\",\n \"start_url\": \"{}/\",\n \"host\": \"{}\",\n \"version\": \"{}\",\n \"host_capabilities\": [\"share\", \"haptics\"],\n \"host_result_kinds\": [\"denied\", \"timeout\", \"unavailable\", \"error\"],\n \"signing\": \"external Android keystore; never commit credentials\"\n}}\n",
|
||||||
json_escape(&config.android_package),
|
json_escape(&config.android_package),
|
||||||
json_escape(&config.app_name),
|
json_escape(&config.app_name),
|
||||||
json_escape(config.origin.trim_end_matches('/')),
|
json_escape(config.origin.trim_end_matches('/')),
|
||||||
@@ -353,7 +367,7 @@ fn android_twa_release_json(config: &WorkoutMobileConfig) -> String {
|
|||||||
|
|
||||||
fn ios_webview_release_json(config: &WorkoutMobileConfig) -> String {
|
fn ios_webview_release_json(config: &WorkoutMobileConfig) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{{\n \"bundle_id\": \"{}\",\n \"name\": \"{}\",\n \"start_url\": \"{}/\",\n \"version\": \"{}\",\n \"host_capabilities\": [\"share\", \"haptics\"],\n \"signing\": \"external Apple team/provisioning profile; never commit credentials\"\n}}\n",
|
"{{\n \"bundle_id\": \"{}\",\n \"name\": \"{}\",\n \"start_url\": \"{}/\",\n \"version\": \"{}\",\n \"host_capabilities\": [\"share\", \"haptics\"],\n \"host_result_kinds\": [\"denied\", \"timeout\", \"unavailable\", \"error\"],\n \"signing\": \"external Apple team/provisioning profile; never commit credentials\"\n}}\n",
|
||||||
json_escape(&config.ios_bundle_id),
|
json_escape(&config.ios_bundle_id),
|
||||||
json_escape(&config.app_name),
|
json_escape(&config.app_name),
|
||||||
json_escape(config.origin.trim_end_matches('/')),
|
json_escape(config.origin.trim_end_matches('/')),
|
||||||
@@ -750,6 +764,9 @@ mod tests {
|
|||||||
assert!(manifest.contains("target/release/hemx-workout-example"));
|
assert!(manifest.contains("target/release/hemx-workout-example"));
|
||||||
assert!(manifest.contains("hemx_axum::runtime_js_path()"));
|
assert!(manifest.contains("hemx_axum::runtime_js_path()"));
|
||||||
assert!(manifest.contains("app-owned command/event/projection records"));
|
assert!(manifest.contains("app-owned command/event/projection records"));
|
||||||
|
assert!(manifest.contains(
|
||||||
|
"\"host_result_kinds\": [\"denied\", \"timeout\", \"unavailable\", \"error\"]"
|
||||||
|
));
|
||||||
assert!(manifest.contains("secrets and signing credentials stay outside the repo"));
|
assert!(manifest.contains("secrets and signing credentials stay outside the repo"));
|
||||||
assert!(manifest.contains("Android signing key not configured"));
|
assert!(manifest.contains("Android signing key not configured"));
|
||||||
}
|
}
|
||||||
@@ -763,6 +780,10 @@ mod tests {
|
|||||||
assert_eq!(origin_host(&config.origin), "workout.example.com");
|
assert_eq!(origin_host(&config.origin), "workout.example.com");
|
||||||
assert!(android.contains("\"start_url\": \"https://workout.example.com/app/\""));
|
assert!(android.contains("\"start_url\": \"https://workout.example.com/app/\""));
|
||||||
assert!(android.contains("\"host\": \"workout.example.com\""));
|
assert!(android.contains("\"host\": \"workout.example.com\""));
|
||||||
|
assert!(android.contains("\"host_capabilities\": [\"share\", \"haptics\"]"));
|
||||||
|
assert!(android.contains(
|
||||||
|
"\"host_result_kinds\": [\"denied\", \"timeout\", \"unavailable\", \"error\"]"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user