This document defines the formal contract between:
It exists to prevent the most common failure mode in multi-store extension systems:
each new store “mostly works”, but data shape, page detection, action semantics, and UI assumptions quietly drift apart.
In plain language: this is the rulebook that keeps every store speaking the same language.
This contract does not define:
packages/contracts must not import browser APIspackages/store-* must not import packages/uiexport type StoreId =
| 'albertsons'
| 'kroger'
| 'amazon'
| 'costco'
| 'walmart'
| 'weee'
| 'target'
| 'temu';
export type VerifiedScope =
| 'safeway'
| 'fred-meyer'
| 'qfc'
| 'amazon'
| 'costco'
| 'walmart'
| 'weee'
| 'target'
| 'temu';
export type PageKind =
| 'product'
| 'search'
| 'deal'
| 'cart'
| 'manage'
| 'account'
| 'unsupported'
| 'unknown';
export type CapabilityId =
| 'extract_product'
| 'extract_search'
| 'extract_deals'
| 'run_action'
| 'export_data';
export type CapabilityStatus =
| 'ready'
| 'unsupported_site'
| 'unsupported_page'
| 'permission_needed'
| 'not_implemented'
| 'degraded'
| 'blocked';
export type ActionKind =
| 'schedule_save_subscribe'
| 'schedule_save_cancel'
| 'capture_product'
| 'capture_search_results'
| 'capture_deals'
| 'filter_non_local_warehouse';
export interface CapabilityState {
capability: CapabilityId;
status: CapabilityStatus;
reasonCode?: ErrorCode;
reasonMessage?: string;
}
export interface DetectionResult {
storeId: StoreId;
verifiedScopes: VerifiedScope[];
matchedHost: string;
pageKind: PageKind;
confidence: number;
capabilityStates: CapabilityState[];
}
export interface MoneyValue {
currency: string;
amount: number;
displayText: string;
}
export interface NormalizedProduct {
sourceStoreId: StoreId;
sourceUrl: string;
title: string;
imageUrl?: string;
price?: MoneyValue;
availabilityLabel?: string;
sku?: string;
}
export interface SearchResultItem {
sourceStoreId: StoreId;
sourceUrl: string;
title: string;
imageUrl?: string;
price?: MoneyValue;
position: number;
}
export interface DealItem {
sourceStoreId: StoreId;
sourceUrl: string;
title: string;
dealLabel: string;
price?: MoneyValue;
}
export type ActionInput =
| {
actionKind: 'schedule_save_subscribe';
dryRun?: boolean;
limit?: number;
}
| {
actionKind: 'schedule_save_cancel';
dryRun?: boolean;
limit?: number;
}
| {
actionKind: 'filter_non_local_warehouse';
dryRun?: boolean;
};
export interface ActionReceipt {
actionKind: ActionKind;
status: 'success' | 'partial' | 'failed';
attempted: number;
succeeded: number;
failed: number;
skipped: number;
errors: Array<{
code: ErrorCode;
message: string;
itemRef?: string;
}>;
}
export type ErrorCode =
| 'UNSUPPORTED_SITE'
| 'UNSUPPORTED_PAGE'
| 'PERMISSION_REQUIRED'
| 'SELECTOR_MISSING'
| 'PARSE_FAILED'
| 'ACTION_PRECONDITION_FAILED'
| 'ACTION_STEP_FAILED'
| 'ACTION_PARTIAL'
| 'RATE_LIMITED'
| 'USER_ABORTED'
| 'STORAGE_UNAVAILABLE'
| 'INTERNAL_ERROR'
| 'NOT_IMPLEMENTED';
export interface StoreAdapter {
storeId: StoreId;
verifiedScopes: VerifiedScope[];
matches(url: URL): boolean;
detect(url: URL, document: Document): DetectionResult;
extractProduct?: (document: Document) => Promise<NormalizedProduct>;
extractSearchResults?: (document: Document) => Promise<SearchResultItem[]>;
extractDeals?: (document: Document) => Promise<DealItem[]>;
runAction?: (
document: Document,
input: ActionInput
) => Promise<ActionReceipt>;
}
matches(url: URL): booleanPurpose:
Rules:
detect(url: URL, document: Document): DetectionResultPurpose:
Rules:
DetectionResultverifiedScopesextractProductOnly present when the adapter genuinely supports product extraction.
Rules:
NormalizedProductextractSearchResultsOnly present when search extraction is real.
Rules:
SearchResultItem[]positionextractDealsOnly present when deal extraction is real.
Rules:
DealItem[]runActionOnly present for action-capable stores or pages.
Rules:
ActionReceiptstatus: 'partial'attempted / succeeded / failed / skippedThe adapter is the source of truth for capability rendering.
UI must only render based on capabilityStates.
Example:
capability: 'extract_deals'status: 'unsupported_page'This prevents the classic failure mode where the UI pretends every store has the same buttons.
| Error Code | Meaning | Typical Use |
|---|---|---|
UNSUPPORTED_SITE |
URL is outside owned scope | runtime or adapter guard |
UNSUPPORTED_PAGE |
store is known, page is not supported | capability downgrade |
PERMISSION_REQUIRED |
host or browser permission missing | capability downgrade or runtime guard |
SELECTOR_MISSING |
expected DOM node not found | parser/action failure |
PARSE_FAILED |
DOM found but data contract invalid | extractor failure |
ACTION_PRECONDITION_FAILED |
action cannot safely start | action guard |
ACTION_STEP_FAILED |
a concrete action step failed | action execution |
ACTION_PARTIAL |
action continued with mixed result | receipt reporting |
RATE_LIMITED |
store throttling or retry window hit | action or request flow |
USER_ABORTED |
user stopped the run | action receipt |
STORAGE_UNAVAILABLE |
runtime storage issue | runtime integration |
INTERNAL_ERROR |
unexpected internal failure | last-resort code |
NOT_IMPLEMENTED |
capability reserved but not yet implemented | explicit non-availability only |
verifiedScopes are not decoration. They are the boundary between engineering grouping and public honesty.
Examples:
store-albertsons may have verifiedScopes: ['safeway']store-kroger may have verifiedScopes: ['fred-meyer', 'qfc']Implications:
verifiedScopesAllowed:
packages/store-* may import:
packages/contractspackages/core if those helpers are store-agnosticForbidden:
packages/store-* importing packages/uipackages/contracts importing runtime or Chrome APIsmatches or detectAn adapter is not considered valid until all of the following are true:
matches is cheap and deterministicdetect returns explicit capability statesrunAction returns a full receipt when supportedverifiedScopes truthfully bound public claim scope