Message contract¶
Messages between the content gate, player, picker, studio and service worker are defined in packages/messaging/src/index.ts. Every typed message has a Zod request schema and a response schema, keyed by message type. The worker parses each inbound message with parseMessage and replies { ok: true, data } or { ok: false, error }.
Typed messages¶
| Type | Request | Response | Notes |
|---|---|---|---|
manifest.get |
origin |
Manifest entries | Empty when no app owns the origin |
tour.get |
id, version |
Content item | Reads a published version |
progress.get |
contentId |
Progress or null | |
progress.set |
Progress | none | |
app.context |
origin |
App key, environment, theme override, help bubble, CSS variables, maxAnnouncements, or null |
|
player.inject |
none | none | Honoured only from frame 0 of a tab |
asset.get |
Asset reference | mime, base64 |
|
events.push |
Up to 100 guidance events | none | The handler discards them |
frame.register |
url |
none | Records a frame of the tab |
locator.resolve |
Optional frameId, locator |
found, rect, strategy |
Cross-frame resolution through the frame agent |
picker.start |
tabId, mode |
none | Modes: element, form, fields, condition-field, record |
picker.stop |
Optional tabId |
none | |
picker.picked |
Locator, preview, optional action (click, input, select), optional orphanFrame |
none | Sent from the picker to the studio |
preview.play |
tabId, item, draft resources, optional fromStep |
none | |
preview.stop |
tabId |
none | |
auth.status |
none | signedIn, roles |
Fixed values in the local MVP |
Internal messages¶
These travel between the worker and injected scripts and are not in the typed contract. Each uses a guidepost. prefix and is checked by the receiver (the frame agent, for example, verifies sender.id equals its own extension id).
| Type | Between |
|---|---|
guidepost.picker.start, guidepost.picker.stop |
Worker to picker |
guidepost.picker.hop |
Picker frames |
guidepost.studio.picked, guidepost.studio.picker-stopped |
Worker to studio |
guidepost.preview.start, guidepost.preview.stop |
Worker to player |
guidepost.frame.resolve |
Worker to frame agent |
Sender checks¶
isTrustedSender(sender, extensionId, grantedOrigins) returns true only when sender.id equals the extension's id and one of these holds:
- the sender has no
origin(extension pages such as the side panel and options page); - the origin starts with
chrome-extension://; - the origin matches a granted origin through
originMatches.
originMatches follows Chrome match patterns: the port is ignored, a leading *. matches subdomains, and the scheme may be http, https or *. Any other pattern shape fails.
Gotchas¶
- A new message type needs an entry in
messageContractsand a handler. The handler table is typed withRecord<MessageType, Handler>, so a missing handler is a compile error. - Responses are validated by
parseResponseonly where callers use it; treat the schema as the contract either way.