Timestamp app-server notifications at emission (#32905)

## What changed

- Add an optional `emittedAtMs` field to server notification envelopes and expose the envelope in the generated JSON and TypeScript schemas.
- Populate the Unix timestamp when app-server emits a notification, before transport routing, so every connection in a fan-out receives the same value.
- Keep the field optional for clients decoding notifications from older app-server versions.

## Testing

- Verify notification serialization includes `emittedAtMs`.
- Verify targeted fan-out reuses one timestamp across connections.

GitOrigin-RevId: b379cb11e8e91c558bcd3d46ce459311a6e81e54
This commit is contained in:
Owen Lin authored and copyberry committed 2026-07-14 00:38:04 +00:00
1 parent 7f3eb8a223
commit c39520f3d1
22 files changed
+400 -171

No files matched your search

@@ -1,6 +1,7 @@
use crate::ClientNotification;
use crate::ClientRequest;
use crate::ServerNotification;
use crate::ServerNotificationEnvelope;
use crate::ServerRequest;
use crate::experimental_api::experimental_fields;
use crate::export_client_notification_schemas;
@@ -128,6 +129,7 @@ pub fn generate_ts_with_options(
ServerRequest::export_all_to(out_dir)?;
export_server_responses(out_dir)?;
ServerNotification::export_all_to(out_dir)?;
ServerNotificationEnvelope::export_all_to(out_dir)?;
if !options.experimental_api {
filter_experimental_ts(out_dir)?;
@@ -1339,6 +1341,7 @@ where
strip_v1_client_request_variants_from_json_schema(&mut schema_value);
} else if file_stem == "ServerNotification" {
strip_v1_server_notification_variants_from_json_schema(&mut schema_value);
add_server_notification_emitted_at_to_json_schema(&mut schema_value)?;
}
enforce_numbered_definition_collision_overrides(file_stem, &mut schema_value);
annotate_schema(&mut schema_value, Some(file_stem));
@@ -1371,6 +1374,25 @@ where
})
}
fn add_server_notification_emitted_at_to_json_schema(schema: &mut Value) -> Result<()> {
let schema = schema
.as_object_mut()
.ok_or_else(|| anyhow!("expected ServerNotification schema to be an object"))?;
schema.insert(
"properties".to_string(),
serde_json::json!({
"emittedAtMs": {
"description": "Unix timestamp (in milliseconds) when app-server emitted this notification.",
"format": "int64",
"type": "integer"
}
}),
);
// Keep this optional in generated client schemas for compatibility with
// older app-server versions. New servers still always emit it.
Ok(())
}
fn enforce_numbered_definition_collision_overrides(schema_name: &str, schema: &mut Value) {
for defs_key in ["definitions", "$defs"] {
let Some(defs) = schema.get(defs_key).and_then(Value::as_object) else {