{ "$schema": "../../schemas/config/frontend-capability-recipes.schema.json", "schemaVersion": 1, "decisionId": "VD-10", "defaultStatus": "NOT_INSTALLED", "productionRuntimeDependencies": [], "catalogOwner": "frontend-platform", "reviewOn": "project-capability-selection", "vendorPackagePatterns": [ "@launchdarkly/*", "@sentry/*", "@opentelemetry/*", "@openapitools/openapi-generator-cli", "@reduxjs/toolkit", "@tanstack/react-virtual", "@uppy/*", "firebase", "idb", "react-window", "redux", "socket.io-client", "tus-js-client", "workbox-window", "xstate", "zustand" ], "recipes": [ { "id": "realtime", "status": "RECIPE_AVAILABLE", "trigger": "The backend exposes ordered push events with a documented resume and authorization protocol.", "forbiddenWhen": ["Polling satisfies the measured freshness requirement.", "Event ordering and reconnect ownership are undefined."], "boundary": "outbound connection plus inbound validated event adapter", "port": "RealtimePort", "fake": "FakeRealtimeAdapter", "failureKinds": ["disconnect", "duplicate", "out-of-order", "auth-expiry"], "lifecycleMethods": ["unsubscribe"], "owner": "project-owner-required", "securityPrivacy": ["Validate every event envelope.", "Never place credentials in URLs or telemetry.", "Refresh authorization through the session boundary."], "bundleBudgetGzipBytes": 12000, "fallback": "Bounded polling or explicitly stale UI.", "removal": ["Remove composition registration.", "Remove adapter and vendor dependency.", "Run recipe-removal and production-bundle gates."], "serverStatePolicy": "query-cache-owned" }, { "id": "offline-indexeddb", "status": "RECIPE_AVAILABLE", "trigger": "A product requirement needs durable offline data or a durable command queue beyond small public preferences.", "forbiddenWhen": ["The data contains credentials.", "The browser would connect directly to a database or object store.", "A normal HTTP cache is sufficient."], "boundary": "application-owned versioned repository output port", "port": "VersionedOfflineRepository", "fake": "MemoryOfflineRepository", "failureKinds": ["quota", "corruption", "migration-rollback"], "lifecycleMethods": ["close"], "owner": "project-owner-required", "securityPrivacy": ["Classify persisted fields.", "Encrypting in the same client is not a credential protection boundary.", "Version and test every migration."], "bundleBudgetGzipBytes": 8000, "fallback": "Online-only query path with an explicit offline state.", "removal": ["Stop writes.", "Migrate or purge owned stores.", "Remove repository composition and dependency."], "serverStatePolicy": "reference-or-command-only" }, { "id": "service-worker-pwa", "status": "RECIPE_AVAILABLE", "trigger": "Installability or a measured offline-shell requirement is approved with cache ownership.", "forbiddenWhen": ["Hosting cache and worker cache ownership conflict.", "Update and rollback UX is undefined."], "boundary": "bootstrap update controller and cache policy adapter", "port": "ServiceWorkerUpdatePort", "fake": "FakeServiceWorkerUpdateAdapter", "failureKinds": ["stale-worker", "update-loop", "offline-fallback"], "lifecycleMethods": ["unregister", "rollback"], "owner": "project-owner-required", "securityPrivacy": ["Never cache authenticated API responses by default.", "Bind cache names to release identity.", "Fail closed on malformed update metadata."], "bundleBudgetGzipBytes": 10000, "fallback": "Normal network application with hosting cache headers.", "removal": ["Deploy an unregister migration.", "Delete owned caches.", "Remove worker registration and manifest."], "serverStatePolicy": "network-cache-policy-only" }, { "id": "file-transfer", "status": "RECIPE_AVAILABLE", "trigger": "The product accepts or delivers files with progress and cancellation requirements.", "forbiddenWhen": ["Allowed size and MIME policy is missing.", "Long-lived credentials would be embedded in URLs."], "boundary": "application file transfer output port behind an authorized backend protocol", "port": "FileTransferPort", "fake": "FakeFileTransferAdapter", "failureKinds": ["size-rejection", "type-rejection", "abort", "expired-url"], "lifecycleMethods": ["cancel-via-AbortSignal"], "owner": "project-owner-required", "securityPrivacy": ["Treat MIME as untrusted metadata.", "Use short-lived opaque resource identifiers.", "Redact file names when classified as personal data."], "bundleBudgetGzipBytes": 6000, "fallback": "Standard request with bounded size and no background continuation.", "removal": ["Cancel active transfers.", "Remove route actions and composition.", "Remove transfer dependency."], "serverStatePolicy": "query-cache-metadata-only" }, { "id": "generated-api", "status": "RECIPE_AVAILABLE", "trigger": "A versioned backend contract justifies generated transport code.", "forbiddenWhen": ["Generated DTOs would escape into domain or presentation.", "Contract drift cannot block CI."], "boundary": "generated client wrapped by a feature gateway facade and mapper", "port": "GeneratedApiFacade", "fake": "FakeGeneratedApiAdapter", "failureKinds": ["contract-drift", "unsupported-field"], "lifecycleMethods": ["cancel-via-AbortSignal"], "owner": "project-owner-required", "securityPrivacy": ["Generate from an authenticated source.", "Review generator execution and output.", "Do not log request bodies."], "bundleBudgetGzipBytes": 16000, "fallback": "Existing typed request builder and runtime response schema.", "removal": ["Restore handwritten gateway.", "Remove generated output and generator.", "Verify DTOs do not remain in public types."], "serverStatePolicy": "query-cache-owned" }, { "id": "feature-flag", "status": "RECIPE_AVAILABLE", "trigger": "A staged rollout or kill switch has a named owner, default and stale policy.", "forbiddenWhen": ["A flag is used as authorization.", "Unknown and unavailable behavior is undefined."], "boundary": "application feature policy output port", "port": "FeatureFlagPort", "fake": "FakeFeatureFlagAdapter", "failureKinds": ["provider-unavailable", "unknown-flag", "stale-value"], "lifecycleMethods": ["dispose-provider-if-installed"], "owner": "project-owner-required", "securityPrivacy": ["Flags are hints, never access control.", "Minimize targeting attributes.", "Apply consent rules to personal attributes."], "bundleBudgetGzipBytes": 10000, "fallback": "Typed local default with an explicit stale decision.", "removal": ["Resolve the rollout permanently.", "Delete flag key and branches.", "Remove provider composition and dependency."], "serverStatePolicy": "policy-cache-only" }, { "id": "web-worker", "status": "RECIPE_AVAILABLE", "trigger": "Profiling shows CPU work blocking the main thread beyond the performance budget.", "forbiddenWhen": ["The task is primarily network I/O.", "Cancellation and stale-result ownership are undefined."], "boundary": "request/result/cancel output port with a validated message adapter", "port": "WorkerTaskPort", "fake": "FakeWorkerTaskAdapter", "failureKinds": ["crash", "stale-result", "transfer-failure"], "lifecycleMethods": ["cancel", "dispose"], "owner": "project-owner-required", "securityPrivacy": ["Validate worker messages.", "Do not send credentials.", "Bound transferred data and worker count."], "bundleBudgetGzipBytes": 14000, "fallback": "Chunked or deferred main-thread execution within a measured limit.", "removal": ["Stop and dispose workers.", "Restore synchronous facade implementation.", "Remove worker entry and chunk."], "serverStatePolicy": "no-server-state" }, { "id": "multi-tab", "status": "RECIPE_AVAILABLE", "trigger": "A documented workflow must synchronize non-sensitive events across tabs.", "forbiddenWhen": ["The server is the correct conflict authority.", "Event version and source identity are undefined."], "boundary": "versioned browser event output/input adapter", "port": "MultiTabPort", "fake": "FakeMultiTabAdapter", "failureKinds": ["self-echo", "duplicate", "conflict"], "lifecycleMethods": ["unsubscribe", "close"], "owner": "project-owner-required", "securityPrivacy": ["Broadcast no credentials or personal payload.", "Validate versions.", "Treat events as hints rather than authorization."], "bundleBudgetGzipBytes": 4000, "fallback": "Refresh from the authoritative server on focus.", "removal": ["Close channels.", "Remove event registry entries.", "Restore focus-based refresh."], "serverStatePolicy": "invalidation-only" }, { "id": "browser-permission", "status": "RECIPE_AVAILABLE", "trigger": "A user-initiated flow requires clipboard, notification or media access.", "forbiddenWhen": ["Permission would be requested at boot.", "Denied, dismissed and unsupported UX are not designed."], "boundary": "presentation input action through a browser capability output port", "port": "BrowserPermissionPort", "fake": "FakeBrowserPermissionAdapter", "failureKinds": ["denied", "dismissed", "unsupported"], "lifecycleMethods": ["stop-media-tracks-if-opened"], "owner": "project-owner-required", "securityPrivacy": ["Require an explicit user gesture.", "Minimize requested scope.", "Do not persist permission as authorization."], "bundleBudgetGzipBytes": 3000, "fallback": "Manual input or copy/download instruction.", "removal": ["Stop acquired resources.", "Remove permission action and adapter.", "Retest denied-path accessibility."], "serverStatePolicy": "no-server-state" }, { "id": "client-workflow", "status": "RECIPE_AVAILABLE", "trigger": "A measured cross-page client-only workflow cannot be represented by URL, local state, context or query cache.", "forbiddenWhen": ["The store would duplicate server response collections.", "A library is selected before state ownership is documented.", "Zustand and Redux Toolkit would both be installed."], "boundary": "workflow-specific local facade; vendor types remain in its adapter", "port": "ClientWorkflowPort", "fake": "FakeClientWorkflowAdapter", "failureKinds": ["reset", "version-mismatch", "server-state-duplication"], "lifecycleMethods": ["unsubscribe", "reset"], "owner": "project-owner-required", "securityPrivacy": ["Persist only explicitly classified workflow fields.", "Never persist credentials.", "Define logout and version reset."], "bundleBudgetGzipBytes": 9000, "fallback": "URL, component state, context and TanStack Query ownership.", "removal": ["Move remaining state to its natural owner.", "Remove facade and one selected store dependency.", "Verify logout/reset."], "serverStatePolicy": "reference-only" }, { "id": "large-data-ui", "status": "RECIPE_AVAILABLE", "trigger": "Production-like profiling proves a list or grid exceeds interaction and rendering budgets.", "forbiddenWhen": ["Pagination solves the scale requirement.", "Keyboard and screen-reader focus behavior is undefined."], "boundary": "presentation facade around virtualizer or data-grid behavior", "port": "LargeDataUiFacade", "fake": "FakeLargeDataUiAdapter", "failureKinds": ["focus-loss", "stale-row", "scale-limit"], "lifecycleMethods": ["dispose-observers-if-installed"], "owner": "project-owner-required", "securityPrivacy": ["Render only authorized rows.", "Do not expose hidden row data to telemetry.", "Preserve accessible row identity."], "bundleBudgetGzipBytes": 30000, "fallback": "Accessible pagination and bounded result sets.", "removal": ["Restore paginated primitive.", "Remove facade adapter and dependency.", "Run keyboard and performance evidence."], "serverStatePolicy": "query-cache-owned" }, { "id": "analytics-error-sink", "status": "RECIPE_AVAILABLE", "trigger": "A production provider, consent policy, retention owner and event registry are approved.", "forbiddenWhen": ["Consent and essential diagnostics are not separated.", "Arbitrary message or attribute keys can bypass redaction."], "boundary": "closed diagnostics/analytics port with provider adapter", "port": "AnalyticsErrorSink", "fake": "RecordingAnalyticsAdapter", "failureKinds": ["consent-denied", "queue-full", "provider-unavailable"], "lifecycleMethods": ["flush", "dispose"], "owner": "project-owner-required", "securityPrivacy": ["Allowlist events and attributes.", "Redact before queueing.", "Apply consent, sampling and retention policy."], "bundleBudgetGzipBytes": 25000, "fallback": "Existing bounded local diagnostics and best-effort telemetry port.", "removal": ["Disable provider delivery.", "Flush or discard by policy.", "Remove adapter, runtime config and dependency."], "serverStatePolicy": "no-server-state" } ] }