chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Opt-in capability contracts.
|
||||
*
|
||||
* This directory is a copyable recipe source, not a production entry. A project
|
||||
* moves only the selected contract into its application-owned boundary and puts
|
||||
* a concrete implementation behind that port.
|
||||
*/
|
||||
export const OPTIONAL_RECIPE_RUNTIME_SENTINEL =
|
||||
"frontend-optional-recipe-must-not-reach-production";
|
||||
|
||||
export type CapabilityFailureCode =
|
||||
| "ABORTED"
|
||||
| "AUTH_EXPIRED"
|
||||
| "BLOCKED"
|
||||
| "CONFLICT"
|
||||
| "CONSENT_DENIED"
|
||||
| "CONTRACT_DRIFT"
|
||||
| "CORRUPT_DATA"
|
||||
| "DISCONNECTED"
|
||||
| "EXPIRED_RESOURCE"
|
||||
| "INTEGRITY_FAILED"
|
||||
| "INVALID_INPUT"
|
||||
| "LIMIT_EXCEEDED"
|
||||
| "MIGRATION_FAILED"
|
||||
| "NOT_FOUND"
|
||||
| "NOT_READABLE"
|
||||
| "PERMISSION_DENIED"
|
||||
| "POLICY_REJECTED"
|
||||
| "PROVIDER_UNAVAILABLE"
|
||||
| "QUOTA_EXCEEDED"
|
||||
| "STALE_RESULT"
|
||||
| "STORAGE_EVICTED"
|
||||
| "UNSUPPORTED";
|
||||
|
||||
export type CapabilityFailure = Readonly<{
|
||||
code: CapabilityFailureCode;
|
||||
retryable: boolean;
|
||||
safeMessage: string;
|
||||
}>;
|
||||
|
||||
export type CapabilityResult<T> =
|
||||
| Readonly<{ ok: true; value: T }>
|
||||
| Readonly<{ ok: false; failure: CapabilityFailure }>;
|
||||
|
||||
export type Cleanup = () => void;
|
||||
|
||||
export type RealtimeEvent<T> = Readonly<{
|
||||
id: string;
|
||||
sequence: number;
|
||||
occurredAt: string;
|
||||
payload: T;
|
||||
}>;
|
||||
|
||||
export interface RealtimeSubscription {
|
||||
readonly resumeToken: string | null;
|
||||
unsubscribe(): void;
|
||||
}
|
||||
|
||||
export interface RealtimePort<T> {
|
||||
subscribe(input: {
|
||||
channel: string;
|
||||
resumeToken?: string;
|
||||
signal?: AbortSignal;
|
||||
onEvent(event: CapabilityResult<RealtimeEvent<T>>): void;
|
||||
}): Promise<CapabilityResult<RealtimeSubscription>>;
|
||||
heartbeat(signal?: AbortSignal): Promise<CapabilityResult<void>>;
|
||||
}
|
||||
|
||||
export interface VersionedOfflineRepository<T extends { id: string }> {
|
||||
open(input: {
|
||||
schemaVersion: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<CapabilityResult<void>>;
|
||||
get(id: string, signal?: AbortSignal): Promise<CapabilityResult<T | null>>;
|
||||
put(value: T, signal?: AbortSignal): Promise<CapabilityResult<void>>;
|
||||
migrate(input: {
|
||||
from: number;
|
||||
to: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<CapabilityResult<void>>;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface ServiceWorkerUpdatePort {
|
||||
inspect(signal?: AbortSignal): Promise<
|
||||
CapabilityResult<Readonly<{ updateAvailable: boolean; version: string | null }>>
|
||||
>;
|
||||
activate(version: string, signal?: AbortSignal): Promise<CapabilityResult<void>>;
|
||||
rollback(signal?: AbortSignal): Promise<CapabilityResult<void>>;
|
||||
unregister(): Promise<CapabilityResult<void>>;
|
||||
}
|
||||
|
||||
export type TransferProgress = Readonly<{
|
||||
phase?:
|
||||
| "VALIDATING"
|
||||
| "PREPARING"
|
||||
| "TRANSFERRING"
|
||||
| "VERIFYING"
|
||||
| "FINALIZING";
|
||||
transferredBytes: number;
|
||||
totalBytes: number | null;
|
||||
}>;
|
||||
|
||||
export type TransferByteSource = Readonly<{
|
||||
byteLength: number | null;
|
||||
chunks: AsyncIterable<Uint8Array>;
|
||||
}>;
|
||||
|
||||
export interface FileTransferPort {
|
||||
upload(input: {
|
||||
file: Readonly<{
|
||||
name: string;
|
||||
size: number;
|
||||
type: string;
|
||||
content: TransferByteSource;
|
||||
}>;
|
||||
signal: AbortSignal;
|
||||
onProgress(progress: TransferProgress): void;
|
||||
}): Promise<CapabilityResult<Readonly<{ resourceId: string }>>>;
|
||||
download(input: {
|
||||
resourceId: string;
|
||||
signal: AbortSignal;
|
||||
onProgress(progress: TransferProgress): void;
|
||||
}): Promise<
|
||||
CapabilityResult<
|
||||
Readonly<{
|
||||
fileName: string;
|
||||
mediaType: string;
|
||||
content: TransferByteSource;
|
||||
}>
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
export interface GeneratedApiFacade {
|
||||
execute<TOutput>(input: {
|
||||
operationId: string;
|
||||
contractVersion: string;
|
||||
body?: unknown;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<CapabilityResult<TOutput>>;
|
||||
}
|
||||
|
||||
export interface FeatureFlagPort<TFlags extends Record<string, boolean | string | number>> {
|
||||
evaluate<TKey extends keyof TFlags>(input: {
|
||||
key: TKey;
|
||||
fallback: TFlags[TKey];
|
||||
maxAgeMs: number;
|
||||
}): Promise<CapabilityResult<TFlags[TKey]>>;
|
||||
}
|
||||
|
||||
export interface WorkerTaskPort<TInput, TOutput> {
|
||||
run(input: {
|
||||
taskId: string;
|
||||
generation: number;
|
||||
payload: TInput;
|
||||
signal: AbortSignal;
|
||||
}): Promise<CapabilityResult<TOutput>>;
|
||||
cancel(taskId: string): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export type MultiTabEvent<T> = Readonly<{
|
||||
eventId: string;
|
||||
sourceId: string;
|
||||
version: number;
|
||||
payload: T;
|
||||
}>;
|
||||
|
||||
export interface MultiTabPort<T> {
|
||||
publish(event: MultiTabEvent<T>): CapabilityResult<void>;
|
||||
subscribe(input: {
|
||||
sourceId: string;
|
||||
onEvent(event: CapabilityResult<MultiTabEvent<T>>): void;
|
||||
}): Cleanup;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export type BrowserCapability =
|
||||
| "clipboard-read"
|
||||
| "clipboard-write"
|
||||
| "media"
|
||||
| "notification";
|
||||
|
||||
export type PermissionDecision = "granted" | "denied" | "dismissed";
|
||||
|
||||
export interface BrowserPermissionPort {
|
||||
request(input: {
|
||||
capability: BrowserCapability;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<CapabilityResult<PermissionDecision>>;
|
||||
}
|
||||
|
||||
export interface ClientWorkflowPort<TState, TEvent> {
|
||||
snapshot(): Readonly<TState>;
|
||||
dispatch(event: TEvent): CapabilityResult<Readonly<TState>>;
|
||||
reset(): void;
|
||||
subscribe(listener: (state: Readonly<TState>) => void): Cleanup;
|
||||
}
|
||||
|
||||
export interface LargeDataUiFacade<TRow extends { id: string }> {
|
||||
window(input: {
|
||||
offset: number;
|
||||
limit: number;
|
||||
generation: number;
|
||||
}): CapabilityResult<ReadonlyArray<TRow>>;
|
||||
focus(rowId: string): CapabilityResult<void>;
|
||||
replace(rows: ReadonlyArray<TRow>, generation: number): void;
|
||||
}
|
||||
|
||||
export type SafeAnalyticsValue = boolean | number | string | null;
|
||||
|
||||
export interface AnalyticsErrorSink {
|
||||
record(input: {
|
||||
kind: "analytics" | "error";
|
||||
eventId: string;
|
||||
consent: "granted" | "denied" | "not-required";
|
||||
attributes: Readonly<Record<string, SafeAnalyticsValue>>;
|
||||
}): CapabilityResult<void>;
|
||||
flush(signal?: AbortSignal): Promise<CapabilityResult<void>>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export type OptionalCapabilityPorts = Readonly<{
|
||||
realtime: RealtimePort<unknown>;
|
||||
offline: VersionedOfflineRepository<{ id: string }>;
|
||||
serviceWorker: ServiceWorkerUpdatePort;
|
||||
fileTransfer: FileTransferPort;
|
||||
generatedApi: GeneratedApiFacade;
|
||||
featureFlag: FeatureFlagPort<Record<string, boolean | string | number>>;
|
||||
worker: WorkerTaskPort<unknown, unknown>;
|
||||
multiTab: MultiTabPort<unknown>;
|
||||
browserPermission: BrowserPermissionPort;
|
||||
clientWorkflow: ClientWorkflowPort<unknown, unknown>;
|
||||
largeDataUi: LargeDataUiFacade<{ id: string }>;
|
||||
analytics: AnalyticsErrorSink;
|
||||
}>;
|
||||
Reference in New Issue
Block a user