chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
export const WEB_PUSH_LIMITS = Object.freeze({
|
||||
decodedHintBytes: 3 * 1024,
|
||||
hintFutureSkewMs: 5 * 60 * 1_000,
|
||||
hintMaxLifetimeMs: 24 * 60 * 60 * 1_000,
|
||||
handlerDeadlineMs: 10_000,
|
||||
fenceOperationDeadlineMs: 2_000,
|
||||
nativeOperationDeadlineMs: 30_000,
|
||||
backendOperationDeadlineMs: 15_000,
|
||||
notificationCleanupCount: 64,
|
||||
notificationCleanupDeadlineMs: 2_000,
|
||||
clientHandoffCount: 32,
|
||||
} as const);
|
||||
|
||||
export const WEB_PUSH_PROTOCOLS = Object.freeze({
|
||||
control: "PUSH_CONTROL_V1",
|
||||
hint: "WEB_PUSH_HINT_V1",
|
||||
click: "NOTIFICATION_CLICK_DATA_V1",
|
||||
registration: "WEB_PUSH_REGISTRATION_V1",
|
||||
reconciliation: "WEB_PUSH_RECONCILIATION_V1",
|
||||
revoke: "WEB_PUSH_REVOKE_V1",
|
||||
clickHandoff: "WEB_PUSH_CLICK_HANDOFF_V1",
|
||||
reconcileRequired: "WEB_PUSH_RECONCILE_REQUIRED_V1",
|
||||
} as const);
|
||||
|
||||
/**
|
||||
* Product IDs stay opaque to the mechanism. The selected composition must
|
||||
* provide a closed registry that resolves these syntactically validated IDs.
|
||||
*/
|
||||
export type NotificationTypeId = string;
|
||||
export type NotificationRouteIntentId = string;
|
||||
|
||||
export type PushAuthoritySnapshot = Readonly<{
|
||||
fenceGeneration: string;
|
||||
sessionBindingEpoch: string;
|
||||
releaseEpoch: string;
|
||||
}>;
|
||||
|
||||
export type PushControlAssociationV1 =
|
||||
| Readonly<{ state: "UNASSOCIATED" }>
|
||||
| Readonly<{
|
||||
state: "ACTIVE" | "REVOKED";
|
||||
associationEpoch: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* One origin-scoped durable authority record shared by the window and worker.
|
||||
* It intentionally contains no account identifier or native subscription
|
||||
* material. `updatedAt` is diagnostic metadata, never an ordering authority.
|
||||
*/
|
||||
export type PushControlV1 = Readonly<{
|
||||
protocol: typeof WEB_PUSH_PROTOCOLS.control;
|
||||
fenceGeneration: string;
|
||||
sessionBindingEpoch: string;
|
||||
releaseEpoch: string;
|
||||
updatedAt: string;
|
||||
association: PushControlAssociationV1;
|
||||
}>;
|
||||
|
||||
export type WebPushHintV1 = Readonly<{
|
||||
protocol: typeof WEB_PUSH_PROTOCOLS.hint;
|
||||
notificationType: NotificationTypeId;
|
||||
notificationId: string;
|
||||
associationEpoch: string;
|
||||
releaseEpoch: string;
|
||||
issuedAt: string;
|
||||
expiresAt: string;
|
||||
routeIntent: NotificationRouteIntentId;
|
||||
}>;
|
||||
|
||||
export type NotificationClickDataV1 = Readonly<{
|
||||
protocol: typeof WEB_PUSH_PROTOCOLS.click;
|
||||
notificationId: string;
|
||||
routeIntent: NotificationRouteIntentId;
|
||||
associationEpoch: string;
|
||||
releaseEpoch: string;
|
||||
expiresAt: string;
|
||||
}>;
|
||||
|
||||
export type WebPushReadinessState =
|
||||
| "PUSH_READY"
|
||||
| "PUSH_PERMISSION_REQUIRED"
|
||||
| "PUSH_DENIED"
|
||||
| "PUSH_UNSUPPORTED"
|
||||
| "PUSH_UNAVAILABLE";
|
||||
|
||||
export type WebPushUnavailableReason =
|
||||
| "ABORTED"
|
||||
| "BACKEND_ASSOCIATION_MISSING"
|
||||
| "BACKEND_REVOKE_AMBIGUOUS"
|
||||
| "BUSY"
|
||||
| "CLOSED"
|
||||
| "LOCAL_FENCE_UNSAFE"
|
||||
| "NATIVE_UNSUBSCRIBE_AMBIGUOUS"
|
||||
| "NATIVE_SUBSCRIPTION_MISSING"
|
||||
| "PERMISSION_DISMISSED"
|
||||
| "REGISTRATION_NOT_ACTIVE"
|
||||
| "REVOKED"
|
||||
| "SESSION_AUTHORITY_CHANGED"
|
||||
| "SUBSCRIPTION_KEY_MISMATCH"
|
||||
| "WORKER_UNAVAILABLE";
|
||||
|
||||
export type WebPushReadiness = Readonly<{
|
||||
state: WebPushReadinessState;
|
||||
reason?: WebPushUnavailableReason;
|
||||
}>;
|
||||
|
||||
export type WebPushOperation =
|
||||
| "CONTROL_OPEN"
|
||||
| "CONTROL_READ"
|
||||
| "CONTROL_PREPARE"
|
||||
| "CONTROL_ACTIVATE"
|
||||
| "CONTROL_REVOKE"
|
||||
| "CONTROL_PURGE"
|
||||
| "PERMISSION_REQUEST"
|
||||
| "SUBSCRIPTION_INSPECT"
|
||||
| "SUBSCRIPTION_CREATE"
|
||||
| "SUBSCRIPTION_RECONCILE"
|
||||
| "SUBSCRIPTION_REVOKE"
|
||||
| "PUSH_DECODE"
|
||||
| "PUSH_HANDLE"
|
||||
| "NOTIFICATION_SHOW"
|
||||
| "NOTIFICATION_CLICK"
|
||||
| "NOTIFICATION_CLEANUP";
|
||||
|
||||
export type WebPushFailureCode =
|
||||
| "ABORTED"
|
||||
| "ASSOCIATION_MISMATCH"
|
||||
| "BLOCKED"
|
||||
| "CONTRACT_REJECTED"
|
||||
| "CONTROL_CORRUPT"
|
||||
| "DEADLINE_EXCEEDED"
|
||||
| "DECLARATIVE_PUSH_FORBIDDEN"
|
||||
| "EXPIRED"
|
||||
| "INVALID_INPUT"
|
||||
| "LIMIT_EXCEEDED"
|
||||
| "NATIVE_FAILURE"
|
||||
| "PERMISSION_DENIED"
|
||||
| "PROVIDER_UNAVAILABLE"
|
||||
| "RELEASE_MISMATCH"
|
||||
| "STALE_AUTHORITY"
|
||||
| "STALE_REVISION"
|
||||
| "TOMBSTONE_CONFLICT"
|
||||
| "UNSUPPORTED";
|
||||
|
||||
export type WebPushFailure = Readonly<{
|
||||
code: WebPushFailureCode;
|
||||
operation: WebPushOperation;
|
||||
retryable: boolean;
|
||||
}>;
|
||||
|
||||
export type WebPushResult<Value> =
|
||||
| Readonly<{ ok: true; value: Value }>
|
||||
| Readonly<{ ok: false; error: WebPushFailure }>;
|
||||
|
||||
export type WebPushObservationEvent =
|
||||
| "web_push_permission_finished"
|
||||
| "web_push_registration_finished"
|
||||
| "web_push_subscription_rotated"
|
||||
| "web_push_hint_processed"
|
||||
| "web_push_notification_finished"
|
||||
| "web_push_click_dispatched"
|
||||
| "web_push_association_revoked";
|
||||
|
||||
export type WebPushObservation = Readonly<{
|
||||
event: WebPushObservationEvent;
|
||||
outcome: "SUCCEEDED" | "FAILED" | "DEGRADED";
|
||||
reason?: WebPushFailureCode | WebPushUnavailableReason;
|
||||
}>;
|
||||
|
||||
export interface WebPushObserver {
|
||||
record(observation: WebPushObservation): void;
|
||||
}
|
||||
|
||||
export function webPushSuccess<Value>(
|
||||
value: Value,
|
||||
): WebPushResult<Value> {
|
||||
return Object.freeze({ ok: true, value });
|
||||
}
|
||||
|
||||
export function webPushFailure(
|
||||
code: WebPushFailureCode,
|
||||
operation: WebPushOperation,
|
||||
retryable = false,
|
||||
): WebPushResult<never> {
|
||||
return Object.freeze({
|
||||
ok: false,
|
||||
error: Object.freeze({ code, operation, retryable }),
|
||||
});
|
||||
}
|
||||
|
||||
export function samePushAuthority(
|
||||
left: PushAuthoritySnapshot,
|
||||
right: PushAuthoritySnapshot,
|
||||
): boolean {
|
||||
return (
|
||||
left.fenceGeneration === right.fenceGeneration &&
|
||||
left.sessionBindingEpoch === right.sessionBindingEpoch &&
|
||||
left.releaseEpoch === right.releaseEpoch
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user