diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 000000000..973e144dd --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,24 @@ +## Crypto Runtime Compatibility + +The crypto module provides runtime capability checking for crypto primitives. + +### checkCapabilities + +Checks available crypto capabilities in the current environment. + +```ts +function checkCapabilities(throwOnMissing: boolean): Map; +function createCryptoAdapter(): CryptoAdapter; +import { createCryptoAdapter, checkCapabilities } from "src/services/crypto"; + +// Check capabilities without throwing +const caps = checkCapabilities(false); +if (!caps.get("globalCrypto")) { + console.warn("Crypto not available!"); +} + +// Create adapter (throws on missing capabilities) +const adapter = createCryptoAdapter(); +const randomBytes = adapter.randomBytes(32); +const encoded = adapter.encode("Hello"); +``` diff --git a/src/components/mail/PostageDisputePanel.tsx b/src/components/mail/PostageDisputePanel.tsx index 997437e22..045535cb8 100644 --- a/src/components/mail/PostageDisputePanel.tsx +++ b/src/components/mail/PostageDisputePanel.tsx @@ -26,12 +26,7 @@ import { cn } from "@/lib/utils"; * Pending | Expired | Disputed | Settled | Refunded | Reclaimed */ export type PostageDisputeStatus = - | "pending" - | "expired" - | "disputed" - | "settled" - | "refunded" - | "reclaimed"; + "pending" | "expired" | "disputed" | "settled" | "refunded" | "reclaimed"; interface PostageDisputePanelProps { postageStatus: PostageDisputeStatus; diff --git a/src/components/mail/bulk-actions.ts b/src/components/mail/bulk-actions.ts index 75d01d516..7551021a8 100644 --- a/src/components/mail/bulk-actions.ts +++ b/src/components/mail/bulk-actions.ts @@ -14,13 +14,7 @@ import { } from "@/features/sender-conversion/types"; export type BulkActionId = - | "archive" - | "star" - | "snooze" - | "mark-read" - | "approve" - | "block" - | "move"; + "archive" | "star" | "snooze" | "mark-read" | "approve" | "block" | "move"; export type BulkSnoozeChoice = "later-today" | "tomorrow" | "next-week"; diff --git a/src/features/compose/RecipientPolicyBanner.tsx b/src/features/compose/RecipientPolicyBanner.tsx index cda9b00b8..35d0863b9 100644 --- a/src/features/compose/RecipientPolicyBanner.tsx +++ b/src/features/compose/RecipientPolicyBanner.tsx @@ -9,12 +9,7 @@ interface RecipientPolicyBannerProps { } type BannerVariant = - | "trusted" - | "blocked" - | "postage_required" - | "verification_required" - | "loading" - | "error"; + "trusted" | "blocked" | "postage_required" | "verification_required" | "loading" | "error"; interface BannerConfig { variant: BannerVariant; diff --git a/src/features/contacts/import/types.ts b/src/features/contacts/import/types.ts index 2892ea805..361ee0937 100644 --- a/src/features/contacts/import/types.ts +++ b/src/features/contacts/import/types.ts @@ -1,10 +1,6 @@ /** Where the contacts are coming from. */ export type ImportSource = - | "csv" - | "provider-gmail" - | "provider-outlook" - | "contacts-api" - | "manual"; + "csv" | "provider-gmail" | "provider-outlook" | "contacts-api" | "manual"; /** How an imported address resolved against known identities. */ export type MatchType = "exact" | "fuzzy" | "ambiguous" | "none"; diff --git a/src/features/demo-admin-dashboard/constants/adminEmptyStates.ts b/src/features/demo-admin-dashboard/constants/adminEmptyStates.ts index d602725d0..2a2c51eae 100644 --- a/src/features/demo-admin-dashboard/constants/adminEmptyStates.ts +++ b/src/features/demo-admin-dashboard/constants/adminEmptyStates.ts @@ -3,12 +3,7 @@ * fake, and safe for public review — no real user data, addresses, or secrets. */ export type AdminEmptyStateKind = - | "messages" - | "senders" - | "attachments" - | "events" - | "validation" - | "kpis"; + "messages" | "senders" | "attachments" | "events" | "validation" | "kpis"; export interface AdminEmptyStateCopy { /** Short, friendly heading. */ diff --git a/src/features/demo-admin-dashboard/docs/AUDIT_LOG.md b/src/features/demo-admin-dashboard/docs/AUDIT_LOG.md index 1dce6d038..b312a023a 100644 --- a/src/features/demo-admin-dashboard/docs/AUDIT_LOG.md +++ b/src/features/demo-admin-dashboard/docs/AUDIT_LOG.md @@ -43,9 +43,7 @@ const formattedString = formatAuditEntry(entry); ```tsx import { AuditLogPanel } from "./components/AuditLogPanel"; -const myAuditEntries = [ - /* ...array of AuditLogEntry objects... */ -]; +const myAuditEntries = [/* ...array of AuditLogEntry objects... */]; ; ``` diff --git a/src/features/demo-admin-dashboard/docs/CALENDAR_EVENT_EDITOR.md b/src/features/demo-admin-dashboard/docs/CALENDAR_EVENT_EDITOR.md index 665cba3e5..b27cebc30 100644 --- a/src/features/demo-admin-dashboard/docs/CALENDAR_EVENT_EDITOR.md +++ b/src/features/demo-admin-dashboard/docs/CALENDAR_EVENT_EDITOR.md @@ -71,9 +71,7 @@ These pure functions are exported from `components/CalendarEventEditor.tsx`: import { calendarEventToEditorState, editorStateToCalendarEvent } from "../types/calendarEvent"; import type { DemoCalendarEvent } from "../types/dataset"; -const event: DemoCalendarEvent = { - /* ... */ -}; +const event: DemoCalendarEvent = {/* ... */}; const editorState = calendarEventToEditorState(event); // ... edit fields ... const updated: DemoCalendarEvent = editorStateToCalendarEvent(editorState); diff --git a/src/features/demo-admin-dashboard/fixtures/senderRecoveryCampaignPreset.ts b/src/features/demo-admin-dashboard/fixtures/senderRecoveryCampaignPreset.ts index 401230e1c..bdc808e7f 100644 --- a/src/features/demo-admin-dashboard/fixtures/senderRecoveryCampaignPreset.ts +++ b/src/features/demo-admin-dashboard/fixtures/senderRecoveryCampaignPreset.ts @@ -1,11 +1,7 @@ import type { CampaignSnapshot } from "../types/campaignSnapshot"; export type SenderRecoveryRequestStatus = - | "unknown" - | "paid-request" - | "approved" - | "blocked" - | "refund-queued"; + "unknown" | "paid-request" | "approved" | "blocked" | "refund-queued"; export interface SenderRecoveryRequestState { id: string; diff --git a/src/features/demo-admin-dashboard/messageGeneration.test.ts b/src/features/demo-admin-dashboard/messageGeneration.test.ts index 9edb88a64..bdb013f3e 100644 --- a/src/features/demo-admin-dashboard/messageGeneration.test.ts +++ b/src/features/demo-admin-dashboard/messageGeneration.test.ts @@ -8,18 +8,16 @@ import type { CampaignTemplate, CampaignChecklistItem } from "./types/campaign"; /** Converts campaign templates into a flat list of message templates for testing. */ const getTestTemplates = (campaignTemplates: CampaignTemplate[]): MessageTemplate[] => { return campaignTemplates.flatMap((t) => - t.checklist.map( - (c: CampaignChecklistItem): MessageTemplate => ({ - id: c.id, - name: c.label, - subject: c.label, - body: c.description, - description: c.description, - category: t.name as TemplateCategory, - recipients: [], - tags: [], - }), - ), + t.checklist.map((c: CampaignChecklistItem): MessageTemplate => ({ + id: c.id, + name: c.label, + subject: c.label, + body: c.description, + description: c.description, + category: t.name as TemplateCategory, + recipients: [], + tags: [], + })), ); }; diff --git a/src/features/demo-admin-dashboard/mockPublishWorkflow.ts b/src/features/demo-admin-dashboard/mockPublishWorkflow.ts index 024f725d6..873dd2285 100644 --- a/src/features/demo-admin-dashboard/mockPublishWorkflow.ts +++ b/src/features/demo-admin-dashboard/mockPublishWorkflow.ts @@ -1,10 +1,5 @@ export type MockPublishStatus = - | "idle" - | "preview" - | "publishing" - | "published" - | "failed" - | "rolled-back"; + "idle" | "preview" | "publishing" | "published" | "failed" | "rolled-back"; export interface MockPublishStep { id: string; diff --git a/src/features/demo-admin-dashboard/templates/templateToDraft.ts b/src/features/demo-admin-dashboard/templates/templateToDraft.ts index 5ceed05fd..72cd0467d 100644 --- a/src/features/demo-admin-dashboard/templates/templateToDraft.ts +++ b/src/features/demo-admin-dashboard/templates/templateToDraft.ts @@ -26,8 +26,7 @@ export function isTemplateInserted(dataset: Draft[], template: MessageTemplate): } export type InsertResult = - | { ok: true; dataset: Draft[]; draft: Draft } - | { ok: false; reason: string }; + { ok: true; dataset: Draft[]; draft: Draft } | { ok: false; reason: string }; /** * Insert a template-derived draft into the dataset. Validates against duplicate diff --git a/src/features/demo-admin-dashboard/templates/types.ts b/src/features/demo-admin-dashboard/templates/types.ts index 15a663cbf..359e1624c 100644 --- a/src/features/demo-admin-dashboard/templates/types.ts +++ b/src/features/demo-admin-dashboard/templates/types.ts @@ -7,12 +7,7 @@ */ export type TemplateCategory = - | "welcome" - | "transactional" - | "security" - | "event" - | "newsletter" - | "internal"; + "welcome" | "transactional" | "security" | "event" | "newsletter" | "internal"; export interface MessageTemplate { /** Stable, unique identifier. */ diff --git a/src/features/demo-admin-dashboard/types/audienceSegment.ts b/src/features/demo-admin-dashboard/types/audienceSegment.ts index 2132994c6..cca432d25 100644 --- a/src/features/demo-admin-dashboard/types/audienceSegment.ts +++ b/src/features/demo-admin-dashboard/types/audienceSegment.ts @@ -1,9 +1,5 @@ export type AudienceSegmentId = - | "investors" - | "founders" - | "events" - | "relay-operators" - | "unknown-senders"; + "investors" | "founders" | "events" | "relay-operators" | "unknown-senders"; export interface AudienceSegment { id: AudienceSegmentId; diff --git a/src/features/demo-admin-dashboard/types/campaignKpi.ts b/src/features/demo-admin-dashboard/types/campaignKpi.ts index c8ca47e51..86ae0dac5 100644 --- a/src/features/demo-admin-dashboard/types/campaignKpi.ts +++ b/src/features/demo-admin-dashboard/types/campaignKpi.ts @@ -1,10 +1,5 @@ export type KpiMetricKind = - | "opens" - | "approvals" - | "replies" - | "refunds" - | "proof_inspections" - | "conversions"; + "opens" | "approvals" | "replies" | "refunds" | "proof_inspections" | "conversions"; export type KpiStatus = "on-track" | "at-risk" | "met" | "missed"; diff --git a/src/features/demo-admin-dashboard/types/campaignTimeline.ts b/src/features/demo-admin-dashboard/types/campaignTimeline.ts index a84e4be75..9ddc2488f 100644 --- a/src/features/demo-admin-dashboard/types/campaignTimeline.ts +++ b/src/features/demo-admin-dashboard/types/campaignTimeline.ts @@ -1,10 +1,5 @@ export type CampaignPhaseKind = - | "planning" - | "warmup" - | "active" - | "cooldown" - | "completed" - | "paused"; + "planning" | "warmup" | "active" | "cooldown" | "completed" | "paused"; export type CampaignPhaseStatus = "upcoming" | "active" | "completed" | "skipped"; diff --git a/src/features/demo-admin-dashboard/types/datasetImport.ts b/src/features/demo-admin-dashboard/types/datasetImport.ts index 89b85b96c..acdc13861 100644 --- a/src/features/demo-admin-dashboard/types/datasetImport.ts +++ b/src/features/demo-admin-dashboard/types/datasetImport.ts @@ -16,5 +16,4 @@ export interface DatasetImportIssue { * On failure, `issues` lists every problem found (the import is rejected whole). */ export type DatasetImportResult = - | { ok: true; drafts: Draft[] } - | { ok: false; issues: DatasetImportIssue[] }; + { ok: true; drafts: Draft[] } | { ok: false; issues: DatasetImportIssue[] }; diff --git a/src/features/demo-admin-dashboard/types/quickFix.ts b/src/features/demo-admin-dashboard/types/quickFix.ts index 960d7f3c6..d3ff1ec0a 100644 --- a/src/features/demo-admin-dashboard/types/quickFix.ts +++ b/src/features/demo-admin-dashboard/types/quickFix.ts @@ -3,10 +3,7 @@ import type { ValidationIssue } from "../validation-types"; /** The kinds of safe, one-click fixes the framework can apply. */ export type QuickFixKind = - | "fill-subject" - | "fill-body" - | "add-recipient" - | "replace-unsafe-recipient"; + "fill-subject" | "fill-body" | "add-recipient" | "replace-unsafe-recipient"; /** * A safe, deterministic repair for a common demo-data validation issue. diff --git a/src/features/design-system/components/trust-badge.tsx b/src/features/design-system/components/trust-badge.tsx index ec4e8d219..68d87e710 100644 --- a/src/features/design-system/components/trust-badge.tsx +++ b/src/features/design-system/components/trust-badge.tsx @@ -17,13 +17,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp * Standardizing these here keeps labels and colors identical everywhere. */ export type TrustState = - | "verified" - | "allowed" - | "unknown" - | "paid" - | "blocked" - | "bridged" - | "encrypted"; + "verified" | "allowed" | "unknown" | "paid" | "blocked" | "bridged" | "encrypted"; export type TrustStateMeta = { label: string; diff --git a/src/features/settings/mailbox-policy-templates.ts b/src/features/settings/mailbox-policy-templates.ts index d850d28dd..15e6779a2 100644 --- a/src/features/settings/mailbox-policy-templates.ts +++ b/src/features/settings/mailbox-policy-templates.ts @@ -1,11 +1,7 @@ import type { UiPreferences, UnknownSenderPolicy } from "@/features/preferences"; export type MailboxPolicyTemplateId = - | "private" - | "public-paid-inbox" - | "investor-inbox" - | "recruiting-inbox" - | "allowlist-only"; + "private" | "public-paid-inbox" | "investor-inbox" | "recruiting-inbox" | "allowlist-only"; export type MailboxPolicyTemplate = { id: MailboxPolicyTemplateId; diff --git a/src/lib/MOTION_PRESETS.md b/src/lib/MOTION_PRESETS.md index e0cba2751..02d9834d9 100644 --- a/src/lib/MOTION_PRESETS.md +++ b/src/lib/MOTION_PRESETS.md @@ -351,12 +351,8 @@ export const entrance = { myNewAnimation: (customParam?: number): Variants => { const config = getConfig(); return { - initial: { - /* ... */ - }, - animate: { - /* ... */ - }, + initial: {/* ... */}, + animate: {/* ... */}, transition: { type: "spring", stiffness: config.springStiffness, diff --git a/src/server/api/abuse-service.ts b/src/server/api/abuse-service.ts index bff65c0dd..7fc4f2421 100644 --- a/src/server/api/abuse-service.ts +++ b/src/server/api/abuse-service.ts @@ -9,12 +9,7 @@ function rateLimited(retryAfterSeconds: number) { export type AbuseRoute = "postage_submit"; export type AbuseCheck = - | "account" - | "device" - | "ip" - | "proof_failure" - | "relay" - | "sender_recipient"; + "account" | "device" | "ip" | "proof_failure" | "relay" | "sender_recipient"; export type AbuseOutagePolicy = "fail_closed" | "fail_open"; export type AbuseDecision = { diff --git a/src/server/api/stealth-coordinator.ts b/src/server/api/stealth-coordinator.ts index c411102e0..1ecb6971a 100644 --- a/src/server/api/stealth-coordinator.ts +++ b/src/server/api/stealth-coordinator.ts @@ -42,8 +42,7 @@ export class StealthCoordinator extends DurableObjectBase { async getIdempotencyRecord(key: string): Promise { const record = (await this.ctx.storage.get(`idempotency:${key}`)) as - | IdempotencyRecord - | undefined; + IdempotencyRecord | undefined; return record ?? null; } diff --git a/src/services/analytics.ts b/src/services/analytics.ts index 1ec72a5a0..02d3f56a6 100644 --- a/src/services/analytics.ts +++ b/src/services/analytics.ts @@ -1,10 +1,5 @@ export type EventCategory = - | "onboarding" - | "policy" - | "send_outcome" - | "request" - | "proof_failure" - | "retention"; + "onboarding" | "policy" | "send_outcome" | "request" | "proof_failure" | "retention"; export type EventPurpose = "activation_measurement" | "reliability_monitoring" | "abuse_prevention"; diff --git a/src/services/crypto/index.ts b/src/services/crypto/index.ts new file mode 100644 index 000000000..e3ce6fd5f --- /dev/null +++ b/src/services/crypto/index.ts @@ -0,0 +1 @@ +export * from "./runtime"; diff --git a/src/services/crypto/runtime.ts b/src/services/crypto/runtime.ts new file mode 100644 index 000000000..2173789a8 --- /dev/null +++ b/src/services/crypto/runtime.ts @@ -0,0 +1,290 @@ +/** + * Runtime crypto adapter with capability checking + * + * This module validates required crypto primitives before use + * and provides injectable implementations for tests. + */ + +/** + * Runtime capability error types + */ +export enum CryptoCapability { + GLOBAL_CRYPTO = "globalCrypto", + SUBTLE_CRYPTO = "subtleCrypto", + BTOA = "btoa", + ATob = "atob", + TEXT_ENCODER = "textEncoder", + TEXT_DECODER = "textDecoder", + RANDOM_VALUES = "randomValues", +} + +/** + * Crypto capability error + */ +export class CryptoCapabilityError extends Error { + constructor( + public capability: CryptoCapability, + message: string, + ) { + super(`[${capability}] ${message}`); + this.name = "CryptoCapabilityError"; + } +} + +/** + * Type-safe crypto interface + */ +export interface CryptoAdapter { + /** Global crypto object */ + crypto: Crypto; + /** Web Crypto subtle interface */ + subtle: SubtleCrypto; + /** Base64 encode */ + btoa: (data: string) => string; + /** Base64 decode */ + atob: (data: string) => string; + /** Text encoder */ + encode: (data: string) => Uint8Array; + /** Text decoder */ + decode: (data: Uint8Array) => string; + /** Secure random values */ + randomBytes: (length: number) => Uint8Array; +} + +/** + * Runtime capability checker + * + * @param throwOnMissing - Whether to throw errors on missing capabilities + * @returns A map of capabilities to their availability + */ +export function checkCapabilities(throwOnMissing: boolean = true): Map { + const capabilities = new Map(); + + // Check global crypto + const hasCrypto = typeof globalThis !== "undefined" && "crypto" in globalThis; + capabilities.set(CryptoCapability.GLOBAL_CRYPTO, hasCrypto); + + if (!hasCrypto && throwOnMissing) { + throw new CryptoCapabilityError( + CryptoCapability.GLOBAL_CRYPTO, + "global crypto is not available in this environment", + ); + } + + // Check subtle crypto + const hasSubtle = + hasCrypto && "subtle" in globalThis.crypto && typeof globalThis.crypto.subtle !== "undefined"; + capabilities.set(CryptoCapability.SUBTLE_CRYPTO, hasSubtle); + + if (!hasSubtle && throwOnMissing) { + throw new CryptoCapabilityError( + CryptoCapability.SUBTLE_CRYPTO, + "Web Crypto subtle is not available in this environment", + ); + } + + // Check btoa + const hasBtoa = typeof globalThis.btoa === "function" && typeof globalThis.atob === "function"; + capabilities.set(CryptoCapability.BTOA, hasBtoa); + + if (!hasBtoa && throwOnMissing) { + throw new CryptoCapabilityError( + CryptoCapability.BTOA, + "base64 encode/decode (btoa/atob) is not available in this environment", + ); + } + + // Check TextEncoder/TextDecoder + const hasTextEncoder = + typeof globalThis.TextEncoder === "function" && typeof globalThis.TextDecoder === "function"; + capabilities.set(CryptoCapability.TEXT_ENCODER, hasTextEncoder); + + if (!hasTextEncoder && throwOnMissing) { + throw new CryptoCapabilityError( + CryptoCapability.TEXT_ENCODER, + "TextEncoder/TextDecoder is not available in this environment", + ); + } + + // Check random values (getRandomValues) + const hasRandomValues = hasCrypto && "getRandomValues" in globalThis.crypto; + capabilities.set(CryptoCapability.RANDOM_VALUES, hasRandomValues); + + if (!hasRandomValues && throwOnMissing) { + throw new CryptoCapabilityError( + CryptoCapability.RANDOM_VALUES, + "crypto.getRandomValues is not available in this environment", + ); + } + + return capabilities; +} + +/** + * Create a crypto adapter for the current runtime + * + * @throws {CryptoCapabilityError} If required capabilities are missing + * @returns A fully configured CryptoAdapter + */ +export function createCryptoAdapter(): CryptoAdapter { + const capabilities = checkCapabilities(true); + + // Validate all required capabilities are present + const required: CryptoCapability[] = [ + CryptoCapability.GLOBAL_CRYPTO, + CryptoCapability.SUBTLE_CRYPTO, + CryptoCapability.BTOA, + CryptoCapability.TEXT_ENCODER, + CryptoCapability.RANDOM_VALUES, + ]; + + for (const cap of required) { + if (!capabilities.get(cap)) { + throw new CryptoCapabilityError(cap, `Required capability ${cap} is not available`); + } + } + + // The actual crypto object (with all methods) + const crypto = globalThis.crypto; + + return { + crypto: crypto, + subtle: crypto.subtle, + btoa: (data: string) => { + if (typeof globalThis.btoa !== "function") { + throw new CryptoCapabilityError(CryptoCapability.BTOA, "btoa is not available"); + } + return globalThis.btoa(data); + }, + atob: (data: string) => { + if (typeof globalThis.atob !== "function") { + throw new CryptoCapabilityError(CryptoCapability.ATob, "atob is not available"); + } + return globalThis.atob(data); + }, + encode: (data: string) => { + if (typeof globalThis.TextEncoder !== "function") { + throw new CryptoCapabilityError( + CryptoCapability.TEXT_ENCODER, + "TextEncoder is not available", + ); + } + return new globalThis.TextEncoder().encode(data); + }, + decode: (data: Uint8Array) => { + if (typeof globalThis.TextDecoder !== "function") { + throw new CryptoCapabilityError( + CryptoCapability.TEXT_DECODER, + "TextDecoder is not available", + ); + } + return new globalThis.TextDecoder().decode(data); + }, + randomBytes: (length: number): Uint8Array => { + if (typeof globalThis.crypto?.getRandomValues !== "function") { + throw new CryptoCapabilityError( + CryptoCapability.RANDOM_VALUES, + "crypto.getRandomValues is not available", + ); + } + // In production, never fall back to insecure randomness + const array = new Uint8Array(length); + globalThis.crypto.getRandomValues(array); + return array; + }, + }; +} + +/** + * Create a test adapter with mocked implementations + */ +export function createTestAdapter(overrides: Partial = {}): CryptoAdapter { + const defaultAdapter: CryptoAdapter = { + crypto: globalThis.crypto, + subtle: globalThis.crypto.subtle, + btoa: (data: string) => globalThis.btoa(data), + atob: (data: string) => globalThis.atob(data), + encode: (data: string) => new globalThis.TextEncoder().encode(data), + decode: (data: Uint8Array) => new globalThis.TextDecoder().decode(data), + randomBytes: (length: number) => { + const array = new Uint8Array(length); + globalThis.crypto.getRandomValues(array); + return array; + }, + }; + + return { + ...defaultAdapter, + ...overrides, + }; +} + +/** + * Create a mock adapter for testing missing capabilities + */ +export function createMockAdapterWithMissingCapabilities( + missing: CryptoCapability[], +): Partial { + const mock: Partial = {}; + + if (!missing.includes(CryptoCapability.GLOBAL_CRYPTO)) { + mock.crypto = globalThis.crypto; + } + + if (!missing.includes(CryptoCapability.SUBTLE_CRYPTO)) { + mock.subtle = globalThis.crypto.subtle; + } + + if (!missing.includes(CryptoCapability.BTOA)) { + mock.btoa = (data: string) => globalThis.btoa(data); + } + + if (!missing.includes(CryptoCapability.ATob)) { + mock.atob = (data: string) => globalThis.atob(data); + } + + if (!missing.includes(CryptoCapability.TEXT_ENCODER)) { + mock.encode = (data: string) => new globalThis.TextEncoder().encode(data); + } + + if (!missing.includes(CryptoCapability.TEXT_DECODER)) { + mock.decode = (data: Uint8Array) => new globalThis.TextDecoder().decode(data); + } + + if (!missing.includes(CryptoCapability.RANDOM_VALUES)) { + mock.randomBytes = (length: number) => { + const array = new Uint8Array(length); + globalThis.crypto.getRandomValues(array); + return array; + }; + } + + return mock; +} + +/** + * Get a string describing the current runtime environment + */ +export function getRuntimeInfo(): string { + if (typeof globalThis === "undefined") { + return "Unknown runtime"; + } + + if (typeof globalThis.WorkerGlobalScope !== "undefined") { + return "Web Worker"; + } + + if (typeof globalThis.window !== "undefined") { + return "Browser"; + } + + if (typeof globalThis.process !== "undefined") { + return "Node.js"; + } + + if (typeof globalThis.Deno !== "undefined") { + return "Deno"; + } + + return "Unknown runtime"; +} diff --git a/src/services/relay/federation.ts b/src/services/relay/federation.ts index 0d3c8b274..3b15287ba 100644 --- a/src/services/relay/federation.ts +++ b/src/services/relay/federation.ts @@ -22,11 +22,7 @@ */ export type DeliveryState = - | "DISCOVERY" - | "HANDOFF" - | "ACKNOWLEDGED" - | "DEDUPLICATED" - | "DEAD_LETTER"; + "DISCOVERY" | "HANDOFF" | "ACKNOWLEDGED" | "DEDUPLICATED" | "DEAD_LETTER"; export interface FederationMessage { id: string; diff --git a/src/tools/v2/team/role-based-mail-access/types.ts b/src/tools/v2/team/role-based-mail-access/types.ts index 9e3e10b33..e1895ea38 100644 --- a/src/tools/v2/team/role-based-mail-access/types.ts +++ b/src/tools/v2/team/role-based-mail-access/types.ts @@ -1,8 +1,5 @@ export type Permission = - | "READ_SENSITIVE" - | "REPLY_AS_TEAM" - | "DELETE_THREAD" - | "VIEW_INTERNAL_NOTES"; + "READ_SENSITIVE" | "REPLY_AS_TEAM" | "DELETE_THREAD" | "VIEW_INTERNAL_NOTES"; export interface Role { id: string; diff --git a/tests/crypto/runtime.test.ts b/tests/crypto/runtime.test.ts new file mode 100644 index 000000000..3870eeaa0 --- /dev/null +++ b/tests/crypto/runtime.test.ts @@ -0,0 +1,161 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { + checkCapabilities, + createCryptoAdapter, + createTestAdapter, + createMockAdapterWithMissingCapabilities, + CryptoCapability, + CryptoCapabilityError, + getRuntimeInfo, +} from "../../src/services/crypto/runtime"; + +describe("Crypto Runtime Compatibility", () => { + describe("checkCapabilities", () => { + it("should detect all capabilities in a browser-like environment", () => { + const capabilities = checkCapabilities(false); + + expect(capabilities.get(CryptoCapability.GLOBAL_CRYPTO)).toBe(true); + expect(capabilities.get(CryptoCapability.SUBTLE_CRYPTO)).toBe(true); + expect(capabilities.get(CryptoCapability.BTOA)).toBe(true); + expect(capabilities.get(CryptoCapability.TEXT_ENCODER)).toBe(true); + expect(capabilities.get(CryptoCapability.RANDOM_VALUES)).toBe(true); + }); + + it("should throw on missing capabilities when throwOnMissing is true", () => { + // Mock a missing capability + const originalCrypto = globalThis.crypto; + // @ts-ignore - temporarily remove crypto for test + delete globalThis.crypto; + + expect(() => checkCapabilities(true)).toThrow(CryptoCapabilityError); + + // Restore crypto + globalThis.crypto = originalCrypto; + }); + + it("should not throw when throwOnMissing is false", () => { + const originalCrypto = globalThis.crypto; + // @ts-ignore - temporarily remove crypto for test + delete globalThis.crypto; + + const capabilities = checkCapabilities(false); + expect(capabilities.get(CryptoCapability.GLOBAL_CRYPTO)).toBe(false); + + // Restore crypto + globalThis.crypto = originalCrypto; + }); + }); + + describe("createCryptoAdapter", () => { + it("should create a fully functional adapter in a browser-like environment", () => { + const adapter = createCryptoAdapter(); + + expect(adapter.crypto).toBeDefined(); + expect(adapter.subtle).toBeDefined(); + expect(adapter.btoa).toBeInstanceOf(Function); + expect(adapter.atob).toBeInstanceOf(Function); + expect(adapter.encode).toBeInstanceOf(Function); + expect(adapter.decode).toBeInstanceOf(Function); + expect(adapter.randomBytes).toBeInstanceOf(Function); + }); + + it("should throw CryptoCapabilityError on missing capabilities", () => { + const originalCrypto = globalThis.crypto; + // @ts-ignore - temporarily remove crypto for test + delete globalThis.crypto; + + expect(() => createCryptoAdapter()).toThrow(CryptoCapabilityError); + + // Restore crypto + globalThis.crypto = originalCrypto; + }); + + it("should generate random bytes securely", () => { + const adapter = createCryptoAdapter(); + const bytes = adapter.randomBytes(32); + + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes.length).toBe(32); + expect(bytes.some((b) => b !== 0)).toBe(true); // Should have some randomness + }); + + it("should encode and decode text correctly", () => { + const adapter = createCryptoAdapter(); + const text = "Hello, World! 🚀"; + + const encoded = adapter.encode(text); + expect(encoded).toBeInstanceOf(Uint8Array); + + const decoded = adapter.decode(encoded); + expect(decoded).toBe(text); + }); + + it("should base64 encode and decode correctly", () => { + const adapter = createCryptoAdapter(); + const text = "Hello, World!"; + + const encoded = adapter.btoa(text); + expect(encoded).toMatch(/^[A-Za-z0-9+/]+=*$/); + + const decoded = adapter.atob(encoded); + expect(decoded).toBe(text); + }); + }); + + describe("createTestAdapter", () => { + it("should create an adapter with overrides", () => { + const mockRandomBytes = vi.fn(() => new Uint8Array([1, 2, 3, 4])); + + const adapter = createTestAdapter({ + randomBytes: mockRandomBytes, + }); + + const result = adapter.randomBytes(4); + expect(mockRandomBytes).toHaveBeenCalledWith(4); + expect(result).toEqual(new Uint8Array([1, 2, 3, 4])); + }); + }); + + describe("createMockAdapterWithMissingCapabilities", () => { + it("should create an adapter missing specific capabilities", () => { + const adapter = createMockAdapterWithMissingCapabilities([ + CryptoCapability.BTOA, + CryptoCapability.TEXT_ENCODER, + ]); + + expect(adapter.btoa).toBeUndefined(); + expect(adapter.encode).toBeUndefined(); + expect(adapter.crypto).toBeDefined(); + expect(adapter.randomBytes).toBeDefined(); + }); + + it("should still have other capabilities intact", () => { + const adapter = createMockAdapterWithMissingCapabilities([CryptoCapability.BTOA]); + + expect(adapter.crypto).toBeDefined(); + expect(adapter.subtle).toBeDefined(); + expect(adapter.randomBytes).toBeDefined(); + }); + }); + + describe("getRuntimeInfo", () => { + it("should return a string describing the runtime", () => { + const info = getRuntimeInfo(); + expect(typeof info).toBe("string"); + expect(info.length).toBeGreaterThan(0); + }); + }); + + describe("Error handling", () => { + it("should create CryptoCapabilityError with correct fields", () => { + const error = new CryptoCapabilityError( + CryptoCapability.GLOBAL_CRYPTO, + "Crypto not available", + ); + + expect(error.name).toBe("CryptoCapabilityError"); + expect(error.capability).toBe(CryptoCapability.GLOBAL_CRYPTO); + expect(error.message).toContain("[globalCrypto]"); + }); + }); +}); diff --git a/tests/unit/api/auth/signed-request-vectors.test.ts b/tests/unit/api/auth/signed-request-vectors.test.ts index 54c568a84..e98e18900 100644 --- a/tests/unit/api/auth/signed-request-vectors.test.ts +++ b/tests/unit/api/auth/signed-request-vectors.test.ts @@ -9,11 +9,7 @@ import { } from "../../../../src/server/api/auth/signed-request"; type VectorError = - | "expired" - | "future" - | "invalid_signature" - | "malformed_request" - | "replayed_nonce"; + "expired" | "future" | "invalid_signature" | "malformed_request" | "replayed_nonce"; interface Vector { name: string; diff --git a/tests/unit/api/openapi.deprecation.test.ts b/tests/unit/api/openapi.deprecation.test.ts index e76bbcc50..5f3c567a7 100644 --- a/tests/unit/api/openapi.deprecation.test.ts +++ b/tests/unit/api/openapi.deprecation.test.ts @@ -21,8 +21,7 @@ describe("OpenAPI deprecation metadata", () => { it("every deprecated operation carries sunset metadata", () => { for (const { path, op } of deprecated) { const meta = op["x-deprecation"] as - | { reason?: string; sunset?: string; migration?: string } - | undefined; + { reason?: string; sunset?: string; migration?: string } | undefined; expect(meta, `${path} x-deprecation`).toBeDefined(); expect(meta?.reason, `${path} deprecation reason`).toBeTruthy(); expect(meta?.sunset, `${path} sunset date`).toMatch(/^\d{4}-\d{2}-\d{2}$/); diff --git a/tools/v1/individual/email-summarizer/services/security.ts b/tools/v1/individual/email-summarizer/services/security.ts index 4bc4b6a6b..12d2be6cb 100644 --- a/tools/v1/individual/email-summarizer/services/security.ts +++ b/tools/v1/individual/email-summarizer/services/security.ts @@ -35,11 +35,7 @@ export const SECURITY_LIMITS = { export type SecurityIssueField = "input" | "subject" | "sender" | "receivedAt" | "body"; export type SecurityIssueCode = - | "not-an-object" - | "missing-field" - | "wrong-type" - | "too-long" - | "invalid-timestamp"; + "not-an-object" | "missing-field" | "wrong-type" | "too-long" | "invalid-timestamp"; export interface SecurityIssue { field: SecurityIssueField; diff --git a/tools/v1/individual/email-tone-rewriter/services/guards.ts b/tools/v1/individual/email-tone-rewriter/services/guards.ts index 72e0c1a26..c47d495ac 100644 --- a/tools/v1/individual/email-tone-rewriter/services/guards.ts +++ b/tools/v1/individual/email-tone-rewriter/services/guards.ts @@ -38,8 +38,7 @@ export interface GuardIssue { * rejection raised before the engine runs any work. */ export type SafeRewriteResult = - | RewriterResult - | { status: "error"; code: GuardErrorCode; message: string }; + RewriterResult | { status: "error"; code: GuardErrorCode; message: string }; // Control characters (except tab and newline) can hide or corrupt content. // eslint-disable-next-line no-control-regex diff --git a/tools/v1/individual/follow-up-reminder/services/followUpReminder.ts b/tools/v1/individual/follow-up-reminder/services/followUpReminder.ts index 4345de762..c8b9b6dba 100644 --- a/tools/v1/individual/follow-up-reminder/services/followUpReminder.ts +++ b/tools/v1/individual/follow-up-reminder/services/followUpReminder.ts @@ -11,11 +11,7 @@ export type ReminderState = "draft" | "no_action"; export type ReminderConfidence = "high" | "medium" | "low"; export type SignalType = - | "explicit_request" - | "absolute_date" - | "relative_date" - | "sender_hint" - | "low_confidence_context"; + "explicit_request" | "absolute_date" | "relative_date" | "sender_hint" | "low_confidence_context"; export interface ReminderSignal { type: SignalType; diff --git a/tools/v1/individual/grammar-cleaner/services/grammarCleaner.ts b/tools/v1/individual/grammar-cleaner/services/grammarCleaner.ts index b246f2a15..97a0824be 100644 --- a/tools/v1/individual/grammar-cleaner/services/grammarCleaner.ts +++ b/tools/v1/individual/grammar-cleaner/services/grammarCleaner.ts @@ -1,9 +1,5 @@ export type GrammarIssueCategory = - | "spelling" - | "grammar" - | "punctuation" - | "capitalization" - | "redundancy"; + "spelling" | "grammar" | "punctuation" | "capitalization" | "redundancy"; export interface TextRange { start: number; @@ -32,10 +28,7 @@ export interface GrammarResult { } export type GrammarErrorCode = - | "empty-body" - | "unsupported-input" - | "input-too-large" - | "unsupported-dataset"; + "empty-body" | "unsupported-input" | "input-too-large" | "unsupported-dataset"; export type GrammarResultStatus = | { status: "ok"; result: GrammarResult } diff --git a/tools/v1/individual/grammar-cleaner/services/guards.ts b/tools/v1/individual/grammar-cleaner/services/guards.ts index 84b68ac9e..55542b61d 100644 --- a/tools/v1/individual/grammar-cleaner/services/guards.ts +++ b/tools/v1/individual/grammar-cleaner/services/guards.ts @@ -17,8 +17,7 @@ export interface GuardIssue { } export type SafeGrammarResult = - | GrammarResultStatus - | { status: "error"; code: GuardErrorCode; message: string }; + GrammarResultStatus | { status: "error"; code: GuardErrorCode; message: string }; // eslint-disable-next-line no-control-regex const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g; diff --git a/tools/v1/team/customer-support-macro-tool/services/macro.service.ts b/tools/v1/team/customer-support-macro-tool/services/macro.service.ts index a9a9edc91..565c4962e 100644 --- a/tools/v1/team/customer-support-macro-tool/services/macro.service.ts +++ b/tools/v1/team/customer-support-macro-tool/services/macro.service.ts @@ -13,12 +13,7 @@ // --------------------------------------------------------------------------- export type MacroCategory = - | "greeting" - | "billing" - | "technical" - | "shipping" - | "refund" - | "general"; + "greeting" | "billing" | "technical" | "shipping" | "refund" | "general"; export interface Macro { id: string; diff --git a/tools/v2/individual/cold-email-writer/types/coldEmailWriter.ts b/tools/v2/individual/cold-email-writer/types/coldEmailWriter.ts index 2a51fe435..c472b0394 100644 --- a/tools/v2/individual/cold-email-writer/types/coldEmailWriter.ts +++ b/tools/v2/individual/cold-email-writer/types/coldEmailWriter.ts @@ -34,10 +34,7 @@ export interface ColdEmailWriterOutput { } export type ColdEmailWriterErrorCode = - | "invalid-input" - | "invalid-options" - | "input-too-large" - | "empty-content"; + "invalid-input" | "invalid-options" | "input-too-large" | "empty-content"; export interface ColdEmailWriterValidationIssue { code: ColdEmailWriterErrorCode; diff --git a/tools/v2/individual/draft-improver/services/types.ts b/tools/v2/individual/draft-improver/services/types.ts index 5ed61d4fe..b062a6454 100644 --- a/tools/v2/individual/draft-improver/services/types.ts +++ b/tools/v2/individual/draft-improver/services/types.ts @@ -75,5 +75,4 @@ export interface DraftImproverError { * (near-instant) call. Success and error states are modelled explicitly. */ export type DraftImproverResult = - | { ok: true; analysis: DraftAnalysis } - | { ok: false; error: DraftImproverError }; + { ok: true; analysis: DraftAnalysis } | { ok: false; error: DraftImproverError }; diff --git a/tools/v2/individual/email-template-library/services/email-template-library.service.ts b/tools/v2/individual/email-template-library/services/email-template-library.service.ts index abaf629dd..b53fe5d86 100644 --- a/tools/v2/individual/email-template-library/services/email-template-library.service.ts +++ b/tools/v2/individual/email-template-library/services/email-template-library.service.ts @@ -117,13 +117,11 @@ export function executeEmailTemplateLibrary( } if (request.operation === "list") { - if ( - !( - request.categoryId === undefined || - request.categoryId === null || - typeof request.categoryId === "string" - ) - ) { + if (!( + request.categoryId === undefined || + request.categoryId === null || + typeof request.categoryId === "string" + )) { return failure( EMAIL_TEMPLATE_LIBRARY_ERROR_CODES.INVALID_REQUEST, "categoryId must be a string or null.", diff --git a/tools/v2/individual/email-template-library/types/index.ts b/tools/v2/individual/email-template-library/types/index.ts index 5e6989e19..87f54d937 100644 --- a/tools/v2/individual/email-template-library/types/index.ts +++ b/tools/v2/individual/email-template-library/types/index.ts @@ -50,9 +50,7 @@ export interface RenderTemplateRequest extends BaseRequest { } export type EmailTemplateLibraryRequest = - | ListTemplatesRequest - | GetTemplateRequest - | RenderTemplateRequest; + ListTemplatesRequest | GetTemplateRequest | RenderTemplateRequest; export interface ListTemplatesResult { operation: "list"; @@ -90,8 +88,7 @@ export interface EmailTemplateLibraryFailure { } export type EmailTemplateLibraryResponse = - | EmailTemplateLibrarySuccess - | EmailTemplateLibraryFailure; + EmailTemplateLibrarySuccess | EmailTemplateLibraryFailure; export interface EmailTemplateLibraryService { execute(request: EmailTemplateLibraryRequest): EmailTemplateLibraryResponse; diff --git a/tools/v2/individual/email-translator/services/translationService.ts b/tools/v2/individual/email-translator/services/translationService.ts index c0a85de0b..354c07f56 100644 --- a/tools/v2/individual/email-translator/services/translationService.ts +++ b/tools/v2/individual/email-translator/services/translationService.ts @@ -5,8 +5,7 @@ import { getTranslationProvider } from "./translationProvider"; * Translation service result wrapper. */ export type TranslationServiceResult = - | { success: true; result: TranslationResult } - | { success: false; error: TranslationError }; + { success: true; result: TranslationResult } | { success: false; error: TranslationError }; /** * Orchestrates translation requests. diff --git a/tools/v2/individual/pdf-summary-tool/INTEGRATION_CONSTRAINTS.md b/tools/v2/individual/pdf-summary-tool/INTEGRATION_CONSTRAINTS.md index aa9945cf6..3946ebe6d 100644 --- a/tools/v2/individual/pdf-summary-tool/INTEGRATION_CONSTRAINTS.md +++ b/tools/v2/individual/pdf-summary-tool/INTEGRATION_CONSTRAINTS.md @@ -212,9 +212,7 @@ const file = userSelectedFile; const records = await db.mail.findMany(); // ❌ DO NOT write to main app database -await db.summary.create({ - /* ... */ -}); +await db.summary.create({/* ... */}); // ❌ DO NOT use main app schema import { schema } from "../../../src/server/schema"; diff --git a/tools/v2/individual/private-note-on-email/types/privateNoteOnEmail.ts b/tools/v2/individual/private-note-on-email/types/privateNoteOnEmail.ts index 542085fc3..d1ba25b9e 100644 --- a/tools/v2/individual/private-note-on-email/types/privateNoteOnEmail.ts +++ b/tools/v2/individual/private-note-on-email/types/privateNoteOnEmail.ts @@ -43,11 +43,7 @@ export interface PrivateNoteAttachmentOutput { } export type PrivateNoteErrorCode = - | "invalid-input" - | "invalid-options" - | "note-too-long" - | "too-many-tags" - | "empty-note"; + "invalid-input" | "invalid-options" | "note-too-long" | "too-many-tags" | "empty-note"; export interface PrivateNoteValidationIssue { field: string; diff --git a/tools/v2/individual/readability-improver/types/readabilityImprover.ts b/tools/v2/individual/readability-improver/types/readabilityImprover.ts index 6d86da2fb..713f24656 100644 --- a/tools/v2/individual/readability-improver/types/readabilityImprover.ts +++ b/tools/v2/individual/readability-improver/types/readabilityImprover.ts @@ -12,11 +12,7 @@ export type IssueSeverity = "info" | "warn"; /** Which rule produced an issue. */ export type ReadabilityIssueType = - | "long-sentence" - | "complex-word" - | "passive-voice" - | "long-paragraph" - | "shouting"; + "long-sentence" | "complex-word" | "passive-voice" | "long-paragraph" | "shouting"; /** Where an issue was found. */ export type IssueSource = "subject" | "body"; diff --git a/tools/v2/team/approval-chain-builder/CONTRACT.md b/tools/v2/team/approval-chain-builder/CONTRACT.md index 65a9f4008..b4f423b17 100644 --- a/tools/v2/team/approval-chain-builder/CONTRACT.md +++ b/tools/v2/team/approval-chain-builder/CONTRACT.md @@ -47,8 +47,7 @@ The result is a discriminated union: ```ts type ApprovalChainBuilderResult = - | { ok: true; data: ApprovalChain } - | { ok: false; error: ApprovalChainError }; + { ok: true; data: ApprovalChain } | { ok: false; error: ApprovalChainError }; ``` A successful `ApprovalChain` contains a generated chain ID, normalized input diff --git a/tools/v2/team/approval-chain-builder/types/contract.ts b/tools/v2/team/approval-chain-builder/types/contract.ts index c81e3d7d6..b6192f186 100644 --- a/tools/v2/team/approval-chain-builder/types/contract.ts +++ b/tools/v2/team/approval-chain-builder/types/contract.ts @@ -67,8 +67,7 @@ export interface ApprovalChainError { } export type ApprovalChainBuilderResult = - | { ok: true; data: ApprovalChain } - | { ok: false; error: ApprovalChainError }; + { ok: true; data: ApprovalChain } | { ok: false; error: ApprovalChainError }; export type ExecuteApprovalChainBuilder = ( input: ApprovalChainBuilderInput, diff --git a/tools/v2/team/audit-log-viewer/contract.ts b/tools/v2/team/audit-log-viewer/contract.ts index 03c5879a5..4bc0d132d 100644 --- a/tools/v2/team/audit-log-viewer/contract.ts +++ b/tools/v2/team/audit-log-viewer/contract.ts @@ -26,8 +26,7 @@ export enum AuditErrorCode { /** Discriminated outcome returned by every contract operation. */ export type AuditResult = - | { ok: true; value: T } - | { ok: false; error: AuditErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: AuditErrorCode; message: string }; /** Operations supported by the audit log viewer contract. */ export type AuditOperation = diff --git a/tools/v2/team/client-priority-scoring/contract.ts b/tools/v2/team/client-priority-scoring/contract.ts index 18b636c5c..d04e788ee 100644 --- a/tools/v2/team/client-priority-scoring/contract.ts +++ b/tools/v2/team/client-priority-scoring/contract.ts @@ -17,8 +17,7 @@ export enum PriorityErrorCode { /** Discriminated outcome returned by every contract operation. */ export type PriorityResult = - | { ok: true; value: T } - | { ok: false; error: PriorityErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: PriorityErrorCode; message: string }; /** Operations supported by the priority contract. */ export type PriorityOperation = { diff --git a/tools/v2/team/client-thread-timeline/contract.ts b/tools/v2/team/client-thread-timeline/contract.ts index d89f2e315..33cbc425c 100644 --- a/tools/v2/team/client-thread-timeline/contract.ts +++ b/tools/v2/team/client-thread-timeline/contract.ts @@ -28,8 +28,7 @@ export enum TimelineErrorCode { /** Discriminated outcome returned by every contract operation. */ export type TimelineResult = - | { ok: true; value: T } - | { ok: false; error: TimelineErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: TimelineErrorCode; message: string }; /** Operations supported by the timeline contract. */ export type TimelineOperation = diff --git a/tools/v2/team/components/contract.ts b/tools/v2/team/components/contract.ts index 84f092972..e53d6b06f 100644 --- a/tools/v2/team/components/contract.ts +++ b/tools/v2/team/components/contract.ts @@ -19,13 +19,11 @@ export enum ComponentErrorCode { /** Discriminated outcome returned by every contract operation. */ export type ComponentResult = - | { ok: true; value: T } - | { ok: false; error: ComponentErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: ComponentErrorCode; message: string }; /** Operations supported by the components contract. */ export type ComponentOperation = - | { operation: "resolve"; input: ResolveComponentInput } - | { operation: "list" }; + { operation: "resolve"; input: ResolveComponentInput } | { operation: "list" }; /** Output produced by the contract, keyed by operation. */ export type ComponentContractOutput = diff --git a/tools/v2/team/docs/contract.ts b/tools/v2/team/docs/contract.ts index 66a70b81a..342d6a025 100644 --- a/tools/v2/team/docs/contract.ts +++ b/tools/v2/team/docs/contract.ts @@ -19,8 +19,7 @@ export enum DocErrorCode { /** Discriminated outcome returned by every contract operation. */ export type DocResult = - | { ok: true; value: T } - | { ok: false; error: DocErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: DocErrorCode; message: string }; /** Operations supported by the docs contract. */ export type DocOperation = { operation: "resolve"; input: ResolveDocInput }; diff --git a/tools/v2/team/invoice-approval-workflow/contract.ts b/tools/v2/team/invoice-approval-workflow/contract.ts index 5db7fa08a..f92237b5f 100644 --- a/tools/v2/team/invoice-approval-workflow/contract.ts +++ b/tools/v2/team/invoice-approval-workflow/contract.ts @@ -23,8 +23,7 @@ export enum InvoiceErrorCode { /** Discriminated outcome returned by every contract operation. */ export type InvoiceResult = - | { ok: true; value: T } - | { ok: false; error: InvoiceErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: InvoiceErrorCode; message: string }; /** Operations supported by the invoice approval contract. */ export type InvoiceOperation = diff --git a/tools/v2/team/knowledge-base-suggestion/core/engine.ts b/tools/v2/team/knowledge-base-suggestion/core/engine.ts index 8a98a7d8c..55c7768ef 100644 --- a/tools/v2/team/knowledge-base-suggestion/core/engine.ts +++ b/tools/v2/team/knowledge-base-suggestion/core/engine.ts @@ -45,8 +45,7 @@ export interface KbCorpusFilterResult { /** Discriminated outcome returned by every operation. */ export type KbResult = - | { ok: true; value: T } - | { ok: false; error: KbErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: KbErrorCode; message: string }; /** Operations supported by the contract. */ export type KbOperation = { operation: "suggest"; input: SuggestInput }; diff --git a/tools/v2/team/manager-review-queue/contract.ts b/tools/v2/team/manager-review-queue/contract.ts index 9e8e2e8ec..0e6309f1a 100644 --- a/tools/v2/team/manager-review-queue/contract.ts +++ b/tools/v2/team/manager-review-queue/contract.ts @@ -23,8 +23,7 @@ export enum ReviewErrorCode { } export type ReviewResult = - | { ok: true; value: T } - | { ok: false; error: ReviewErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: ReviewErrorCode; message: string }; export type ReviewOperation = | { operation: "fetch"; input: FetchQueueInput } diff --git a/tools/v2/team/project-mail-binder/types.ts b/tools/v2/team/project-mail-binder/types.ts index 6419af165..6842906c0 100644 --- a/tools/v2/team/project-mail-binder/types.ts +++ b/tools/v2/team/project-mail-binder/types.ts @@ -36,10 +36,7 @@ export type BinderStateSuccess = { }; export type BinderState = - | BinderStateEmpty - | BinderStateLoading - | BinderStateError - | BinderStateSuccess; + BinderStateEmpty | BinderStateLoading | BinderStateError | BinderStateSuccess; // --------------------------------------------------------------------------- // Type guards diff --git a/tools/v2/team/response-time-tracker/docs/execution-contract.md b/tools/v2/team/response-time-tracker/docs/execution-contract.md index 8e62ef569..95f77e40a 100644 --- a/tools/v2/team/response-time-tracker/docs/execution-contract.md +++ b/tools/v2/team/response-time-tracker/docs/execution-contract.md @@ -38,8 +38,7 @@ fixtures, not production callers. ```typescript type ResponseTimeQueryResult = - | { ok: true; data: ResponseTimeQueryData } - | { ok: false; error: ResponseTimeQueryError }; + { ok: true; data: ResponseTimeQueryData } | { ok: false; error: ResponseTimeQueryError }; interface ResponseTimeQueryData { entries: ResponseTimeEntry[]; diff --git a/tools/v2/team/response-time-tracker/services/execution-contract.ts b/tools/v2/team/response-time-tracker/services/execution-contract.ts index b6af4fe31..a9548f182 100644 --- a/tools/v2/team/response-time-tracker/services/execution-contract.ts +++ b/tools/v2/team/response-time-tracker/services/execution-contract.ts @@ -37,8 +37,7 @@ export interface ResponseTimeQueryError { } export type ResponseTimeQueryResult = - | { ok: true; data: ResponseTimeQueryData } - | { ok: false; error: ResponseTimeQueryError }; + { ok: true; data: ResponseTimeQueryData } | { ok: false; error: ResponseTimeQueryError }; function isValidRange(range: DateRange): boolean { const start = new Date(range.start).getTime(); diff --git a/tools/v2/team/shared-contact-notes/contract.ts b/tools/v2/team/shared-contact-notes/contract.ts index 45d67842d..a22116ae4 100644 --- a/tools/v2/team/shared-contact-notes/contract.ts +++ b/tools/v2/team/shared-contact-notes/contract.ts @@ -25,8 +25,7 @@ export enum NoteErrorCode { /** Discriminated outcome returned by every contract operation. */ export type NotesResult = - | { ok: true; value: T } - | { ok: false; error: NoteErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: NoteErrorCode; message: string }; /** Operations supported by the notes contract. */ export type NotesOperation = diff --git a/tools/v2/team/suspicious-sender-watchlist/contract.ts b/tools/v2/team/suspicious-sender-watchlist/contract.ts index 246012532..0d49f34c8 100644 --- a/tools/v2/team/suspicious-sender-watchlist/contract.ts +++ b/tools/v2/team/suspicious-sender-watchlist/contract.ts @@ -33,8 +33,7 @@ export enum WatchlistErrorCode { /** Discriminated outcome returned by every contract operation. */ export type WatchlistResult = - | { ok: true; value: T } - | { ok: false; error: WatchlistErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: WatchlistErrorCode; message: string }; /** Inputs accepted by the contract, keyed by operation. */ export type WatchlistContractInput = diff --git a/tools/v2/team/team-digest-generator/contract.ts b/tools/v2/team/team-digest-generator/contract.ts index 2f2f781b0..132376a2e 100644 --- a/tools/v2/team/team-digest-generator/contract.ts +++ b/tools/v2/team/team-digest-generator/contract.ts @@ -24,8 +24,7 @@ export enum DigestErrorCode { /** Discriminated outcome returned by every contract operation. */ export type DigestResult = - | { ok: true; value: T } - | { ok: false; error: DigestErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: DigestErrorCode; message: string }; /** Operations supported by the digest contract. */ export type DigestOperation = { diff --git a/tools/v2/team/team-inbox-rules-builder/docs/ARCHITECTURE.md b/tools/v2/team/team-inbox-rules-builder/docs/ARCHITECTURE.md index 009cec47b..52c21ab0b 100644 --- a/tools/v2/team/team-inbox-rules-builder/docs/ARCHITECTURE.md +++ b/tools/v2/team/team-inbox-rules-builder/docs/ARCHITECTURE.md @@ -206,14 +206,7 @@ interface ConditionGroup { // Available actions type RuleActionType = - | "fileToFolder" - | "forwardTo" - | "markAs" - | "flag" - | "notify" - | "autoReply" - | "addLabel" - | "delete"; + "fileToFolder" | "forwardTo" | "markAs" | "flag" | "notify" | "autoReply" | "addLabel" | "delete"; // Action configuration interface RuleAction { diff --git a/tools/v2/team/team-inbox-rules-builder/types/execution.ts b/tools/v2/team/team-inbox-rules-builder/types/execution.ts index 46c226cb0..5efdc16e5 100644 --- a/tools/v2/team/team-inbox-rules-builder/types/execution.ts +++ b/tools/v2/team/team-inbox-rules-builder/types/execution.ts @@ -12,10 +12,7 @@ export interface TeamInboxRulesExecutionInput { /** Stable error codes. Consumers should branch on these codes, not messages. */ export type TeamInboxRulesExecutionErrorCode = - | "INVALID_INPUT" - | "INVALID_MAIL" - | "INVALID_RULE" - | "EXECUTION_FAILED"; + "INVALID_INPUT" | "INVALID_MAIL" | "INVALID_RULE" | "EXECUTION_FAILED"; export interface TeamInboxRulesTriggeredAction { ruleId: string; diff --git a/tools/v2/team/team-inbox-rules-builder/types/rules.ts b/tools/v2/team/team-inbox-rules-builder/types/rules.ts index 6e1d32a37..692a78025 100644 --- a/tools/v2/team/team-inbox-rules-builder/types/rules.ts +++ b/tools/v2/team/team-inbox-rules-builder/types/rules.ts @@ -42,14 +42,7 @@ export interface ConditionGroup { } export type RuleActionType = - | "fileToFolder" - | "forwardTo" - | "markAs" - | "flag" - | "notify" - | "autoReply" - | "addLabel" - | "delete"; + "fileToFolder" | "forwardTo" | "markAs" | "flag" | "notify" | "autoReply" | "addLabel" | "delete"; export interface RuleAction { id: ActionId; diff --git a/tools/v2/team/team-security-flagging/contract/execution-contract.d.ts b/tools/v2/team/team-security-flagging/contract/execution-contract.d.ts index 7bb3f4fad..f6a620dcd 100644 --- a/tools/v2/team/team-security-flagging/contract/execution-contract.d.ts +++ b/tools/v2/team/team-security-flagging/contract/execution-contract.d.ts @@ -46,8 +46,7 @@ export interface SecurityFlaggingError { } export type SecurityFlaggingOutput = - | { ok: true; data: SecurityFlaggingRecord } - | { ok: false; error: SecurityFlaggingError }; + { ok: true; data: SecurityFlaggingRecord } | { ok: false; error: SecurityFlaggingError }; /** I/O and environmental behavior supplied by the backend caller. */ export interface SecurityFlaggingDependencies { diff --git a/tools/v2/team/team-workload-balancer/contract.ts b/tools/v2/team/team-workload-balancer/contract.ts index a489ffe4f..c77add57c 100644 --- a/tools/v2/team/team-workload-balancer/contract.ts +++ b/tools/v2/team/team-workload-balancer/contract.ts @@ -20,8 +20,7 @@ export enum WorkloadErrorCode { /** Discriminated outcome returned by every contract operation. */ export type WorkloadResult = - | { ok: true; value: T } - | { ok: false; error: WorkloadErrorCode; message: string }; + { ok: true; value: T } | { ok: false; error: WorkloadErrorCode; message: string }; /** Operations supported by the workload contract. */ export type WorkloadOperation = {