@sylphx/sdk 0.15.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/compute.ts","../src/config/database-pricing.ts","../src/config/referrals.ts","../src/error-extract.ts","../src/connection-url.ts","../src/config.ts","../src/csv.ts","../src/formatting.ts","../src/json.ts","../src/utils.ts","../src/utils/user-agent.ts","../src/workspaces.ts","../src/config/auth.ts","../src/config/billing.ts","../src/config/console-keys.ts","../src/config/instance-types.ts","../src/config/platform-plans.ts","../src/debug.ts","../src/rest-client.ts","../src/key-validation.ts","../src/index.ts","../src/auth.ts","../src/dpop.ts","../src/platform-auth.ts","../src/platform-impersonation.ts","../src/platform-jwt.ts","../src/platform-oauth.ts","../src/oauth-token.ts","../src/platform-password.ts","../src/platform-sessions.ts","../src/platform-user.ts","../src/audit.ts","../src/rate-limits.ts","../src/functions.ts","../src/realtime.ts","../src/realtime-admin.ts","../src/admin.ts","../src/analytics.ts","../src/ai.ts","../src/billing.ts","../src/storage.ts","../src/lib/retry.ts","../src/lib/notifications/service-worker.ts","../src/notifications.ts","../src/lib/triggers/index.ts","../src/lib/tasks/handler.ts","../src/lib/tasks/api-functions.ts","../src/flags.ts","../src/webhooks.ts","../src/email.ts","../src/consent.ts","../src/referrals.ts","../src/lib/engagement/types.ts","../src/engagement.ts","../src/orgs.ts","../src/permissions.ts","../src/roles.ts","../src/secrets.ts","../src/search.ts","../src/database.ts","../src/kv.ts","../src/deploy.ts","../src/monitoring.ts","../src/sandbox.ts","../src/workers.ts","../src/logs.ts","../src/misc.ts","../src/user.ts","../src/security.ts","../src/oauth.ts","../src/promo.ts"],"sourcesContent":["/**\n * SDK Constants — Single Source of Truth\n *\n * Shared constants used across the SDK. Centralizing these\n * prevents magic number duplication and makes changes easier.\n *\n * IMPORTANT: All time-based constants should be used consistently\n * across the SDK. Never hardcode magic numbers like 30000, 5 * 60 * 1000, etc.\n */\n\n// =============================================================================\n// API Configuration\n// =============================================================================\n\n/**\n * Canonical environment variable name for the client connection URL.\n *\n * Format: sylphx://pk_prod_{hex}@tenant-slug.api.sylphx.com\n * Browser-safe (publishable key only).\n */\nexport const ENV_URL = 'SYLPHX_URL'\n\n/**\n * Canonical environment variable name for the Next.js public connection URL.\n *\n * Same format as ENV_URL, browser-safe.\n */\nexport const ENV_PUBLIC_URL = 'NEXT_PUBLIC_SYLPHX_URL'\n\n/**\n * Canonical environment variable name for the server secret connection URL.\n *\n * Format: sylphx://sk_prod_{hex}@tenant-slug.api.sylphx.com\n * Server-side only — never expose to client.\n */\nexport const ENV_SECRET_URL = 'SYLPHX_SECRET_URL'\n\n/**\n * Resolve the connection URL from environment variables.\n *\n * Checks SYLPHX_URL first, then NEXT_PUBLIC_SYLPHX_URL.\n */\nexport function resolveUrl(explicit?: string): string | undefined {\n\treturn explicit || process.env[ENV_URL] || process.env[ENV_PUBLIC_URL]\n}\n\n/**\n * Resolve the secret connection URL from environment variables.\n */\nexport function resolveSecretUrl(explicit?: string): string | undefined {\n\treturn explicit || process.env[ENV_SECRET_URL]\n}\n\n/**\n * SDK API path for the per-project subdomain-based SDK server.\n *\n * The full base URL is built as: https://{tenant-slug}.api.sylphx.com/v1\n */\nexport const SDK_API_PATH = `/v1`\nexport const SDK_API_VERSION = 'v1'\n\n/**\n * Default SDK API host.\n *\n * Management API: https://api.sylphx.com/v1/*\n * BaaS/SDK API: https://{tenant-slug}.api.sylphx.com/v1/*\n */\nexport const DEFAULT_SDK_API_HOST = 'api.sylphx.com'\nexport const DEFAULT_PLATFORM_URL = 'https://sylphx.com'\n\n/**\n * Default auth route prefix\n *\n * Used for OAuth callbacks and signout routes.\n * Must match the middleware's authPrefix config.\n */\nexport const DEFAULT_AUTH_PREFIX = '/auth'\n\n/**\n * SDK package version\n *\n * Sent in X-SDK-Version header for debugging and analytics.\n * Update this when releasing new SDK versions.\n */\nexport const SDK_VERSION = '0.5.0'\n\n/**\n * SDK platform identifier\n *\n * Sent in X-SDK-Platform header to identify the runtime environment.\n */\ntype SdkRuntimeGlobal = typeof globalThis & {\n\treadonly EdgeRuntime?: string\n}\n\nfunction detectSdkPlatform(): 'browser' | 'edge' | 'node' {\n\tif (typeof window !== 'undefined') return 'browser'\n\tconst runtimeGlobal = globalThis as SdkRuntimeGlobal\n\tif (typeof runtimeGlobal.EdgeRuntime !== 'undefined') return 'edge'\n\treturn 'node'\n}\n\nexport const SDK_PLATFORM = detectSdkPlatform()\n\n// =============================================================================\n// Timeouts & Durations\n// =============================================================================\n\n/** Default request timeout in milliseconds (30 seconds) */\nexport const DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Sandbox create waits for the Runtime API to provision runtime resources and for\n * the exec-server readiness probe to pass. The server-side readiness budget is\n * 120s; this client budget adds transport margin without changing the default\n * timeout for ordinary API calls.\n */\nexport const SANDBOX_CREATE_TIMEOUT_MS = 150_000\n\n/**\n * Token expiry buffer in milliseconds (30 seconds)\n *\n * Refresh tokens this many milliseconds BEFORE they expire\n * to account for network latency and clock skew.\n */\nexport const TOKEN_EXPIRY_BUFFER_MS = 30_000\n\n/**\n * Session token lifetime in seconds (5 minutes)\n *\n * Matches Clerk's short-lived access token pattern.\n * Used for cookie maxAge and React Query staleTime.\n */\nexport const SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60\n\n/** Session token lifetime in milliseconds (for React Query staleTime) */\nexport const SESSION_TOKEN_LIFETIME_MS = SESSION_TOKEN_LIFETIME_SECONDS * 1000\n\n/**\n * Refresh token lifetime in seconds (30 days)\n *\n * Long-lived token for silent refresh.\n */\nexport const REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60\n\n// =============================================================================\n// Feature Flags Cache\n// =============================================================================\n\n/**\n * Feature flags cache TTL in milliseconds (5 minutes)\n *\n * How long to cache flags before fetching fresh values.\n * Matches LaunchDarkly's default streaming connection behavior.\n */\nexport const FLAGS_CACHE_TTL_MS = 5 * 60 * 1000\n\n/**\n * Feature flags stale-while-revalidate window in milliseconds (1 minute)\n *\n * Allow serving stale flags while fetching fresh values.\n */\nexport const FLAGS_STALE_WHILE_REVALIDATE_MS = 60 * 1000\n\n// =============================================================================\n// Retry & Backoff\n// =============================================================================\n\n/** Maximum retry delay for exponential backoff (30 seconds) */\nexport const MAX_RETRY_DELAY_MS = 30_000\n\n/** Base retry delay for exponential backoff (1 second) */\nexport const BASE_RETRY_DELAY_MS = 1_000\n\n/** Maximum number of retries for network requests */\nexport const MAX_RETRIES = 3\n\n// =============================================================================\n// Analytics\n// =============================================================================\n\n/**\n * Analytics session timeout in milliseconds (30 minutes)\n *\n * After this much inactivity, a new session is started.\n */\nexport const ANALYTICS_SESSION_TIMEOUT_MS = 30 * 60 * 1000\n\n// =============================================================================\n// Webhooks\n// =============================================================================\n\n/**\n * Maximum age for webhook signature validation (5 minutes)\n *\n * Reject webhooks with timestamps older than this.\n */\nexport const WEBHOOK_MAX_AGE_MS = 5 * 60 * 1000\n\n/**\n * Clock skew allowance for webhook validation (30 seconds)\n *\n * Allow timestamps this far in the future.\n */\nexport const WEBHOOK_CLOCK_SKEW_MS = 30 * 1000\n\n// =============================================================================\n// PKCE (OAuth)\n// =============================================================================\n\n/**\n * PKCE code verifier TTL in milliseconds (10 minutes)\n *\n * How long the code verifier is stored during OAuth flow.\n */\nexport const PKCE_CODE_TTL_MS = 10 * 60 * 1000\n\n// =============================================================================\n// Jobs\n// =============================================================================\n\n/**\n * Job dead-letter queue retention in milliseconds (7 days)\n */\nexport const JOBS_DLQ_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\n// =============================================================================\n// Session Replay\n// =============================================================================\n\n/**\n * Maximum session replay recording duration in milliseconds (60 minutes)\n */\nexport const SESSION_REPLAY_MAX_DURATION_MS = 60 * 60 * 1000\n\n/**\n * Session replay upload interval in milliseconds (5 seconds)\n */\nexport const SESSION_REPLAY_UPLOAD_INTERVAL_MS = 5_000\n\n/**\n * Session replay scroll event throttle interval (150 ms)\n */\nexport const SESSION_REPLAY_SCROLL_THROTTLE_MS = 150\n\n/**\n * Session replay media time update throttle interval (800 ms)\n */\nexport const SESSION_REPLAY_MEDIA_THROTTLE_MS = 800\n\n/**\n * Session replay rage click detection window (1 second)\n */\nexport const SESSION_REPLAY_RAGE_CLICK_WINDOW_MS = 1_000\n\n/**\n * Session replay dead click detection timeout (500 ms)\n */\nexport const SESSION_REPLAY_DEAD_CLICK_TIMEOUT_MS = 500\n\n/**\n * Session replay scroll heat detection window (2 seconds)\n */\nexport const SESSION_REPLAY_SCROLL_HEAT_WINDOW_MS = 2_000\n\n/**\n * Session replay status check interval (5 seconds)\n */\nexport const SESSION_REPLAY_STATUS_CHECK_MS = 5_000\n\n// =============================================================================\n// Analytics (Extended)\n// =============================================================================\n\n/**\n * Analytics event flush interval in milliseconds (5 seconds)\n */\nexport const ANALYTICS_FLUSH_INTERVAL_MS = 5_000\n\n/**\n * Analytics maximum text length for autocapture (100 characters)\n */\nexport const ANALYTICS_MAX_TEXT_LENGTH = 100\n\n/**\n * Analytics flush timeout in milliseconds (1 second)\n */\nexport const ANALYTICS_FLUSH_TIMEOUT_MS = 1_000\n\n/**\n * Analytics interval check in milliseconds (1 second)\n */\nexport const ANALYTICS_INTERVAL_CHECK_MS = 1_000\n\n/**\n * Analytics retry base delay in milliseconds (1 second)\n * Exponential backoff: delay = base * 2^retries (with jitter)\n */\nexport const ANALYTICS_RETRY_BASE_DELAY_MS = 1_000\n\n/**\n * Analytics retry max delay in milliseconds (30 seconds)\n */\nexport const ANALYTICS_RETRY_MAX_DELAY_MS = 30_000\n\n/**\n * Analytics retry jitter factor (±20%)\n * Prevents thundering herd when multiple clients retry simultaneously\n */\nexport const ANALYTICS_RETRY_JITTER = 0.2\n\n/**\n * Analytics maximum retries before dropping event (Segment pattern: 10)\n */\nexport const ANALYTICS_MAX_RETRIES = 10\n\n// =============================================================================\n// Feature Flags (Extended)\n// =============================================================================\n\n/**\n * Feature flags exposure deduplication window (1 hour)\n *\n * Prevents duplicate exposure events for A/B tests within this window.\n */\nexport const FLAGS_EXPOSURE_DEDUPE_WINDOW_MS = 60 * 60 * 1000\n\n/**\n * Flag stream initial reconnection delay (1 second)\n */\nexport const FLAGS_STREAM_INITIAL_RECONNECT_MS = 1_000\n\n/**\n * Flag stream maximum reconnection delay (30 seconds)\n */\nexport const FLAGS_STREAM_MAX_RECONNECT_MS = 30_000\n\n/**\n * Flag stream heartbeat timeout (45 seconds)\n */\nexport const FLAGS_STREAM_HEARTBEAT_TIMEOUT_MS = 45_000\n\n/**\n * Flag HTTP polling interval fallback (60 seconds)\n */\nexport const FLAGS_HTTP_POLLING_INTERVAL_MS = 60_000\n\n// =============================================================================\n// Jobs (Extended)\n// =============================================================================\n\n/**\n * Default retry delay sequence for exponential backoff (ms)\n */\nexport const DEFAULT_RETRY_DELAYS_MS = [1_000, 5_000, 15_000, 30_000, 60_000] as const\n\n/**\n * Default job timeout in milliseconds (60 seconds)\n */\nexport const JOB_DEFAULT_TIMEOUT_MS = 60_000\n\n/**\n * Default job status polling interval (2 seconds)\n */\nexport const JOB_POLL_INTERVAL_MS = 2_000\n\n// =============================================================================\n// Storage Keys & Prefixes\n// =============================================================================\n\n/**\n * Storage key prefix for SDK data\n */\nexport const STORAGE_KEY_PREFIX = 'sylphx_'\n\n/**\n * localStorage key for cached feature flags\n */\nexport const FLAGS_CACHE_KEY = 'sylphx_feature_flags'\n\n/**\n * localStorage key for feature flags cache timestamp\n */\nexport const FLAGS_CACHE_TIMESTAMP_KEY = 'sylphx_feature_flags_ts'\n\n/**\n * localStorage key for feature flags overrides\n */\nexport const FLAGS_OVERRIDES_KEY = 'sylphx_feature_flags_overrides'\n\n/**\n * localStorage key for active organization\n */\nexport const ORG_STORAGE_KEY = 'sylphx_active_org'\n\n/**\n * BroadcastChannel name for cross-tab org sync\n */\nexport const ORG_BROADCAST_CHANNEL = 'sylphx_org_sync'\n\n/**\n * Storage prefix for PKCE verifiers\n */\nexport const PKCE_STORAGE_PREFIX = 'sylphx_pkce_'\n\n/**\n * Test key for checking storage availability\n */\nexport const STORAGE_TEST_KEY = '__sylphx_test__'\n\n/**\n * Cookie/storage name for analytics sessions\n */\nexport const ANALYTICS_SESSION_KEY = 'sylphx_session'\n\n/**\n * Default storage key for flags persistence\n */\nexport const FLAGS_STORAGE_KEY = 'sylphx_flags'\n\n// =============================================================================\n// Click ID & Attribution\n// =============================================================================\n\n/**\n * Click ID attribution window in milliseconds (90 days)\n *\n * How long click IDs are stored for conversion attribution.\n */\nexport const CLICK_ID_EXPIRY_MS = 90 * 24 * 60 * 60 * 1000\n\n// =============================================================================\n// React Query Stale Times\n// =============================================================================\n\n/**\n * React Query staleTime for frequently-changing data (1 minute)\n *\n * Use for: real-time metrics, live feeds, active sessions\n */\nexport const STALE_TIME_FREQUENT_MS = 60 * 1_000\n\n/**\n * React Query staleTime for moderately-changing data (2 minutes)\n *\n * Use for: subscriptions, user profiles, preferences\n */\nexport const STALE_TIME_MODERATE_MS = 2 * 60 * 1_000\n\n/**\n * React Query staleTime for stable/config data (5 minutes)\n *\n * Use for: plans, feature flags, app config\n */\nexport const STALE_TIME_STABLE_MS = 5 * 60 * 1_000\n\n/**\n * React Query staleTime for webhook stats (30 seconds)\n */\nexport const STALE_TIME_STATS_MS = 30 * 1_000\n\n// =============================================================================\n// UI Component Timeouts\n// =============================================================================\n\n/**\n * Copy-to-clipboard feedback display duration (2 seconds)\n */\nexport const UI_COPY_FEEDBACK_MS = 2_000\n\n/**\n * Form success message display duration (3 seconds)\n */\nexport const UI_FORM_SUCCESS_MS = 3_000\n\n/**\n * General notification display duration (5 seconds)\n */\nexport const UI_NOTIFICATION_MS = 5_000\n\n/**\n * Prompt auto-show delay (3 seconds)\n */\nexport const UI_PROMPT_DELAY_MS = 3_000\n\n/**\n * Redirect delay after action (3 seconds)\n */\nexport const UI_REDIRECT_DELAY_MS = 3_000\n\n/**\n * Animation out duration (200 ms)\n */\nexport const UI_ANIMATION_OUT_MS = 200\n\n/**\n * Animation in duration (300 ms)\n */\nexport const UI_ANIMATION_IN_MS = 300\n\n// =============================================================================\n// Email & Verification\n// =============================================================================\n\n/**\n * Email resend cooldown tick interval (1 second)\n */\nexport const EMAIL_RESEND_COOLDOWN_TICK_MS = 1_000\n\n/**\n * New user detection threshold (1 minute)\n *\n * Users created within this window are considered \"new\" for signup tracking.\n */\nexport const NEW_USER_THRESHOLD_MS = 60 * 1_000\n\n// =============================================================================\n// Web Vitals Thresholds\n// =============================================================================\n\n/**\n * FCP (First Contentful Paint) \"good\" threshold (1800 ms)\n */\nexport const WEB_VITALS_FCP_GOOD_MS = 1_800\n\n/**\n * FCP (First Contentful Paint) \"poor\" threshold (3000 ms)\n */\nexport const WEB_VITALS_FCP_POOR_MS = 3_000\n\n// =============================================================================\n// Storage Sizes\n// =============================================================================\n\n/**\n * Multipart upload threshold (5 MB)\n *\n * Files larger than this use multipart upload for better reliability.\n */\nexport const STORAGE_MULTIPART_THRESHOLD_BYTES = 5 * 1024 * 1024\n\n/**\n * Default max file size for uploads (5 MB)\n */\nexport const STORAGE_DEFAULT_MAX_SIZE_BYTES = 5 * 1024 * 1024\n\n/**\n * Avatar max file size (2 MB)\n */\nexport const STORAGE_AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024\n\n/**\n * Large file max size for file uploads (10 MB)\n */\nexport const STORAGE_LARGE_MAX_SIZE_BYTES = 10 * 1024 * 1024\n\n// =============================================================================\n// Cache TTLs\n// =============================================================================\n\n/**\n * JWK cache TTL (1 hour)\n */\nexport const JWK_CACHE_TTL_MS = 60 * 60 * 1000\n\n// =============================================================================\n// Analytics Event Tracking\n// =============================================================================\n\n/**\n * Max tracked event IDs to keep in memory\n */\nexport const ANALYTICS_MAX_TRACKED_EVENT_IDS = 1000\n\n/**\n * Number of event IDs to keep after cleanup\n */\nexport const ANALYTICS_TRACKED_IDS_KEEP = 500\n\n/**\n * Analytics queue limit before force flush\n */\nexport const ANALYTICS_QUEUE_LIMIT = 100\n\n// =============================================================================\n// Session Replay (Extended)\n// =============================================================================\n\n/**\n * Session replay check interval (1 second)\n */\nexport const SESSION_REPLAY_CHECK_INTERVAL_MS = 1_000\n\n/**\n * Success feedback delay for invite/account actions (1.5 seconds)\n */\nexport const UI_SUCCESS_REDIRECT_MS = 1_500\n\n// =============================================================================\n// String Truncation Limits\n// =============================================================================\n\n/**\n * Max message length for logging (1000 chars)\n */\nexport const LOG_MESSAGE_MAX_LENGTH = 1_000\n\n/**\n * Max DOM snapshot length for debugging (1000 chars)\n */\nexport const DOM_SNAPSHOT_MAX_LENGTH = 1_000\n\n/**\n * Max stack trace length for error tracking (500 chars)\n */\nexport const STACK_TRACE_MAX_LENGTH = 500\n\n/**\n * Google Consent Mode wait for update timeout (500 ms)\n */\nexport const CONSENT_WAIT_FOR_UPDATE_MS = 500\n\n// =============================================================================\n// Time Unit Conversions\n// =============================================================================\n\n/** Milliseconds per minute (60,000) */\nexport const MS_PER_MINUTE = 60_000\n\n/** Milliseconds per hour (3,600,000) */\nexport const MS_PER_HOUR = 3_600_000\n\n/** Milliseconds per day (86,400,000) */\nexport const MS_PER_DAY = 86_400_000\n\n/** Seconds per minute (60) */\nexport const SECONDS_PER_MINUTE = 60\n\n/** Seconds per hour (3,600) */\nexport const SECONDS_PER_HOUR = 3_600\n\n// =============================================================================\n// Z-Index Values\n// =============================================================================\n\n/** Z-index for modal overlays (9999) */\nexport const Z_INDEX_OVERLAY = 9999\n\n/** Z-index for critical overlays like feature gates (99999) */\nexport const Z_INDEX_CRITICAL_OVERLAY = 99999\n\n// =============================================================================\n// API Key Expiry (seconds)\n// =============================================================================\n\n/** API key expiry: 1 day (86,400 seconds) */\nexport const API_KEY_EXPIRY_1_DAY = 86_400\n\n/** API key expiry: 7 days (604,800 seconds) */\nexport const API_KEY_EXPIRY_7_DAYS = 604_800\n\n/** API key expiry: 30 days (2,592,000 seconds) */\nexport const API_KEY_EXPIRY_30_DAYS = 2_592_000\n\n/** API key expiry: 90 days (7,776,000 seconds) */\nexport const API_KEY_EXPIRY_90_DAYS = 7_776_000\n\n/** API key expiry: 1 year (31,536,000 seconds) */\nexport const API_KEY_EXPIRY_1_YEAR = 31_536_000\n\n// =============================================================================\n// Web Vitals Thresholds (Google standards)\n// =============================================================================\n\n/** LCP (Largest Contentful Paint) \"good\" threshold (2500 ms) */\nexport const WEB_VITALS_LCP_GOOD_MS = 2_500\n\n/** LCP (Largest Contentful Paint) \"poor\" threshold (4000 ms) */\nexport const WEB_VITALS_LCP_POOR_MS = 4_000\n\n/** INP (Interaction to Next Paint) \"good\" threshold (200 ms) */\nexport const WEB_VITALS_INP_GOOD_MS = 200\n\n/** INP (Interaction to Next Paint) \"poor\" threshold (500 ms) */\nexport const WEB_VITALS_INP_POOR_MS = 500\n\n/** TTFB (Time to First Byte) \"good\" threshold (800 ms) */\nexport const WEB_VITALS_TTFB_GOOD_MS = 800\n\n/** TTFB (Time to First Byte) \"poor\" threshold (1800 ms) */\nexport const WEB_VITALS_TTFB_POOR_MS = 1_800\n\n// =============================================================================\n// Security\n// =============================================================================\n\n/** Minimum password length (NIST SP 800-63B recommends 12+) */\nexport const MIN_PASSWORD_LENGTH = 12\n\n// =============================================================================\n// AI\n// =============================================================================\n\n/** Default context window for AI models (4096 tokens) */\nexport const DEFAULT_CONTEXT_WINDOW = 4_096\n\n// =============================================================================\n// Circuit Breaker (AWS/Resilience4j pattern)\n// =============================================================================\n\n/**\n * Circuit breaker failure threshold\n *\n * Number of failures in the window before circuit opens.\n */\nexport const CIRCUIT_BREAKER_FAILURE_THRESHOLD = 5\n\n/**\n * Circuit breaker failure window in milliseconds (10 seconds)\n *\n * Time window for counting failures.\n */\nexport const CIRCUIT_BREAKER_WINDOW_MS = 10_000\n\n/**\n * Circuit breaker open duration in milliseconds (30 seconds)\n *\n * How long the circuit stays open before allowing a test request.\n */\nexport const CIRCUIT_BREAKER_OPEN_DURATION_MS = 30_000\n\n// =============================================================================\n// ETag Cache (HTTP conditional requests)\n// =============================================================================\n\n/**\n * Maximum ETag cache entries\n *\n * LRU eviction when exceeded.\n */\nexport const ETAG_CACHE_MAX_ENTRIES = 100\n\n/**\n * ETag cache TTL in milliseconds (5 minutes)\n *\n * How long cached responses are valid.\n */\nexport const ETAG_CACHE_TTL_MS = 5 * 60 * 1000\n","/**\n * Sylphx SDK Error Classes\n *\n * Typed error classes for better error handling and debugging.\n * Compatible with tRPC error codes and provides rich context.\n *\n * @example\n * ```typescript\n * import { SylphxError, isRetryableError, getErrorMessage } from '@sylphx/sdk'\n *\n * try {\n * await sylphx.auth.login.mutate({ email, password })\n * } catch (error) {\n * if (error instanceof SylphxError) {\n * console.log(error.code) // 'UNAUTHORIZED'\n * console.log(error.isRetryable) // false\n * }\n * if (isRetryableError(error)) {\n * // Safe to retry\n * }\n * }\n * ```\n */\n\nimport { BASE_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, MAX_RETRY_DELAY_MS } from './constants'\n\n// ============================================================================\n// Error Codes (aligned with tRPC and HTTP semantics)\n// ============================================================================\n\nexport type SylphxErrorCode =\n\t// Client errors (4xx)\n\t| 'BAD_REQUEST' // 400 - Invalid input\n\t| 'UNAUTHORIZED' // 401 - Not authenticated\n\t| 'FORBIDDEN' // 403 - Not authorized\n\t| 'NOT_FOUND' // 404 - Resource not found\n\t| 'CONFLICT' // 409 - Resource conflict (e.g., duplicate)\n\t| 'PAYLOAD_TOO_LARGE' // 413 - Request too large\n\t| 'UNPROCESSABLE_ENTITY' // 422 - Validation failed\n\t| 'TOO_MANY_REQUESTS' // 429 - Rate limited\n\t| 'QUOTA_EXCEEDED' // 402/429 - Quota/plan limit exceeded\n\t// Server errors (5xx)\n\t| 'INTERNAL_SERVER_ERROR' // 500 - Server error\n\t| 'NOT_IMPLEMENTED' // 501 - Feature not available\n\t| 'BAD_GATEWAY' // 502 - Upstream error\n\t| 'SERVICE_UNAVAILABLE' // 503 - Temporarily unavailable\n\t| 'GATEWAY_TIMEOUT' // 504 - Upstream timeout\n\t// Network/Client errors\n\t| 'NETWORK_ERROR' // Network failure\n\t| 'TIMEOUT' // Request timeout\n\t| 'ABORTED' // Request aborted\n\t// SDK-specific\n\t| 'PARSE_ERROR' // JSON/response parse error\n\t| 'UNKNOWN' // Unknown error\n\n/**\n * Simplified semantic error codes (DX-friendly aliases).\n * Maps to the more granular SylphxErrorCode internally.\n *\n * @example\n * ```ts\n * if (SylphxError.isRateLimited(err)) {\n * console.log(`Retry after ${err.retryAfter}s`)\n * }\n * ```\n */\nexport type ErrorCode =\n\t| 'UNAUTHORIZED'\n\t| 'FORBIDDEN'\n\t| 'NOT_FOUND'\n\t| 'RATE_LIMITED'\n\t| 'QUOTA_EXCEEDED'\n\t| 'VALIDATION_ERROR'\n\t| 'NETWORK_ERROR'\n\t| 'UPSTREAM_ERROR'\n\t| 'INTERNAL_ERROR'\n\n/**\n * HTTP status code mapping for error codes\n */\nexport const ERROR_CODE_STATUS: Record<SylphxErrorCode, number> = {\n\tBAD_REQUEST: 400,\n\tUNAUTHORIZED: 401,\n\tFORBIDDEN: 403,\n\tNOT_FOUND: 404,\n\tCONFLICT: 409,\n\tPAYLOAD_TOO_LARGE: 413,\n\tUNPROCESSABLE_ENTITY: 422,\n\tTOO_MANY_REQUESTS: 429,\n\tQUOTA_EXCEEDED: 402,\n\tINTERNAL_SERVER_ERROR: 500,\n\tNOT_IMPLEMENTED: 501,\n\tBAD_GATEWAY: 502,\n\tSERVICE_UNAVAILABLE: 503,\n\tGATEWAY_TIMEOUT: 504,\n\tNETWORK_ERROR: 0,\n\tTIMEOUT: 0,\n\tABORTED: 0,\n\tPARSE_ERROR: 0,\n\tUNKNOWN: 0,\n}\n\n/**\n * Retryable error codes (safe to retry automatically)\n */\nexport const RETRYABLE_CODES: Set<SylphxErrorCode> = new Set([\n\t'NETWORK_ERROR',\n\t'TIMEOUT',\n\t'BAD_GATEWAY',\n\t'SERVICE_UNAVAILABLE',\n\t'GATEWAY_TIMEOUT',\n\t'TOO_MANY_REQUESTS', // With backoff\n\t'INTERNAL_SERVER_ERROR', // Sometimes transient\n])\n\n// ============================================================================\n// Error Classes\n// ============================================================================\n\nexport interface SylphxErrorOptions {\n\t/** Error code for programmatic handling */\n\tcode?: SylphxErrorCode\n\t/** HTTP status code (inferred from code if not provided) */\n\tstatus?: number\n\t/** Additional context data */\n\tdata?: Record<string, unknown>\n\t/** Original error that caused this */\n\tcause?: Error\n\t/** Retry-After header value (seconds) for rate limiting */\n\tretryAfter?: number\n}\n\n/**\n * Base error class for all Sylphx SDK errors\n *\n * @example\n * ```typescript\n * throw new SylphxError('Invalid email format', {\n * code: 'BAD_REQUEST',\n * data: { field: 'email' }\n * })\n * ```\n */\nexport class SylphxError extends Error {\n\t/** Error code for programmatic handling */\n\treadonly code: SylphxErrorCode\n\n\t/** HTTP status code */\n\treadonly status: number\n\n\t/** Additional context data */\n\treadonly data?: Record<string, unknown>\n\n\t/** Whether this error is safe to retry */\n\treadonly isRetryable: boolean\n\n\t/** Retry-After value in seconds (for rate limiting) */\n\treadonly retryAfter?: number\n\n\t/** Timestamp when error occurred */\n\treadonly timestamp: Date\n\n\tconstructor(message: string, options: SylphxErrorOptions = {}) {\n\t\tsuper(message, { cause: options.cause })\n\t\tthis.name = 'SylphxError'\n\t\tthis.code = options.code ?? 'UNKNOWN'\n\t\tthis.status = options.status ?? ERROR_CODE_STATUS[this.code]\n\t\tthis.data = options.data\n\t\tthis.isRetryable = RETRYABLE_CODES.has(this.code)\n\t\tthis.retryAfter = options.retryAfter\n\t\tthis.timestamp = new Date()\n\n\t\t// Maintain proper stack trace in V8\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, SylphxError)\n\t\t}\n\t}\n\n\t// ============================================================================\n\t// Static Type Guards (DX helpers)\n\t// ============================================================================\n\n\t/**\n\t * Check if error is a rate-limit error (429 Too Many Requests)\n\t */\n\tstatic isRateLimited(err: unknown): err is SylphxError & { code: 'TOO_MANY_REQUESTS' } {\n\t\treturn err instanceof SylphxError && err.code === 'TOO_MANY_REQUESTS'\n\t}\n\n\t/**\n\t * Check if error is an account lockout error (too many failed login attempts).\n\t * When true, `error.data?.lockoutUntil` contains the ISO 8601 timestamp when the lockout expires.\n\t */\n\tstatic isAccountLocked(\n\t\terr: unknown,\n\t): err is SylphxError & { code: 'TOO_MANY_REQUESTS'; data: { lockoutUntil: string | null } } {\n\t\treturn (\n\t\t\terr instanceof SylphxError &&\n\t\t\terr.code === 'TOO_MANY_REQUESTS' &&\n\t\t\terr.data?.code === 'ACCOUNT_LOCKED'\n\t\t)\n\t}\n\n\t/**\n\t * Check if error is a quota exceeded error (plan limit reached)\n\t */\n\tstatic isQuotaExceeded(err: unknown): err is SylphxError & { code: 'QUOTA_EXCEEDED' } {\n\t\treturn err instanceof SylphxError && err.code === 'QUOTA_EXCEEDED'\n\t}\n\n\t/**\n\t * Check if error is an authentication error (401 Unauthorized)\n\t */\n\tstatic isUnauthorized(err: unknown): err is SylphxError & { code: 'UNAUTHORIZED' } {\n\t\treturn err instanceof SylphxError && err.code === 'UNAUTHORIZED'\n\t}\n\n\t/**\n\t * Check if error is a not-found error (404 Not Found)\n\t */\n\tstatic isNotFound(err: unknown): err is SylphxError & { code: 'NOT_FOUND' } {\n\t\treturn err instanceof SylphxError && err.code === 'NOT_FOUND'\n\t}\n\n\t/**\n\t * Check if error is an authorization error (403 Forbidden)\n\t */\n\tstatic isForbidden(err: unknown): err is SylphxError & { code: 'FORBIDDEN' } {\n\t\treturn err instanceof SylphxError && err.code === 'FORBIDDEN'\n\t}\n\n\t/**\n\t * Check if error is a validation error (422 Unprocessable Entity)\n\t */\n\tstatic isValidationError(err: unknown): err is SylphxError & { code: 'UNPROCESSABLE_ENTITY' } {\n\t\treturn err instanceof SylphxError && err.code === 'UNPROCESSABLE_ENTITY'\n\t}\n\n\t/**\n\t * Check if error is a network error (no response received)\n\t */\n\tstatic isNetworkError(err: unknown): err is SylphxError & { code: 'NETWORK_ERROR' } {\n\t\treturn err instanceof SylphxError && err.code === 'NETWORK_ERROR'\n\t}\n\n\t/**\n\t * Check if error is an upstream/gateway error (502/504)\n\t */\n\tstatic isUpstreamError(err: unknown): err is SylphxError {\n\t\treturn (\n\t\t\terr instanceof SylphxError &&\n\t\t\t(err.code === 'BAD_GATEWAY' ||\n\t\t\t\terr.code === 'GATEWAY_TIMEOUT' ||\n\t\t\t\terr.code === 'SERVICE_UNAVAILABLE')\n\t\t)\n\t}\n\n\t/**\n\t * Convert to JSON-serializable object\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tcode: this.code,\n\t\t\tstatus: this.status,\n\t\t\tdata: this.data,\n\t\t\tisRetryable: this.isRetryable,\n\t\t\tretryAfter: this.retryAfter,\n\t\t\ttimestamp: this.timestamp.toISOString(),\n\t\t}\n\t}\n}\n\n/**\n * Network-related errors (no response received)\n */\nexport class NetworkError extends SylphxError {\n\tconstructor(message = 'Network request failed', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'NETWORK_ERROR' })\n\t\tthis.name = 'NetworkError'\n\t}\n}\n\n/**\n * Request timeout errors\n */\nexport class TimeoutError extends SylphxError {\n\t/** Timeout duration in milliseconds */\n\treadonly timeout: number\n\n\tconstructor(timeout: number, options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(`Request timed out after ${timeout}ms`, {\n\t\t\t...options,\n\t\t\tcode: 'TIMEOUT',\n\t\t})\n\t\tthis.name = 'TimeoutError'\n\t\tthis.timeout = timeout\n\t}\n}\n\n/**\n * Authentication errors (401)\n */\nexport class AuthenticationError extends SylphxError {\n\tconstructor(message = 'Authentication required', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'UNAUTHORIZED' })\n\t\tthis.name = 'AuthenticationError'\n\t}\n}\n\n/**\n * Authorization errors (403)\n */\nexport class AuthorizationError extends SylphxError {\n\tconstructor(message = 'Permission denied', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'FORBIDDEN' })\n\t\tthis.name = 'AuthorizationError'\n\t}\n}\n\n/**\n * Validation errors (422)\n */\nexport class ValidationError extends SylphxError {\n\t/** Field-specific errors */\n\treadonly fieldErrors?: Record<string, string[]>\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & {\n\t\t\tfieldErrors?: Record<string, string[]>\n\t\t},\n\t) {\n\t\tsuper(message, { ...options, code: 'UNPROCESSABLE_ENTITY' })\n\t\tthis.name = 'ValidationError'\n\t\tthis.fieldErrors = options?.fieldErrors\n\t}\n\n\t/**\n\t * Get error message for a specific field\n\t */\n\tgetFieldError(field: string): string | undefined {\n\t\treturn this.fieldErrors?.[field]?.[0]\n\t}\n}\n\n/**\n * Rate limit metadata (Stripe SDK pattern)\n */\nexport interface RateLimitInfo {\n\t/** Maximum requests allowed in window */\n\tlimit?: number\n\t/** Remaining requests in current window */\n\tremaining?: number\n\t/** Unix timestamp (seconds) when limit resets */\n\tresetAt?: number\n\t/** Seconds until limit resets (Retry-After header) */\n\tretryAfter?: number\n}\n\n/**\n * Rate limit errors (429)\n *\n * Provides full rate limit metadata for consumer apps to implement\n * proper backoff UI (countdown timers, retry buttons, etc.)\n *\n * @example\n * ```typescript\n * try {\n * await sendEmail(config, options)\n * } catch (error) {\n * if (error instanceof RateLimitError) {\n * const waitSeconds = error.retryAfter ?? 60\n * console.log(`Rate limited. Retry after ${waitSeconds}s`)\n * console.log(`Remaining: ${error.remaining}/${error.limit}`)\n * console.log(`Resets at: ${new Date(error.resetAt! * 1000)}`)\n * }\n * }\n * ```\n */\nexport class RateLimitError extends SylphxError {\n\t/** Maximum requests allowed in window */\n\treadonly limit?: number\n\n\t/** Remaining requests in current window */\n\treadonly remaining?: number\n\n\t/** Unix timestamp (seconds) when limit resets */\n\treadonly resetAt?: number\n\n\tconstructor(\n\t\tmessage = 'Too many requests',\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & RateLimitInfo,\n\t) {\n\t\tsuper(message, { ...options, code: 'TOO_MANY_REQUESTS' })\n\t\tthis.name = 'RateLimitError'\n\t\tthis.limit = options?.limit\n\t\tthis.remaining = options?.remaining\n\t\tthis.resetAt = options?.resetAt\n\t}\n\n\t/**\n\t * Get Date when rate limit resets\n\t */\n\tgetResetDate(): Date | undefined {\n\t\treturn this.resetAt ? new Date(this.resetAt * 1000) : undefined\n\t}\n\n\t/**\n\t * Get human-readable retry message\n\t */\n\tgetRetryMessage(): string {\n\t\tif (this.retryAfter) {\n\t\t\treturn `Please retry after ${this.retryAfter} seconds`\n\t\t}\n\t\tif (this.resetAt) {\n\t\t\tconst seconds = Math.max(0, this.resetAt - Math.floor(Date.now() / 1000))\n\t\t\treturn `Rate limit resets in ${seconds} seconds`\n\t\t}\n\t\treturn 'Please wait before retrying'\n\t}\n}\n\n/**\n * Resource not found errors (404)\n */\nexport class NotFoundError extends SylphxError {\n\t/** Type of resource that wasn't found */\n\treadonly resourceType?: string\n\n\t/** ID of the resource that wasn't found */\n\treadonly resourceId?: string\n\n\tconstructor(\n\t\tmessage = 'Resource not found',\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & {\n\t\t\tresourceType?: string\n\t\t\tresourceId?: string\n\t\t},\n\t) {\n\t\tsuper(message, { ...options, code: 'NOT_FOUND' })\n\t\tthis.name = 'NotFoundError'\n\t\tthis.resourceType = options?.resourceType\n\t\tthis.resourceId = options?.resourceId\n\t}\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Check if an error is a Sylphx SDK error\n */\nexport function isSylphxError(error: unknown): error is SylphxError {\n\treturn error instanceof SylphxError\n}\n\n/**\n * Check if an error is safe to retry\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (error instanceof SylphxError) {\n\t\treturn error.isRetryable\n\t}\n\n\t// Check for network errors\n\tif (error instanceof Error) {\n\t\tconst message = error.message.toLowerCase()\n\t\tconst name = error.name.toLowerCase()\n\n\t\t// Network errors\n\t\tif (name === 'typeerror' && message.includes('fetch')) return true\n\t\tif (name === 'networkerror') return true\n\n\t\t// Timeout patterns\n\t\tif (message.includes('timeout')) return true\n\t\tif (message.includes('timed out')) return true\n\n\t\t// Connection errors\n\t\tif (message.includes('econnrefused')) return true\n\t\tif (message.includes('econnreset')) return true\n\t\tif (message.includes('socket')) return true\n\n\t\t// Server errors that might be transient\n\t\tif (message.includes('502')) return true\n\t\tif (message.includes('503')) return true\n\t\tif (message.includes('504')) return true\n\t}\n\n\treturn false\n}\n\n/**\n * Extract error message from any error type\n */\nexport function getErrorMessage(error: unknown, fallback = 'An unknown error occurred'): string {\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error\n\t}\n\tif (error && typeof error === 'object' && 'message' in error) {\n\t\tconst message = (error as { message: unknown }).message\n\t\tif (typeof message === 'string') return message\n\t}\n\tif (error && typeof error === 'object' && 'response' in error) {\n\t\tconst response = (error as { response?: { data?: { message?: string; error?: string } } })\n\t\t\t.response\n\t\tif (response?.data?.message) return response.data.message\n\t\tif (response?.data?.error) return response.data.error\n\t}\n\treturn fallback\n}\n\n/**\n * Get error code from any error type\n */\nexport function getErrorCode(error: unknown): SylphxErrorCode {\n\tif (error instanceof SylphxError) {\n\t\treturn error.code\n\t}\n\treturn 'UNKNOWN'\n}\n\nfunction getErrorStatus(error: unknown): number | undefined {\n\tif (!error || typeof error !== 'object') return undefined\n\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\treturn (error as { status: number }).status\n\t}\n\tif ('statusCode' in error && typeof (error as { statusCode: unknown }).statusCode === 'number') {\n\t\treturn (error as { statusCode: number }).statusCode\n\t}\n\treturn undefined\n}\n\nfunction getSafeErrorKey(error: unknown): string | undefined {\n\tif (error instanceof SylphxError) return error.code\n\tif (!error || typeof error !== 'object') return undefined\n\tif ('code' in error && typeof (error as { code: unknown }).code === 'string') {\n\t\treturn (error as { code: string }).code\n\t}\n\tconst status = getErrorStatus(error)\n\treturn status == null ? undefined : String(status)\n}\n\nexport interface ErrorDetails {\n\treadonly message: string\n\treadonly code?: string\n\treadonly name?: string\n\treadonly stack?: string\n\treadonly status?: number\n\treadonly cause?: unknown\n}\n\nexport function getErrorDetails(\n\terror: unknown,\n\tfallbackMessage = 'An unknown error occurred',\n): ErrorDetails {\n\tconst details: ErrorDetails = {\n\t\tmessage: getErrorMessage(error, fallbackMessage),\n\t}\n\tconst code = getSafeErrorKey(error)\n\tif (code) {\n\t\tObject.assign(details, { code })\n\t}\n\tconst status = getErrorStatus(error)\n\tif (status != null) {\n\t\tObject.assign(details, { status })\n\t}\n\tif (error instanceof Error) {\n\t\tObject.assign(details, {\n\t\t\tname: error.name,\n\t\t\tstack: error.stack,\n\t\t\t...(error.cause ? { cause: error.cause } : {}),\n\t\t})\n\t}\n\treturn details\n}\n\nconst INTERNAL_ERROR_PATTERNS = [\n\t/sql/i,\n\t/database/i,\n\t/postgres/i,\n\t/neon/i,\n\t/drizzle/i,\n\t/prisma/i,\n\t/constraint/i,\n\t/foreign key/i,\n\t/unique violation/i,\n\t/null value/i,\n\t/column/i,\n\t/table/i,\n\t/relation/i,\n\t/trpc/i,\n\t/internal server/i,\n\t/unexpected/i,\n\t/cannot read propert/i,\n\t/undefined is not/i,\n\t/null is not/i,\n\t/cannot find module/i,\n\t/econnrefused/i,\n\t/etimedout/i,\n\t/enotfound/i,\n]\n\nconst SAFE_ERROR_MESSAGES: Record<string, string> = {\n\t'400': 'Invalid request. Please check your input and try again.',\n\t'401': 'Please sign in to continue.',\n\t'403': \"You don't have permission to perform this action.\",\n\t'404': 'The requested resource was not found.',\n\t'409': 'This action conflicts with existing data. Please refresh and try again.',\n\t'429': 'Too many requests. Please wait a moment and try again.',\n\t'500': 'Something went wrong on our end. Please try again later.',\n\t'502': 'Service temporarily unavailable. Please try again in a moment.',\n\t'503': 'Service temporarily unavailable. Please try again in a moment.',\n\tBAD_REQUEST: 'Invalid request. Please check your input.',\n\tUNAUTHORIZED: 'Please sign in to continue.',\n\tFORBIDDEN: \"You don't have permission to perform this action.\",\n\tNOT_FOUND: 'The requested item was not found.',\n\tCONFLICT: 'This action conflicts with existing data.',\n\tTOO_MANY_REQUESTS: 'Too many requests. Please wait a moment.',\n\tINTERNAL_SERVER_ERROR: 'Something went wrong. Please try again later.',\n\tTIMEOUT: 'Request timed out. Please try again.',\n\tPRECONDITION_FAILED: 'This action cannot be completed right now.',\n\tQUOTA_EXCEEDED: \"You've reached your usage limit. Please upgrade your plan.\",\n\tPAYMENT_REQUIRED: 'Payment is required to continue.',\n\tINVALID_CREDENTIALS: 'Invalid email or password.',\n\tEMAIL_NOT_VERIFIED: 'Please verify your email address.',\n\tACCOUNT_LOCKED: 'Your account has been locked. Please contact support.',\n\tSESSION_EXPIRED: 'Your session has expired. Please sign in again.',\n}\n\nexport function getSafeErrorMessage(\n\terror: unknown,\n\tfallback = 'Something went wrong. Please try again.',\n): string {\n\tconst errorCode = getSafeErrorKey(error)\n\tif (errorCode && SAFE_ERROR_MESSAGES[errorCode]) return SAFE_ERROR_MESSAGES[errorCode]\n\n\tif (error && typeof error === 'object') {\n\t\tconst coded = error as { data?: { code?: string } }\n\t\tif (coded.data?.code && SAFE_ERROR_MESSAGES[coded.data.code]) {\n\t\t\treturn SAFE_ERROR_MESSAGES[coded.data.code]\n\t\t}\n\t}\n\n\tconst rawMessage = getErrorMessage(error, '')\n\tif (\n\t\trawMessage &&\n\t\trawMessage.length < 200 &&\n\t\t!rawMessage.includes('\\n') &&\n\t\t!rawMessage.includes('at ') &&\n\t\t!INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(rawMessage))\n\t) {\n\t\treturn rawMessage\n\t}\n\n\treturn fallback\n}\n\nexport function isChallengeRequired(err: unknown): boolean {\n\tif (!(err instanceof Error)) return false\n\tif (err.message.includes('challenge')) return true\n\tconst errWithData = err as { data?: { code?: string } }\n\treturn errWithData.data?.code === 'FORBIDDEN'\n}\n\n/**\n * Convert any error to SylphxError\n */\nexport function toSylphxError(error: unknown): SylphxError {\n\tif (error instanceof SylphxError) {\n\t\treturn error\n\t}\n\n\tif (error instanceof Error) {\n\t\t// Try to infer error type from message/name\n\t\tconst message = error.message.toLowerCase()\n\t\tconst name = error.name.toLowerCase()\n\n\t\t// Network errors\n\t\tif (name === 'typeerror' && message.includes('fetch')) {\n\t\t\treturn new NetworkError(error.message, { cause: error })\n\t\t}\n\t\tif (name === 'aborterror' || message.includes('aborted')) {\n\t\t\treturn new SylphxError(error.message, { code: 'ABORTED', cause: error })\n\t\t}\n\n\t\t// Timeout\n\t\tif (message.includes('timeout')) {\n\t\t\treturn new TimeoutError(DEFAULT_TIMEOUT_MS, { cause: error })\n\t\t}\n\n\t\t// HTTP status codes in message\n\t\tif (message.includes('401') || message.includes('unauthorized')) {\n\t\t\treturn new AuthenticationError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('403') || message.includes('forbidden')) {\n\t\t\treturn new AuthorizationError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('404') || message.includes('not found')) {\n\t\t\treturn new NotFoundError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('429') || message.includes('rate limit')) {\n\t\t\treturn new RateLimitError(error.message, { cause: error })\n\t\t}\n\n\t\treturn new SylphxError(error.message, { cause: error })\n\t}\n\n\treturn new SylphxError(getErrorMessage(error))\n}\n\n/**\n * Calculate exponential backoff delay with jitter\n *\n * @param attempt - Retry attempt number (0-indexed)\n * @param baseDelay - Base delay in milliseconds (default: 1000)\n * @param maxDelay - Maximum delay in milliseconds (default: 30000)\n * @returns Delay in milliseconds with jitter\n */\nexport function exponentialBackoff(\n\tattempt: number,\n\tbaseDelay = BASE_RETRY_DELAY_MS,\n\tmaxDelay = MAX_RETRY_DELAY_MS,\n): number {\n\t// Calculate exponential delay: baseDelay * 2^attempt\n\tconst exponentialDelay = baseDelay * 2 ** attempt\n\n\t// Cap at maxDelay\n\tconst cappedDelay = Math.min(exponentialDelay, maxDelay)\n\n\t// Add jitter (±25% randomness)\n\tconst jitter = cappedDelay * 0.25 * (Math.random() * 2 - 1)\n\n\treturn Math.round(cappedDelay + jitter)\n}\n","export {\n\tDEFAULT_MACHINE_SIZE,\n\tisMachineSize,\n\tMACHINE_CONFIGS,\n\tMACHINE_MAX_INSTANCES,\n\tMACHINE_RESOURCE_REQUIREMENTS,\n\tMACHINE_SIZES,\n\ttype MachineConfig,\n\ttype MachineResourceRequirements,\n\ttype MachineTierResources,\n\tparseMachineSize,\n\tresolveMachineConfig,\n\tresolveMachineMaxInstances,\n\tresolveMachineResources,\n\tresolveMachineTierResources,\n\ttoPublicMachineSize,\n} from '@sylphx/contract/compute'\n","/**\n * Database Pricing Configuration (SSOT)\n *\n * All database billing constants centralized here.\n * Used by billing calculations, usage tracking, and cost display.\n *\n * Pricing Strategy:\n * - Self-hosted infra on AX162-R: flat ~$270/month\n * - Competitive pricing with 75-99% margins\n *\n * Customer Prices (updated for self-hosted, 2026-02):\n * - Compute: $0.08/hour (25% below Neon $0.106/hr)\n * - Storage: $0.25/GB-month (29% below Neon $0.35/GB)\n * - Transfer: $0.09/GB (matches Supabase)\n */\n\n// ==========================================\n// Compute Pricing\n// ==========================================\n\n/** Price per compute hour in microdollars ($0.08/hour = 80,000 microdollars) */\nexport const COMPUTE_PRICE_PER_HOUR_MICRODOLLARS = 80_000\n\n/** Free compute hours per month (platform free tier) */\nexport const FREE_COMPUTE_HOURS = 3\n\n// ==========================================\n// Storage Pricing\n// ==========================================\n\n/** Price per GB-month in microdollars ($0.25/GB-month = 250,000 microdollars) */\nexport const STORAGE_PRICE_PER_GB_MONTH_MICRODOLLARS = 250_000\n\n/** Free storage in GB (256 MB) */\nexport const FREE_STORAGE_GB = 0.25\n\n// ==========================================\n// Transfer Pricing\n// ==========================================\n\n/** Price per GB data transfer ($0.09/GB = 90,000 microdollars) */\nexport const TRANSFER_PRICE_PER_GB_MICRODOLLARS = 90_000\n\n// ==========================================\n// KV Storage Free Tier\n// ==========================================\n// Applied in code at billing time because platform_pricing.free_tier_amount\n// is an integer column and cannot represent 0.25 GB. Same pattern as\n// FREE_STORAGE_GB for databases above.\n\n/** KV free storage in GB (256 MB) */\nexport const KV_FREE_STORAGE_GB = 0.25\n\n// ==========================================\n// Time Constants\n// ==========================================\n\n/** Hours per month (AWS/GCP standard for billing) */\nexport const HOURS_PER_MONTH = 730\n","/**\n * Referrals Configuration (SSOT)\n *\n * Single source of truth for referral system configuration.\n * Used by: referral router, SDK referral endpoints\n */\n\n// ==========================================\n// Rewards\n// ==========================================\n\n/** Default points awarded per successful referral */\nexport const DEFAULT_POINTS_REWARD = 100\n\n// ==========================================\n// Discount Configuration\n// ==========================================\n\n/** Number of months the referral discount is valid */\nexport const DISCOUNT_DURATION_MONTHS = 3\n\n/** Default discount percentage */\nexport const DISCOUNT_PERCENT = 20\n\n/** Premium trial days from referral */\nexport const PREMIUM_TRIAL_DAYS = 7\n\n// ==========================================\n// Code Generation\n// ==========================================\n\n/** Default referral code length */\nconst REFERRAL_CODE_LENGTH = 8\n\n/**\n * Characters used for referral codes (uppercase alphanumeric, no confusing chars).\n * 32 characters total — divides evenly into 256, so byte % 32 has zero modulo bias.\n */\nconst REFERRAL_CODE_CHARS = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'\n\n/**\n * Generate a cryptographically secure referral code\n *\n * Uses crypto.getRandomValues for uniform random bytes (0-255).\n * Since REFERRAL_CODE_CHARS has exactly 32 characters and 256 / 32 = 8,\n * byte % 32 produces perfectly uniform distribution with zero modulo bias.\n *\n * Entropy: 8 chars * log2(32) = 40 bits (~1.1 trillion possible codes)\n *\n * Format: 8 uppercase alphanumeric characters (excluding ambiguous: 0, O, I, L, 1)\n */\nexport function generateReferralCode(): string {\n\tconst bytes = new Uint8Array(REFERRAL_CODE_LENGTH)\n\tcrypto.getRandomValues(bytes)\n\n\tlet code = ''\n\tfor (let i = 0; i < REFERRAL_CODE_LENGTH; i++) {\n\t\tcode += REFERRAL_CODE_CHARS[bytes[i] % REFERRAL_CODE_CHARS.length]\n\t}\n\treturn code\n}\n","export function getErrorMessage(error: unknown, fallback = 'Unknown error'): string {\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\n\tif (typeof error === 'string') {\n\t\treturn error\n\t}\n\n\tif (error && typeof error === 'object' && 'message' in error) {\n\t\tconst message = (error as { message: unknown }).message\n\t\tif (typeof message === 'string') {\n\t\t\treturn message\n\t\t}\n\t}\n\n\tif (error && typeof error === 'object' && 'response' in error) {\n\t\tconst response = (error as { response?: { data?: { message?: string; error?: string } } })\n\t\t\t.response\n\t\tif (response?.data?.message) return response.data.message\n\t\tif (response?.data?.error) return response.data.error\n\t}\n\n\treturn fallback\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== 'object') {\n\t\treturn undefined\n\t}\n\n\tif ('code' in error && typeof (error as { code: unknown }).code === 'string') {\n\t\treturn (error as { code: string }).code\n\t}\n\n\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\treturn String((error as { status: number }).status)\n\t}\n\n\tif ('statusCode' in error && typeof (error as { statusCode: unknown }).statusCode === 'number') {\n\t\treturn String((error as { statusCode: number }).statusCode)\n\t}\n\n\treturn undefined\n}\n\nexport interface ErrorDetails {\n\tmessage: string\n\tcode?: string\n\tname?: string\n\tstack?: string\n\tstatus?: number\n\tcause?: unknown\n}\n\nexport function getErrorDetails(error: unknown, fallbackMessage = 'Unknown error'): ErrorDetails {\n\tconst details: ErrorDetails = {\n\t\tmessage: getErrorMessage(error, fallbackMessage),\n\t}\n\n\tif (error instanceof Error) {\n\t\tdetails.name = error.name\n\t\tdetails.stack = error.stack\n\t\tif (error.cause) {\n\t\t\tdetails.cause = error.cause\n\t\t}\n\t}\n\n\tconst code = getErrorCode(error)\n\tif (code) {\n\t\tdetails.code = code\n\t}\n\n\tif (error && typeof error === 'object') {\n\t\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\t\tdetails.status = (error as { status: number }).status\n\t\t} else if (\n\t\t\t'statusCode' in error &&\n\t\t\ttypeof (error as { statusCode: unknown }).statusCode === 'number'\n\t\t) {\n\t\t\tdetails.status = (error as { statusCode: number }).statusCode\n\t\t}\n\t}\n\n\treturn details\n}\n\nconst INTERNAL_ERROR_PATTERNS = [\n\t/sql/i,\n\t/database/i,\n\t/postgres/i,\n\t/neon/i,\n\t/drizzle/i,\n\t/prisma/i,\n\t/constraint/i,\n\t/foreign key/i,\n\t/unique violation/i,\n\t/null value/i,\n\t/column/i,\n\t/table/i,\n\t/relation/i,\n\t/trpc/i,\n\t/internal server/i,\n\t/unexpected/i,\n\t/cannot read propert/i,\n\t/undefined is not/i,\n\t/null is not/i,\n\t/cannot find module/i,\n\t/econnrefused/i,\n\t/etimedout/i,\n\t/enotfound/i,\n]\n\nconst SAFE_ERROR_MESSAGES: Record<string, string> = {\n\t'400': 'Invalid request. Please check your input and try again.',\n\t'401': 'Please sign in to continue.',\n\t'403': \"You don't have permission to perform this action.\",\n\t'404': 'The requested resource was not found.',\n\t'409': 'This action conflicts with existing data. Please refresh and try again.',\n\t'429': 'Too many requests. Please wait a moment and try again.',\n\t'500': 'Something went wrong on our end. Please try again later.',\n\t'502': 'Service temporarily unavailable. Please try again in a moment.',\n\t'503': 'Service temporarily unavailable. Please try again in a moment.',\n\tBAD_REQUEST: 'Invalid request. Please check your input.',\n\tUNAUTHORIZED: 'Please sign in to continue.',\n\tFORBIDDEN: \"You don't have permission to perform this action.\",\n\tNOT_FOUND: 'The requested item was not found.',\n\tCONFLICT: 'This action conflicts with existing data.',\n\tTOO_MANY_REQUESTS: 'Too many requests. Please wait a moment.',\n\tINTERNAL_SERVER_ERROR: 'Something went wrong. Please try again later.',\n\tTIMEOUT: 'Request timed out. Please try again.',\n\tPRECONDITION_FAILED: 'This action cannot be completed right now.',\n\tQUOTA_EXCEEDED: \"You've reached your usage limit. Please upgrade your plan.\",\n\tPAYMENT_REQUIRED: 'Payment is required to continue.',\n\tINVALID_CREDENTIALS: 'Invalid email or password.',\n\tEMAIL_NOT_VERIFIED: 'Please verify your email address.',\n\tACCOUNT_LOCKED: 'Your account has been locked. Please contact support.',\n\tSESSION_EXPIRED: 'Your session has expired. Please sign in again.',\n}\n\nexport function getSafeErrorMessage(\n\terror: unknown,\n\tfallback = 'Something went wrong. Please try again.',\n): string {\n\tconst rawMessage = getErrorMessage(error, '')\n\tconst errorCode = getErrorCode(error)\n\n\tif (errorCode && SAFE_ERROR_MESSAGES[errorCode]) {\n\t\treturn SAFE_ERROR_MESSAGES[errorCode]\n\t}\n\n\tif (error && typeof error === 'object') {\n\t\tconst trpcError = error as { data?: { code?: string } }\n\t\tif (trpcError.data?.code && SAFE_ERROR_MESSAGES[trpcError.data.code]) {\n\t\t\treturn SAFE_ERROR_MESSAGES[trpcError.data.code]\n\t\t}\n\t}\n\n\tif (rawMessage && !INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(rawMessage))) {\n\t\tif (rawMessage.length < 200 && !rawMessage.includes('\\n') && !rawMessage.includes('at ')) {\n\t\t\treturn rawMessage\n\t\t}\n\t}\n\n\treturn fallback\n}\n\nexport function isChallengeRequired(err: unknown): boolean {\n\tif (err instanceof Error) {\n\t\tif (err.message.includes('challenge')) return true\n\n\t\tconst errWithData = err as { data?: { code?: string } }\n\t\tif (errWithData.data?.code === 'FORBIDDEN') return true\n\t}\n\treturn false\n}\n","/**\n * Sylphx Connection URL — Single Source of Truth (ADR-123)\n *\n * Implements the canonical connection string format defined in ADR-055 §5.\n * This module is the SDK-owned SSOT per ADR-123 (SDK/application boundary).\n * Consuming applications MUST import from `@sylphx/sdk` rather than duplicating\n * this logic.\n *\n * Hosted format:\n * sylphx://{credential}@{tenant-slug}.api.sylphx.com[:port][/v{version}]\n *\n * Custom/self-hosted domains are also accepted as long as the first DNS label\n * is the tenant slug.\n *\n * Examples:\n * sylphx://pk_prod_f19e5cdc3cc54f7ff81bdc26ec5bfbad@bold-river-a1b2c3.api.sylphx.com\n * sylphx://sk_prod_5120bfeb5120bfeb5120bfeb5120bfeb@bold-river-a1b2c3.api.sylphx.com/v1\n * sylphx://pk_dev_abc12345abc12345abc12345abc12345@calm-peak-z9x4d5.sylphx.dev\n *\n * Invariants:\n * - Protocol is always `sylphx:` (no exceptions)\n * - Credential matches `(pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}`\n * - Host's first DNS label is the resource slug (validated by slug regex)\n * - `apiBaseUrl` is always HTTPS, with `/v{version}` appended (default `v1`)\n *\n * Parsing uses the WHATWG `URL` constructor — custom regex parsing is banned\n * because it is notoriously brittle (ADR-055 §5.3).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ConnectionCredentialType = 'pk' | 'sk'\nexport type ConnectionEnv = 'dev' | 'stg' | 'prod' | 'prev'\n\nexport interface ParsedConnectionUrl {\n\t/** Full credential string, e.g. `pk_prod_f19e...` */\n\treadonly credential: string\n\t/** Credential kind — `pk` (publishable) or `sk` (secret) */\n\treadonly credentialType: ConnectionCredentialType\n\t/** Target environment encoded in the credential */\n\treadonly env: ConnectionEnv\n\t/** First DNS label of the host — the resource slug (e.g. `bold-river-a1b2c3`) */\n\treadonly slug: string\n\t/** Full host including port when present (e.g. `bold-river-a1b2c3.api.sylphx.com`) */\n\treadonly host: string\n\t/** Ready-to-use SDK base URL, always HTTPS (e.g. `https://bold-river-a1b2c3.api.sylphx.com/v1`) */\n\treadonly apiBaseUrl: string\n}\n\nexport interface BuildConnectionUrlInput {\n\t/** Credential — must match the credential format regex */\n\treadonly credential: string\n\t/** Resource slug — validated DNS label */\n\treadonly slug: string\n\t/** SDK API domain suffix; defaults to `api.sylphx.com`. Use `sylphx.dev` for dev. */\n\treadonly domain?: string\n\t/** API version suffix, e.g. `v1`. Defaults to `v1`. Pass empty string to omit. */\n\treadonly version?: string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SYLPHX_PROTOCOL = 'sylphx:'\nconst DEFAULT_DOMAIN = 'api.sylphx.com'\nconst DEFAULT_VERSION = 'v1'\n\n/**\n * Credential format — opaque token with type, env, optional project ref, and\n * hex payload. Ref-scoped credentials are emitted by Platform app-env injection;\n * legacy credentials without the ref remain valid for existing deploys.\n */\nexport const CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32,64}$/\n\n/** Version segment, e.g. `v1`, `v2`, `v10`. */\nconst VERSION_REGEX = /^v[0-9]+$/\n\n/**\n * Slug validation regex — RFC 1035 DNS label.\n * Lowercase letters, numbers, and hyphens; must start and end with alnum.\n * Length 1–63.\n */\nconst SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/\n\n// ============================================================================\n// Errors\n// ============================================================================\n\nexport class InvalidConnectionUrlError extends Error {\n\treadonly code = 'INVALID_CONNECTION_URL' as const\n\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = 'InvalidConnectionUrlError'\n\t\tObject.setPrototypeOf(this, InvalidConnectionUrlError.prototype)\n\t}\n}\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\nfunction fail(reason: string): never {\n\tthrow new InvalidConnectionUrlError(`Invalid Sylphx connection URL: ${reason}`)\n}\n\nfunction parseCredential(raw: string): {\n\tcredentialType: ConnectionCredentialType\n\tenv: ConnectionEnv\n} {\n\tconst match = CREDENTIAL_REGEX.exec(raw)\n\tif (!match) {\n\t\tfail(`credential must match (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}, got \"${raw}\"`)\n\t}\n\treturn {\n\t\tcredentialType: match[1] as ConnectionCredentialType,\n\t\tenv: match[2] as ConnectionEnv,\n\t}\n}\n\nfunction validateSlug(candidate: string): string {\n\tif (!candidate || candidate.length > 63 || !SLUG_REGEX.test(candidate)) {\n\t\tfail(`slug \"${candidate}\" is not a valid DNS label (lowercase alnum + hyphens, 1-63 chars)`)\n\t}\n\treturn candidate\n}\n\nfunction validateDomain(domain: string): string {\n\tif (!domain || domain.includes('/') || domain.includes('@') || domain.includes(' ')) {\n\t\tfail(`domain \"${domain}\" is not a valid hostname suffix`)\n\t}\n\t// Reject schemes — domain is just a hostname suffix\n\tif (domain.includes('://')) {\n\t\tfail(`domain \"${domain}\" must not contain a scheme`)\n\t}\n\treturn domain.toLowerCase()\n}\n\nfunction normaliseVersion(version: string | undefined): string {\n\tif (version === undefined) return DEFAULT_VERSION\n\tif (version === '') return ''\n\tif (!VERSION_REGEX.test(version)) {\n\t\tfail(`version \"${version}\" must match /^v[0-9]+$/`)\n\t}\n\treturn version\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Build a canonical Sylphx connection URL.\n *\n * Throws `InvalidConnectionUrlError` if any component is malformed.\n */\nexport function buildConnectionUrl(input: BuildConnectionUrlInput): string {\n\tconst { credential, slug, domain = DEFAULT_DOMAIN, version = DEFAULT_VERSION } = input\n\n\tparseCredential(credential)\n\tconst safeSlug = validateSlug(slug)\n\tconst safeDomain = validateDomain(domain)\n\tconst safeVersion = normaliseVersion(version)\n\n\tconst host = `${safeSlug}.${safeDomain}`\n\tconst pathSuffix = safeVersion ? `/${safeVersion}` : ''\n\treturn `${SYLPHX_PROTOCOL}//${credential}@${host}${pathSuffix}`\n}\n\n/**\n * Parse a Sylphx connection URL into its structured components.\n *\n * Throws `InvalidConnectionUrlError` on any structural problem.\n */\nexport function parseConnectionUrl(url: string): ParsedConnectionUrl {\n\tif (typeof url !== 'string' || url.length === 0) {\n\t\tfail('url must be a non-empty string')\n\t}\n\n\tlet parsed: URL\n\ttry {\n\t\tparsed = new URL(url)\n\t} catch {\n\t\tfail(`not a valid URL: \"${url}\"`)\n\t}\n\n\tif (parsed.protocol !== SYLPHX_PROTOCOL) {\n\t\tfail(`protocol must be \"sylphx:\", got \"${parsed.protocol}\"`)\n\t}\n\n\t// URL parses `sylphx://cred@host/path` but places `cred` in `username` only\n\t// when the authority is parsed. For non-special schemes, browsers/Node parse\n\t// the authority uniformly — we defensively read both `username` and `host`.\n\tconst credential = decodeURIComponent(parsed.username)\n\tif (!credential) {\n\t\tfail('missing credential (expected `sylphx://<credential>@<host>`)')\n\t}\n\tif (parsed.password) {\n\t\tfail('connection URL must not contain a password component')\n\t}\n\n\tconst { credentialType, env } = parseCredential(credential)\n\n\tconst host = parsed.host\n\tif (!host) {\n\t\tfail('missing host')\n\t}\n\n\t// Extract slug from the first DNS label\n\tconst hostname = parsed.hostname\n\tconst firstDot = hostname.indexOf('.')\n\tif (firstDot <= 0) {\n\t\tfail(`host \"${hostname}\" must contain at least one dot (slug.domain)`)\n\t}\n\tconst slugCandidate = hostname.slice(0, firstDot)\n\tconst domainSuffix = hostname.slice(firstDot + 1)\n\tif (!domainSuffix) {\n\t\tfail(`host \"${hostname}\" has empty domain suffix`)\n\t}\n\tconst slug = validateSlug(slugCandidate)\n\n\t// Path is either empty, `/`, or `/v{N}`\n\tconst rawPath = parsed.pathname.replace(/^\\/+/, '').replace(/\\/+$/, '')\n\tlet version = DEFAULT_VERSION\n\tif (rawPath !== '') {\n\t\tif (!VERSION_REGEX.test(rawPath)) {\n\t\t\tfail(`path \"${parsed.pathname}\" must be empty or match /v{N}`)\n\t\t}\n\t\tversion = rawPath\n\t}\n\n\tif (parsed.search) {\n\t\tfail('connection URL must not contain a query string')\n\t}\n\tif (parsed.hash) {\n\t\tfail('connection URL must not contain a fragment')\n\t}\n\n\tconst apiBaseUrl = `https://${host}/${version}`\n\n\treturn {\n\t\tcredential,\n\t\tcredentialType,\n\t\tenv,\n\t\tslug,\n\t\thost,\n\t\tapiBaseUrl,\n\t}\n}\n","/**\n * SDK Configuration — ADR-055 Connection URL API\n *\n * v0.5.0: The primary entry point is `createClient(url)` which accepts\n * a `sylphx://` connection URL. The old `createConfig({ ref, publicKey })`\n * API is removed.\n *\n * @example\n * ```typescript\n * import { createClient } from '@sylphx/sdk'\n *\n * const sylphx = createClient(process.env.SYLPHX_URL!)\n * // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n */\n\nimport {\n\tCREDENTIAL_REGEX,\n\tInvalidConnectionUrlError,\n\ttype ParsedConnectionUrl,\n\tparseConnectionUrl,\n} from './connection-url'\nimport { DEFAULT_SDK_API_HOST, DEFAULT_TIMEOUT_MS } from './constants'\nimport {\n\tNetworkError,\n\tRateLimitError,\n\tSylphxError,\n\ttype SylphxErrorCode,\n\tTimeoutError,\n} from './errors'\n\nexport type {\n\tBuildConnectionUrlInput,\n\tConnectionCredentialType,\n\tConnectionEnv,\n\tParsedConnectionUrl,\n} from './connection-url'\n// Re-export connection URL primitives for consumers (ADR-123 SSOT)\nexport {\n\tbuildConnectionUrl,\n\tCREDENTIAL_REGEX,\n\tInvalidConnectionUrlError,\n\tparseConnectionUrl,\n} from './connection-url'\n\n// =============================================================================\n// Legacy format detection — produces clear migration errors\n// =============================================================================\n\n/** Matches a bare ref-scoped credential mistakenly passed without the `sylphx://` URL wrapper. */\nconst LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/\n\n/** Matches old app_* format: app_{env}_{anything} */\nconst LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/\n\nconst MIGRATION_MESSAGE =\n\t'API key format has changed. Use a sylphx:// connection URL instead.\\n\\n' +\n\t'New format: sylphx://pk_prod_{ref?}_{hex}@your-slug.api.sylphx.com\\n\\n' +\n\t'Generate new credentials from the Sylphx Console → Your App → Environments.\\n' +\n\t'See https://docs.sylphx.com/migration for details.'\n\n/**\n * Detect legacy key formats and throw a helpful migration error.\n */\nfunction rejectLegacyKeyFormat(input: string): void {\n\tconst trimmed = input.trim().toLowerCase()\n\n\tif (LEGACY_APP_KEY_PATTERN.test(trimmed)) {\n\t\tthrow new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: 'BAD_REQUEST' })\n\t}\n\n\tif (LEGACY_EMBEDDED_REF_PATTERN.test(trimmed)) {\n\t\tthrow new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: 'BAD_REQUEST' })\n\t}\n}\n\n// =============================================================================\n// Config types\n// =============================================================================\n\n/**\n * SDK Configuration object — immutable, frozen.\n *\n * Created by `createClient()` or `createServerClient()`.\n * Passed to all pure SDK functions (`track()`, `signIn()`, etc.).\n */\nexport interface SylphxConfig {\n\t/** The credential string (pk_* or sk_*) */\n\treadonly credential: string\n\t/** Credential type: 'pk' (publishable) or 'sk' (secret) */\n\treadonly credentialType: 'pk' | 'sk'\n\t/** Target environment: dev, stg, prod, or prev */\n\treadonly env: 'dev' | 'stg' | 'prod' | 'prev'\n\t/** Resource slug (first DNS label), e.g. 'bold-river-a1b2c3' */\n\treadonly slug: string\n\t/** Pre-computed API base URL, e.g. 'https://bold-river-a1b2c3.api.sylphx.com/v1' */\n\treadonly baseUrl: string\n\t/** Optional access token for authenticated requests */\n\treadonly accessToken?: string\n\t/**\n\t * Secret key — populated when credentialType is 'sk'.\n\t * Backward-compatible alias for `credential` when credential is sk_*.\n\t */\n\treadonly secretKey?: string\n\t/**\n\t * Publishable key — populated when credentialType is 'pk'.\n\t * Backward-compatible alias for `credential` when credential is pk_*.\n\t */\n\treadonly publicKey?: string\n\t/**\n\t * @deprecated Use `slug`. Backward-compatible alias.\n\t */\n\treadonly ref: string\n}\n\n/**\n * Explicit components input — alternative to connection URL string.\n *\n * For multi-tenant apps or cases where components are stored separately.\n */\nexport interface SylphxClientInput {\n\t/** Resource slug, e.g. 'bold-river-a1b2c3' */\n\tslug?: string\n\t/** Publishable key (pk_*) — client-safe */\n\tpublicKey?: string\n\t/** Secret key (sk_*) — server-side only */\n\tsecretKey?: string\n\t/** Optional access token */\n\taccessToken?: string\n\t/** API domain override (default: api.sylphx.com) */\n\tdomain?: string\n\t/**\n\t * @deprecated Use `slug`. Accepted for backward compatibility during migration.\n\t */\n\tref?: string\n\t/**\n\t * @deprecated Use `domain`. Accepted for backward compatibility during migration.\n\t */\n\tplatformUrl?: string\n}\n\n/**\n * Build a frozen SylphxConfig with backward-compat fields (secretKey, publicKey, ref).\n */\nfunction freezeConfig(opts: {\n\tcredential: string\n\tcredentialType: 'pk' | 'sk'\n\tenv: 'dev' | 'stg' | 'prod' | 'prev'\n\tslug: string\n\tbaseUrl: string\n\taccessToken?: string\n}): SylphxConfig {\n\treturn Object.freeze({\n\t\tcredential: opts.credential,\n\t\tcredentialType: opts.credentialType,\n\t\tenv: opts.env,\n\t\tslug: opts.slug,\n\t\tbaseUrl: opts.baseUrl,\n\t\taccessToken: opts.accessToken,\n\t\t// Backward-compat aliases\n\t\tsecretKey: opts.credentialType === 'sk' ? opts.credential : undefined,\n\t\tpublicKey: opts.credentialType === 'pk' ? opts.credential : undefined,\n\t\tref: opts.slug,\n\t})\n}\n\n// =============================================================================\n// createClient — primary entry point (ADR-055)\n// =============================================================================\n\n/**\n * Create a Sylphx client from a connection URL or explicit components.\n *\n * This is the primary SDK entry point for client-side (browser) usage.\n * Accepts a `sylphx://` connection URL or an explicit components object.\n *\n * @example Connection URL (recommended)\n * ```typescript\n * const sylphx = createClient(process.env.NEXT_PUBLIC_SYLPHX_URL!)\n * // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n *\n * @example Explicit components\n * ```typescript\n * const sylphx = createClient({\n * slug: 'bold-river-a1b2c3',\n * publicKey: 'pk_prod_f19e...',\n * })\n * ```\n */\nexport function createClient(input: string | SylphxClientInput): SylphxConfig {\n\tif (typeof input === 'string') {\n\t\treturn createConfigFromUrl(input)\n\t}\n\treturn createConfigFromComponents(input)\n}\n\n/**\n * Create a Sylphx server client from a connection URL or explicit components.\n *\n * Equivalent to `createClient()` but validates that a secret key (sk_*) is provided.\n * Use this for server-side operations that require elevated permissions.\n *\n * @example Connection URL (recommended)\n * ```typescript\n * const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)\n * // Parses: sylphx://sk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n *\n * @example Explicit components\n * ```typescript\n * const sylphx = createServerClient({\n * slug: 'bold-river-a1b2c3',\n * secretKey: 'sk_prod_5120...',\n * })\n * ```\n */\nexport function createServerClient(input: string | SylphxClientInput): SylphxConfig {\n\tconst config = createClient(input)\n\n\tif (config.credentialType !== 'sk') {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] createServerClient() requires a secret key (sk_*). ' +\n\t\t\t\t'Use a SYLPHX_SECRET_URL with an sk_ credential, or pass { secretKey } in the components object.',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\treturn config\n}\n\n// =============================================================================\n// Internal construction\n// =============================================================================\n\nfunction createConfigFromUrl(url: string): SylphxConfig {\n\tif (!url || typeof url !== 'string') {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] Connection URL is required. Set SYLPHX_URL or NEXT_PUBLIC_SYLPHX_URL environment variable.\\n\\n' +\n\t\t\t\t'Format: sylphx://pk_prod_{hex}@your-slug.api.sylphx.com',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\tconst trimmed = url.trim()\n\n\t// Detect legacy key formats passed as the URL string\n\trejectLegacyKeyFormat(trimmed)\n\n\t// If someone passes a bare key instead of a URL, give a helpful error\n\tif (!trimmed.startsWith('sylphx://')) {\n\t\t// Check if it looks like a new-format bare credential\n\t\tif (CREDENTIAL_REGEX.test(trimmed)) {\n\t\t\tthrow new SylphxError(\n\t\t\t\t'[Sylphx] Received a bare credential instead of a connection URL.\\n\\n' +\n\t\t\t\t\t'Wrap it in a connection URL: sylphx://<credential>@<slug>.api.sylphx.com\\n' +\n\t\t\t\t\t'Or use createClient({ slug, publicKey }) for explicit components.',\n\t\t\t\t{ code: 'BAD_REQUEST' },\n\t\t\t)\n\t\t}\n\t\tthrow new SylphxError(\n\t\t\t`[Sylphx] Invalid connection URL — must start with \"sylphx://\". Got: \"${trimmed.slice(0, 30)}...\"`,\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\tlet parsed: ParsedConnectionUrl\n\ttry {\n\t\tparsed = parseConnectionUrl(trimmed)\n\t} catch (err) {\n\t\tif (err instanceof InvalidConnectionUrlError) {\n\t\t\tthrow new SylphxError(err.message, { code: 'BAD_REQUEST', cause: err })\n\t\t}\n\t\tthrow err\n\t}\n\n\treturn freezeConfig({\n\t\tcredential: parsed.credential,\n\t\tcredentialType: parsed.credentialType,\n\t\tenv: parsed.env,\n\t\tslug: parsed.slug,\n\t\tbaseUrl: parsed.apiBaseUrl,\n\t})\n}\n\nfunction createConfigFromComponents(input: SylphxClientInput): SylphxConfig {\n\tconst credential = input.secretKey || input.publicKey\n\tif (!credential) {\n\t\tthrow new SylphxError('[Sylphx] Either publicKey or secretKey must be provided.', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\t// Accept deprecated `ref` as fallback for `slug` during migration\n\tconst resolvedSlug = input.slug || input.ref\n\n\tif (!resolvedSlug) {\n\t\tthrow new SylphxError('[Sylphx] slug is required when using explicit components.', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\t// Try to validate as ADR-055 credential first; if invalid, check for legacy\n\tconst trimmedCred = credential.trim().toLowerCase()\n\n\tif (CREDENTIAL_REGEX.test(trimmedCred)) {\n\t\t// Connection credential: pk/sk_{env}_{optional ref}_{hex}\n\t\tconst match = CREDENTIAL_REGEX.exec(trimmedCred)!\n\t\tconst credentialType = match[1] as 'pk' | 'sk'\n\t\tconst env = match[2] as 'dev' | 'stg' | 'prod' | 'prev'\n\n\t\tconst slug = resolvedSlug.trim().toLowerCase()\n\t\t// Default to `api.sylphx.com` (DNS-only) — `sylphx.com` is Cloudflare-\n\t\t// proxied with no per-slug origin mapping and returns 404 for any\n\t\t// `<slug>.sylphx.com` request. The legacy components path below\n\t\t// (4-segment credentials) already defaults to `api.sylphx.com`; this\n\t\t// brings the new-format path in line so both behave consistently and\n\t\t// match the ADR-055 SYLPHX_URL injected by the platform.\n\t\tconst domain = input.domain?.trim() || DEFAULT_SDK_API_HOST\n\t\tconst baseUrl = `https://${slug}.${domain}/v1`\n\n\t\treturn freezeConfig({\n\t\t\tcredential: trimmedCred,\n\t\t\tcredentialType,\n\t\t\tenv,\n\t\t\tslug,\n\t\t\tbaseUrl,\n\t\t\taccessToken: input.accessToken,\n\t\t})\n\t}\n\n\t// Backward compat: accept old-format keys (pk_{env}_{ref}_{hex} or sk_{env}_{ref}_{hex})\n\t// These have 4 underscore-delimited segments. Extract env from segment 1.\n\tconst parts = trimmedCred.split('_')\n\tconst prefix = parts[0] as 'pk' | 'sk'\n\tif ((prefix === 'pk' || prefix === 'sk') && parts.length >= 3) {\n\t\tconst envSegment = parts[1]\n\t\tconst validEnvs = ['dev', 'stg', 'prod', 'prev']\n\t\tconst env = validEnvs.includes(envSegment)\n\t\t\t? (envSegment as 'dev' | 'stg' | 'prod' | 'prev')\n\t\t\t: 'prod'\n\n\t\tconst slug = resolvedSlug.trim().toLowerCase()\n\t\t// Accept deprecated platformUrl for base URL construction\n\t\tlet baseUrl: string\n\t\tif (input.platformUrl) {\n\t\t\tconst platform = input.platformUrl.trim().replace(/\\/$/, '')\n\t\t\tbaseUrl = platform.includes('/v1') ? platform : `${platform}/v1`\n\t\t} else {\n\t\t\tconst domain = input.domain?.trim() || DEFAULT_SDK_API_HOST\n\t\t\tbaseUrl = `https://${slug}.${domain}/v1`\n\t\t}\n\n\t\treturn freezeConfig({\n\t\t\tcredential: trimmedCred,\n\t\t\tcredentialType: prefix,\n\t\t\tenv,\n\t\t\tslug,\n\t\t\tbaseUrl,\n\t\t\taccessToken: input.accessToken,\n\t\t})\n\t}\n\n\tthrow new SylphxError(\n\t\t`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}. Got: \"${trimmedCred.slice(0, 30)}...\"`,\n\t\t{ code: 'BAD_REQUEST' },\n\t)\n}\n\n// =============================================================================\n// Config utilities\n// =============================================================================\n\n/**\n * Create a new config with an updated access token.\n *\n * Returns a new frozen config — does not mutate the original.\n *\n * @example\n * ```typescript\n * const authenticatedConfig = withToken(config, 'access_token_here')\n * ```\n */\nexport function withToken(config: SylphxConfig, accessToken: string): SylphxConfig {\n\treturn Object.freeze({\n\t\t...config,\n\t\taccessToken,\n\t})\n}\n\n// =============================================================================\n// Backward compatibility — createConfig (deprecated, will be removed)\n// =============================================================================\n\n/**\n * @deprecated Use `createClient()` or `createServerClient()` instead.\n * This function is kept temporarily for migration but will be removed.\n */\nexport type SylphxConfigInput = string | SylphxClientInput\n\n/**\n * @deprecated Use `createClient()` instead. See ADR-055.\n */\nexport const createConfig = createClient\n\n// =============================================================================\n// Request helpers — internal SDK plumbing\n// =============================================================================\n\n/**\n * Map HTTP status code to SylphxErrorCode\n */\nfunction httpStatusToErrorCode(status: number): SylphxErrorCode {\n\tswitch (status) {\n\t\tcase 400:\n\t\t\treturn 'BAD_REQUEST'\n\t\tcase 401:\n\t\t\treturn 'UNAUTHORIZED'\n\t\tcase 403:\n\t\t\treturn 'FORBIDDEN'\n\t\tcase 404:\n\t\t\treturn 'NOT_FOUND'\n\t\tcase 409:\n\t\t\treturn 'CONFLICT'\n\t\tcase 413:\n\t\t\treturn 'PAYLOAD_TOO_LARGE'\n\t\tcase 422:\n\t\t\treturn 'UNPROCESSABLE_ENTITY'\n\t\tcase 429:\n\t\t\treturn 'TOO_MANY_REQUESTS'\n\t\tcase 500:\n\t\t\treturn 'INTERNAL_SERVER_ERROR'\n\t\tcase 501:\n\t\t\treturn 'NOT_IMPLEMENTED'\n\t\tcase 502:\n\t\t\treturn 'BAD_GATEWAY'\n\t\tcase 503:\n\t\t\treturn 'SERVICE_UNAVAILABLE'\n\t\tcase 504:\n\t\t\treturn 'GATEWAY_TIMEOUT'\n\t\tdefault:\n\t\t\treturn status >= 500 ? 'INTERNAL_SERVER_ERROR' : 'BAD_REQUEST'\n\t}\n}\n\n/**\n * Internal: Build headers for API requests.\n *\n * Sends the credential as `x-app-secret` header.\n */\nexport function buildHeaders(config: SylphxConfig): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t}\n\n\tif (config.credential) {\n\t\theaders['x-app-secret'] = config.credential\n\t}\n\tif (config.accessToken) {\n\t\theaders.Authorization = `Bearer ${config.accessToken}`\n\t}\n\n\treturn headers\n}\n\n/**\n * Internal: Build REST API URL.\n *\n * Appends path to the pre-computed base URL.\n */\nexport function buildApiUrl(config: SylphxConfig, path: string): string {\n\tconst base = config.baseUrl.replace(/\\/$/, '')\n\tconst cleanPath = path.startsWith('/') ? path : `/${path}`\n\treturn `${base}${cleanPath}`\n}\n\n/**\n * Internal: Call REST API endpoint.\n *\n * Features:\n * - Request timeout (default 30s) prevents infinite hangs\n * - Proper HTTP status code mapping to error codes\n * - Safe JSON parsing with error handling\n * - Idempotency key support (Stripe pattern)\n */\nexport async function callApi<TOutput>(\n\tconfig: SylphxConfig,\n\tpath: string,\n\toptions: {\n\t\tmethod?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\t\tbody?: unknown\n\t\tquery?: Record<string, string | number | boolean | undefined>\n\t\ttimeout?: number\n\t\tsignal?: AbortSignal\n\t\tidempotencyKey?: string\n\t\theaders?: Record<string, string>\n\t} = {},\n): Promise<TOutput> {\n\tconst {\n\t\tmethod = 'GET',\n\t\tbody,\n\t\tquery,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t\tsignal,\n\t\tidempotencyKey,\n\t\theaders: extraHeaders,\n\t} = options\n\n\tlet url = buildApiUrl(config, path)\n\n\t// Add query parameters\n\tif (query) {\n\t\tconst params = new URLSearchParams()\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tparams.set(key, String(value))\n\t\t\t}\n\t\t}\n\t\tconst queryString = params.toString()\n\t\tif (queryString) {\n\t\t\turl += `?${queryString}`\n\t\t}\n\t}\n\n\t// Create AbortController for timeout\n\tconst controller = new AbortController()\n\tconst timeoutId = setTimeout(() => controller.abort(), timeout)\n\n\t// Combine user signal with timeout signal\n\tconst combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal\n\n\tconst headers = buildHeaders(config)\n\n\t// Add idempotency key header for safe retries (Stripe pattern)\n\tif (idempotencyKey) {\n\t\theaders['Idempotency-Key'] = idempotencyKey\n\t}\n\n\t// Merge user-supplied headers\n\tif (extraHeaders) {\n\t\tfor (const [k, v] of Object.entries(extraHeaders)) {\n\t\t\theaders[k] = v\n\t\t}\n\t}\n\n\tconst fetchOptions: RequestInit = {\n\t\tmethod,\n\t\theaders,\n\t\tsignal: combinedSignal,\n\t}\n\n\tif (body) {\n\t\tfetchOptions.body = JSON.stringify(body)\n\t}\n\n\tlet response: Response\n\ttry {\n\t\tresponse = await fetch(url, fetchOptions)\n\t} catch (error) {\n\t\tclearTimeout(timeoutId)\n\n\t\t// Handle abort/timeout\n\t\tif (error instanceof Error) {\n\t\t\tif (error.name === 'AbortError') {\n\t\t\t\t// Check if it was our timeout or user cancellation\n\t\t\t\tif (controller.signal.aborted && !signal?.aborted) {\n\t\t\t\t\tthrow new TimeoutError(timeout)\n\t\t\t\t}\n\t\t\t\tthrow new SylphxError('Request aborted', {\n\t\t\t\t\tcode: 'ABORTED',\n\t\t\t\t\tcause: error,\n\t\t\t\t})\n\t\t\t}\n\t\t\t// Network errors\n\t\t\tthrow new NetworkError(error.message, { cause: error })\n\t\t}\n\t\tthrow new NetworkError('Network request failed')\n\t} finally {\n\t\tclearTimeout(timeoutId)\n\t}\n\n\tif (!response.ok) {\n\t\tconst errorBody = await response.text().catch(() => '')\n\t\tlet errorMessage = 'Request failed'\n\t\tlet errorData: Record<string, unknown> | undefined\n\n\t\t// Safe JSON parsing\n\t\tif (errorBody) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(errorBody) as {\n\t\t\t\t\terror?: { message?: string }\n\t\t\t\t\tmessage?: string\n\t\t\t\t}\n\t\t\t\terrorMessage = parsed.error?.message ?? parsed.message ?? errorMessage\n\t\t\t\terrorData = parsed.error as Record<string, unknown> | undefined\n\t\t\t} catch {\n\t\t\t\terrorMessage = response.statusText || errorMessage\n\t\t\t}\n\t\t}\n\n\t\tconst errorCode = httpStatusToErrorCode(response.status)\n\n\t\t// Extract rate limit headers (Stripe SDK pattern)\n\t\tconst retryAfterHeader = response.headers.get('Retry-After')\n\t\tconst rateLimitLimit = response.headers.get('X-RateLimit-Limit')\n\t\tconst rateLimitRemaining = response.headers.get('X-RateLimit-Remaining')\n\t\tconst rateLimitReset = response.headers.get('X-RateLimit-Reset')\n\n\t\tconst retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : undefined\n\n\t\t// Use specialized RateLimitError for 429 responses\n\t\tif (response.status === 429) {\n\t\t\tthrow new RateLimitError(errorMessage || 'Too many requests', {\n\t\t\t\tstatus: response.status,\n\t\t\t\tdata: errorData,\n\t\t\t\tretryAfter,\n\t\t\t\tlimit: rateLimitLimit ? Number.parseInt(rateLimitLimit, 10) : undefined,\n\t\t\t\tremaining: rateLimitRemaining ? Number.parseInt(rateLimitRemaining, 10) : undefined,\n\t\t\t\tresetAt: rateLimitReset ? Number.parseInt(rateLimitReset, 10) : undefined,\n\t\t\t})\n\t\t}\n\n\t\tthrow new SylphxError(errorMessage, {\n\t\t\tcode: errorCode,\n\t\t\tstatus: response.status,\n\t\t\tdata: errorData,\n\t\t\tretryAfter,\n\t\t})\n\t}\n\n\t// Handle empty responses (204 No Content)\n\tconst text = await response.text()\n\tif (!text) {\n\t\treturn {} as TOutput\n\t}\n\n\t// Safe JSON parsing for response body\n\ttry {\n\t\treturn JSON.parse(text) as TOutput\n\t} catch (error) {\n\t\tthrow new SylphxError('Failed to parse response', {\n\t\t\tcode: 'PARSE_ERROR',\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\tdata: { body: text.slice(0, 200) },\n\t\t})\n\t}\n}\n","/**\n * CSV utilities for browser and server SDK consumers.\n */\n\n/**\n * Escape a CSV field to handle commas, quotes, and newlines.\n *\n * Handles null/undefined by returning an empty field. Wraps values containing\n * RFC 4180 special characters in double quotes and escapes internal quotes.\n */\nexport function escapeCsvField(value: string | null | undefined): string {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n\t\treturn `\"${value.replace(/\"/g, '\"\"')}\"`\n\t}\n\treturn value\n}\n","/**\n * Formatting Utilities\n *\n * Shared formatting functions for consistent display across the project.\n */\n\n// ==========================================\n// Number Formatting\n// ==========================================\n\n/**\n * Calculate percentage with consistent rounding.\n * SSOT for all success rate, completion rate calculations.\n *\n * @param count - The numerator (e.g., successful count)\n * @param total - The denominator (e.g., total count)\n * @param decimals - Number of decimal places (default: 2)\n * @returns Percentage value (0-100)\n *\n * @example\n * ```ts\n * calculatePercentage(75, 100) // 75\n * calculatePercentage(1, 3) // 33.33\n * calculatePercentage(0, 0) // 100 (safe division)\n * ```\n */\nexport function calculatePercentage(count: number, total: number, decimals = 2): number {\n\tif (total === 0) return 100 // Default to 100% when no data\n\tconst multiplier = 10 ** (decimals + 2)\n\treturn Math.round((count / total) * multiplier) / 10 ** decimals\n}\n\n// ==========================================\n// Currency Formatting\n// ==========================================\n\n/**\n * Format microdollars to currency string\n * @param microdollars Amount in microdollars (1 dollar = 1,000,000 microdollars)\n * @param options Intl.NumberFormat options\n */\nexport function formatMicrodollars(\n\tmicrodollars: number,\n\toptions?: Intl.NumberFormatOptions,\n): string {\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency: 'USD',\n\t\t...options,\n\t}).format(microdollars / 1_000_000)\n}\n\n/**\n * Format cents to currency string\n * @param cents Amount in cents (100 cents = 1 dollar)\n *\n * @example\n * ```ts\n * formatCents(1999) // \"$19.99\"\n * formatCents(100) // \"$1.00\"\n * ```\n */\nexport function formatCents(cents: number): string {\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency: 'USD',\n\t}).format(cents / 100)\n}\n\n/**\n * Format dollars to currency string with optional compact notation\n * @param amount Amount in dollars\n * @param compact Use compact notation for large amounts (default: false)\n *\n * @example\n * ```ts\n * formatCurrency(1999.99) // \"$1,999.99\"\n * formatCurrency(1999.99, true) // \"$2.0K\"\n * formatCurrency(1999.99, { currency: 'EUR' }) // \"€1,999.99\"\n * formatCurrency(1999.99, { compact: true }) // \"$2.0K\"\n * ```\n *\n * Second argument accepts either a bare `boolean` (back-compat for\n * the historical `compact` flag) or an options object with\n * `{ currency, compact }`. The options form is required for any UI\n * surface that displays multi-currency amounts (billing, invoices,\n * usage statements) — previously two local copies of this function\n * lived in `billing-management.tsx` to work around the missing\n * currency parameter.\n */\nexport function formatCurrency(\n\tamount: number,\n\toptsOrCompact: boolean | { compact?: boolean; currency?: string; decimals?: number } = false,\n): string {\n\tconst opts = typeof optsOrCompact === 'boolean' ? { compact: optsOrCompact } : optsOrCompact\n\tconst currency = (opts.currency ?? 'USD').toUpperCase()\n\tconst decimals = opts.decimals ?? 2\n\tif (opts.compact && Math.abs(amount) >= 1000) {\n\t\treturn new Intl.NumberFormat('en-US', {\n\t\t\tstyle: 'currency',\n\t\t\tcurrency,\n\t\t\tnotation: 'compact',\n\t\t\tminimumFractionDigits: 1,\n\t\t\tmaximumFractionDigits: 1,\n\t\t}).format(amount)\n\t}\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency,\n\t\tminimumFractionDigits: decimals,\n\t\tmaximumFractionDigits: decimals,\n\t}).format(amount)\n}\n\n/**\n * Format percentage with sign for trend display\n * @param value Percentage value (not multiplied by 100)\n *\n * @example\n * ```ts\n * formatPercent(12.5) // \"+12.5%\"\n * formatPercent(-5.2) // \"-5.2%\"\n * formatPercent(0) // \"+0.0%\"\n * ```\n */\nexport function formatPercent(value: number): string {\n\treturn `${value >= 0 ? '+' : ''}${value.toFixed(1)}%`\n}\n\n/**\n * Format number with abbreviated suffix (K, M, B) or compact notation\n * @param num Number to format\n * @param compact Use Intl compact notation (default: false, uses K/M/B suffix)\n *\n * @example\n * ```ts\n * formatNumber(1234) // \"1,234\"\n * formatNumber(1234567) // \"1.2M\"\n * formatNumber(1234, true) // \"1.2K\" (Intl compact)\n * ```\n */\nexport function formatNumber(num: number, compact = false): string {\n\tif (compact && num >= 1000) {\n\t\treturn new Intl.NumberFormat('en-US', {\n\t\t\tnotation: 'compact',\n\t\t\tmaximumFractionDigits: 1,\n\t\t}).format(num)\n\t}\n\tif (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)}B`\n\tif (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`\n\tif (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`\n\treturn num.toLocaleString()\n}\n\n/**\n * Format duration in milliseconds to human-readable string.\n * SSOT for latency display in traces, performance, and monitoring.\n *\n * @param ms Duration in milliseconds\n * @returns Formatted string (e.g., \"<1ms\", \"42ms\", \"1.23s\")\n *\n * @example\n * ```ts\n * formatDuration(0.5) // \"<1ms\"\n * formatDuration(42) // \"42ms\"\n * formatDuration(1500) // \"1.50s\"\n * ```\n */\nexport function formatDuration(ms: number): string {\n\tif (ms < 1) return '<1ms'\n\tif (ms < 1000) return `${Math.round(ms)}ms`\n\treturn `${(ms / 1000).toFixed(2)}s`\n}\n\n/**\n * Format bytes to human-readable string\n * @param bytes Number of bytes\n * @param decimals Number of decimal places (default: 1)\n */\nexport function formatBytes(bytes: number | null | undefined, decimals = 1): string {\n\tif (bytes == null || bytes === 0 || Number.isNaN(bytes)) return '0 B'\n\tconst k = 1024\n\tconst sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k))\n\treturn `${Number.parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`\n}\n\n// ==========================================\n// Status Badge Variants (Declarative)\n// ==========================================\n\n/** Badge variant type for consistency */\ntype BadgeVariant = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'outline'\n\n/**\n * Billing status to badge variant mapping.\n * Declarative configuration — add new statuses here.\n */\nconst BILLING_STATUS_VARIANTS: ReadonlyMap<string, BadgeVariant> = new Map([\n\t// Active/healthy states\n\t['healthy', 'default'],\n\t['active', 'default'],\n\t// Pending/low states\n\t['low', 'secondary'],\n\t['pending', 'secondary'],\n\t// Success states\n\t['paid', 'success'],\n\t['completed', 'success'],\n\t// Warning states\n\t['grace_period', 'warning'],\n\t['overdue', 'warning'],\n\t// Error states\n\t['critical', 'error'],\n\t['blocked', 'error'],\n\t['suspended', 'error'],\n\t['failed', 'error'],\n])\n\n/**\n * Get billing status badge variant.\n * Pure function — no side effects, deterministic output.\n *\n * @param status Billing account status\n * @returns Badge variant for display\n */\nexport function getBillingStatusVariant(status: string): BadgeVariant {\n\treturn BILLING_STATUS_VARIANTS.get(status) ?? 'outline'\n}\n\n/**\n * Invoice status to badge variant mapping.\n * Declarative configuration — add new statuses here.\n */\nconst INVOICE_STATUS_VARIANTS: ReadonlyMap<string, BadgeVariant> = new Map([\n\t['draft', 'secondary'],\n\t['pending', 'default'],\n\t['paid', 'success'],\n\t['overdue', 'warning'],\n\t['failed', 'error'],\n\t['cancelled', 'error'],\n])\n\n/**\n * Get invoice status badge variant.\n * Pure function — no side effects, deterministic output.\n *\n * @param status Invoice status\n * @returns Badge variant for display\n */\nexport function getInvoiceStatusVariant(status: string): BadgeVariant {\n\treturn INVOICE_STATUS_VARIANTS.get(status) ?? 'outline'\n}\n\n/**\n * Safely parse a date value, returning null for invalid dates.\n */\nfunction parseDate(date: Date | string): Date | null {\n\tconst d = typeof date === 'string' ? new Date(date) : date\n\t// Check for invalid date (NaN check on timestamp)\n\treturn Number.isNaN(d.getTime()) ? null : d\n}\n\n/**\n * Format date for display\n * @param date Date to format (null returns fallback)\n * @param options Intl.DateTimeFormat options\n * @param fallback Value to return when date is null (default: '-')\n */\nexport function formatDate(\n\tdate: Date | string | null,\n\toptions?: Intl.DateTimeFormatOptions,\n\tfallback = '-',\n): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleDateString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: 'numeric',\n\t\t...options,\n\t})\n}\n\n/**\n * Format date with time for display\n * @param date Date to format (null returns fallback)\n * @param options Override options\n * @param fallback Value to return when date is null (default: '-')\n */\nexport function formatDateTime(\n\tdate: Date | string | null,\n\toptions?: Intl.DateTimeFormatOptions,\n\tfallback = '-',\n): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\thour: '2-digit',\n\t\tminute: '2-digit',\n\t\t...options,\n\t})\n}\n\n/** Relative time formatter using native Intl API */\nconst relativeTimeFormatter = new Intl.RelativeTimeFormat('en', {\n\tnumeric: 'auto',\n})\n\n/** Time divisions for relative time calculation */\nconst TIME_DIVISIONS: { amount: number; unit: Intl.RelativeTimeFormatUnit }[] = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n]\n\n/**\n * Format relative time (e.g., \"2 hours ago\")\n *\n * Uses native Intl.RelativeTimeFormat for proper localization.\n *\n * @param date Date to format (null returns 'Never')\n */\nexport function formatRelativeTime(date: Date | string | null): string {\n\tif (!date) return 'Never'\n\tconst d = parseDate(date)\n\tif (!d) return 'Never'\n\tlet seconds = (d.getTime() - Date.now()) / 1000\n\n\tfor (const { amount, unit } of TIME_DIVISIONS) {\n\t\tif (Math.abs(seconds) < amount) {\n\t\t\treturn relativeTimeFormatter.format(Math.round(seconds), unit)\n\t\t}\n\t\tseconds /= amount\n\t}\n\n\t// Fallback (shouldn't reach here)\n\treturn formatDate(d)\n}\n\n/**\n * Format relative time in compact form (e.g., \"2h ago\", \"3d ago\").\n * SSOT for dense UI contexts: tables, feeds, badges.\n *\n * Uses short suffixes (s/m/h/d/w) instead of Intl.RelativeTimeFormat words.\n * For prose contexts, use {@link formatRelativeTime} instead.\n *\n * @param date Date to format (null returns 'Never')\n *\n * @example\n * ```ts\n * formatRelativeTimeShort(new Date()) // \"Just now\"\n * formatRelativeTimeShort('2024-01-01T00:00:00Z') // \"3d ago\"\n * formatRelativeTimeShort(null) // \"Never\"\n * ```\n */\nexport function formatRelativeTimeShort(date: Date | string | null): string {\n\tif (!date) return 'Never'\n\tconst d = parseDate(date)\n\tif (!d) return 'Never'\n\tconst diffMs = Date.now() - d.getTime()\n\tconst diffSecs = Math.floor(diffMs / 1000)\n\tconst diffMins = Math.floor(diffMs / 60_000)\n\tconst diffHours = Math.floor(diffMs / 3_600_000)\n\tconst diffDays = Math.floor(diffMs / 86_400_000)\n\n\tif (diffSecs < 10) return 'Just now'\n\tif (diffSecs < 60) return `${diffSecs}s ago`\n\tif (diffMins < 60) return `${diffMins}m ago`\n\tif (diffHours < 24) return `${diffHours}h ago`\n\tif (diffDays === 1) return 'Yesterday'\n\tif (diffDays < 7) return `${diffDays}d ago`\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`\n\treturn d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n}\n\n/**\n * Format month and year (e.g., \"January 2024\")\n * SSOT for billing period display, invoice headers.\n * @param date Date to format (null returns fallback)\n * @param fallback Value to return when date is null\n */\nexport function formatMonthYear(date: Date | string | null, fallback = '-'): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })\n}\n\n/**\n * Format time only (e.g., \"2:30 PM\")\n * SSOT for log timestamps, activity feeds.\n * @param date Date to format (null returns fallback)\n * @param fallback Value to return when date is null\n */\nexport function formatTime(date: Date | string | null, fallback = '-'): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })\n}\n","/**\n * Safely parse a JSON string, returning a fallback value on failure instead of throwing.\n *\n * Use this when malformed input is a normal (non-exceptional) case — e.g. parsing\n * user-provided data, localStorage values, or Redis cache entries where the caller\n * simply wants a default on failure.\n *\n * For cases where parse failure is truly exceptional and the caller needs to handle\n * the error explicitly, use a standard try-catch with proper logging instead.\n */\nexport function safeJsonParse<T = unknown>(input: string, fallback?: T): T | null {\n\ttry {\n\t\treturn JSON.parse(input) as T\n\t} catch {\n\t\treturn fallback ?? null\n\t}\n}\n","/**\n * Utility Functions\n */\n\n/**\n * Get the base URL for API requests\n *\n * Use cases:\n * - getBaseUrl(): For relative URLs in browser, absolute in SSR (tRPC, API calls)\n * - getBaseUrl('origin'): For absolute URLs that need the actual origin (auth, sharing)\n *\n * Priority: NEXT_PUBLIC_APP_URL > localhost\n */\nexport function getBaseUrl(mode: 'relative' | 'origin' = 'relative'): string {\n\tif (typeof (globalThis as { window?: unknown }).window !== 'undefined') {\n\t\t// Browser: use relative or actual origin\n\t\treturn mode === 'origin'\n\t\t\t? (globalThis as { window: { location: { origin: string } } }).window.location.origin\n\t\t\t: ''\n\t}\n\n\t// SSR/Server: use environment configuration\n\tif (process.env.NEXT_PUBLIC_APP_URL) {\n\t\treturn process.env.NEXT_PUBLIC_APP_URL\n\t}\n\n\t// Fallback for development\n\tconst port = process.env.PORT ?? '3000'\n\treturn `http://localhost:${port}`\n}\n\n/**\n * HTML entity map for escaping\n */\nconst HTML_ENTITIES: Record<string, string> = {\n\t'&': '&amp;',\n\t'<': '&lt;',\n\t'>': '&gt;',\n\t'\"': '&quot;',\n\t\"'\": '&#039;',\n}\n\n/**\n * Escape HTML special characters to prevent XSS\n *\n * Uses single-pass regex replacement for efficiency.\n */\nexport function escapeHtml(str: string): string {\n\treturn str.replace(/[&<>\"']/g, (char) => HTML_ENTITIES[char])\n}\n\n// ==========================================\n// Slug Utilities\n// ==========================================\n\n/**\n * Generate a URL-friendly slug from text\n *\n * @param text - Text to convert to slug\n * @param maxLength - Optional maximum length (default: no limit)\n * @returns Lowercase slug with hyphens\n *\n * @example\n * generateSlug('My Awesome App') // 'my-awesome-app'\n * generateSlug('Hello World!') // 'hello-world'\n * generateSlug('My Org Name', 48) // 'my-org-name' (max 48 chars)\n */\nexport function generateSlug(text: string, maxLength?: number): string {\n\tconst slug = text\n\t\t.toLowerCase()\n\t\t.trim()\n\t\t.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens\n\t\t.replace(/^-+|-+$/g, '') // Trim leading/trailing hyphens\n\t\t.replace(/-{2,}/g, '-') // Collapse multiple hyphens\n\n\tif (maxLength && slug.length > maxLength) {\n\t\t// Trim to maxLength, avoiding mid-word cut\n\t\treturn slug.slice(0, maxLength).replace(/-+$/, '')\n\t}\n\n\treturn slug\n}\n","/**\n * User Agent Parsing Utilities\n *\n * Extracts browser, OS, and device type from user agent strings.\n * Simple implementation - no external dependencies.\n */\n\nexport interface ParsedUserAgent {\n\tbrowser: string | null\n\tos: string | null\n\tdeviceType: 'desktop' | 'mobile' | 'tablet' | null\n}\n\n/**\n * Parse a user agent string to extract browser, OS, and device type.\n * Returns null values if unable to determine.\n */\nexport function parseUserAgent(ua: string): ParsedUserAgent {\n\tif (!ua) {\n\t\treturn { browser: null, os: null, deviceType: null }\n\t}\n\n\treturn {\n\t\tbrowser: detectBrowser(ua),\n\t\tos: detectOS(ua),\n\t\tdeviceType: detectDeviceType(ua),\n\t}\n}\n\n/**\n * Detect browser from user agent\n */\nfunction detectBrowser(ua: string): string | null {\n\t// Order matters - check more specific browsers first\n\tif (ua.includes('Edg/')) {\n\t\tconst match = ua.match(/Edg\\/(\\d+)/)\n\t\treturn match ? `Edge ${match[1]}` : 'Edge'\n\t}\n\tif (ua.includes('OPR/') || ua.includes('Opera')) {\n\t\tconst match = ua.match(/OPR\\/(\\d+)/) || ua.match(/Opera\\/(\\d+)/)\n\t\treturn match ? `Opera ${match[1]}` : 'Opera'\n\t}\n\tif (ua.includes('Brave')) {\n\t\treturn 'Brave'\n\t}\n\tif (ua.includes('Chrome/')) {\n\t\tconst match = ua.match(/Chrome\\/(\\d+)/)\n\t\treturn match ? `Chrome ${match[1]}` : 'Chrome'\n\t}\n\tif (ua.includes('Safari/') && !ua.includes('Chrome')) {\n\t\tconst match = ua.match(/Version\\/(\\d+)/)\n\t\treturn match ? `Safari ${match[1]}` : 'Safari'\n\t}\n\tif (ua.includes('Firefox/')) {\n\t\tconst match = ua.match(/Firefox\\/(\\d+)/)\n\t\treturn match ? `Firefox ${match[1]}` : 'Firefox'\n\t}\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\n\t\tconst match = ua.match(/(?:MSIE |rv:)(\\d+)/)\n\t\treturn match ? `IE ${match[1]}` : 'Internet Explorer'\n\t}\n\treturn null\n}\n\n/**\n * Detect operating system from user agent\n */\nfunction detectOS(ua: string): string | null {\n\t// Mobile OS - check first\n\tif (ua.includes('iPhone') || ua.includes('iPad')) {\n\t\tconst match = ua.match(/OS (\\d+[_\\d]*)/)\n\t\tif (match) {\n\t\t\tconst version = match[1].replace(/_/g, '.')\n\t\t\treturn `iOS ${version}`\n\t\t}\n\t\treturn 'iOS'\n\t}\n\tif (ua.includes('Android')) {\n\t\tconst match = ua.match(/Android (\\d+[.\\d]*)/)\n\t\treturn match ? `Android ${match[1]}` : 'Android'\n\t}\n\n\t// Desktop OS\n\tif (ua.includes('Mac OS X') || ua.includes('macOS')) {\n\t\tconst match = ua.match(/Mac OS X (\\d+[_\\d]*)/) || ua.match(/macOS (\\d+[_\\d]*)/)\n\t\tif (match) {\n\t\t\tconst version = match[1].replace(/_/g, '.')\n\t\t\treturn `macOS ${version}`\n\t\t}\n\t\treturn 'macOS'\n\t}\n\tif (ua.includes('Windows NT')) {\n\t\tconst match = ua.match(/Windows NT ([\\d.]+)/)\n\t\tif (match) {\n\t\t\tconst ntVersion = match[1]\n\t\t\t// Map NT versions to Windows versions\n\t\t\tconst windowsVersions: Record<string, string> = {\n\t\t\t\t'10.0': 'Windows 10/11',\n\t\t\t\t'6.3': 'Windows 8.1',\n\t\t\t\t'6.2': 'Windows 8',\n\t\t\t\t'6.1': 'Windows 7',\n\t\t\t\t'6.0': 'Windows Vista',\n\t\t\t\t'5.1': 'Windows XP',\n\t\t\t}\n\t\t\treturn windowsVersions[ntVersion] || `Windows NT ${ntVersion}`\n\t\t}\n\t\treturn 'Windows'\n\t}\n\tif (ua.includes('Linux')) {\n\t\tif (ua.includes('Ubuntu')) return 'Ubuntu'\n\t\tif (ua.includes('Fedora')) return 'Fedora'\n\t\tif (ua.includes('Debian')) return 'Debian'\n\t\treturn 'Linux'\n\t}\n\tif (ua.includes('CrOS')) {\n\t\treturn 'Chrome OS'\n\t}\n\treturn null\n}\n\n/**\n * Detect device type from user agent\n */\nfunction detectDeviceType(ua: string): 'desktop' | 'mobile' | 'tablet' | null {\n\t// Check for tablets first (before mobile, as some tablets have \"Mobile\" in UA)\n\tif (\n\t\tua.includes('iPad') ||\n\t\tua.includes('Tablet') ||\n\t\t(ua.includes('Android') && !ua.includes('Mobile'))\n\t) {\n\t\treturn 'tablet'\n\t}\n\n\t// Check for mobile\n\tif (\n\t\tua.includes('iPhone') ||\n\t\tua.includes('iPod') ||\n\t\tua.includes('Android') ||\n\t\tua.includes('Mobile') ||\n\t\tua.includes('webOS') ||\n\t\tua.includes('BlackBerry') ||\n\t\tua.includes('IEMobile') ||\n\t\tua.includes('Opera Mini')\n\t) {\n\t\treturn 'mobile'\n\t}\n\n\t// Desktop (Windows, Mac, Linux without mobile indicators)\n\tif (\n\t\tua.includes('Windows NT') ||\n\t\tua.includes('Mac OS X') ||\n\t\tua.includes('macOS') ||\n\t\tua.includes('Linux') ||\n\t\tua.includes('CrOS')\n\t) {\n\t\treturn 'desktop'\n\t}\n\n\treturn null\n}\n","import type {\n\tCreateWorkspaceInput,\n\tForkWorkspaceInput,\n\tListWorkspacesResult,\n\tManagedWorkspace,\n\tWorkspaceResult,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\nexport type WorkspaceRecord = ManagedWorkspace\nexport type { CreateWorkspaceInput, ForkWorkspaceInput }\n\nexport async function listWorkspaces(config: SylphxConfig): Promise<readonly WorkspaceRecord[]> {\n\tconst result = await callApi<ListWorkspacesResult>(config, '/workspaces')\n\treturn result.workspaces\n}\n\nexport async function getWorkspace(\n\tconfig: SylphxConfig,\n\tworkspaceId: string,\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(\n\t\tconfig,\n\t\t`/workspaces/${encodeURIComponent(workspaceId)}`,\n\t)\n\treturn result.workspace\n}\n\nexport async function createWorkspace(\n\tconfig: SylphxConfig,\n\tinput: CreateWorkspaceInput,\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(config, '/workspaces', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t\ttimeout: 300_000,\n\t})\n\treturn result.workspace\n}\n\nexport async function forkWorkspace(\n\tconfig: SylphxConfig,\n\tsourceWorkspaceId: string,\n\tinput: ForkWorkspaceInput = {},\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(\n\t\tconfig,\n\t\t`/workspaces/${encodeURIComponent(sourceWorkspaceId)}/forks`,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: input,\n\t\t\ttimeout: 5 * 60_000,\n\t\t},\n\t)\n\treturn result.workspace\n}\n\nexport async function deleteWorkspace(config: SylphxConfig, workspaceId: string): Promise<void> {\n\tawait callApi<Record<string, unknown>>(config, `/workspaces/${encodeURIComponent(workspaceId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n","/**\n * Authentication Configuration (SSOT)\n *\n * Single source of truth for authentication-related constants.\n * Used by: validation schemas, auth forms, password policies\n */\n\n// ==========================================\n// Password Policy\n// ==========================================\n\n/** Minimum password length */\nexport const MIN_PASSWORD_LENGTH = 8\n\n/** Maximum password length */\nexport const MAX_PASSWORD_LENGTH = 128\n\n/** Password requirements for display in UI */\nexport const PASSWORD_REQUIREMENTS = {\n\tminLength: MIN_PASSWORD_LENGTH,\n\tmaxLength: MAX_PASSWORD_LENGTH,\n\tdescription: `Must be at least ${MIN_PASSWORD_LENGTH} characters`,\n\tplaceholder: `Min. ${MIN_PASSWORD_LENGTH} characters`,\n} as const\n","/**\n * Billing Configuration (SSOT)\n *\n * Single source of truth for billing-related configuration.\n * Used by: billing pages, usage tracking, invoicing\n */\n\n// ==========================================\n// Billing Constants\n// ==========================================\n\n/** Bytes per gigabyte — use instead of hardcoding 1024*1024*1024 */\nexport const BYTES_PER_GB = 1024 * 1024 * 1024\n\n/** Microdollars per cent ($0.01 = 10,000 microdollars) */\nexport const MICRODOLLARS_PER_CENT = 10_000\n\n/** Invoice payment due after billing period ends (days) */\nexport const INVOICE_DUE_DAYS = 15\n\n// ==========================================\n// Service Billing Metrics (SSOT)\n// ==========================================\n// IMPORTANT: These strings must match the metrics used in:\n// - Platform pricing table (platform_pricing)\n// - Services marketing page (services/_data/services.ts)\n// - SDK routes that call recordServiceUsage()\n\n/**\n * Billing metrics per service\n * These are user-facing metric names (NOT technical terms like 'commands')\n */\nexport const SERVICE_METRICS = {\n\t// KV (Key-Value Store)\n\tkv: {\n\t\toperations: 'operations', // User-friendly: \"100K operations\"\n\t\tstorage: 'storage', // GB-month\n\t},\n\t// Realtime (Pub/Sub Messaging)\n\trealtime: {\n\t\tmessages: 'messages', // User-friendly: \"100K messages\"\n\t\tconnections: 'connections', // SSE subscribe connections\n\t},\n\t// Other services follow the same pattern\n\tai: {\n\t\ttokens: 'tokens',\n\t},\n\temail: {\n\t\temails: 'emails',\n\t\tmarketingEmails: 'marketing_emails',\n\t},\n\tnotifications: {\n\t\tsends: 'sends',\n\t},\n\tanalytics: {\n\t\tevents: 'events',\n\t\tforwarding: 'forwarding',\n\t},\n\tstorage: {\n\t\tcapacity: 'capacity',\n\t\tuploads: 'uploads',\n\t\tegress: 'egress',\n\t},\n\tauth: {\n\t\tmau: 'mau',\n\t},\n\tflags: {\n\t\tevaluations: 'evaluations',\n\t},\n\tconsent: {\n\t\trecords: 'records',\n\t},\n\treferrals: {\n\t\tconversions: 'conversions',\n\t},\n\tengagement: {\n\t\toperations: 'operations',\n\t},\n\tbilling: {\n\t\tsubscriptions: 'subscriptions',\n\t\tusageRecords: 'usage_records',\n\t},\n\tsearch: {\n\t\tdocuments: 'documents',\n\t\tsearches: 'searches',\n\t},\n\twebhooks: {\n\t\tdeliveries: 'deliveries',\n\t},\n\tmonitoring: {\n\t\terrors: 'errors',\n\t},\n\tjobs: {\n\t\tinvocations: 'invocations',\n\t\tcronSchedules: 'cron_schedules',\n\t},\n\tdatabase: {\n\t\tcomputeSeconds: 'compute_seconds',\n\t\tstorage: 'storage',\n\t\tdataTransferBytes: 'data_transfer_bytes',\n\t},\n\tdeploy: {\n\t\tbuildMinutes: 'build_minutes',\n\t},\n} as const\n\n// ==========================================\n// Compute Rates (ADR-034)\n// ==========================================\n\n/** Active vCPU rate: $0.024/hr = $0.0004/min = 400 microdollars/min (ADR-034) */\nexport const COMPUTE_VCPU_ACTIVE_RATE_MICRODOLLARS = 400\n\n/** Idle vCPU rate: $0.003/hr = $0.00005/min = 50 microdollars/min — 1/8 of active (ADR-034) */\nexport const COMPUTE_VCPU_IDLE_RATE_MICRODOLLARS = 50\n\n/** RAM rate: $0.010/GB-hr = $0.000167/GB-min = 167 microdollars/GB-min (ADR-034) */\nexport const COMPUTE_RAM_RATE_MICRODOLLARS = 167\n\n// ==========================================\n// Build Machine Rates (ADR-034)\n// ==========================================\n\n/** Build minute prices by machine type in microdollars per minute (ADR-034) */\nexport const BUILD_MINUTE_PRICES: Record<string, number> = {\n\tstandard: 14_000, // $0.014/min\n\tlarge: 30_000, // $0.030/min\n\txlarge: 126_000, // $0.126/min\n}\n\n/** Build minute size multipliers for quota tracking (ADR-034) */\nexport const BUILD_SIZE_MULTIPLIERS: Record<string, number> = {\n\tstandard: 1,\n\tlarge: 2,\n\txlarge: 9,\n}\n\n// ==========================================\n// Build Minutes Included Per Plan (ADR-034)\n// ==========================================\n\n/** Build minutes included per month by plan tier (ADR-034) */\nexport const BUILD_MINUTES_INCLUDED: Record<string, number> = {\n\tfree: 100,\n\tpro: 500,\n\tteam: 2_000,\n\tenterprise: 10_000,\n}\n\n// ==========================================\n// CI/CD Pricing (Legacy — backward compat)\n// ==========================================\n\n/**\n * @deprecated Use BUILD_MINUTE_PRICES.standard instead (ADR-034).\n * Kept for backward compatibility with existing billing pipelines.\n *\n * CI compute-minute price in microdollars.\n * Now references the standard build machine rate from ADR-034.\n */\nexport const CI_BUILD_MINUTE_PRICE_MICRODOLLARS = BUILD_MINUTE_PRICES.standard\n\n/**\n * @deprecated Use BUILD_MINUTES_INCLUDED[plan] instead (ADR-034).\n * Kept for backward compatibility. Maps to the `team` tier as the\n * previous default (2,000 free minutes).\n */\nexport const CI_FREE_MINUTES_PER_MONTH = BUILD_MINUTES_INCLUDED.team\n\n/**\n * Size multipliers for CI compute-minute accounting (legacy GitHub labels).\n *\n * Keys are **GitHub Actions runner labels** (not build machine type names).\n * These labels arrive on workflow_job webhooks and are stored as-is in\n * githubCiJobs.resourceClass. The billing pipeline maps them to multipliers\n * here; the build pipeline maps them to BuildMachineType via\n * normalizeBuildMachineType() in build-machine.ts.\n *\n * @see BUILD_SIZE_MULTIPLIERS for canonical build machine multipliers (ADR-034).\n */\nexport const CI_SIZE_MULTIPLIERS: Record<string, number> = {\n\tnano: 1,\n\tsmall: 1,\n\tstandard: 1,\n\tlarge: 2,\n\txlarge: 4,\n\t'2xlarge': 8,\n} as const\n\n/** macOS runner per-size multipliers (ADR-035: per-tier billing) */\nexport const CI_MACOS_SIZE_MULTIPLIERS: Record<string, number> = {\n\tstandard: 10,\n\tlarge: 20,\n\txlarge: 40,\n} as const\n\n/** @deprecated Use CI_MACOS_SIZE_MULTIPLIERS[size] instead. */\nexport const CI_MACOS_MULTIPLIER = CI_MACOS_SIZE_MULTIPLIERS.standard\n\n// Type-safe metric access\nexport type ServiceMetrics = typeof SERVICE_METRICS\nexport type KvMetric = keyof typeof SERVICE_METRICS.kv\nexport type RealtimeMetric = keyof typeof SERVICE_METRICS.realtime\n\n// ==========================================\n// Credit System\n// ==========================================\n\n/** Credit expiry period in months */\nexport const CREDIT_EXPIRY_MONTHS = 12\n\n// ==========================================\n// Payment Processing\n// ==========================================\n\n/** Maximum payment retry attempts before suspending account */\nexport const MAX_PAYMENT_ATTEMPTS = 3\n\n// ==========================================\n// Permission Roles\n// ==========================================\n\n/** Roles that can access billing pages */\nexport const BILLING_ALLOWED_ROLES = ['super_admin', 'admin', 'billing'] as const\nexport type BillingAllowedRole = (typeof BILLING_ALLOWED_ROLES)[number]\n\n/** Check if a role has billing access */\nexport function hasBillingAccess(role: string): boolean {\n\treturn BILLING_ALLOWED_ROLES.includes(role as BillingAllowedRole)\n}\n","/**\n * Console SDK Key Utilities\n *\n * The Platform Console is Customer Zero — it uses the exact same key format\n * as every other customer: pk_{env}_{ref}_{hex} / sk_{env}_{ref}_{hex}.\n *\n * No special key construction. No legacy app_* format. No special lookup paths.\n *\n * Keys are set via environment variables, just like any customer app:\n * NEXT_PUBLIC_SYLPHX_KEY = pk_prod_nlbaz63pd2gz_97ef4f90c48e7378b0f00a1e2cb8c15e\n * SYLPHX_SECRET_KEY = sk_prod_nlbaz63pd2gz_edea406b7988099f5826c143b0f6bd94...\n */\n\n/** Console project slug — must match bootstrap.ts PLATFORM_CONSOLE_APP.slug */\nexport const CONSOLE_APP_SLUG = 'sylphx-console'\n\n/**\n * Determine environment prefix from build/runtime environment.\n * Used by sdk-cookies.ts for cookie naming until it migrates to SDK-native getCookieNames().\n * NOTE: still actively consumed by sdk-cookies.ts and sdk-login.ts — remove only after\n * those modules parse the env prefix from NEXT_PUBLIC_SYLPHX_KEY directly.\n */\nexport function getEnvPrefix(): 'dev' | 'stg' | 'prod' {\n\tconst envType = process.env.NEXT_PUBLIC_ENV_TYPE\n\tif (envType === 'development') return 'dev'\n\tif (envType === 'staging') return 'stg'\n\tif (envType === 'production') return 'prod'\n\treturn process.env.NODE_ENV === 'production' ? 'prod' : 'dev'\n}\n","/**\n * Instance Type Catalog — SSOT\n *\n * Defines the compute instance types available for Sylphx platform workloads.\n * Each instance type maps to Kubernetes resource requests/limits for kata-clh\n * (Cloud Hypervisor) microVMs, along with billing rates and plan eligibility.\n *\n * Rates are in microdollars (1 USD = 1,000,000 µ$) per minute.\n *\n * Memory overcommit (ADR-028): CLH uses demand paging (mmap without MAP_POPULATE).\n * Host physical RAM is allocated on-demand as guest pages are touched, NOT pre-allocated\n * at VM boot. Verified 2026-03-30: a pod with limits=4Gi only consumed +8Mi host RAM\n * when idle. Memory requests are set to 50% of limits (2x overcommit) for efficient\n * scheduler bin-packing. CPU requests are 25% of limits (4x overcommit).\n *\n * ADR-034 T-shirt sizing: canonical names are xs/sm/md/lg/xl/2xl/4xl.\n * Legacy names (starter-1x, standard-1x, etc.) are kept as aliases for backward\n * compatibility with existing database values and API consumers.\n */\n\nimport type { PlatformPlanId } from './platform-plans'\n\n// ==========================================\n// Instance Type Types\n// ==========================================\n\nexport type InstanceTypeId =\n\t| 'xs'\n\t| 'sm'\n\t| 'md'\n\t| 'lg'\n\t| 'xl'\n\t| '2xl'\n\t| '4xl'\n\t// @deprecated — legacy names, use T-shirt sizes instead\n\t| 'starter-1x'\n\t| 'standard-1x'\n\t| 'standard-2x'\n\t| 'performance-m'\n\t| 'performance-l'\n\t| 'performance-xl'\n\nexport interface InstanceTypeDefinition {\n\tid: InstanceTypeId\n\tname: string\n\t/** Kubernetes CPU limit (e.g. '2000m') */\n\tcpuLimit: string\n\t/** Kubernetes memory limit (e.g. '8Gi') */\n\tmemoryLimit: string\n\t/** Kubernetes CPU request (e.g. '500m') */\n\tcpuRequest: string\n\t/** Kubernetes memory request (50% of limit — CLH demand paging, ADR-028) */\n\tmemoryRequest: string\n\t/** Billing vCPU count for metering denormalization */\n\tvcpus: number\n\t/** Billing memory in MiB for metering denormalization */\n\tmemoryMib: number\n\t/** Rate per vCPU per minute in microdollars */\n\tvcpuMinuteRateMicrodollars: number\n\t/** Rate per GiB per minute in microdollars */\n\tgbMinuteRateMicrodollars: number\n\t/** Platform plans that may provision this instance type */\n\tallowedPlans: PlatformPlanId[]\n\t/**\n\t * Maximum platform-managed instance count for this instance type tier.\n\t * Caps horizontal scale-out on smaller tiers. Users can set a lower\n\t * per-service maximum through ScalePolicy, but never exceed this ceiling.\n\t */\n\tmaxInstances: number\n\t/** Marketing bullet points for instance type cards */\n\thighlights: string[]\n\t/** Whether this instance type is deprecated (legacy name) */\n\tdeprecated?: boolean\n}\n\n// ==========================================\n// Billing Rates\n// ==========================================\n\n/** µ$/vCPU-minute — ~$0.028/vCPU-hour, competitive with Railway */\nconst VCPU_MINUTE_RATE = 463\n\n/** µ$/GiB-minute — ~$0.014/GiB-hour, competitive with Railway */\nconst GIB_MINUTE_RATE = 232\n\n// ==========================================\n// Alias Mapping (legacy → canonical)\n// ==========================================\n\n/**\n * Maps legacy instance type names to their canonical T-shirt size equivalents (ADR-034).\n *\n * Database values and API consumers may still use old names — this mapping lets\n * resolveInstanceType() transparently return the canonical definition without\n * requiring a data migration.\n */\nexport const INSTANCE_TYPE_ALIASES: Record<string, string> = {\n\t'starter-1x': 'xs',\n\t'standard-1x': 'sm',\n\t'standard-2x': 'md',\n\t'performance-m': 'lg',\n\t'performance-l': 'xl',\n\t'performance-xl': '2xl',\n}\n\n/**\n * Resolve a potentially-aliased instance type ID to its canonical T-shirt size.\n * Returns the input unchanged if it is already canonical or unknown.\n *\n * Use for display/UI and when accepting user input for NEW configurations.\n * Do NOT use in runtime paths (billing, K8s reconciler) — both old and new names\n * exist in INSTANCE_TYPES with their original specs, so direct lookup is correct\n * and avoids changing billing/resource behavior for existing services.\n */\nexport function resolveCanonicalInstanceType(id: string): string {\n\treturn INSTANCE_TYPE_ALIASES[id] ?? id\n}\n\n// ==========================================\n// Instance Type Definitions (ADR-034 T-shirt sizes)\n// ==========================================\n\nexport const INSTANCE_TYPES: Record<InstanceTypeId, InstanceTypeDefinition> = {\n\t// ---- Canonical T-shirt sizes (ADR-034) ----\n\n\txs: {\n\t\tid: 'xs',\n\t\tname: 'XS',\n\t\tcpuLimit: '250m',\n\t\tmemoryLimit: '512Mi',\n\t\tcpuRequest: '63m',\n\t\tmemoryRequest: '256Mi',\n\t\tvcpus: 0.25,\n\t\tmemoryMib: 512,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['free', 'starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 1,\n\t\thighlights: ['0.25 vCPU, 0.5 GiB RAM', 'Available on all plans', 'Ideal for dev/staging'],\n\t},\n\n\tsm: {\n\t\tid: 'sm',\n\t\tname: 'SM',\n\t\tcpuLimit: '500m',\n\t\tmemoryLimit: '1Gi',\n\t\tcpuRequest: '125m',\n\t\tmemoryRequest: '512Mi',\n\t\tvcpus: 0.5,\n\t\tmemoryMib: 1024,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 3,\n\t\thighlights: ['0.5 vCPU, 1 GiB RAM', 'Good for lightweight services', 'Starter plan default'],\n\t},\n\n\tmd: {\n\t\tid: 'md',\n\t\tname: 'MD',\n\t\tcpuLimit: '1000m',\n\t\tmemoryLimit: '2Gi',\n\t\tcpuRequest: '250m',\n\t\tmemoryRequest: '1Gi',\n\t\tvcpus: 1,\n\t\tmemoryMib: 2048,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: ['1 vCPU, 2 GiB RAM', 'Good for web apps and APIs', 'Pro plan default'],\n\t},\n\n\tlg: {\n\t\tid: 'lg',\n\t\tname: 'LG',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '4Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '2Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 4096,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['pro', 'team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['2 vCPUs, 4 GiB RAM', 'High-throughput APIs and workers', 'Team plan default'],\n\t},\n\n\txl: {\n\t\tid: 'xl',\n\t\tname: 'XL',\n\t\tcpuLimit: '4000m',\n\t\tmemoryLimit: '8Gi',\n\t\tcpuRequest: '1000m',\n\t\tmemoryRequest: '4Gi',\n\t\tvcpus: 4,\n\t\tmemoryMib: 8192,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['4 vCPUs, 8 GiB RAM', 'CI builds and ML inference', 'Enterprise plan default'],\n\t},\n\n\t'2xl': {\n\t\tid: '2xl',\n\t\tname: '2XL',\n\t\tcpuLimit: '8000m',\n\t\tmemoryLimit: '16Gi',\n\t\tcpuRequest: '2000m',\n\t\tmemoryRequest: '8Gi',\n\t\tvcpus: 8,\n\t\tmemoryMib: 16384,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['8 vCPUs, 16 GiB RAM', 'Heavy compute and large builds', 'Team/Enterprise'],\n\t},\n\n\t'4xl': {\n\t\tid: '4xl',\n\t\tname: '4XL',\n\t\tcpuLimit: '16000m',\n\t\tmemoryLimit: '32Gi',\n\t\tcpuRequest: '4000m',\n\t\tmemoryRequest: '16Gi',\n\t\tvcpus: 16,\n\t\tmemoryMib: 32768,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['16 vCPUs, 32 GiB RAM', 'Maximum compute power', 'Enterprise exclusive'],\n\t},\n\n\t// ---- Legacy names (deprecated — kept for backward compat with DB values) ----\n\n\t/** @deprecated Use 'xs' instead */\n\t'starter-1x': {\n\t\tid: 'starter-1x',\n\t\tname: 'Starter 1x',\n\t\tcpuLimit: '1000m',\n\t\tmemoryLimit: '2Gi',\n\t\tcpuRequest: '250m',\n\t\tmemoryRequest: '1Gi',\n\t\tvcpus: 1,\n\t\tmemoryMib: 2048,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['free', 'starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 3,\n\t\thighlights: ['1 vCPU, 2 GiB RAM', 'Available on all plans', 'Ideal for lightweight services'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'sm' instead */\n\t'standard-1x': {\n\t\tid: 'standard-1x',\n\t\tname: 'Standard 1x',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '4Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '2Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 4096,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: ['2 vCPUs, 4 GiB RAM', 'Good for web apps and APIs', 'Starter plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'md' instead */\n\t'standard-2x': {\n\t\tid: 'standard-2x',\n\t\tname: 'Standard 2x',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '8Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '4Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 8192,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: [\n\t\t\t'2 vCPUs, 8 GiB RAM',\n\t\t\t'Double memory for data-heavy workloads',\n\t\t\t'Pro plan default',\n\t\t],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'lg' instead */\n\t'performance-m': {\n\t\tid: 'performance-m',\n\t\tname: 'Performance M',\n\t\tcpuLimit: '4000m',\n\t\tmemoryLimit: '16Gi',\n\t\tcpuRequest: '1000m',\n\t\tmemoryRequest: '8Gi',\n\t\tvcpus: 4,\n\t\tmemoryMib: 16384,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['pro', 'team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['4 vCPUs, 16 GiB RAM', 'High-throughput APIs and workers', 'Team plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'xl' instead */\n\t'performance-l': {\n\t\tid: 'performance-l',\n\t\tname: 'Performance L',\n\t\tcpuLimit: '8000m',\n\t\tmemoryLimit: '32Gi',\n\t\tcpuRequest: '2000m',\n\t\tmemoryRequest: '16Gi',\n\t\tvcpus: 8,\n\t\tmemoryMib: 32768,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['8 vCPUs, 32 GiB RAM', 'CI builds and ML inference', 'Enterprise plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use '2xl' instead */\n\t'performance-xl': {\n\t\tid: 'performance-xl',\n\t\tname: 'Performance XL',\n\t\tcpuLimit: '16000m',\n\t\tmemoryLimit: '64Gi',\n\t\tcpuRequest: '4000m',\n\t\tmemoryRequest: '32Gi',\n\t\tvcpus: 16,\n\t\tmemoryMib: 65536,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['16 vCPUs, 64 GiB RAM', 'Heavy compute and large builds', 'Enterprise exclusive'],\n\t\tdeprecated: true,\n\t},\n} as const\n\n// ==========================================\n// Display Ordering\n// ==========================================\n\n/** Ordered list of canonical instance type IDs for display (smallest to largest) */\nexport const INSTANCE_TYPE_ORDER: InstanceTypeId[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '4xl']\n\n/**\n * @deprecated Use INSTANCE_TYPE_ORDER instead.\n * Ordered list of legacy instance type IDs — kept for backward compat.\n */\nexport const LEGACY_INSTANCE_TYPE_ORDER: InstanceTypeId[] = [\n\t'starter-1x',\n\t'standard-1x',\n\t'standard-2x',\n\t'performance-m',\n\t'performance-l',\n\t'performance-xl',\n]\n\n// ==========================================\n// Helpers\n// ==========================================\n\nconst DEFAULT_INSTANCE_TYPE: Record<PlatformPlanId, InstanceTypeId> = {\n\tfree: 'xs',\n\tstarter: 'sm',\n\tpro: 'md',\n\tteam: 'lg',\n\tenterprise: 'xl',\n}\n\n/** Get the default instance type for a given platform plan */\nexport function getDefaultInstanceType(plan: PlatformPlanId): InstanceTypeId {\n\treturn DEFAULT_INSTANCE_TYPE[plan]\n}\n\n/** Get all canonical (non-deprecated) instance types available for a given platform plan, in display order */\nexport function getAvailableInstanceTypes(plan: PlatformPlanId): InstanceTypeDefinition[] {\n\treturn INSTANCE_TYPE_ORDER.map((id) => INSTANCE_TYPES[id]).filter(\n\t\t(t) => t.allowedPlans.includes(plan) && !t.deprecated,\n\t)\n}\n\n/** Resolve Kubernetes resource spec for a given instance type (accepts aliases) */\nexport function resolveResources(id: InstanceTypeId): {\n\trequests: { cpu: string; memory: string }\n\tlimits: { cpu: string; memory: string }\n} {\n\tconst t = INSTANCE_TYPES[id]\n\treturn {\n\t\trequests: { cpu: t.cpuRequest, memory: t.memoryRequest },\n\t\tlimits: { cpu: t.cpuLimit, memory: t.memoryLimit },\n\t}\n}\n\n/** Resolve the platform-managed instance ceiling for a given instance type. */\nexport function resolveMaxInstances(id: InstanceTypeId): number {\n\treturn INSTANCE_TYPES[id].maxInstances\n}\n\n/** Default instance ceiling when no instance type is resolved. */\nexport const DEFAULT_MAX_INSTANCES = 10\n\n/** Type guard: check if an arbitrary string is a valid InstanceTypeId (including legacy aliases) */\nexport function isValidInstanceType(id: string): id is InstanceTypeId {\n\treturn id in INSTANCE_TYPES\n}\n\n/** Validate that an instance type exists and is permitted for the given plan */\nexport function validateInstanceTypeForPlan(\n\tid: string,\n\tplan: PlatformPlanId,\n): { valid: boolean; error?: string } {\n\t// Resolve alias to canonical name for validation\n\tconst canonicalId = resolveCanonicalInstanceType(id)\n\n\tif (!isValidInstanceType(canonicalId)) {\n\t\treturn { valid: false, error: `Unknown instance type: ${id}` }\n\t}\n\n\tconst definition = INSTANCE_TYPES[canonicalId]\n\tif (!definition.allowedPlans.includes(plan)) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `Instance type \"${definition.name}\" is not available on the ${plan} plan`,\n\t\t}\n\t}\n\n\treturn { valid: true }\n}\n","/**\n * Platform Plan Tiers — SSOT\n *\n * Defines the Sylphx Platform plan tier system (ADR-034).\n * NOTE: This is for *platform* plans (what the organization pays Sylphx for).\n * It is separate from `plans` (which are in-app subscription products\n * that customers create for their own end-users).\n *\n * All prices in cents (USD). Credits in microdollars (1 USD = 1,000,000 µ$).\n *\n * ADR-034 tiers: Free → Pro ($20/mo) → Team ($20/user/mo) → Enterprise (custom)\n * The 'starter' tier is deprecated but kept in the type union for backward\n * compatibility with existing database rows and API consumers.\n */\n\n// ==========================================\n// Plan Tier Types\n// ==========================================\n\n/**\n * Platform plan identifiers.\n * 'starter' is deprecated (ADR-034) — retained for backward compat with existing records.\n */\nexport type PlatformPlanId = 'free' | 'starter' | 'pro' | 'team' | 'enterprise'\n\nexport type BuildMachineTier = 'standard' | 'large' | 'xlarge'\n\nexport interface PlatformPlanLimits {\n\t/** Max projects across all environments */\n\tmaxProjects: number | null\n\t/** Max organization members */\n\tmaxMembers: number | null\n\t/** Max custom domains */\n\tmaxCustomDomains: number | null\n\t/** Max concurrent CI runners */\n\tmaxConcurrentRunners: number | null\n\t/** Max concurrent macOS CI runners */\n\tmaxMacosRunners: number | null\n\t/** Max managed databases */\n\tmaxDatabases: number | null\n\t/** CI job max duration in seconds */\n\tciMaxJobDurationSeconds: number\n\t/** API rate limit (requests per minute) */\n\tapiRateLimitPerMin: number\n\t/** Audit log retention in days (0 = off) */\n\tauditLogDays: number\n\t/** Max instances per service (null = custom/negotiated) */\n\tmaxInstances: number | null\n\t/** Included build minutes per billing period */\n\tincludedBuildMinutes: number\n\t/** Included outbound bandwidth in GB per billing period */\n\tincludedBandwidthGb: number\n\t/** Log retention in days */\n\tlogRetentionDays: number\n\t/** Build machine tier determining build speed */\n\tbuildMachineTier: BuildMachineTier\n}\n\nexport interface PlatformPlanFeatures {\n\t/** Custom domain support */\n\tcustomDomains: boolean\n\t/** SSO / SAML support */\n\tsso: boolean\n\t/** Priority CI queue */\n\tpriorityCi: boolean\n\t/** macOS CI runners */\n\tmacosCi: boolean\n\t/** Shared / Priority / Dedicated */\n\tsupport: 'community' | 'email' | 'priority' | 'dedicated'\n\t/** SLA uptime guarantee string (e.g. '99.9%') */\n\tsla: string | null\n\t/** Role-based access control */\n\trbac: boolean\n\t/** Advanced analytics / insights */\n\tadvancedAnalytics: boolean\n\t/** White-label branding removal */\n\twhiteLabel: boolean\n}\n\nexport interface PlatformPlanDefinition {\n\tid: PlatformPlanId\n\tname: string\n\t/** Monthly price in cents (0 = free, null = custom) */\n\tpriceMonthly: number | null\n\t/** Annual price in cents, ~20% discount (null = custom or N/A) */\n\tpriceAnnual: number | null\n\t/** Included platform compute credits per billing period (microdollars) */\n\tincludedCreditsMicrodollars: number\n\t/** Whether price is per seat (per member) — Team plan */\n\tperSeat?: boolean\n\tfeatures: PlatformPlanFeatures\n\tlimits: PlatformPlanLimits\n\t/** Marketing bullet points for pricing cards */\n\thighlights: string[]\n\t/** Optional badge label (e.g. \"Most Popular\") */\n\tbadge?: string\n\t/** CTA button text */\n\tcta: string\n\t/** Whether this is a custom/enterprise plan with contact sales flow */\n\tisCustom?: boolean\n\t/**\n\t * Deprecated plan — no longer available for new subscriptions.\n\t * Existing subscribers are grandfathered until they change plans.\n\t */\n\tdeprecated?: boolean\n}\n\n// ==========================================\n// Plan Definitions\n// ==========================================\n\nexport const PLATFORM_PLANS: Record<PlatformPlanId, PlatformPlanDefinition> = {\n\tfree: {\n\t\tid: 'free',\n\t\tname: 'Free',\n\t\tpriceMonthly: 0,\n\t\tpriceAnnual: 0,\n\t\tincludedCreditsMicrodollars: 5_000_000, // $5\n\t\tfeatures: {\n\t\t\tcustomDomains: false,\n\t\t\tsso: false,\n\t\t\tpriorityCi: false,\n\t\t\tmacosCi: false,\n\t\t\tsupport: 'community',\n\t\t\tsla: null,\n\t\t\trbac: false,\n\t\t\tadvancedAnalytics: false,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: 1,\n\t\t\tmaxMembers: 1,\n\t\t\tmaxCustomDomains: 0,\n\t\t\tmaxConcurrentRunners: 1,\n\t\t\tmaxMacosRunners: 0,\n\t\t\tmaxDatabases: 1,\n\t\t\tciMaxJobDurationSeconds: 30 * 60, // 30 min\n\t\t\tapiRateLimitPerMin: 60,\n\t\t\tauditLogDays: 0,\n\t\t\tmaxInstances: 1,\n\t\t\tincludedBuildMinutes: 100,\n\t\t\tincludedBandwidthGb: 100,\n\t\t\tlogRetentionDays: 1,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: [\n\t\t\t'1 project',\n\t\t\t'$5 compute credits/mo',\n\t\t\t'100 build minutes',\n\t\t\t'100 GB bandwidth',\n\t\t\t'Community support',\n\t\t],\n\t\tcta: 'Start free',\n\t},\n\n\t/**\n\t * @deprecated ADR-034 removed the Starter tier. Retained for backward compatibility\n\t * with existing subscribers. Not shown in pricing UI for new sign-ups.\n\t */\n\tstarter: {\n\t\tid: 'starter',\n\t\tname: 'Starter',\n\t\tdeprecated: true,\n\t\tpriceMonthly: 1900, // $19\n\t\tpriceAnnual: 18240, // $19 x 12 x 0.80 = $182.40\n\t\tincludedCreditsMicrodollars: 19_000_000, // $19\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: false,\n\t\t\tpriorityCi: false,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'email',\n\t\t\tsla: null,\n\t\t\trbac: false,\n\t\t\tadvancedAnalytics: false,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: 10,\n\t\t\tmaxMembers: 10,\n\t\t\tmaxCustomDomains: 5,\n\t\t\tmaxConcurrentRunners: 5,\n\t\t\tmaxMacosRunners: 1,\n\t\t\tmaxDatabases: 10,\n\t\t\tciMaxJobDurationSeconds: 60 * 60, // 1 hr\n\t\t\tapiRateLimitPerMin: 300,\n\t\t\tauditLogDays: 30,\n\t\t\tmaxInstances: 3,\n\t\t\tincludedBuildMinutes: 250,\n\t\t\tincludedBandwidthGb: 500,\n\t\t\tlogRetentionDays: 7,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: ['$19 credits/mo', '10 projects', 'Custom domains', 'macOS CI', 'Email support'],\n\t\tcta: 'Get started',\n\t},\n\n\tpro: {\n\t\tid: 'pro',\n\t\tname: 'Pro',\n\t\tpriceMonthly: 2000, // $20\n\t\tpriceAnnual: 19200, // $20 x 12 x 0.80 = $192 ($16/mo)\n\t\tincludedCreditsMicrodollars: 20_000_000, // $20\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: false,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'priority',\n\t\t\tsla: '99.9%',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null, // unlimited\n\t\t\tmaxMembers: 25,\n\t\t\tmaxCustomDomains: null, // unlimited\n\t\t\tmaxConcurrentRunners: 20,\n\t\t\tmaxMacosRunners: 5,\n\t\t\tmaxDatabases: null, // unlimited\n\t\t\tciMaxJobDurationSeconds: 2 * 60 * 60, // 2 hrs\n\t\t\tapiRateLimitPerMin: 1000,\n\t\t\tauditLogDays: 90,\n\t\t\tmaxInstances: 10,\n\t\t\tincludedBuildMinutes: 500,\n\t\t\tincludedBandwidthGb: 1000,\n\t\t\tlogRetentionDays: 14,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: [\n\t\t\t'Unlimited projects',\n\t\t\t'$20 credits/mo',\n\t\t\t'500 build minutes',\n\t\t\t'1 TB bandwidth',\n\t\t\t'Priority support',\n\t\t\t'SLA 99.9%',\n\t\t],\n\t\tbadge: 'Most Popular',\n\t\tcta: 'Start Pro',\n\t},\n\n\tteam: {\n\t\tid: 'team',\n\t\tname: 'Team',\n\t\tpriceMonthly: 2000, // $20/user\n\t\tpriceAnnual: 19200, // $192/user/yr ($16/user/mo)\n\t\tincludedCreditsMicrodollars: 20_000_000, // $20/user\n\t\tperSeat: true,\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: true,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'dedicated',\n\t\t\tsla: '99.95%',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: true,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null, // unlimited\n\t\t\tmaxMembers: null, // unlimited\n\t\t\tmaxCustomDomains: null, // unlimited\n\t\t\tmaxConcurrentRunners: null, // unlimited\n\t\t\tmaxMacosRunners: null, // unlimited\n\t\t\tmaxDatabases: null, // unlimited\n\t\t\tciMaxJobDurationSeconds: 6 * 60 * 60, // 6 hrs\n\t\t\tapiRateLimitPerMin: 5000,\n\t\t\tauditLogDays: 365,\n\t\t\tmaxInstances: 20,\n\t\t\tincludedBuildMinutes: 2000,\n\t\t\tincludedBandwidthGb: 5000,\n\t\t\tlogRetentionDays: 30,\n\t\t\tbuildMachineTier: 'large',\n\t\t},\n\t\thighlights: [\n\t\t\t'$20/user/mo',\n\t\t\t'SSO / SAML',\n\t\t\t'2,000 large build minutes',\n\t\t\t'5 TB bandwidth',\n\t\t\t'Dedicated support',\n\t\t\t'SLA 99.95%',\n\t\t],\n\t\tcta: 'Get Team',\n\t},\n\n\tenterprise: {\n\t\tid: 'enterprise',\n\t\tname: 'Enterprise',\n\t\tpriceMonthly: null, // custom\n\t\tpriceAnnual: null, // custom\n\t\tincludedCreditsMicrodollars: 0, // negotiated\n\t\tisCustom: true,\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: true,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'dedicated',\n\t\t\tsla: 'Custom',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: true,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null,\n\t\t\tmaxMembers: null,\n\t\t\tmaxCustomDomains: null,\n\t\t\tmaxConcurrentRunners: null,\n\t\t\tmaxMacosRunners: null,\n\t\t\tmaxDatabases: null,\n\t\t\tciMaxJobDurationSeconds: 12 * 60 * 60,\n\t\t\tapiRateLimitPerMin: 0, // 0 = unlimited / negotiated\n\t\t\tauditLogDays: 730,\n\t\t\tmaxInstances: null, // custom\n\t\t\tincludedBuildMinutes: 0, // negotiated\n\t\t\tincludedBandwidthGb: 0, // negotiated\n\t\t\tlogRetentionDays: 90,\n\t\t\tbuildMachineTier: 'xlarge',\n\t\t},\n\t\thighlights: [\n\t\t\t'Custom credit volume',\n\t\t\t'Volume discounts',\n\t\t\t'Dedicated infrastructure',\n\t\t\t'Custom SLA',\n\t\t\t'White-glove onboarding',\n\t\t\t'Custom contracts',\n\t\t],\n\t\tcta: 'Contact sales',\n\t},\n} as const\n\n// ==========================================\n// Helpers\n// ==========================================\n\n/** Active (non-deprecated) plan IDs for display in pricing UI */\nexport const PLATFORM_PLAN_ORDER: PlatformPlanId[] = ['free', 'pro', 'team', 'enterprise']\n\n/**\n * Full plan order including deprecated tiers.\n * Useful for admin screens and migration tooling that must handle legacy plans.\n */\nexport const PLATFORM_PLAN_ORDER_ALL: PlatformPlanId[] = [\n\t'free',\n\t'starter',\n\t'pro',\n\t'team',\n\t'enterprise',\n]\n\n/** Check whether a plan is deprecated and should not be offered to new subscribers */\nexport function isPlanDeprecated(planId: PlatformPlanId): boolean {\n\treturn PLATFORM_PLANS[planId].deprecated === true\n}\n\n/** Get only active (non-deprecated) plan definitions, in display order */\nexport function getActivePlans(): PlatformPlanDefinition[] {\n\treturn PLATFORM_PLAN_ORDER.map((id) => PLATFORM_PLANS[id])\n}\n\n/** Convert microdollars to human-readable dollar string (e.g. \"$5\") */\nexport function microsToDollars(microdollars: number): string {\n\treturn `$${(microdollars / 1_000_000).toFixed(0)}`\n}\n\n/** Convert cents to human-readable dollar string (e.g. \"$19\") */\nexport function centsToDollars(cents: number): string {\n\treturn `$${(cents / 100).toFixed(0)}`\n}\n\n/** Get monthly price display string */\nexport function getPlanMonthlyPrice(plan: PlatformPlanDefinition, annual = false): string {\n\tif (plan.isCustom) return 'Custom'\n\tconst cents =\n\t\tannual && plan.priceAnnual != null ? Math.round(plan.priceAnnual / 12) : plan.priceMonthly\n\tif (cents == null) return 'Custom'\n\tif (cents === 0) return '$0'\n\tconst base = centsToDollars(cents)\n\treturn plan.perSeat ? `${base}/user` : base\n}\n","/**\n * SDK Debug Mode\n *\n * Centralized debug logging for the SDK.\n *\n * Enable via:\n * - Browser: `localStorage.setItem('sylphx_debug', 'true')`\n * - Node.js: `SYLPHX_DEBUG=true`\n *\n * Debug messages are namespaced with [Sylphx] prefix for easy filtering.\n */\n\n// ============================================================================\n// Debug Configuration\n// ============================================================================\n\n/** Storage key for browser-side debug toggle */\nconst DEBUG_STORAGE_KEY = 'sylphx_debug'\n\n/**\n * Check if debug mode is enabled\n *\n * Checks multiple sources in order:\n * 1. localStorage (browser)\n * 2. SYLPHX_DEBUG environment variable\n * 3. NODE_ENV === 'development' with explicit opt-in\n */\nfunction isDebugEnabled(): boolean {\n\t// Browser environment\n\tif (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {\n\t\ttry {\n\t\t\treturn localStorage.getItem(DEBUG_STORAGE_KEY) === 'true'\n\t\t} catch {\n\t\t\t// localStorage may be blocked in some contexts\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Node.js environment\n\tif (typeof process !== 'undefined' && process.env) {\n\t\treturn process.env.SYLPHX_DEBUG === 'true'\n\t}\n\n\treturn false\n}\n\n// Cache the debug state to avoid repeated localStorage/env checks\nlet debugModeCache: boolean | null = null\n\n/**\n * Whether debug mode is currently enabled\n *\n * Cached after first access for performance.\n */\nexport function getDebugMode(): boolean {\n\tif (debugModeCache === null) {\n\t\tdebugModeCache = isDebugEnabled()\n\t}\n\treturn debugModeCache\n}\n\n/**\n * Reset debug mode cache (for testing)\n */\nexport function resetDebugModeCache(): void {\n\tdebugModeCache = null\n}\n\n// ============================================================================\n// Debug Logging\n// ============================================================================\n\n/** Debug log categories */\nexport type DebugCategory =\n\t| 'auth'\n\t| 'api'\n\t| 'analytics'\n\t| 'flags'\n\t| 'storage'\n\t| 'cache'\n\t| 'token'\n\t| 'webhook'\n\t| 'error'\n\n/**\n * Log a debug message with category prefix\n *\n * @example\n * ```ts\n * debugLog('auth', 'Token refreshed', { expiresIn: 300 })\n * // [Sylphx auth] Token refreshed { expiresIn: 300 }\n * ```\n */\nexport function debugLog(category: DebugCategory, message: string, data?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst _prefix = `[Sylphx ${category}]`\n\n\tif (data !== undefined) {\n\t} else {\n\t}\n}\n\n/**\n * Log a debug warning with category prefix\n */\nexport function debugWarn(category: DebugCategory, message: string, data?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst prefix = `[Sylphx ${category}]`\n\n\tif (data !== undefined) {\n\t\tconsole.warn(prefix, message, data)\n\t} else {\n\t\tconsole.warn(prefix, message)\n\t}\n}\n\n/**\n * Log a debug error with category prefix\n *\n * Note: This always logs when debug mode is enabled, regardless of error severity.\n * Production error tracking should use the error tracking service, not this.\n */\nexport function debugError(category: DebugCategory, message: string, error?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst prefix = `[Sylphx ${category}]`\n\n\tif (error !== undefined) {\n\t\tconsole.error(prefix, message, error)\n\t} else {\n\t\tconsole.error(prefix, message)\n\t}\n}\n\n// ============================================================================\n// Performance Timing\n// ============================================================================\n\n/**\n * Create a debug timer for measuring operation duration\n *\n * @example\n * ```ts\n * const timer = debugTimer('api', 'Fetching user profile')\n * // ... operation ...\n * timer.end() // Logs duration if debug mode enabled\n * ```\n */\nexport function debugTimer(category: DebugCategory, operation: string): { end: () => void } {\n\tif (!getDebugMode()) {\n\t\treturn { end: () => {} }\n\t}\n\n\tconst start = performance.now()\n\n\treturn {\n\t\tend() {\n\t\t\tconst duration = performance.now() - start\n\t\t\tdebugLog(category, `${operation} completed`, {\n\t\t\t\tdurationMs: Math.round(duration),\n\t\t\t})\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Browser Console Helpers\n// ============================================================================\n\n/**\n * Enable debug mode from browser console\n *\n * Call this in the browser console to enable debug logging:\n * ```js\n * window.__sylphx?.enableDebug()\n * ```\n */\nexport function enableDebug(): void {\n\tif (typeof localStorage === 'undefined') {\n\t\tconsole.warn('[Sylphx] Debug mode can only be enabled in browser environments')\n\t\treturn\n\t}\n\n\ttry {\n\t\tlocalStorage.setItem(DEBUG_STORAGE_KEY, 'true')\n\t\tdebugModeCache = true\n\t} catch (e) {\n\t\tconsole.warn('[Sylphx] Failed to enable debug mode:', e)\n\t}\n}\n\n/**\n * Disable debug mode from browser console\n */\nexport function disableDebug(): void {\n\tif (typeof localStorage === 'undefined') {\n\t\tconsole.warn('[Sylphx] Debug mode can only be disabled in browser environments')\n\t\treturn\n\t}\n\n\ttry {\n\t\tlocalStorage.removeItem(DEBUG_STORAGE_KEY)\n\t\tdebugModeCache = false\n\t} catch (e) {\n\t\tconsole.warn('[Sylphx] Failed to disable debug mode:', e)\n\t}\n}\n\n// ============================================================================\n// Global Window Helper (Browser Only)\n// ============================================================================\n\n/**\n * Install debug helpers on window.__sylphx\n *\n * This is called automatically when the SDK is loaded in the browser,\n * providing developers easy console access to debug utilities.\n */\nexport function installGlobalDebugHelpers(): void {\n\tif (typeof window === 'undefined') return\n\n\t// Use type assertion to extend window\n\tconst w = window as typeof window & {\n\t\t__sylphx?: {\n\t\t\tenableDebug: typeof enableDebug\n\t\t\tdisableDebug: typeof disableDebug\n\t\t\tisDebugEnabled: typeof getDebugMode\n\t\t}\n\t}\n\n\tw.__sylphx = {\n\t\tenableDebug,\n\t\tdisableDebug,\n\t\tisDebugEnabled: getDebugMode,\n\t}\n}\n","/**\n * REST Client for Sylphx Platform\n *\n * Type-safe REST API client built on plain `fetch` (no runtime `openapi-fetch`\n * dependency — ADR-084 routes types through `@sylphx/contract` so the codegen\n * layer no longer needs a transport library of its own). Public surface is\n * preserved: consumers still call `client.GET('/path')`, `client.POST(...)`,\n * etc. The middleware chain (deduplication → circuit breaker → ETag →\n * retry) runs in the same order as the previous implementation.\n *\n * @example\n * ```typescript\n * import { createRestClient } from '@sylphx/sdk'\n *\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * const { data: user, error } = await client.GET('/auth/me')\n * const { data: result } = await client.POST('/auth/login', {\n * body: { email, password },\n * })\n * ```\n */\n\nimport {\n\tBASE_RETRY_DELAY_MS,\n\tCIRCUIT_BREAKER_FAILURE_THRESHOLD,\n\tCIRCUIT_BREAKER_OPEN_DURATION_MS,\n\tCIRCUIT_BREAKER_WINDOW_MS,\n\tDEFAULT_SDK_API_HOST,\n\tDEFAULT_TIMEOUT_MS,\n\tETAG_CACHE_MAX_ENTRIES,\n\tETAG_CACHE_TTL_MS,\n\tMAX_RETRY_DELAY_MS,\n\tSDK_API_PATH,\n\tSDK_PLATFORM,\n\tSDK_VERSION,\n} from './constants'\nimport { exponentialBackoff } from './errors'\nimport { validateAndSanitizeSecretKey } from './key-validation'\n\n/**\n * Retry configuration for automatic request retries\n */\nexport interface RetryConfig {\n\t/** Maximum number of retries (default: 3) */\n\tmaxRetries?: number\n\t/** Base delay in milliseconds (default: 1000) */\n\tbaseDelay?: number\n\t/** Maximum delay in milliseconds (default: 30000) */\n\tmaxDelay?: number\n\t/** Custom function to determine if error is retryable */\n\tshouldRetry?: (status: number, attempt: number) => boolean\n\t/** Request timeout in milliseconds (default: 30000) */\n\ttimeout?: number\n}\n\n/**\n * Request deduplication configuration\n */\nexport interface DeduplicationConfig {\n\t/** Enable request deduplication (default: true) */\n\tenabled?: boolean\n\t/** HTTP methods to deduplicate (default: ['GET']) */\n\tmethods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')[]\n}\n\ntype DeduplicatedMethod = NonNullable<DeduplicationConfig['methods']>[number]\n\n/**\n * Circuit breaker configuration (AWS/Resilience4j pattern)\n *\n * Prevents cascade failures by fast-failing when service is unhealthy.\n * States: CLOSED (normal) → OPEN (failing) → HALF_OPEN (testing)\n */\nexport interface CircuitBreakerConfig {\n\t/** Enable circuit breaker (default: true) */\n\tenabled?: boolean\n\t/** Number of failures before opening circuit (default: 5) */\n\tfailureThreshold?: number\n\t/** Time window for counting failures in ms (default: 10000) */\n\twindowMs?: number\n\t/** How long circuit stays open in ms (default: 30000) */\n\topenDurationMs?: number\n\t/** Custom function to determine if response is a failure */\n\tisFailure?: (status: number) => boolean\n}\n\n/**\n * ETag/Conditional request configuration (HTTP caching pattern)\n *\n * Enables HTTP conditional requests with If-None-Match header\n * to avoid re-downloading unchanged data (saves bandwidth).\n */\nexport interface ETagConfig {\n\t/** Enable ETag caching (default: true for GET requests) */\n\tenabled?: boolean\n\t/** Maximum cache entries (default: 100) */\n\tmaxEntries?: number\n\t/** Cache TTL in milliseconds (default: 5 minutes) */\n\tttlMs?: number\n}\n\n/**\n * Configuration for the REST client\n *\n * The app key identifies the app — no separate app ID needed.\n */\nexport interface RestClientConfig {\n\t/**\n\t * Your app key — identifies the app and environment.\n\t *\n\t * Accepts either:\n\t * - Secret key (sk_dev_, sk_stg_, sk_prod_) — full access, server-side only\n\t * - Publishable key (app_dev_, app_stg_, app_prod_) — limited access, safe for client\n\t */\n\tsecretKey: string\n\t/** Platform URL (default: https://sylphx.com) */\n\tplatformUrl?: string\n\t/** Retry configuration (default: 3 retries with exponential backoff) */\n\tretry?: RetryConfig | false\n\t/**\n\t * Request deduplication configuration (default: enabled for GET)\n\t *\n\t * Prevents duplicate concurrent requests for the same resource.\n\t * When multiple components request the same data simultaneously,\n\t * only one API call is made and the result is shared.\n\t */\n\tdeduplication?: DeduplicationConfig | false\n\t/**\n\t * Circuit breaker configuration (default: enabled)\n\t *\n\t * Prevents cascade failures by fast-failing when service is unhealthy.\n\t * Opens after 5 failures in 10s, stays open for 30s, then allows test request.\n\t */\n\tcircuitBreaker?: CircuitBreakerConfig | false\n\t/**\n\t * ETag caching configuration (default: enabled for GET)\n\t *\n\t * Uses HTTP conditional requests to avoid re-downloading unchanged data.\n\t * Saves bandwidth by returning 304 Not Modified when content hasn't changed.\n\t */\n\tetag?: ETagConfig | false\n}\n\n/**\n * Dynamic configuration that can change at runtime (e.g., access token)\n */\nexport interface RestDynamicConfig {\n\t/** Your secret key (sk_* or app_*) — identifies the app */\n\tsecretKey?: string\n\t/** Platform URL (default: https://sylphx.com) */\n\tplatformUrl?: string\n\t/** Get the current access token (called on each request) */\n\tgetAccessToken?: () => string | null | undefined\n\t/** Retry configuration (default: 3 retries with exponential backoff) */\n\tretry?: RetryConfig | false\n\t/** Request deduplication configuration (default: enabled for GET) */\n\tdeduplication?: DeduplicationConfig | false\n\t/** Circuit breaker configuration (default: enabled) */\n\tcircuitBreaker?: CircuitBreakerConfig | false\n\t/** ETag caching configuration (default: enabled for GET) */\n\tetag?: ETagConfig | false\n}\n\n// ============================================================================\n// Middleware Plumbing\n// ============================================================================\n\n/**\n * Middleware contract — kept structurally identical to the previous\n * `openapi-fetch` shape so existing implementations (dedup / circuit breaker\n * / ETag / retry) compose without change.\n */\nexport interface Middleware {\n\tonRequest?: (ctx: { request: Request }) => Promise<Request | undefined> | Request | undefined\n\tonFetch?: (ctx: {\n\t\trequest: Request\n\t\tnext: (request: Request) => Promise<Response>\n\t}) => Promise<Response | undefined> | Response | undefined\n\tonResponse?: (ctx: {\n\t\trequest: Request\n\t\tresponse: Response\n\t}) => Promise<Response | undefined> | Response | undefined\n}\n\n/**\n * Run middleware pipeline over a request → response round-trip.\n *\n * `onRequest` handlers may mutate/replace the outgoing `Request`. One of them\n * may throw to short-circuit the call (circuit breaker uses this). All\n * `onResponse` handlers run in the order middleware was registered, so later\n * middleware observes earlier middleware's transforms.\n */\nasync function runPipeline(\n\tmiddlewares: readonly Middleware[],\n\tinitial: Request,\n): Promise<Response> {\n\tlet request = initial\n\tfor (const mw of middlewares) {\n\t\tif (mw.onRequest) {\n\t\t\tconst next = await mw.onRequest({ request })\n\t\t\tif (next) request = next\n\t\t}\n\t}\n\n\tconst fetchWithMiddleware = middlewares.reduceRight<(request: Request) => Promise<Response>>(\n\t\t(next, mw) =>\n\t\t\tmw.onFetch\n\t\t\t\t? async (request) => {\n\t\t\t\t\t\tconst response = await mw.onFetch?.({ request, next })\n\t\t\t\t\t\treturn response ?? next(request)\n\t\t\t\t\t}\n\t\t\t\t: next,\n\t\t(request) => fetch(request),\n\t)\n\n\tlet response = await fetchWithMiddleware(request)\n\n\tfor (const mw of middlewares) {\n\t\tif (mw.onResponse) {\n\t\t\tconst next = await mw.onResponse({ request, response })\n\t\t\tif (next) response = next\n\t\t}\n\t}\n\n\treturn response\n}\n\n/**\n * Serialize a URL + query object. Undefined values are dropped. Values are\n * coerced to string via URLSearchParams (matches the previous openapi-fetch\n * behaviour — `{ foo: undefined }` does not emit `?foo=undefined`).\n */\nfunction buildUrl(baseUrl: string, path: string, params?: Record<string, unknown>): string {\n\tconst url = `${baseUrl}${path}`\n\tif (!params) return url\n\tconst entries = Object.entries(params).filter(([, v]) => v !== undefined)\n\tif (entries.length === 0) return url\n\tconst search = new URLSearchParams(\n\t\tentries.map(([k, v]) => [k, String(v)] as [string, string]),\n\t).toString()\n\treturn `${url}?${search}`\n}\n\nfunction cloneRequestWithHeaders(\n\trequest: Request,\n\tupdateHeaders: (headers: Headers) => void,\n): Request {\n\tconst headers = new Headers(request.headers)\n\tupdateHeaders(headers)\n\treturn new Request(request, { headers })\n}\n\n/**\n * Options for an HTTP request — structural subset of `openapi-fetch`'s\n * `FetchOptions` so existing callsites (`client.GET('/path', { params: {...} })`)\n * continue to compile.\n */\nexport interface RequestOptions {\n\tbody?: unknown\n\tparams?: {\n\t\tpath?: Record<string, string>\n\t\tquery?: Record<string, unknown>\n\t}\n\theaders?: Record<string, string>\n}\n\n/**\n * Response envelope — `{ data, error, response }` mirroring `openapi-fetch`.\n * `data` is present on 2xx, `error` on non-2xx; both carry the parsed JSON\n * body when the server returns one. `response` is always the raw `Response`\n * so consumers can read headers (Content-Type, Retry-After, etc.).\n */\nexport interface FetchResponse<TOk, TError> {\n\tdata?: TOk\n\terror?: TError\n\tresponse: Response\n}\n\n/**\n * Replace `{path}` style tokens with URL-encoded values. Mirrors\n * openapi-fetch's `params.path` substitution.\n */\nfunction interpolatePath(path: string, pathParams?: Record<string, string>): string {\n\tif (!pathParams) return path\n\treturn path.replace(/\\{(\\w+)\\}/g, (_match, key: string) => {\n\t\tconst value = pathParams[key]\n\t\tif (value === undefined) return `{${key}}`\n\t\treturn encodeURIComponent(value)\n\t})\n}\n\nasync function executeRequest<TOk = unknown, TError = unknown>(\n\tmethod: string,\n\tbaseUrl: string,\n\tpath: string,\n\toptions: RequestOptions | undefined,\n\tbaseHeaders: Record<string, string>,\n\tmiddlewares: readonly Middleware[],\n): Promise<FetchResponse<TOk, TError>> {\n\tconst finalPath = interpolatePath(path, options?.params?.path)\n\tconst url = buildUrl(baseUrl, finalPath, options?.params?.query)\n\n\tconst headers: Record<string, string> = { ...baseHeaders, ...options?.headers }\n\tconst init: RequestInit = { method, headers }\n\tif (options?.body !== undefined) {\n\t\tinit.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body)\n\t\tif (!headers['Content-Type'] && !headers['content-type']) {\n\t\t\theaders['Content-Type'] = 'application/json'\n\t\t}\n\t}\n\n\tconst request = new Request(url, init)\n\tconst response = await runPipeline(middlewares, request)\n\n\t// openapi-fetch's envelope:\n\t// - 2xx → { data: parsedBody, response }\n\t// - non-2xx → { error: parsedBody, response }\n\t// - parse failure → parsed becomes undefined on either branch (consumers\n\t// fall back to `response.text()` themselves).\n\tconst contentType = response.headers.get('content-type') ?? ''\n\tlet parsed: unknown\n\tif (contentType.includes('json')) {\n\t\ttry {\n\t\t\tparsed = await response.clone().json()\n\t\t} catch {\n\t\t\tparsed = undefined\n\t\t}\n\t} else if (response.status !== 204 && response.status !== 205) {\n\t\ttry {\n\t\t\tconst text = await response.clone().text()\n\t\t\tparsed = text === '' ? undefined : text\n\t\t} catch {\n\t\t\tparsed = undefined\n\t\t}\n\t}\n\n\tif (response.ok) {\n\t\treturn { data: parsed as TOk, response }\n\t}\n\treturn { error: parsed as TError, response }\n}\n\n/**\n * The typed method surface. Callers pass a path + options; return envelope\n * follows openapi-fetch's shape so migration is drop-in for the small number\n * of internal callers that relied on `.GET` / `.POST` etc.\n */\nexport interface RestClient {\n\tGET<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPOST<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPUT<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPATCH<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tDELETE<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tHEAD<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tOPTIONS<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tuse(...middleware: readonly Middleware[]): void\n}\n\nexport type DynamicRestClient = RestClient\n\n/**\n * Build a method-dispatch REST client. Each verb thin-wraps `executeRequest`\n * so middleware composition and request shape stay identical across verbs.\n */\nfunction buildClient(\n\tbaseUrl: string,\n\tbaseHeaders: Record<string, string>,\n): { client: RestClient; middlewares: Middleware[] } {\n\tconst middlewares: Middleware[] = []\n\tconst dispatch =\n\t\t<TOk, TError>(method: string) =>\n\t\t(path: string, options?: RequestOptions) =>\n\t\t\texecuteRequest<TOk, TError>(method, baseUrl, path, options, baseHeaders, middlewares)\n\n\tconst client: RestClient = {\n\t\tGET: dispatch('GET'),\n\t\tPOST: dispatch('POST'),\n\t\tPUT: dispatch('PUT'),\n\t\tPATCH: dispatch('PATCH'),\n\t\tDELETE: dispatch('DELETE'),\n\t\tHEAD: dispatch('HEAD'),\n\t\tOPTIONS: dispatch('OPTIONS'),\n\t\tuse(...mws: readonly Middleware[]) {\n\t\t\tmiddlewares.push(...mws)\n\t\t},\n\t}\n\n\treturn { client, middlewares }\n}\n\n/**\n * Create auth middleware that adds app credentials, access token, and SDK headers\n */\nfunction createAuthMiddleware(config: RestDynamicConfig): Middleware {\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\treturn cloneRequestWithHeaders(request, (headers) => {\n\t\t\t\t// Add SDK identification headers for debugging and analytics\n\t\t\t\theaders.set('X-SDK-Version', SDK_VERSION)\n\t\t\t\theaders.set('X-SDK-Platform', SDK_PLATFORM)\n\n\t\t\t\t// Add secret key if provided — identifies the app\n\t\t\t\tif (config.secretKey) {\n\t\t\t\t\theaders.set('x-app-secret', config.secretKey)\n\t\t\t\t}\n\n\t\t\t\t// Add access token if available\n\t\t\t\tconst token = config.getAccessToken?.()\n\t\t\t\tif (token) {\n\t\t\t\t\theaders.set('Authorization', `Bearer ${token}`)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t}\n}\n\n/**\n * Check if a status code is retryable\n */\nfunction isRetryableStatus(status: number): boolean {\n\treturn status >= 500 || status === 429\n}\n\n// ============================================================================\n// Request Deduplication (React Query/SWR pattern)\n// ============================================================================\n\n/**\n * Generate a unique key for a request (for deduplication)\n */\nasync function getRequestKey(request: Request): Promise<string> {\n\tconst body = request.body ? await request.clone().text() : ''\n\treturn `${request.method}:${request.url}:${body}`\n}\n\n/**\n * Create request deduplication middleware (React Query/SWR pattern)\n *\n * Features:\n * - Deduplicates concurrent identical requests\n * - Only applies to GET requests by default (safe to dedupe)\n * - POST/PUT/DELETE are always executed (mutations must run)\n * - Cleans up in-flight tracking after completion\n *\n * @param config - Whether to enable deduplication (default: GET only)\n */\nfunction createDeduplicationMiddleware(\n\tconfig: { enabled?: boolean; methods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')[] } = {},\n): Middleware {\n\tconst { enabled = true, methods = ['GET'] } = config\n\n\tif (!enabled) return {}\n\n\tconst inFlightRequests = new Map<string, Promise<Response>>()\n\n\treturn {\n\t\tasync onFetch({ request, next }) {\n\t\t\t// Only dedupe specified methods (default: GET only)\n\t\t\tconst method = request.method.toUpperCase() as DeduplicatedMethod\n\t\t\tif (!methods.includes(method)) {\n\t\t\t\treturn next(request)\n\t\t\t}\n\n\t\t\tconst key = await getRequestKey(request)\n\t\t\tconst existing = inFlightRequests.get(key)\n\t\t\tif (existing) {\n\t\t\t\tconst cachedResponse = await existing\n\t\t\t\treturn cachedResponse.clone()\n\t\t\t}\n\n\t\t\tconst responsePromise = next(request).then((response) => response.clone())\n\t\t\tinFlightRequests.set(key, responsePromise)\n\n\t\t\ttry {\n\t\t\t\tconst response = await responsePromise\n\t\t\t\treturn response.clone()\n\t\t\t} finally {\n\t\t\t\tinFlightRequests.delete(key)\n\t\t\t}\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Circuit Breaker (AWS/Resilience4j pattern)\n// ============================================================================\n\n/**\n * Circuit breaker state machine\n *\n * CLOSED: Normal operation, requests pass through\n * OPEN: Service unhealthy, all requests fast-fail\n * HALF_OPEN: Testing recovery, allows one request\n */\nexport type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN'\n\n/**\n * Error thrown when circuit is open\n */\nexport class CircuitBreakerOpenError extends Error {\n\treadonly remainingMs: number\n\n\tconstructor(remainingMs: number) {\n\t\tsuper(`Circuit breaker is open. Retry after ${Math.ceil(remainingMs / 1000)}s`)\n\t\tthis.name = 'CircuitBreakerOpenError'\n\t\tthis.remainingMs = remainingMs\n\t}\n}\n\n/**\n * Circuit breaker instance with state management\n */\ninterface CircuitBreaker {\n\tstate: CircuitState\n\tfailures: number[]\n\topenedAt: number | null\n\tconfig: Required<CircuitBreakerConfig>\n}\n\n/**\n * Create a fresh circuit breaker instance.\n *\n * Each REST client gets its own instance — no shared module-level singleton.\n * This prevents cross-client state bleed and makes testing reliable.\n */\nfunction createCircuitBreakerInstance(config: CircuitBreakerConfig = {}): CircuitBreaker {\n\treturn {\n\t\tstate: 'CLOSED',\n\t\tfailures: [],\n\t\topenedAt: null,\n\t\tconfig: {\n\t\t\tenabled: config.enabled ?? true,\n\t\t\tfailureThreshold: config.failureThreshold ?? CIRCUIT_BREAKER_FAILURE_THRESHOLD,\n\t\t\twindowMs: config.windowMs ?? CIRCUIT_BREAKER_WINDOW_MS,\n\t\t\topenDurationMs: config.openDurationMs ?? CIRCUIT_BREAKER_OPEN_DURATION_MS,\n\t\t\tisFailure: config.isFailure ?? ((status) => status >= 500 || status === 429),\n\t\t},\n\t}\n}\n\n/**\n * Record a failure and potentially open the circuit\n */\nfunction recordFailure(cb: CircuitBreaker): void {\n\tconst now = Date.now()\n\n\t// Remove old failures outside the window\n\tcb.failures = cb.failures.filter((t) => now - t < cb.config.windowMs)\n\n\t// Add new failure\n\tcb.failures.push(now)\n\n\t// Check if threshold exceeded\n\tif (cb.failures.length >= cb.config.failureThreshold) {\n\t\tcb.state = 'OPEN'\n\t\tcb.openedAt = now\n\t}\n}\n\n/**\n * Record a success and potentially close the circuit\n */\nfunction recordSuccess(cb: CircuitBreaker): void {\n\tif (cb.state === 'HALF_OPEN') {\n\t\t// Test request succeeded, close the circuit\n\t\tcb.state = 'CLOSED'\n\t\tcb.failures = []\n\t\tcb.openedAt = null\n\t}\n}\n\n/**\n * Check if circuit should allow request\n */\nfunction shouldAllowRequest(cb: CircuitBreaker): {\n\tallowed: boolean\n\tremainingMs?: number\n} {\n\tconst now = Date.now()\n\n\tswitch (cb.state) {\n\t\tcase 'CLOSED':\n\t\t\treturn { allowed: true }\n\n\t\tcase 'OPEN': {\n\t\t\tconst elapsed = now - (cb.openedAt ?? now)\n\t\t\tif (elapsed >= cb.config.openDurationMs) {\n\t\t\t\t// Timeout expired, transition to half-open\n\t\t\t\tcb.state = 'HALF_OPEN'\n\t\t\t\treturn { allowed: true }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\tremainingMs: cb.config.openDurationMs - elapsed,\n\t\t\t}\n\t\t}\n\n\t\tcase 'HALF_OPEN':\n\t\t\t// Only allow one test request at a time\n\t\t\t// In production, you'd use a flag to track if test is in progress\n\t\t\treturn { allowed: true }\n\n\t\tdefault:\n\t\t\treturn { allowed: true }\n\t}\n}\n\n/**\n * Create circuit breaker middleware (AWS/Resilience4j pattern)\n *\n * Features:\n * - Fast-fails when service is unhealthy (prevents cascade failures)\n * - Auto-recovery with half-open state for testing\n * - Configurable failure threshold and timeout\n * - Only counts server errors (5xx) and rate limits (429)\n * - Per-client instance: no shared module-level state\n */\nfunction createCircuitBreakerMiddleware(\n\tconfig: CircuitBreakerConfig | false | undefined,\n): Middleware {\n\tif (config === false) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\t// Create a fresh circuit breaker per client (not a module-level singleton)\n\tconst cb = createCircuitBreakerInstance(config ?? {})\n\n\t// Track for deprecated getCircuitBreakerState() / resetCircuitBreaker() test helpers\n\t_lastCircuitBreaker = cb\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\tif (!cb.config.enabled) {\n\t\t\t\treturn request\n\t\t\t}\n\n\t\t\tconst check = shouldAllowRequest(cb)\n\t\t\tif (!check.allowed) {\n\t\t\t\tthrow new CircuitBreakerOpenError(check.remainingMs!)\n\t\t\t}\n\n\t\t\treturn request\n\t\t},\n\t\tasync onResponse({ response }) {\n\t\t\tif (!cb.config.enabled) {\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\tif (cb.config.isFailure(response.status)) {\n\t\t\t\trecordFailure(cb)\n\t\t\t} else {\n\t\t\t\trecordSuccess(cb)\n\t\t\t}\n\n\t\t\treturn response\n\t\t},\n\t}\n}\n\n/**\n * Module-level reference to the most recently created circuit breaker.\n *\n * Used ONLY by the deprecated `getCircuitBreakerState()` / `resetCircuitBreaker()`\n * helpers (kept for backward compatibility with test suites).\n *\n * Production code should use `client.circuitBreaker.getState()` / `.reset()`\n * from the client object returned by `createRestClient()`.\n */\nlet _lastCircuitBreaker: CircuitBreaker | null = null\n\n/**\n * @deprecated Prefer creating a new `createRestClient()` for isolated state in tests.\n * Resets the most recently created circuit breaker and clears the reference,\n * so `getCircuitBreakerState()` returns null until the next client is created.\n */\nexport function resetCircuitBreaker(): void {\n\tif (_lastCircuitBreaker) {\n\t\t_lastCircuitBreaker.state = 'CLOSED'\n\t\t_lastCircuitBreaker.failures = []\n\t\t_lastCircuitBreaker.openedAt = null\n\t}\n\t_lastCircuitBreaker = null\n}\n\n/**\n * @deprecated Prefer `client.circuitBreaker.getState()` for per-instance state.\n * Returns state of the most recently created circuit breaker (test helper only).\n */\nexport function getCircuitBreakerState(): {\n\tstate: CircuitState\n\tfailures: number\n\topenedAt: number | null\n} | null {\n\tif (!_lastCircuitBreaker) return null\n\treturn {\n\t\tstate: _lastCircuitBreaker.state,\n\t\tfailures: _lastCircuitBreaker.failures.length,\n\t\topenedAt: _lastCircuitBreaker.openedAt,\n\t}\n}\n\n// ============================================================================\n// ETag Cache (HTTP conditional requests)\n// ============================================================================\n\n/**\n * Cached response entry with ETag\n */\ninterface ETagCacheEntry {\n\tetag: string\n\tbody: string\n\ttimestamp: number\n}\n\n/**\n * ETag cache with LRU eviction\n */\nconst etagCache = new Map<string, ETagCacheEntry>()\n\n/**\n * Generate cache key for request\n */\nfunction getETagCacheKey(request: Request): string {\n\treturn `${request.method}:${request.url}`\n}\n\n/**\n * Evict oldest entries when cache is full\n */\nfunction evictOldEntries(maxEntries: number, ttlMs: number): void {\n\tconst now = Date.now()\n\n\t// First, remove expired entries\n\tfor (const [key, entry] of etagCache) {\n\t\tif (now - entry.timestamp > ttlMs) {\n\t\t\tetagCache.delete(key)\n\t\t}\n\t}\n\n\t// If still over limit, remove oldest entries (LRU)\n\tif (etagCache.size > maxEntries) {\n\t\tconst entries = Array.from(etagCache.entries())\n\t\tentries.sort((a, b) => a[1].timestamp - b[1].timestamp)\n\n\t\tconst toRemove = entries.slice(0, entries.length - maxEntries)\n\t\tfor (const [key] of toRemove) {\n\t\t\tetagCache.delete(key)\n\t\t}\n\t}\n}\n\n/**\n * Create ETag middleware for HTTP conditional requests\n *\n * Features:\n * - Caches responses with ETag headers\n * - Sends If-None-Match on subsequent requests\n * - Returns cached response on 304 Not Modified\n * - LRU eviction when cache is full\n * - TTL-based expiration\n */\nfunction createETagMiddleware(config: ETagConfig | false | undefined): Middleware {\n\tif (config === false) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\tconst {\n\t\tenabled = true,\n\t\tmaxEntries = ETAG_CACHE_MAX_ENTRIES,\n\t\tttlMs = ETAG_CACHE_TTL_MS,\n\t} = config ?? {}\n\n\tif (!enabled) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\t// Only cache GET requests\n\t\t\tif (request.method !== 'GET') {\n\t\t\t\treturn request\n\t\t\t}\n\n\t\t\tconst cacheKey = getETagCacheKey(request)\n\t\t\tconst cached = etagCache.get(cacheKey)\n\n\t\t\tif (cached) {\n\t\t\t\t// Check TTL\n\t\t\t\tif (Date.now() - cached.timestamp > ttlMs) {\n\t\t\t\t\tetagCache.delete(cacheKey)\n\t\t\t\t} else {\n\t\t\t\t\treturn cloneRequestWithHeaders(request, (headers) => {\n\t\t\t\t\t\theaders.set('If-None-Match', cached.etag)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn request\n\t\t},\n\t\tasync onResponse({ request, response }) {\n\t\t\t// Only cache GET requests\n\t\t\tif (request.method !== 'GET') {\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\tconst cacheKey = getETagCacheKey(request)\n\n\t\t\t// Handle 304 Not Modified\n\t\t\tif (response.status === 304) {\n\t\t\t\tconst cached = etagCache.get(cacheKey)\n\t\t\t\tif (cached) {\n\t\t\t\t\t// Update timestamp (LRU)\n\t\t\t\t\tcached.timestamp = Date.now()\n\n\t\t\t\t\t// Return cached response with original body\n\t\t\t\t\treturn new Response(cached.body, {\n\t\t\t\t\t\tstatus: 200,\n\t\t\t\t\t\theaders: response.headers,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// No cache, return original response\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\t// Cache successful responses with ETag\n\t\t\tif (response.ok) {\n\t\t\t\tconst etag = response.headers.get('ETag')\n\t\t\t\tif (etag) {\n\t\t\t\t\t// Clone response to read body (can only read once)\n\t\t\t\t\tconst cloned = response.clone()\n\t\t\t\t\tconst body = await cloned.text()\n\n\t\t\t\t\t// Evict old entries if needed\n\t\t\t\t\tevictOldEntries(maxEntries, ttlMs)\n\n\t\t\t\t\t// Cache the response\n\t\t\t\t\tetagCache.set(cacheKey, {\n\t\t\t\t\t\tetag,\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn response\n\t\t},\n\t}\n}\n\n/**\n * Clear ETag cache (for testing)\n */\nexport function clearETagCache(): void {\n\tetagCache.clear()\n}\n\n/**\n * Get ETag cache stats (for monitoring)\n */\nexport function getETagCacheStats(): { size: number; entries: string[] } {\n\treturn {\n\t\tsize: etagCache.size,\n\t\tentries: Array.from(etagCache.keys()),\n\t}\n}\n\n// ============================================================================\n// Retry Middleware\n// ============================================================================\n\n/**\n * Per-request body storage for retry middleware.\n *\n * Using a WeakMap<Request, string | null> ensures each request has its own\n * stored body, preventing race conditions when multiple concurrent requests\n * use the same client instance.\n *\n * WeakMap keys are garbage-collected when the Request object is GC'd,\n * so no manual cleanup is needed.\n */\nconst retryBodyMap = new WeakMap<Request, string | null>()\n\n/**\n * Create retry middleware with exponential backoff and timeout\n *\n * Features:\n * - Request timeout (default 30s) prevents infinite hangs\n * - Exponential backoff with jitter for retries\n * - Respects Retry-After header for rate limiting\n * - Per-request body storage (WeakMap) — safe for concurrent requests\n */\nfunction createRetryMiddleware(retryConfig: RetryConfig | false | undefined): Middleware {\n\tif (retryConfig === false) {\n\t\t// No-op middleware - just passes through\n\t\treturn {\n\t\t\tasync onResponse({ response }) {\n\t\t\t\treturn response\n\t\t\t},\n\t\t}\n\t}\n\n\tconst {\n\t\tmaxRetries = 3,\n\t\tbaseDelay = BASE_RETRY_DELAY_MS,\n\t\tmaxDelay = MAX_RETRY_DELAY_MS,\n\t\tshouldRetry = isRetryableStatus,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t} = retryConfig ?? {}\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\t// Read body before it's consumed, store per-request in WeakMap\n\t\t\tconst body = request.body ? await request.clone().text() : null\n\n\t\t\t// Add timeout signal\n\t\t\tconst controller = new AbortController()\n\t\t\tsetTimeout(() => controller.abort(), timeout)\n\n\t\t\tconst newRequest = new Request(request.url, {\n\t\t\t\tmethod: request.method,\n\t\t\t\theaders: request.headers,\n\t\t\t\tbody,\n\t\t\t\tsignal: controller.signal,\n\t\t\t})\n\n\t\t\t// Associate body with this specific request object (concurrent-safe)\n\t\t\tretryBodyMap.set(newRequest, body)\n\n\t\t\treturn newRequest\n\t\t},\n\t\tasync onResponse({ response, request }) {\n\t\t\t// Retrieve body for this specific request (not shared across requests)\n\t\t\tconst originalBody = retryBodyMap.get(request) ?? null\n\n\t\t\tlet attempt = 0\n\t\t\tlet currentResponse = response\n\n\t\t\t// Check if we need to retry using the shouldRetry callback\n\t\t\twhile (attempt < maxRetries && shouldRetry(currentResponse.status, attempt)) {\n\t\t\t\tconst retryAfter = currentResponse.headers.get('Retry-After')\n\t\t\t\tconst delay = retryAfter\n\t\t\t\t\t? Number.parseInt(retryAfter, 10) * 1000\n\t\t\t\t\t: exponentialBackoff(attempt, baseDelay, maxDelay)\n\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\tattempt++\n\n\t\t\t\t// Create timeout for retry\n\t\t\t\tconst controller = new AbortController()\n\t\t\t\tconst timeoutId = setTimeout(() => controller.abort(), timeout)\n\n\t\t\t\ttry {\n\t\t\t\t\t// Reconstruct request with per-request stored body and new signal\n\t\t\t\t\tconst retryRequest = new Request(request.url, {\n\t\t\t\t\t\tmethod: request.method,\n\t\t\t\t\t\theaders: request.headers,\n\t\t\t\t\t\tbody: originalBody,\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t})\n\n\t\t\t\t\tconst newResponse = await fetch(retryRequest)\n\t\t\t\t\tclearTimeout(timeoutId)\n\n\t\t\t\t\t// If successful or non-retryable client error, return\n\t\t\t\t\tif (newResponse.ok || !shouldRetry(newResponse.status, attempt)) {\n\t\t\t\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\t\t\t\treturn newResponse\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentResponse = newResponse\n\t\t\t\t} catch (error) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t// On network/timeout error during retry, continue to next attempt\n\t\t\t\t\tif (attempt >= maxRetries) {\n\t\t\t\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\treturn currentResponse\n\t\t},\n\t}\n}\n\n/**\n * Validate and sanitize REST client configuration (SSOT helper)\n */\nfunction validateClientConfig(config: { secretKey?: string; platformUrl?: string }) {\n\treturn {\n\t\tsecretKey: validateAndSanitizeSecretKey(config.secretKey),\n\t\tbaseUrl: (config.platformUrl || `https://${DEFAULT_SDK_API_HOST}`).trim(),\n\t}\n}\n\n/**\n * Create a type-safe REST API client.\n *\n * Uses plain `fetch` with a configurable middleware chain (deduplication →\n * circuit breaker → ETag → retry). All endpoints accept a string path; the\n * return type is openapi-fetch-compatible (`{ data, error, response }`).\n *\n * @example\n * ```typescript\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * const { data: user } = await client.GET('/auth/me')\n * const { data: plans } = await client.GET('/billing/plans')\n * const { data: result } = await client.POST('/auth/login', {\n * body: { email: 'test@example.com', password: 'secret' },\n * })\n * ```\n */\nexport function createRestClient(config: RestClientConfig): RestClient {\n\tconst { secretKey, baseUrl } = validateClientConfig(config)\n\n\tconst { client, middlewares } = buildClient(`${baseUrl}${SDK_API_PATH}`, {\n\t\t'Content-Type': 'application/json',\n\t\t'x-app-secret': secretKey,\n\t})\n\n\t// Add deduplication middleware first (before other middleware)\n\tif (config.deduplication !== false) {\n\t\tmiddlewares.push(createDeduplicationMiddleware(config.deduplication))\n\t}\n\n\t// Add circuit breaker middleware (before retry)\n\tif (config.circuitBreaker !== false) {\n\t\tmiddlewares.push(createCircuitBreakerMiddleware(config.circuitBreaker))\n\t}\n\n\t// Add ETag caching middleware (before retry, for HTTP conditional requests)\n\tif (config.etag !== false) {\n\t\tmiddlewares.push(createETagMiddleware(config.etag))\n\t}\n\n\t// Add retry middleware (last, so it can retry after circuit allows)\n\tmiddlewares.push(createRetryMiddleware(config.retry))\n\n\treturn client\n}\n\n/**\n * Create a dynamic REST client with runtime token injection.\n *\n * Use this when you need to inject an access token that may change. Tokens\n * should be read from HttpOnly cookies via a server endpoint, never from\n * localStorage (XSS vulnerability).\n *\n * @example\n * ```typescript\n * const client = createDynamicRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * getAccessToken: async () => (await cookies()).get('session')?.value,\n * })\n * ```\n */\nexport function createDynamicRestClient(config: RestDynamicConfig): DynamicRestClient {\n\tconst { secretKey, baseUrl } = validateClientConfig(config)\n\n\tconst validatedConfig: RestDynamicConfig = {\n\t\t...config,\n\t\tsecretKey,\n\t\tplatformUrl: baseUrl,\n\t}\n\n\tconst { client, middlewares } = buildClient(`${baseUrl}${SDK_API_PATH}`, {\n\t\t'Content-Type': 'application/json',\n\t})\n\n\t// Add deduplication middleware first (before other middleware)\n\tif (config.deduplication !== false) {\n\t\tmiddlewares.push(createDeduplicationMiddleware(config.deduplication))\n\t}\n\n\t// Add auth middleware (runs on each request)\n\tmiddlewares.push(createAuthMiddleware(validatedConfig))\n\n\t// Add circuit breaker middleware (before retry)\n\tif (config.circuitBreaker !== false) {\n\t\tmiddlewares.push(createCircuitBreakerMiddleware(config.circuitBreaker))\n\t}\n\n\t// Add ETag caching middleware (before retry, for HTTP conditional requests)\n\tif (config.etag !== false) {\n\t\tmiddlewares.push(createETagMiddleware(config.etag))\n\t}\n\n\t// Add retry middleware (last, so it can retry after circuit allows)\n\tmiddlewares.push(createRetryMiddleware(config.retry))\n\n\treturn client\n}\n\n/**\n * Check if a REST response has an error\n */\nexport function hasError<T, E>(response: {\n\tdata?: T\n\terror?: E\n}): response is {\n\tdata: undefined\n\terror: E\n} {\n\treturn response.error !== undefined\n}\n\n/**\n * Extract error message from REST error response\n */\nexport function getRestErrorMessage(error: unknown): string {\n\tif (error && typeof error === 'object' && 'error' in error) {\n\t\tconst err = error as { error?: { message?: string } }\n\t\treturn err.error?.message ?? 'An unknown error occurred'\n\t}\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\treturn 'An unknown error occurred'\n}\n","/**\n * API Key Validation — Single Source of Truth\n *\n * OAuth 2.0 standard key validation for Sylphx Platform.\n * ALL key validation, sanitization, and environment detection logic lives here.\n *\n * Principles:\n * 1. Fail fast - Invalid input rejected immediately with clear errors\n * 2. Helpful errors - Tell users exactly what's wrong and how to fix it\n * 3. Development warnings - Warn about issues that would fail in production\n * 4. No silent fixes - Transparency over convenience (but warn + continue)\n * 5. Single Source of Truth - All key logic in one place\n *\n * Key Formats (ADR-021):\n * - Publishable key: pk_(dev|stg|prod|prev)_{ref}_{32hex} — client-safe (new)\n * - Secret Key: sk_(dev|stg|prod|prev)_{ref}_{64hex} — server-side only\n *\n * Legacy Key Formats (backward-compat):\n * - App ID (old): app_(dev|stg|prod|prev)_[identifier] — Public identifier\n *\n * Special Internal Formats (NOT rotated):\n * - Console bootstrap: app_prod_platform_{slug} / sk_prod_platform_{slug}\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Environment type derived from key prefix */\nexport type EnvironmentType = 'development' | 'staging' | 'production' | 'preview'\n\n/** Key type - publicKey (pk_*), appId (legacy app_*), or secret (sk_*) */\nexport type KeyType = 'publicKey' | 'appId' | 'secret'\n\n/** Validation result with clear error information */\nexport interface KeyValidationResult {\n\t/** Whether the key is valid (possibly after sanitization) */\n\tvalid: boolean\n\t/** The sanitized key to use (only if valid) */\n\tsanitizedKey: string\n\t/** Detected key type */\n\tkeyType?: KeyType\n\t/** Detected environment */\n\tenvironment?: EnvironmentType\n\t/** Error message if invalid */\n\terror?: string\n\t/** Warning message if key was auto-fixed */\n\twarning?: string\n\t/** Detected issues for debugging */\n\tissues?: string[]\n}\n\n// =============================================================================\n// Patterns — Strict Format Validation\n// =============================================================================\n\n/**\n * Publishable key pattern: pk_(dev|stg|prod|prev)_{32hex} (ADR-055) or\n * pk_(dev|stg|prod|prev)_{ref}_{32hex} (legacy ADR-021).\n * - Prefix: pk_ (publishable key, safe for client)\n * - Environment: dev, stg, prod, or prev\n * - Ref: optional 12-char base36 project ref\n * - Token: 32 hex chars (128-bit random)\n *\n * Example: pk_prod_2dubco39o9so_f19e5cdc3cc54f7ff81bdc26ec5bfbad\n */\nconst PUBLIC_KEY_PATTERN = /^pk_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32}$/\n\n/**\n * Legacy App ID pattern: app_(dev|stg|prod|prev)_[identifier]\n * - Prefix: app_ (application identifier, public) — legacy format, backward-compat\n * - Environment: dev, stg, prod, or prev\n * - Suffix: alphanumeric with underscores/hyphens (hex for apps, or internal identifiers)\n *\n * Accepts both legacy app_* format and ADR-021 pk_* publishable keys.\n * pk_{env}_{ref}_{hex} is the standard customer key — Customer Zero uses it as appId.\n */\nconst APP_ID_PATTERN = /^(app|pk)_(dev|stg|prod|prev)_[a-z0-9_-]+$/\n\n/**\n * Secret key pattern: sk_(dev|stg|prod|prev)_[identifier]\n * - Prefix: sk_ (secret key)\n * - Environment: dev, stg, prod, or prev\n * - Suffix: alphanumeric with underscores/hyphens\n *\n * Accepts both old format (sk_prod_{64hex}) and new format (sk_prod_{ref}_{64hex}).\n */\nconst SECRET_KEY_PATTERN = /^sk_(dev|stg|prod|prev)_[a-z0-9_-]+$/\n\n/** Environment prefix to type mapping */\nconst ENV_PREFIX_MAP: Record<string, EnvironmentType> = {\n\tdev: 'development',\n\tstg: 'staging',\n\tprod: 'production',\n\tprev: 'preview',\n}\n\n// =============================================================================\n// Core Validation Functions\n// =============================================================================\n\n/**\n * Detect common issues with a key (whitespace, newlines, etc.)\n */\nfunction detectKeyIssues(key: string): string[] {\n\tconst issues: string[] = []\n\tif (key !== key.trim()) issues.push('whitespace')\n\tif (key.includes('\\n')) issues.push('newline')\n\tif (key.includes('\\r')) issues.push('carriage-return')\n\tif (key.includes(' ')) issues.push('space')\n\tif (key !== key.toLowerCase()) issues.push('uppercase-chars')\n\treturn issues\n}\n\n/**\n * Create a helpful warning message for keys that needed sanitization\n */\nfunction createSanitizationWarning(keyType: KeyType, issues: string[], envVarName: string): string {\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\treturn (\n\t\t`[Sylphx] ${keyTypeName} contains ${issues.join(', ')}. ` +\n\t\t`This is commonly caused by Vercel CLI's 'env pull' command.\\n\\n` +\n\t\t`To fix permanently:\\n` +\n\t\t`1. Go to Vercel Dashboard → Your Project → Settings → Environment Variables\\n` +\n\t\t`2. Edit ${envVarName}\\n` +\n\t\t`3. Remove any trailing whitespace or newline characters\\n` +\n\t\t`4. Redeploy your application\\n\\n` +\n\t\t`The SDK will automatically sanitize the key, but fixing the source is recommended.`\n\t)\n}\n\n/**\n * Create a helpful error message for invalid keys\n */\nfunction createInvalidKeyError(keyType: KeyType, key: string, envVarName: string): string {\n\tconst maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key\n\tconst formatHint =\n\t\tkeyType === 'appId'\n\t\t\t? 'pk_(dev|stg|prod|prev)_{ref}_{hex} or app_(dev|stg|prod|prev)_[id]'\n\t\t\t: 'sk_(dev|stg|prod|prev)_{ref}_{hex}'\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\n\treturn (\n\t\t`[Sylphx] Invalid ${keyTypeName} format.\\n\\n` +\n\t\t`Expected format: ${formatHint}\\n` +\n\t\t`Received: \"${maskedKey}\"\\n\\n` +\n\t\t`Please check your ${envVarName} environment variable.\\n` +\n\t\t`You can find your keys in the Sylphx Console → API Keys.\\n\\n` +\n\t\t`Common issues:\\n` +\n\t\t`• Key has uppercase characters (must be lowercase)\\n` +\n\t\t`• Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)\\n` +\n\t\t`• Key has invalid environment (must be dev, stg, prod, or prev)\\n` +\n\t\t`• Key was copied with extra whitespace`\n\t)\n}\n\n/**\n * Extract environment from a validated key\n */\nfunction extractEnvironment(key: string): EnvironmentType | undefined {\n\t// Match pk_, app_, or sk_ prefix followed by environment\n\tconst match = key.match(/^(?:app|pk|sk)_(dev|stg|prod|prev)_/)\n\tif (!match) return undefined\n\treturn ENV_PREFIX_MAP[match[1]]\n}\n\n/**\n * Internal: Generic key validation logic for specific key types\n */\nfunction validateKeyForType(\n\tkey: string | undefined | null,\n\tkeyType: KeyType,\n\tpattern: RegExp,\n\tenvVarName: string,\n): KeyValidationResult {\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\n\t// Check if key is provided\n\tif (!key) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tsanitizedKey: '',\n\t\t\terror:\n\t\t\t\t`[Sylphx] ${keyTypeName} is required. ` +\n\t\t\t\t`Set ${envVarName} in your environment variables.`,\n\t\t\tissues: ['missing'],\n\t\t}\n\t}\n\n\t// Detect issues before validation\n\tconst issues = detectKeyIssues(key)\n\n\t// Check if key matches expected format exactly\n\tif (pattern.test(key)) {\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsanitizedKey: key,\n\t\t\tkeyType,\n\t\t\tenvironment: extractEnvironment(key),\n\t\t\tissues: [],\n\t\t}\n\t}\n\n\t// Key doesn't match - try sanitization (trim + lowercase)\n\tconst sanitized = key.trim().toLowerCase()\n\n\tif (pattern.test(sanitized)) {\n\t\t// Sanitization fixes the issue\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsanitizedKey: sanitized,\n\t\t\tkeyType,\n\t\t\tenvironment: extractEnvironment(sanitized),\n\t\t\twarning: createSanitizationWarning(keyType, issues, envVarName),\n\t\t\tissues,\n\t\t}\n\t}\n\n\t// Sanitization doesn't fix it - key format is genuinely wrong\n\treturn {\n\t\tvalid: false,\n\t\tsanitizedKey: '',\n\t\terror: createInvalidKeyError(keyType, key, envVarName),\n\t\tissues: [...issues, 'invalid-format'],\n\t}\n}\n\n// =============================================================================\n// Public API — Publishable Key (ADR-021: pk_*) and legacy App ID (app_*)\n// =============================================================================\n\n/**\n * Validate an ADR-021 publishable key (pk_*).\n *\n * @example\n * ```typescript\n * const result = validatePublicKey('pk_prod_...')\n * if (!result.valid) throw new Error(result.error)\n * ```\n */\nexport function validatePublicKey(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'publicKey', PUBLIC_KEY_PATTERN, 'publishable credential')\n}\n\n/**\n * Validate and sanitize a publishable key, throwing on invalid input.\n */\nexport function validateAndSanitizePublicKey(key: string | undefined | null): string {\n\tconst result = validatePublicKey(key)\n\tif (!result.valid) throw new Error(result.error)\n\tif (result.warning) console.warn(result.warning)\n\treturn result.sanitizedKey\n}\n\n/**\n * Validate a legacy App ID (app_*) and return detailed results.\n *\n * @deprecated Use validatePublicKey() for new pk_* keys (ADR-021).\n *\n * @example\n * ```typescript\n * const result = validateAppId(process.env.NEXT_PUBLIC_SYLPHX_APP_ID)\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * if (result.warning) {\n * console.warn(result.warning)\n * }\n * ```\n */\nexport function validateAppId(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'appId', APP_ID_PATTERN, 'NEXT_PUBLIC_SYLPHX_APP_ID')\n}\n\n/**\n * Validate and sanitize App ID, logging warnings.\n *\n * @deprecated Use validateAndSanitizePublicKey() for new pk_* keys (ADR-021).\n * @throws Error if the key is invalid and cannot be sanitized\n * @returns The sanitized App ID\n */\nexport function validateAndSanitizeAppId(key: string | undefined | null): string {\n\tconst result = validateAppId(key)\n\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\n\tif (result.warning) {\n\t\tconsole.warn(result.warning)\n\t}\n\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Secret Keys\n// =============================================================================\n\n/**\n * Validate a secret key and return detailed results\n *\n * @example\n * ```typescript\n * const result = validateSecretKey('sk_prod_...')\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * ```\n */\nexport function validateSecretKey(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'secret', SECRET_KEY_PATTERN, 'secret credential')\n}\n\n/**\n * Validate and sanitize secret key, logging warnings\n *\n * @throws Error if the key is invalid and cannot be sanitized\n * @returns The sanitized secret key\n */\nexport function validateAndSanitizeSecretKey(key: string | undefined | null): string {\n\tconst result = validateSecretKey(key)\n\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\n\tif (result.warning) {\n\t\tconsole.warn(result.warning)\n\t}\n\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Environment Detection (SSOT)\n// =============================================================================\n\n/**\n * Detect environment type from any key (App ID or Secret Key)\n *\n * @example\n * ```typescript\n * detectEnvironment('sk_dev_abc123') // 'development'\n * detectEnvironment('app_prod_xyz789') // 'production'\n * detectEnvironment('sk_stg_qwe456') // 'staging'\n * ```\n *\n * @throws Error if key format is invalid\n */\nexport function detectEnvironment(key: string): EnvironmentType {\n\t// Validate and sanitize first\n\tconst sanitized = key.trim().toLowerCase()\n\n\t// Check all key types (ADR-021 and legacy)\n\tif (sanitized.startsWith('pk_')) {\n\t\tconst result = validatePublicKey(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tif (sanitized.startsWith('sk_')) {\n\t\tconst result = validateSecretKey(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tif (sanitized.startsWith('app_')) {\n\t\tconst result = validateAppId(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tthrow new Error(\n\t\t`[Sylphx] Invalid key format. Key must start with 'pk_' (publishable), 'sk_' (secret), or 'app_' (legacy App ID).`,\n\t)\n}\n\n/**\n * Check if running in development environment based on key\n */\nexport function isDevelopmentKey(key: string): boolean {\n\treturn detectEnvironment(key) === 'development'\n}\n\n/**\n * Check if running in production environment based on key\n */\nexport function isProductionKey(key: string): boolean {\n\treturn detectEnvironment(key) === 'production'\n}\n\n// =============================================================================\n// Public API — Cookie Namespace (SSOT)\n// =============================================================================\n\n/**\n * Get the cookie namespace for a given secret key\n *\n * Used by auth middleware to namespace cookies per environment.\n * This prevents dev/staging/prod cookies from conflicting.\n *\n * @example\n * ```typescript\n * getCookieNamespace('sk_dev_abc123') // 'sylphx_dev'\n * getCookieNamespace('sk_prod_xyz789') // 'sylphx_prod'\n * ```\n */\nexport function getCookieNamespace(secretKey: string): string {\n\tconst env = detectEnvironment(secretKey)\n\tconst shortEnv =\n\t\tenv === 'development' ? 'dev' : env === 'staging' ? 'stg' : env === 'preview' ? 'prev' : 'prod'\n\treturn `sylphx_${shortEnv}`\n}\n\n// =============================================================================\n// Public API — Key Type Detection\n// =============================================================================\n\n/**\n * Detect the type of key.\n *\n * @returns 'publicKey' (pk_*), 'appId' (legacy app_*), 'secret' (sk_*), or null if unknown\n */\nexport function detectKeyType(key: string): KeyType | null {\n\tconst sanitized = key.trim().toLowerCase()\n\tif (sanitized.startsWith('pk_')) return 'publicKey'\n\tif (sanitized.startsWith('app_')) return 'appId'\n\tif (sanitized.startsWith('sk_')) return 'secret'\n\treturn null\n}\n\n/**\n * Check if a key is a publishable/public key (pk_* or legacy app_*)\n */\nexport function isPublishableKey(key: string): boolean {\n\tconst t = detectKeyType(key)\n\treturn t === 'publicKey' || t === 'appId'\n}\n\n/**\n * Check if a key is an App ID (legacy app_* format)\n *\n * @deprecated Use isPublishableKey() to also accept new pk_* keys\n */\nexport function isAppId(key: string): boolean {\n\treturn detectKeyType(key) === 'appId'\n}\n\n/**\n * Check if a key is a secret key (sk_*)\n */\nexport function isSecretKey(key: string): boolean {\n\treturn detectKeyType(key) === 'secret'\n}\n\n/**\n * Validate any key (auto-detects type)\n *\n * Use this when you accept either App ID or Secret Key.\n * The function auto-detects the key type and validates accordingly.\n *\n * @example\n * ```typescript\n * const result = validateKey('sk_prod_...')\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * const sanitizedKey = result.sanitizedKey\n * ```\n */\nexport function validateKey(key: string | undefined | null): KeyValidationResult {\n\tconst keyType = key ? detectKeyType(key) : null\n\n\tif (keyType === 'publicKey') {\n\t\treturn validatePublicKey(key)\n\t}\n\tif (keyType === 'appId') {\n\t\treturn validateAppId(key)\n\t}\n\tif (keyType === 'secret') {\n\t\treturn validateSecretKey(key)\n\t}\n\n\t// Unknown key type - return detailed error\n\treturn {\n\t\tvalid: false,\n\t\tsanitizedKey: '',\n\t\terror: key\n\t\t\t? `Invalid key format. Keys must start with 'pk_' (publishable), 'app_' (legacy), or 'sk_' (secret), followed by environment (dev/stg/prod/prev). Got: ${key.slice(0, 20)}...`\n\t\t\t: 'API key is required but was not provided.',\n\t\tissues: key ? ['invalid_format'] : ['missing'],\n\t}\n}\n\n/**\n * Validate any key and return sanitized version (throws on error)\n *\n * Use this when you need the key value and want to throw on invalid input.\n */\nexport function validateAndSanitizeKey(key: string | undefined | null): string {\n\tconst result = validateKey(key)\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\tif (result.warning) {\n\t\tconsole.warn(`[Sylphx] ${result.warning}`)\n\t}\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Runtime Environment Detection\n// =============================================================================\n\n/**\n * Check if we're in development mode (based on NODE_ENV or hostname)\n */\nexport function isDevelopmentRuntime(): boolean {\n\tif (typeof process !== 'undefined' && process.env) {\n\t\treturn process.env.NODE_ENV === 'development'\n\t}\n\tif (typeof window !== 'undefined') {\n\t\treturn window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'\n\t}\n\treturn false\n}\n","/**\n * @sylphx/sdk\n *\n * State-of-the-art platform SDK with pure functions.\n *\n * ## Architecture (see ADR.md for full details)\n *\n * This SDK follows Firebase's architecture pattern:\n * - **Pure functions** - No hidden state, config passed explicitly\n * - **Tree-shakeable** - Import only what you use, bundler removes the rest\n * - **4 entry points only** - Separate only when peer dependencies differ\n *\n * ## Entry Points\n *\n * | Entry | Purpose | Peer Dependencies |\n * |-------|---------|-------------------|\n * | `@sylphx/sdk` | All pure functions | None |\n * | `@sylphx/sdk/react` | React hooks & components | react, react-dom |\n * | `@sylphx/sdk/server` | Server utilities (JWT, webhooks) | jose |\n * | `@sylphx/sdk/nextjs` | Next.js integration | next |\n *\n * ## Service Quick Reference\n *\n * | Service | Key Functions | Description |\n * |---------|---------------|-------------|\n * | Config | `createClient`, `createServerClient`, `withToken` | SDK configuration |\n * | Auth | `signIn`, `signUp`, `signOut` | Authentication |\n * | Analytics | `track`, `page`, `identify` | Event tracking |\n * | AI | `chat`, `embed`, `complete` | AI/LLM operations |\n * | Database | `getDatabaseConnectionString`, `getDatabaseStatus` | Provisioned PostgreSQL |\n * | KV | `kvSet`, `kvGet`, `kvRateLimit` | Managed key-value store |\n * | Realtime | `realtimeEmit`, `getRealtimeHistory` | Managed pub/sub with durable history |\n * | Billing | `getPlans`, `createCheckout` | Subscriptions |\n * | Storage | `storage.uploads.create`, `storage.files.list` | File storage (ADR-100) |\n * | Tasks | `scheduleTask`, `createCron` | Background tasks |\n * | Flags | `checkFlag`, `getFlags`, `getAllFlags` | Feature flags |\n * | Consent | `hasConsent`, `setConsents` | GDPR/CCPA |\n * | Referrals | `getReferralStats`, `redeemReferralCode` | Referral system |\n * | Webhooks | `getWebhookConfig`, `replayWebhookDelivery` | Webhook management |\n * | Email | `sendEmail`, `sendTemplatedEmail` | Email sending |\n * | Notifications | `registerPush`, `sendPush` | Push notifications |\n * | Engagement | `getStreak`, `unlockAchievement` | Gamification |\n * | Monitoring | `captureException`, `captureMessage` | Error tracking |\n * | Deploy | `triggerDeploy`, `getDeployStatus` | CI/CD deployments |\n * | Compute | `MACHINE_SIZES`, `resolveMachineTierResources` | Public machine sizing |\n * | Orgs | `createOrganization`, `inviteOrganizationMember` | Multi-tenancy |\n * | Permissions | `listPermissions`, `hasPermission` | RBAC permissions |\n * | Roles | `listRoles`, `assignMemberRole` | RBAC roles |\n *\n * ## Usage\n *\n * @example\n * ```typescript\n * // Pure functions - tree-shakeable, works anywhere\n * import { createServerClient, track, signIn, getPlans } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Analytics\n * await track(config, { event: 'purchase', properties: { amount: 99 } })\n *\n * // Auth\n * const result = await signIn(config, { email, password })\n *\n * // Billing\n * const plans = await getPlans(config)\n * ```\n *\n * @example\n * ```typescript\n * // REST client for direct API access\n * import { createRestClient, createServerClient } from '@sylphx/sdk'\n *\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Type-safe REST calls\n * const { data: plans } = await client.GET('/billing/plans')\n * const { data: user } = await client.GET('/auth/me')\n * ```\n */\n\nexport {\n\tDEFAULT_MACHINE_SIZE,\n\tisMachineSize,\n\tMACHINE_CONFIGS,\n\tMACHINE_MAX_INSTANCES,\n\tMACHINE_RESOURCE_REQUIREMENTS,\n\tMACHINE_SIZES,\n\ttype MachineConfig,\n\ttype MachineResourceRequirements,\n\ttype MachineTierResources,\n\tparseMachineSize,\n\tresolveMachineConfig,\n\tresolveMachineMaxInstances,\n\tresolveMachineResources,\n\tresolveMachineTierResources,\n\ttoPublicMachineSize,\n} from './compute'\nexport * from './config/database-pricing'\nexport * from './config/referrals'\nexport {\n\ttype ErrorDetails as ExtractedErrorDetails,\n\tgetErrorDetails as extractErrorDetails,\n\tgetErrorMessage as extractErrorMessage,\n} from './error-extract'\n\n// =============================================================================\n// Configuration (Foundation)\n// =============================================================================\n\nexport {\n\ttype BuildConnectionUrlInput,\n\tbuildConnectionUrl,\n\ttype ConnectionCredentialType,\n\ttype ConnectionEnv,\n\tCREDENTIAL_REGEX,\n\tcreateClient,\n\tcreateConfig,\n\tcreateServerClient,\n\tInvalidConnectionUrlError,\n\ttype ParsedConnectionUrl,\n\tparseConnectionUrl,\n\ttype SylphxClientInput,\n\ttype SylphxConfig,\n\ttype SylphxConfigInput,\n\twithToken,\n} from './config'\n\n// =============================================================================\n// CSV Utilities\n// =============================================================================\n\nexport { escapeCsvField } from './csv'\n\n// =============================================================================\n// Browser-Safe Shared Utilities\n// =============================================================================\n\nexport {\n\tcalculatePercentage,\n\tformatBytes,\n\tformatCents,\n\tformatCurrency,\n\tformatDate,\n\tformatDateTime,\n\tformatDuration,\n\tformatMicrodollars,\n\tformatMonthYear,\n\tformatNumber,\n\tformatPercent,\n\tformatRelativeTime,\n\tformatRelativeTimeShort,\n\tformatTime,\n\tgetBillingStatusVariant,\n\tgetInvoiceStatusVariant,\n} from './formatting'\n\nexport { safeJsonParse } from './json'\nexport { escapeHtml, generateSlug, getBaseUrl } from './utils'\nexport { type ParsedUserAgent, parseUserAgent } from './utils/user-agent'\nexport {\n\tcreateWorkspace,\n\tdeleteWorkspace,\n\tforkWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\ttype CreateWorkspaceInput,\n\ttype ForkWorkspaceInput,\n\ttype WorkspaceRecord,\n} from './workspaces'\n\n// =============================================================================\n// Shared Product Configuration\n// =============================================================================\n\nexport {\n\tMAX_PASSWORD_LENGTH,\n\tMIN_PASSWORD_LENGTH,\n\tPASSWORD_REQUIREMENTS,\n} from './config/auth'\n\nexport {\n\tBILLING_ALLOWED_ROLES,\n\ttype BillingAllowedRole,\n\tBUILD_MINUTE_PRICES,\n\tBUILD_MINUTES_INCLUDED,\n\tBUILD_SIZE_MULTIPLIERS,\n\tBYTES_PER_GB,\n\tCI_BUILD_MINUTE_PRICE_MICRODOLLARS,\n\tCI_FREE_MINUTES_PER_MONTH,\n\tCI_MACOS_MULTIPLIER,\n\tCI_MACOS_SIZE_MULTIPLIERS,\n\tCI_SIZE_MULTIPLIERS,\n\tCOMPUTE_RAM_RATE_MICRODOLLARS,\n\tCOMPUTE_VCPU_ACTIVE_RATE_MICRODOLLARS,\n\tCOMPUTE_VCPU_IDLE_RATE_MICRODOLLARS,\n\tCREDIT_EXPIRY_MONTHS,\n\thasBillingAccess,\n\tINVOICE_DUE_DAYS,\n\ttype KvMetric,\n\tMAX_PAYMENT_ATTEMPTS,\n\tMICRODOLLARS_PER_CENT,\n\ttype RealtimeMetric,\n\tSERVICE_METRICS,\n\ttype ServiceMetrics,\n} from './config/billing'\n\nexport { CONSOLE_APP_SLUG, getEnvPrefix } from './config/console-keys'\n\nexport {\n\tDEFAULT_MAX_INSTANCES,\n\tgetAvailableInstanceTypes,\n\tgetDefaultInstanceType,\n\tINSTANCE_TYPE_ALIASES,\n\tINSTANCE_TYPE_ORDER,\n\tINSTANCE_TYPES,\n\ttype InstanceTypeDefinition,\n\ttype InstanceTypeId,\n\tisValidInstanceType,\n\tLEGACY_INSTANCE_TYPE_ORDER,\n\tresolveCanonicalInstanceType,\n\tresolveMaxInstances,\n\tresolveResources,\n\tvalidateInstanceTypeForPlan,\n} from './config/instance-types'\n\nexport {\n\ttype BuildMachineTier,\n\tcentsToDollars,\n\tgetActivePlans,\n\tgetPlanMonthlyPrice,\n\tisPlanDeprecated,\n\tmicrosToDollars,\n\tPLATFORM_PLAN_ORDER,\n\tPLATFORM_PLAN_ORDER_ALL,\n\tPLATFORM_PLANS,\n\ttype PlatformPlanDefinition,\n\ttype PlatformPlanFeatures,\n\ttype PlatformPlanId,\n\ttype PlatformPlanLimits,\n} from './config/platform-plans'\n\n// =============================================================================\n// Debug Utilities\n// =============================================================================\n\nexport {\n\ttype DebugCategory,\n\tdebugError,\n\tdebugLog,\n\tdebugTimer,\n\tdebugWarn,\n\tdisableDebug,\n\tenableDebug,\n\tgetDebugMode,\n\tinstallGlobalDebugHelpers,\n\tresetDebugModeCache,\n} from './debug'\n\n// =============================================================================\n// REST Client\n// =============================================================================\n\nexport {\n\ttype CircuitBreakerConfig,\n\tCircuitBreakerOpenError,\n\ttype CircuitState,\n\tcreateDynamicRestClient,\n\tcreateRestClient,\n\ttype DeduplicationConfig,\n\ttype DynamicRestClient,\n\tgetCircuitBreakerState,\n\tgetRestErrorMessage,\n\thasError,\n\ttype RestClient,\n\ttype RestClientConfig,\n\ttype RestDynamicConfig,\n\ttype RetryConfig,\n\tresetCircuitBreaker,\n} from './rest-client'\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nexport {\n\tAuthenticationError,\n\tAuthorizationError,\n\tERROR_CODE_STATUS,\n\ttype ErrorCode,\n\texponentialBackoff,\n\tgetErrorCode,\n\tgetErrorDetails,\n\tgetErrorMessage,\n\tgetSafeErrorMessage,\n\tisChallengeRequired,\n\tisRetryableError,\n\tisSylphxError,\n\tNetworkError,\n\tNotFoundError,\n\tRateLimitError,\n\tRETRYABLE_CODES,\n\tSylphxError,\n\ttype SylphxErrorCode,\n\ttype SylphxErrorOptions,\n\tTimeoutError,\n\ttoSylphxError,\n\tValidationError,\n} from './errors'\n\n// =============================================================================\n// Auth Functions\n// =============================================================================\n\nexport {\n\tcookies,\n\ttype DeviceApproveInput,\n\ttype DeviceApproveResult,\n\ttype DeviceDenyInput,\n\ttype DeviceDenyResult,\n\ttype DeviceGrant,\n\ttype DeviceInitInput,\n\ttype DevicePollResult,\n\tdevice,\n\t// DPoP — sender-constrained access tokens (ADR-089 Phase 5.1e)\n\tdpop,\n\textendedSignUp,\n\tforgotPassword,\n\tgetOrgScopedToken,\n\tgetSession,\n\ttype ImpersonationActive,\n\ttype ImpersonationEndResult,\n\ttype ImpersonationInfo,\n\ttype ImpersonationStartResult,\n\ttype InviteUserRequest,\n\ttype InviteUserResponse,\n\timpersonation,\n\tintrospectToken,\n\tinviteUser,\n\t// Generated API types (from OpenAPI spec)\n\ttype LoginRequest,\n\ttype LoginResponse,\n\ttype MeResponse,\n\ttype MintAccessTokenClaims,\n\ttype MintAccessTokenResult,\n\t// SDK-specific types\n\ttype OrgScopedTokenResponse,\n\ttype OrgTokenPayload,\n\toauth,\n\ttype PlatformAccessTokenClaims,\n\t// Platform auth — refresh-token rotation + logout (ADR-089 SoC Tier-1)\n\ttype PlatformLogoutInput,\n\t// Platform password (ADR-089 Phase 2c)\n\ttype PlatformPasswordChangeInput,\n\ttype PlatformPasswordChangeResult,\n\ttype PlatformPasswordSetInput,\n\ttype PlatformPasswordSetResult,\n\ttype PlatformPasswordStatusResult,\n\ttype PlatformRefreshInput,\n\ttype PlatformRefreshResult,\n\t// Platform sessions (ADR-089 Phase 2b)\n\ttype PlatformSessionRenameInput,\n\ttype PlatformSessionRenameResult,\n\ttype PlatformSessionRevokeAllResult,\n\ttype PlatformSessionRevokeInput,\n\ttype PlatformSessionRevokeOtherResult,\n\ttype PlatformSessionRevokeResult,\n\ttype PlatformSessionsListResult,\n\ttype PlatformUserDeleteInput,\n\ttype PlatformUserDeleteResult,\n\ttype PlatformUserExportResult,\n\t// Platform user resolver (ADR-089 Phase 3b)\n\ttype PlatformUserRecord,\n\ttype PlatformUserResolution,\n\tpassword,\n\tplatformAuth,\n\ttype RegisterInput,\n\ttype RegisterRequest,\n\ttype RegisterResponse,\n\ttype ResendEmailVerificationRequest,\n\ttype ResendEmailVerificationResponse,\n\ttype RevokeTokenOptions,\n\trefreshToken,\n\tresendVerificationEmail,\n\tresetPassword,\n\tresetPlatformCookieCache,\n\tresetPlatformJwksCache,\n\trevokeAllTokens,\n\trevokeToken,\n\ttype SessionResult,\n\tsessions,\n\tsignIn,\n\tsignOut,\n\tsignUp,\n\tswitchOrg,\n\ttype TokenIntrospectionResult,\n\ttype TokenResponse,\n\ttype TwoFactorVerifyRequest,\n\tuser,\n\tverifyAccessToken,\n\tverifyEmail,\n\tverifyTwoFactor,\n} from './auth'\n\n// =============================================================================\n// Audit (Σ1 SoC rename — was `auth.audit`, now top-level `audit`)\n// =============================================================================\n\nexport { type AuditQueryFilter, type AuditQueryResult, audit } from './audit'\n\n// =============================================================================\n// Rate-Limits (Σ1 SoC rename — was `auth.rateLimits`)\n// =============================================================================\n\nexport {\n\ttype RateLimitStatusFilter,\n\ttype RateLimitStatusResult,\n\ttype RateLimitStrategiesFilter,\n\ttype RateLimitStrategiesResult,\n\ttype RateLimitStrategyDeleteInput,\n\ttype RateLimitStrategyDeleteResult,\n\ttype RateLimitStrategyUpsertInput,\n\ttype RateLimitStrategyUpsertResult,\n\trateLimits,\n} from './rate-limits'\n\n// =============================================================================\n// Realtime Admin + Functions Admin (Σ1 SoC rename — were nested inside `auth`\n// and re-exported as `realtimeAdmin` / `functionsInternal`; now nested under\n// `realtime.admin.*` and `functions.admin.*` at the package root)\n// =============================================================================\n\nimport { functionsAdmin } from './functions'\nimport * as realtimeCustomer from './realtime'\nimport { realtimeAdmin } from './realtime-admin'\n\nexport type { PlatformFunctionsDownloadBundleResult } from './functions'\nexport type {\n\tPlatformRealtimeChannel,\n\tPlatformRealtimeCreateChannelResult,\n\tPlatformRealtimeDeleteChannelResult,\n\tPlatformRealtimeListChannelsResult,\n\tPlatformRealtimeStatusResult,\n} from './realtime-admin'\n\n/**\n * `realtime` top-level namespace — customer-app data plane exposes\n * `realtime.emit` / `realtime.history`, Platform admin surface nests\n * at `realtime.admin.channels.*` (ADR-089 Phase 3a / Σ1 SoC rename).\n */\nexport const realtime = {\n\tadmin: realtimeAdmin.channels,\n\temit: realtimeCustomer.realtimeEmit,\n\thistory: realtimeCustomer.getRealtimeHistory,\n} as const\n\n/**\n * `functions` top-level namespace — Platform admin surface nests at\n * `functions.admin.downloadBundle` (ADR-089 Phase 3a / Σ1 SoC rename).\n * Customer-app function invocation surface is TBD.\n */\nexport const functions = {\n\tadmin: functionsAdmin,\n} as const\n\n// =============================================================================\n// Admin Functions (Server-side only — requires secretKey)\n// =============================================================================\n\nexport {\n\ttype AdminUser,\n\tdeleteUser,\n\tgetUser,\n\tgetUserByEmail,\n\ttype ListUsersOptions,\n\ttype ListUsersResult,\n\tlistUsers,\n\tsuspendUser,\n\tupdateUser,\n\tupdateUserMetadata,\n} from './admin'\n\n// =============================================================================\n// Analytics Functions\n// =============================================================================\n\nexport {\n\ttype BatchEvent,\n\tcreateTracker,\n\tgenerateAnonymousId,\n\ttype IdentifyInput,\n\tidentify,\n\ttype PageInput,\n\tpage,\n\ttype TrackInput,\n\ttrack,\n\ttrackBatch,\n} from './analytics'\n\n// =============================================================================\n// AI Functions\n// =============================================================================\n\nexport {\n\ttype AIModel,\n\ttype AIModelsResponse,\n\ttype AIRateLimitResponse,\n\t// Generated API types (from OpenAPI spec)\n\ttype AIUsageResponse,\n\ttype ChatInput,\n\t// SDK-specific types (OpenAI-compatible chat format)\n\ttype ChatMessage,\n\ttype ChatResult,\n\ttype ChatStreamChunk,\n\ttype ContentPart,\n\tchat,\n\tchatStream,\n\tcomplete,\n\ttype EmbedInput,\n\ttype EmbedResult,\n\tembed,\n\tstreamToString,\n\ttype Tool,\n\ttype ToolCall,\n} from './ai'\n\n// =============================================================================\n// Billing Functions\n// =============================================================================\n\nexport {\n\ttype BalanceResponse,\n\ttype CheckoutRequest,\n\ttype CheckoutResponse,\n\tcreateCheckout,\n\tcreatePortalSession,\n\tgetBillingBalance,\n\tgetBillingUsage,\n\tgetPlans,\n\tgetSubscription,\n\t// Generated API types\n\ttype Plan,\n\ttype PortalRequest,\n\ttype PortalResponse,\n\ttype Subscription,\n\ttype UsageResponse,\n} from './billing'\n\n// =============================================================================\n// Storage (ADR-100 — `storage.*` is the ONLY public surface)\n// =============================================================================\n\nexport {\n\ttype CopyFileOptions,\n\ttype File as StorageFile,\n\ttype FileId,\n\ttype FileVersion,\n\ttype FileVersionId,\n\ttype FileVisibility,\n\ttype ListFilesOptions,\n\ttype SignedUrlDisposition,\n\ttype SignedUrlOptions,\n\tstorage,\n\ttype UploadCreateOptions,\n\ttype UploadId,\n\ttype UploadProgressEvent,\n} from './storage'\n\n// =============================================================================\n// Notifications Functions\n// =============================================================================\n\n// Service Worker utilities for push notifications (OneSignal/FCM pattern)\nexport {\n\tcreateServiceWorkerScript,\n\tinitPushServiceWorker,\n\ttype PushNotificationPayload,\n\ttype PushServiceWorkerConfig,\n\tregisterPushServiceWorker,\n} from './lib/notifications/service-worker'\nexport {\n\tcampaigns as pushCampaigns,\n\tgetPushPreferences,\n\ttype PushCampaign,\n\ttype PushCampaignStats,\n\ttype PushCampaignVariant,\n\ttype PushNotification,\n\ttype PushSegment,\n\ttype PushSegmentFilter,\n\ttype PushSubscription,\n\tregisterPush,\n\tsegments as pushSegments,\n\tsendPush,\n\tunregisterPush,\n\tupdatePushPreferences,\n} from './notifications'\n\n// =============================================================================\n// Tasks Functions\n// =============================================================================\n\nexport {\n\ttype CronInput,\n\ttype CronSchedule,\n\tcancelTask,\n\tcreateCron,\n\tdeleteCron,\n\tgetTask,\n\tlistTasks,\n\tpauseCron,\n\tresumeCron,\n\tscheduleTask,\n\ttype TaskInput,\n\ttype TaskResult,\n\ttype TaskStatus,\n} from './tasks'\n\n// TaskTriggerConfig exported via './tasks' → lib/tasks → task.ts\n\n// =============================================================================\n// Native Tasks Engine — Handler API\n// =============================================================================\n\nexport {\n\tcreateStepContext,\n\tcreateTasksHandler,\n\tStepCompleteSignal,\n\tStepSleepSignal,\n\tverifySignature as verifyTaskSignature,\n} from './lib/tasks/handler'\n\nexport type {\n\tNativeStepContext,\n\tNativeTaskDefinition,\n\tTaskRunStatus as NativeTaskRunStatus,\n} from './lib/tasks/types'\n\n// =============================================================================\n// Feature Flags Functions\n// =============================================================================\n\nexport {\n\tcheckFlag,\n\ttype FlagContext,\n\ttype FlagResult,\n\tgetAllFlags,\n\tgetFlagPayload,\n\tgetFlags,\n\tgetVariant,\n\tisEnabled,\n} from './flags'\n\n// =============================================================================\n// Webhooks Functions\n// =============================================================================\n\nexport {\n\tgetWebhookConfig,\n\tgetWebhookDeliveries,\n\tgetWebhookDelivery,\n\tgetWebhookStats,\n\treplayWebhookDelivery,\n\tupdateWebhookConfig,\n\ttype WebhookConfig,\n\ttype WebhookConfigUpdate,\n\ttype WebhookDeliveriesResult,\n\ttype WebhookDelivery,\n\ttype WebhookStats,\n} from './webhooks'\n\n// =============================================================================\n// Email Functions\n// =============================================================================\n\nexport {\n\tcancelScheduledEmail,\n\tgetScheduledEmail,\n\tgetScheduledEmailStats,\n\tisEmailConfigured,\n\ttype ListScheduledEmailsOptions,\n\tlistScheduledEmails,\n\trescheduleEmail,\n\ttype ScheduledEmail,\n\ttype ScheduledEmailStats,\n\ttype ScheduledEmailsResult,\n\ttype ScheduleEmailOptions,\n\ttype SendEmailOptions,\n\ttype SendResult,\n\ttype SendTemplatedEmailOptions,\n\ttype SendToUserOptions,\n\tscheduleEmail,\n\tsendEmail,\n\tsendEmailToUser,\n\tsendTemplatedEmail,\n} from './email'\n\n// =============================================================================\n// Consent Functions (GDPR/CCPA)\n// =============================================================================\n\nexport {\n\tacceptAllConsents,\n\ttype ConsentCategory,\n\ttype ConsentHistoryEntry,\n\ttype ConsentHistoryResult,\n\ttype ConsentPurposeDefaults,\n\t// Types\n\ttype ConsentType,\n\tdeclineOptionalConsents,\n\ttype GetConsentHistoryInput,\n\ttype GetConsentsInput,\n\tgetConsentHistory,\n\t// Runtime functions\n\tgetConsentTypes,\n\tgetUserConsents,\n\thasConsent,\n\ttype LinkAnonymousConsentsInput,\n\tlinkAnonymousConsents,\n\ttype SetConsentsInput,\n\tsetConsents,\n\ttype UserConsent,\n} from './consent'\n\n// =============================================================================\n// Referrals Functions\n// =============================================================================\n\nexport {\n\tgetMyReferralCode,\n\tgetReferralLeaderboard,\n\tgetReferralStats,\n\ttype LeaderboardEntry,\n\ttype LeaderboardOptions,\n\ttype LeaderboardResult,\n\ttype RedeemReferralInput,\n\ttype RedeemResult,\n\ttype ReferralCode,\n\ttype ReferralStats,\n\tredeemReferralCode,\n\tregenerateReferralCode,\n} from './referrals'\n\n// =============================================================================\n// Engagement Functions (Streaks, Leaderboards, Achievements)\n// =============================================================================\n\nexport {\n\t// Constants\n\tACHIEVEMENT_TIER_CONFIG,\n\ttype AchievementCategory,\n\ttype AchievementCriteria,\n\ttype AchievementCriterion,\n\ttype AchievementDefinition,\n\ttype AchievementTier,\n\ttype AchievementType,\n\ttype AchievementUnlockEvent,\n\ttype CriteriaOperator,\n\tgetAchievement,\n\tgetAchievementPoints,\n\t// Achievement functions\n\tgetAchievements,\n\tgetAllStreaks,\n\t// Leaderboard functions\n\tgetLeaderboard,\n\t// Streak functions\n\tgetStreak,\n\tgetUserLeaderboardRank,\n\tincrementAchievementProgress,\n\ttype LeaderboardAggregation,\n\ttype LeaderboardDefinition,\n\ttype LeaderboardEntry as EngagementLeaderboardEntry,\n\ttype LeaderboardQueryOptions,\n\ttype LeaderboardResetPeriod,\n\ttype LeaderboardResult as EngagementLeaderboardResult,\n\ttype LeaderboardSortDirection,\n\ttype RecordActivityInput,\n\ttype RecordActivityResult,\n\trecordStreakActivity,\n\trecoverStreak,\n\t// Types\n\ttype StreakDefinition,\n\ttype StreakFrequency,\n\ttype StreakState,\n\ttype SubmitScoreInput,\n\ttype SubmitScoreResult,\n\tsubmitScore,\n\ttype UserAchievement,\n\tunlockAchievement,\n} from './engagement'\n\n// =============================================================================\n// Organization Functions\n// =============================================================================\n\nexport {\n\tacceptOrganizationInvitation,\n\ttype CreateOrgInput,\n\tcanDeleteOrganization,\n\tcanManageMembers,\n\tcanManageSettings,\n\tcreateOrganization,\n\tdeleteOrganization,\n\tgetOrganization,\n\t// Invitations\n\tgetOrganizationInvitations,\n\t// Members\n\tgetOrganizationMembers,\n\t// CRUD\n\tgetOrganizations,\n\t// Permission helpers\n\thasRole,\n\ttype InviteMemberInput,\n\tinviteOrganizationMember,\n\tleaveOrganization,\n\tlistOrganizations,\n\t// Types\n\ttype Organization,\n\ttype OrganizationInvitation,\n\ttype OrganizationMember,\n\ttype OrganizationMembership,\n\ttype OrganizationsListResult,\n\ttype OrgRole,\n\tremoveOrganizationMember,\n\trevokeOrganizationInvitation,\n\ttype UpdateOrgInput,\n\ttype UserOrganization,\n\tupdateOrganization,\n\tupdateOrganizationMemberRole,\n} from './orgs'\n\n// =============================================================================\n// Permission Functions (RBAC)\n// =============================================================================\n\nexport {\n\ttype CreatePermissionInput,\n\tcreatePermission,\n\tdeletePermission,\n\tgetMemberPermissions,\n\thasAllPermissions,\n\thasAnyPermission,\n\thasPermission,\n\tlistPermissions,\n\ttype MemberPermissionsResult,\n\ttype Permission,\n} from './permissions'\n\n// =============================================================================\n// Role Functions (RBAC)\n// =============================================================================\n\nexport {\n\tassignMemberRole,\n\ttype CreateRoleInput,\n\tcreateRole,\n\tdeleteRole,\n\tgetRole,\n\tlistRoles,\n\ttype Role,\n\ttype UpdateRoleInput,\n\tupdateRole,\n} from './roles'\n\n// =============================================================================\n// Secrets Functions\n// =============================================================================\n\nexport {\n\ttype GetSecretInput,\n\ttype GetSecretResult,\n\ttype GetSecretsInput,\n\ttype GetSecretsResult,\n\tgetAllSecrets,\n\tgetSecret,\n\tgetSecrets,\n\thasSecret,\n\ttype ListSecretKeysInput,\n\tlistSecretKeys,\n\ttype SecretKeyInfo,\n} from './secrets'\n\n// =============================================================================\n// Search Functions\n// =============================================================================\n\nexport {\n\ttype BatchIndexInput,\n\ttype BatchIndexResult,\n\tbatchIndex,\n\ttype DeleteDocumentInput,\n\tdeleteDocument,\n\ttype FacetsResponse,\n\ttype GetFacetsInput,\n\tgetFacets,\n\tgetSearchStats,\n\ttype IndexDocumentInput,\n\ttype IndexDocumentResult,\n\tindexDocument,\n\ttype SearchInput,\n\ttype SearchResponse,\n\ttype SearchResultItem,\n\ttype SearchStatsResult,\n\ttype SearchType,\n\tsearch,\n\ttype TrackClickInput,\n\ttrackClick,\n\ttype UpsertDocumentInput,\n\ttype UpsertDocumentResult,\n\tupsertDocument,\n} from './search'\n\n// =============================================================================\n// Database Functions (Platform-provisioned PostgreSQL)\n// =============================================================================\n\nexport {\n\ttype DatabaseConnectionInfo,\n\ttype DatabaseStatus,\n\ttype DatabaseStatusInfo,\n\tgetDatabaseConnectionString,\n\tgetDatabaseStatus,\n} from './database'\n\n// =============================================================================\n// KV Functions (managed key-value store)\n// =============================================================================\n\nexport {\n\ttype KvExpireRequest,\n\ttype KvHgetallRequest,\n\ttype KvHgetRequest,\n\ttype KvHsetRequest,\n\ttype KvIncrRequest,\n\ttype KvLpushRequest,\n\ttype KvLrangeRequest,\n\ttype KvMgetRequest,\n\ttype KvMsetRequest,\n\ttype KvRateLimitRequest,\n\ttype KvRateLimitResult,\n\ttype KvScanOptions,\n\ttype KvScanResult,\n\ttype KvSetOptions,\n\ttype KvSetRequest,\n\ttype KvZaddRequest,\n\ttype KvZMember,\n\ttype KvZrangeRequest,\n\tkvDelete,\n\tkvExists,\n\tkvExpire,\n\tkvGet,\n\tkvGetJSON,\n\tkvHget,\n\tkvHgetall,\n\tkvHset,\n\tkvIncr,\n\tkvLpush,\n\tkvLrange,\n\tkvMget,\n\tkvMset,\n\tkvRateLimit,\n\tkvScan,\n\tkvSet,\n\tkvSetJSON,\n\tkvZadd,\n\tkvZrange,\n} from './kv'\n\n// =============================================================================\n// Realtime Functions (managed pub/sub)\n// =============================================================================\n\nexport {\n\tgetRealtimeHistory,\n\ttype RealtimeEmitRequest,\n\ttype RealtimeEmitResponse,\n\ttype RealtimeHistoryRequest,\n\ttype RealtimeHistoryResponse,\n\trealtimeEmit,\n\ttype StreamMessage,\n} from './realtime'\n\n// =============================================================================\n// Deploy Functions (CI/CD)\n// =============================================================================\n\nexport {\n\ttype BuildLog,\n\ttype BuildLogHistoryResponse,\n\ttype DeployHistoryResponse,\n\ttype DeployInfo,\n\ttype DeployStatus,\n\tdeleteEnvVar,\n\ttype EnvVar,\n\tgetBuildLogHistory,\n\tgetDeployHistory,\n\tgetDeployStatus,\n\tlistEnvVars,\n\ttype RollbackDeployRequest,\n\trollbackDeploy,\n\ttype SetEnvVarRequest,\n\tsetEnvVar,\n\ttype TriggerDeployRequest,\n\ttriggerDeploy,\n} from './deploy'\n\n// =============================================================================\n// Monitoring Functions (Error Tracking)\n// =============================================================================\n\nexport {\n\ttype Breadcrumb,\n\ttype CaptureExceptionRequest,\n\ttype CaptureMessageRequest,\n\tcaptureException,\n\tcaptureExceptionRaw,\n\tcaptureMessage,\n\ttype ExceptionFrame,\n\ttype ExceptionValue,\n\ttype MonitoringResponse,\n\ttype MonitoringSeverity,\n} from './monitoring'\n\n// =============================================================================\n// Sandbox Functions (Ephemeral Compute)\n// =============================================================================\n\nexport {\n\ttype CommandResult,\n\ttype ExecEvent,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype FileEvent,\n\ttype ProcessEvent,\n\ttype ProcessInfo,\n\ttype ProcessStartOptions,\n\ttype ProcessSummary,\n\tSandboxClient,\n\ttype SandboxFile,\n\tSandboxFiles,\n\ttype SandboxMachineSize,\n\ttype SandboxOptions,\n\tSandboxProcesses,\n\ttype SandboxRecord,\n\tSandboxWatch,\n\ttype WatchEntry,\n\ttype WatchOptions,\n} from './sandbox'\n\n// =============================================================================\n// Workers (Batch Run-to-Completion Compute)\n// =============================================================================\n\nexport type { RunWorkerOptions } from './workers'\n\n// =============================================================================\n// Triggers (ADR-040 — Unified scheduling + event dispatch)\n// =============================================================================\n\nexport {\n\ttype CreateTriggerOptions,\n\ttype CronSource,\n\ttype EventSource,\n\ttype HttpTarget,\n\ttype ListTriggersResult,\n\ttype PublishEventResult,\n\ttype RunTarget,\n\ttype TaskTarget,\n\ttype Trigger,\n\ttype TriggerRunMachineSize,\n\ttype TriggerSource,\n\ttype TriggerSourceType,\n\ttype TriggerStatus,\n\tTriggersClient,\n\ttype TriggerTarget,\n\ttype TriggerTargetType,\n\ttype UpdateTriggerOptions,\n} from './lib/triggers'\n\n// =============================================================================\n// Runs (ADR-040 — formerly Workers)\n// =============================================================================\n\nexport {\n\ttype CreateRunOptions,\n\ttype ListRunsOptions,\n\ttype ListRunsResult,\n\ttype Run,\n\t// Primary exports (ADR-040)\n\tRunHandle,\n\ttype RunLogsResult,\n\ttype RunMachineSize,\n\ttype RunResult,\n\ttype RunStatus,\n\tRunsClient,\n\ttype RunVolumeMount,\n\t// Backward-compat aliases\n\tWorkerHandle,\n\ttype WorkerLogsResult,\n\ttype WorkerResult,\n\ttype WorkerRun,\n\ttype WorkerStatus,\n\tWorkersClient,\n\ttype WorkerVolumeMount,\n} from './workers'\n\n// =============================================================================\n// Common Types (SDK-specific helpers — SSOT: ./types)\n// =============================================================================\n\nexport type {\n\tAccessTokenPayload,\n\tErrorResponse,\n\tPaginatedResponse,\n\tPaginationInput,\n\tSuccessResponse,\n\tUser,\n} from './types'\n\n// =============================================================================\n// Application Logs (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype IngestLogsResult,\n\tingestLogs,\n\ttype LogEntry,\n\ttype LogLevel,\n\ttype QueryLogsOptions,\n\ttype QueryLogsResult,\n\tqueryLogs,\n\ttype StoredLogEntry,\n} from './logs'\n\n// =============================================================================\n// Misc — project metadata + step-up challenge (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype ChallengeMethod,\n\ttype ChallengeType,\n\ttype ChallengeVerifyInput,\n\ttype ChallengeVerifyResult,\n\tgetProjectMetadata,\n\ttype ProjectMetadata,\n\tverifyChallenge,\n} from './misc'\n\n// =============================================================================\n// User (customer-app profile + sessions, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype DeleteAccountResult,\n\tdeleteUserAccount,\n\texportUserData,\n\tgetUserProfile,\n\tgetUserSecurity,\n\tlistUserSessions,\n\trenameUserSession,\n\trevokeUserSession,\n\ttype UserDataExport,\n\ttype UserFullProfile,\n\ttype UserSecuritySettings,\n\ttype UserSession,\n\ttype UserSessionsList,\n\ttype UserUpdateProfileInput,\n\tupdateUserProfile,\n} from './user'\n\n// =============================================================================\n// Security — passwords, 2FA, passkeys, alerts (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype BackupCodesResult,\n\tconfirmEmailChange,\n\tdeletePasskey,\n\tdisableTwoFactor,\n\tdisconnectOAuthProvider,\n\ttype EmailChangeInput,\n\ttype EmailConfirmInput,\n\tgetBackupCodes,\n\tgetSecurityScore,\n\tlistPasskeys,\n\tlistSecurityAlerts,\n\tmarkAllSecurityAlertsRead,\n\tmarkSecurityAlertRead,\n\ttype PasskeyRegistrationInput,\n\ttype PasskeyRegistrationOptions,\n\ttype PasskeySummary,\n\ttype PasskeysList,\n\ttype PasswordSetInput,\n\tregenerateBackupCodes,\n\trenamePasskey,\n\trequestEmailChange,\n\ttype SecurityAlert,\n\ttype SecurityAlertsList,\n\ttype SecurityScoreResult,\n\tsetPassword,\n\tsetupTwoFactor,\n\tstartPasskeyRegistration,\n\ttype TwoFactorEnableResult,\n\ttype TwoFactorSetupResult,\n\tverifyPasskeyRegistration,\n\tverifyTwoFactorEnable,\n} from './security'\n\n// =============================================================================\n// Customer-app OAuth flow (social login, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\tauthorizeOAuth,\n\texchangeOAuthCode,\n\tgeneratePkce,\n\tgetOidcDiscoveryDocument,\n\tlistOAuthProviders,\n\ttype OAuthAuthorizeInput,\n\ttype OAuthAuthorizeResult,\n\ttype OAuthCodeExchangeInput,\n\ttype OAuthProvider,\n\ttype OAuthProvidersResult,\n\ttype OidcDiscoveryDocument,\n\ttype OidcUserInfoResponse,\n\ttype PkceMethod,\n\tparseOAuthCallback,\n\tuserInfo,\n} from './oauth'\n\n// =============================================================================\n// Promo codes (validate / redeem + admin CRUD, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype CreatePromoInput,\n\tcreatePromo,\n\tdeletePromo,\n\tgetPromo,\n\ttype ListPromosOptions,\n\ttype ListPromosResult,\n\ttype ListRedemptionsOptions,\n\ttype ListRedemptionsResult,\n\tlistPromoRedemptions,\n\tlistPromos,\n\ttype PromoCode,\n\ttype PromoRedemption,\n\ttype PromoStatus,\n\ttype PromoType,\n\ttype PromoValidationPreview,\n\ttype RedeemPromoInput,\n\ttype RedeemPromoResult,\n\tredeemPromo,\n\ttype UpdatePromoInput,\n\tupdatePromo,\n\ttype ValidatePromoInput,\n\ttype ValidatePromoResult,\n\tvalidatePromo,\n} from './promo'\n\n// =============================================================================\n// React-layer types (tRPC-like wrappers — SSOT: ./react/types)\n// =============================================================================\n// Re-exported from the non-React entry for backcompat; these ride along\n// at type-only cost (sideEffects: false) and don't tug in react runtime.\n\nexport type {\n\tAIListModelsOptions,\n\tAIListModelsResponse,\n\tAIMessage,\n\tAIMessageRole,\n\tAIModelInfo,\n\tAIProvider,\n\tAIRateLimitInfo,\n\tAIRequestType,\n\tAIStreamChunk,\n\tAITool,\n\tAIToolCall,\n\tAIUsageStats,\n\tChatCompletionInput,\n\tChatCompletionResponse,\n\tEmbeddingInput,\n\tEmbeddingResponse,\n\tLoginHistoryEntry,\n\tSecuritySettings,\n\tTextCompletionInput,\n\tTextCompletionResponse,\n\tUserProfile,\n\tVisionInput,\n} from './react/types'\n","/**\n * Auth Functions\n *\n * Pure functions for authentication - no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /api/sdk/auth/* for all operations.\n *\n * Types are re-exported from `@sylphx/contract` (ADR-084). The contract is\n * the single source of truth for every wire shape — this module only adds\n * SDK-specific ergonomics (User brand swap, introspection result, invite\n * envelopes, org-token claims).\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tAuthTokensResponse,\n\tDeviceApproveRequest as ContractDeviceApproveRequest,\n\tDeviceApproveResponse as ContractDeviceApproveResponse,\n\tDeviceDenyRequest as ContractDeviceDenyRequest,\n\tDeviceDenyResponse as ContractDeviceDenyResponse,\n\tDeviceInitRequest as ContractDeviceInitRequest,\n\tDeviceInitResponse as ContractDeviceInitResponse,\n\tDevicePollResponse as ContractDevicePollResponse,\n\tLoginRequest as ContractLoginRequest,\n\tLoginResponse as ContractLoginResponse,\n\tRegisterRequest as ContractRegisterRequest,\n\tRegisterResponse as ContractRegisterResponse,\n\tResendEmailVerificationRequest as ContractResendEmailVerificationRequest,\n\tResendEmailVerificationResponse as ContractResendEmailVerificationResponse,\n\tTwoFactorVerifyRequest as ContractTwoFactorVerifyRequest,\n\tUserFullProfile,\n} from '@sylphx/contract'\nimport { authEndpoints } from '@sylphx/contract'\nimport { buildApiUrl, callApi, type SylphxConfig } from './config'\nimport type { User } from './types'\n\nexport { dpop } from './dpop'\nexport type {\n\tOAuthClientCredentialsResult,\n\tOAuthPollError,\n\tOAuthPollResult,\n\tOAuthTokenResult,\n} from './oauth-token'\nexport type {\n\tOrgAuthPolicy,\n\tOrgAuthPolicyUpdate,\n\tPasskeySignupChallengeInput,\n\tPasskeySignupChallengeResult,\n\tPasskeySignupResult,\n\tPasskeySignupVerifyInput,\n} from './passkey'\nexport {\n\torgPolicies,\n\tPasskeyPolicyViolationError,\n\tpasskey,\n} from './passkey'\nexport type {\n\tPlatformLogoutInput,\n\tPlatformRefreshInput,\n\tPlatformRefreshResult,\n} from './platform-auth'\nexport { platformAuth } from './platform-auth'\nexport type {\n\tImpersonationActive,\n\tImpersonationChallenge,\n\tImpersonationConsentDecision,\n\tImpersonationConsentResponse,\n\tImpersonationEndResult,\n\tImpersonationInfo,\n\tImpersonationRequestRow,\n\tImpersonationStartChallengeInput,\n\tImpersonationStartResult,\n\tImpersonationStartStepupInput,\n\tImpersonationStartStepupResult,\n} from './platform-impersonation'\nexport { impersonation } from './platform-impersonation'\nexport type {\n\tPlatformAccessTokenClaims,\n\tPlatformUserRecord,\n\tPlatformUserResolution,\n} from './platform-jwt'\nexport {\n\tcookies,\n\tresetPlatformCookieCache,\n\tresetPlatformJwksCache,\n\tverifyAccessToken,\n} from './platform-jwt'\nexport type {\n\tMintAccessTokenClaims,\n\tMintAccessTokenResult,\n\tOAuthIntrospectInput,\n\tOAuthIntrospectResult,\n\tOAuthRevokeInput,\n} from './platform-oauth'\nexport { oauth } from './platform-oauth'\nexport type {\n\tPlatformPasswordChangeInput,\n\tPlatformPasswordChangeResult,\n\tPlatformPasswordSetInput,\n\tPlatformPasswordSetResult,\n\tPlatformPasswordStatusResult,\n} from './platform-password'\nexport { password } from './platform-password'\nexport type {\n\tPlatformSessionRenameInput,\n\tPlatformSessionRenameResult,\n\tPlatformSessionRevokeAllResult,\n\tPlatformSessionRevokeInput,\n\tPlatformSessionRevokeOtherResult,\n\tPlatformSessionRevokeResult,\n\tPlatformSessionsListResult,\n} from './platform-sessions'\nexport { sessions } from './platform-sessions'\nexport type {\n\tDataExportJob,\n\tPlatformUserDeleteInput,\n\tPlatformUserDeleteResult,\n\tPlatformUserExportResult,\n} from './platform-user'\nexport { user } from './platform-user'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type LoginRequest = ContractLoginRequest\nexport type LoginResponse = ContractLoginResponse\nexport type RegisterRequest = ContractRegisterRequest\nexport type RegisterResponse = ContractRegisterResponse\nexport type ResendEmailVerificationRequest = ContractResendEmailVerificationRequest\nexport type ResendEmailVerificationResponse = ContractResendEmailVerificationResponse\n/**\n * Token response — contract's `AuthTokensResponse.user` (optional `AuthUser`)\n * is re-mapped to the SDK's broader `User` type so legacy callers keep the\n * familiar brand. `AuthUser` and `User` are structurally identical, but\n * the SDK surface has wider reach (cookies, middleware, React hooks) and\n * renaming is out of scope for ADR-084 cleanup.\n */\nexport type TokenResponse = Omit<AuthTokensResponse, 'user'> & {\n\tuser: User\n}\nexport type TwoFactorVerifyRequest = ContractTwoFactorVerifyRequest\n/**\n * `GET /auth/me` — contract's `UserFullProfile` already includes the\n * optional `emailVerified` flag the backend returns, so the SDK can just\n * alias the contract type directly.\n */\nexport type MeResponse = UserFullProfile\n\n// SDK-specific types (not directly from API schema)\n/**\n * Token introspection result (RFC 7662)\n */\nexport interface TokenIntrospectionResult {\n\t/** Whether the token is active/valid */\n\tactive: boolean\n\t/** Token type (access_token or refresh_token) */\n\ttoken_type?: 'access_token' | 'refresh_token'\n\t/** User ID */\n\tsub?: string\n\t/** User email */\n\temail?: string\n\t/** User name */\n\tname?: string\n\t/** App ID */\n\tclient_id?: string\n\t/** Audience */\n\taud?: string\n\t/** Issuer */\n\tiss?: string\n\t/** Expiration time (Unix timestamp) */\n\texp?: number\n\t/** Issued at time (Unix timestamp) */\n\tiat?: number\n\t/** User role */\n\trole?: string\n\t/** Email verification status */\n\temail_verified?: boolean\n}\n\n/**\n * Token revocation options\n */\nexport interface RevokeTokenOptions {\n\t/** Revoke all tokens for a user in this app */\n\trevokeAll?: boolean\n\t/** User ID (required when revoking all) */\n\tuserId?: string\n}\n\n// SDK-specific types (not in generated API)\nexport interface SessionResult {\n\tuser: {\n\t\tid: string\n\t\temail: string\n\t\tname: string | null\n\t\timage: string | null\n\t\temailVerified: boolean\n\t} | null\n}\n\n/**\n * Extended registration input with metadata and invitation token support.\n * Use extendedSignUp() when you need to pass metadata or an invitation token.\n */\nexport interface RegisterInput {\n\temail: string\n\tpassword: string\n\tname?: string\n\tmetadata?: Record<string, unknown>\n\tinvitationToken?: string\n}\n\n/**\n * Org context claims present in org-scoped tokens (after switch-org).\n *\n * The JWT carries the role key only. Permissions are resolved server-side\n * via cached role→permissions lookup (WorkOS pattern). This keeps\n * tokens small and ensures permission changes take effect without token refresh.\n */\nexport interface OrgTokenPayload {\n\torg_id: string\n\torg_slug: string\n\t/** RBAC role key (e.g. \"hr_manager\", \"admin\"). Permissions resolved server-side. */\n\torg_role: string\n}\n\nexport interface OrgScopedTokenResponse {\n\t/** Org-scoped access token. */\n\ttoken: string\n\t/** Org-scoped access token, matching the SDK's token naming convention. */\n\taccessToken: string\n\t/** Token lifetime in seconds, when provided by the runtime. */\n\texpiresIn?: number\n\t/** Bearer token type, when provided by the runtime. */\n\ttokenType?: string\n\t/** User envelope returned by the runtime for session hydration. */\n\tuser?: User\n}\n\ninterface OrgScopedTokenWireResponse {\n\taccessToken?: string\n\taccess_token?: string\n\texpiresIn?: number\n\texpires_in?: number\n\ttokenType?: string\n\ttoken_type?: string\n\tuser?: User\n}\n\n/**\n * Invite a user request payload.\n */\nexport interface InviteUserRequest {\n\temail: string\n\tmetadata?: Record<string, unknown>\n\tredirectUrl?: string\n}\n\n/**\n * Response from inviteUser.\n */\nexport interface InviteUserResponse {\n\tinvitationToken: string\n\texpiresAt: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Sign in with email and password\n *\n * @example\n * ```typescript\n * const result = await signIn(config, { email: 'user@example.com', password: 'secret' })\n * if (result.requiresTwoFactor) {\n * // Handle 2FA flow\n * } else {\n * // Save tokens\n * const authenticatedConfig = withToken(config, result.accessToken!)\n * }\n * ```\n */\nexport async function signIn(config: SylphxConfig, input: LoginRequest): Promise<LoginResponse> {\n\t// Contract-derived path/method and wire body (SSOT per ADR-084).\n\tconst body = input satisfies ContractLoginRequest\n\tconst endpoint = authEndpoints.signIn\n\treturn callApi<LoginResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Sign up with email and password\n *\n * @example\n * ```typescript\n * const result = await signUp(config, {\n * email: 'user@example.com',\n * password: 'secret',\n * name: 'John Doe',\n * })\n * // User needs to verify email\n * ```\n */\nexport async function signUp(\n\tconfig: SylphxConfig,\n\tinput: RegisterRequest,\n): Promise<RegisterResponse> {\n\t// Contract-derived path/method and richer registration body (SSOT per ADR-084).\n\tconst endpoint = authEndpoints.signUp\n\treturn callApi<RegisterResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: input,\n\t})\n}\n\n/**\n * Sign out (revoke tokens)\n *\n * @example\n * ```typescript\n * await signOut(config)\n * ```\n */\nexport async function signOut(config: SylphxConfig): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = authEndpoints.signOut\n\tawait callApi<void>(config, endpoint.path, { method: endpoint.method })\n}\n\n/**\n * Refresh access token\n *\n * @example\n * ```typescript\n * const tokens = await refreshToken(config, refreshTokenString)\n * const newConfig = withToken(config, tokens.accessToken)\n * ```\n */\nexport async function refreshToken(config: SylphxConfig, token: string): Promise<TokenResponse> {\n\treturn callApi<TokenResponse>(config, '/auth/token', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: token,\n\t\t\tclient_secret: config.secretKey,\n\t\t},\n\t})\n}\n\n/**\n * Verify email with token\n *\n * @example\n * ```typescript\n * await verifyEmail(config, token)\n * ```\n */\nexport async function verifyEmail(config: SylphxConfig, token: string): Promise<void> {\n\tawait callApi<void>(config, '/auth/verify-email', {\n\t\tmethod: 'POST',\n\t\tbody: { token },\n\t})\n}\n\n/**\n * Request password reset email\n *\n * @example\n * ```typescript\n * await forgotPassword(config, 'user@example.com', {\n * redirectUrl: 'https://app.example.com/reset-password'\n * })\n * ```\n */\nexport async function forgotPassword(\n\tconfig: SylphxConfig,\n\temail: string,\n\toptions: { redirectUrl?: string } = {},\n): Promise<void> {\n\tawait callApi<{ success: boolean }>(config, '/auth/forgot-password', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\temail,\n\t\t\t...(options.redirectUrl ? { redirectUrl: options.redirectUrl } : {}),\n\t\t},\n\t})\n}\n\n/**\n * Request a verification email resend.\n *\n * The Platform response is intentionally privacy-preserving: it never\n * indicates whether the email exists or is already verified.\n *\n * @example\n * ```typescript\n * await resendVerificationEmail(config, 'user@example.com')\n * ```\n */\nexport async function resendVerificationEmail(config: SylphxConfig, email: string): Promise<void> {\n\tconst endpoint = authEndpoints.resendEmailVerification\n\tconst body = { email } satisfies ResendEmailVerificationRequest\n\tawait callApi<ResendEmailVerificationResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Reset password with token\n *\n * @example\n * ```typescript\n * await resetPassword(config, { token, password: 'newpassword' })\n * ```\n */\nexport async function resetPassword(\n\tconfig: SylphxConfig,\n\tinput: { token: string; password: string },\n): Promise<void> {\n\tawait callApi<{ success: boolean }>(config, '/auth/reset-password', {\n\t\tmethod: 'POST',\n\t\tbody: { token: input.token, password: input.password },\n\t})\n}\n\n/**\n * Get current session (requires authenticated config)\n *\n * @example\n * ```typescript\n * const session = await getSession(authenticatedConfig)\n * if (session.user) {\n * console.log(`Logged in as ${session.user.email}`)\n * }\n * ```\n */\nexport async function getSession(config: SylphxConfig): Promise<SessionResult> {\n\tif (!config.accessToken) {\n\t\treturn { user: null }\n\t}\n\n\t// Contract-derived path/method (SSOT per ADR-084). The contract's\n\t// `SessionResult` uses branded `UserId` — the SDK currently surfaces\n\t// the raw string on `SessionResult['user'].id` because consumers pass it\n\t// to URL builders, cookies, etc. that don't understand the brand. The\n\t// structural shape is otherwise identical, so this remains a public SDK\n\t// compatibility boundary.\n\tconst endpoint = authEndpoints.getSession\n\ttry {\n\t\tconst user = await callApi<SessionResult['user']>(config, endpoint.path, {\n\t\t\tmethod: endpoint.method,\n\t\t})\n\t\treturn { user }\n\t} catch {\n\t\treturn { user: null }\n\t}\n}\n\n/**\n * Verify 2FA code (when signIn returns requiresTwoFactor: true)\n *\n * @example\n * ```typescript\n * const result = await signIn(config, credentials)\n * if (result.requiresTwoFactor) {\n * const tokens = await verifyTwoFactor(config, result.userId!, code)\n * }\n * ```\n */\nexport async function verifyTwoFactor(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tcode: string,\n): Promise<TokenResponse> {\n\treturn callApi<TokenResponse>(config, '/auth/verify-2fa', {\n\t\tmethod: 'POST',\n\t\tbody: { userId, code },\n\t})\n}\n\n/**\n * Introspect a token to check its validity (RFC 7662)\n *\n * Use this to verify token status without decoding. Essential for:\n * - Checking if a token has been revoked\n * - Validating tokens at the edge\n * - Security-critical operations\n *\n * @example\n * ```typescript\n * const result = await introspectToken(config, accessToken)\n * if (!result.active) {\n * // Token is invalid, revoked, or expired\n * await refreshTokens()\n * }\n * ```\n */\nexport async function introspectToken(\n\tconfig: SylphxConfig,\n\ttoken: string,\n\ttokenTypeHint?: 'access_token' | 'refresh_token',\n): Promise<TokenIntrospectionResult> {\n\tconst response = await fetch(buildApiUrl(config, '/auth/introspect'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t// RFC 7662 §2: server-to-server call — authenticate with secret key\n\t\t\t'x-app-secret': config.secretKey ?? '',\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\ttoken,\n\t\t\ttoken_type_hint: tokenTypeHint,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\t// Per RFC 7662, errors should return inactive\n\t\treturn { active: false }\n\t}\n\n\treturn response.json()\n}\n\n/**\n * Revoke a token (RFC 7009)\n *\n * Use cases:\n * - Sign out user from specific device\n * - Security response to compromised token\n * - User-initiated session termination\n *\n * @example\n * ```typescript\n * // Revoke single refresh token\n * await revokeToken(config, refreshToken)\n *\n * // Revoke all tokens for a user (logout everywhere)\n * await revokeToken(config, '', { revokeAll: true, userId: 'user-123' })\n * ```\n */\nexport async function revokeToken(\n\tconfig: SylphxConfig,\n\ttoken: string,\n\toptions?: RevokeTokenOptions,\n): Promise<void> {\n\tawait fetch(buildApiUrl(config, '/auth/revoke'), {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({\n\t\t\ttoken: options?.revokeAll ? undefined : token,\n\t\t\tclient_secret: config.secretKey,\n\t\t\tuser_id: options?.userId,\n\t\t\trevoke_all: options?.revokeAll,\n\t\t}),\n\t})\n\t// Per RFC 7009, always succeeds (200 OK)\n}\n\n/**\n * Revoke all tokens for a user (logout from all devices)\n *\n * Convenience wrapper around revokeToken with revokeAll option.\n *\n * @example\n * ```typescript\n * // After password change, revoke all sessions\n * await revokeAllTokens(config, userId)\n * ```\n */\nexport async function revokeAllTokens(config: SylphxConfig, userId: string): Promise<void> {\n\tawait revokeToken(config, '', { revokeAll: true, userId })\n}\n\n/**\n * Sign up with extended input (metadata + invitation token support).\n *\n * Use this instead of signUp() when you need to:\n * - Pass metadata on registration (e.g., org context, role, referral info)\n * - Register with an invitation token\n *\n * @example\n * ```typescript\n * const result = await extendedSignUp(config, {\n * email: 'user@example.com',\n * password: 'secret',\n * name: 'John Doe',\n * metadata: { orgId: 'org-123', role: 'employee' },\n * invitationToken: 'inv_...',\n * })\n * ```\n */\nexport async function extendedSignUp(\n\tconfig: SylphxConfig,\n\tinput: RegisterInput,\n): Promise<RegisterResponse> {\n\treturn callApi<RegisterResponse>(config, '/auth/register', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Invite a user to sign up for this project.\n * Server-side only (requires secretKey).\n * Sends an email invitation; user signs up via signUp() or extendedSignUp() with the invitation token.\n *\n * @example\n * ```typescript\n * const invite = await inviteUser(config, {\n * email: 'newemployee@company.com',\n * metadata: { role: 'employee', orgId: 'org-123' },\n * redirectUrl: 'https://app.example.com/signup',\n * })\n * console.log(invite.invitationToken, invite.expiresAt)\n * ```\n */\nexport async function inviteUser(\n\tconfig: SylphxConfig,\n\tinput: InviteUserRequest,\n): Promise<InviteUserResponse> {\n\treturn callApi<InviteUserResponse>(config, '/auth/invite', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nfunction normalizeOrgScopedTokenResponse(data: OrgScopedTokenWireResponse): OrgScopedTokenResponse {\n\tconst accessToken = data.accessToken ?? data.access_token\n\tif (!accessToken) {\n\t\tthrow new Error('Invalid org-scoped token response: missing access token')\n\t}\n\n\treturn {\n\t\ttoken: accessToken,\n\t\taccessToken,\n\t\texpiresIn: data.expiresIn ?? data.expires_in,\n\t\ttokenType: data.tokenType ?? data.token_type,\n\t\tuser: data.user,\n\t}\n}\n\n/**\n * Exchange current user token for an org-scoped token.\n * The returned access_token JWT includes org_id, org_slug, org_role claims.\n *\n * @example\n * const { token } = await getOrgScopedToken(withToken(config, currentToken), 'org_xxx')\n */\nexport async function getOrgScopedToken(\n\tconfig: SylphxConfig,\n\torgId: string,\n): Promise<OrgScopedTokenResponse> {\n\tconst data = await callApi<OrgScopedTokenWireResponse>(config, '/auth/switch-org', {\n\t\tmethod: 'POST',\n\t\tbody: { orgId },\n\t})\n\treturn normalizeOrgScopedTokenResponse(data)\n}\n\n/**\n * @deprecated Use getOrgScopedToken(config, orgId). Kept as the shorter\n * organization switch alias for existing SDK callers.\n */\nexport async function switchOrg(\n\tconfig: SylphxConfig,\n\torgId: string,\n): Promise<OrgScopedTokenResponse> {\n\treturn getOrgScopedToken(config, orgId)\n}\n\n// ============================================================================\n// OAuth 2.0 Device Authorization Grant (RFC 8628)\n// ============================================================================\n//\n// The device flow is the *bootstrap* leg for headless clients (CLI, TV apps,\n// IoT) that can't host a browser. It is deliberately separate from the\n// pk_/sk_-credentialled `SylphxConfig` above because the device flow runs\n// BEFORE the client has any credential — that's the whole point.\n//\n// Accordingly the device-flow methods accept a `baseUrl` argument directly\n// rather than a `SylphxConfig`. For the Sylphx platform itself this will be\n// `https://your-app.api.sylphx.com/v1` (configurable via SYLPHX_BAAS_URL for\n// dev/staging). Callers that already have a `SylphxConfig` for their own app\n// can pass `config.baseUrl` — both surfaces resolve to the same endpoints.\n//\n// See ADR-089 Phase 2a for the migration notes and the apps/runtime handler.\n\nexport type DeviceInitInput = ContractDeviceInitRequest\nexport type DeviceGrant = ContractDeviceInitResponse\nexport type DevicePollResult = ContractDevicePollResponse\nexport type DeviceApproveInput = ContractDeviceApproveRequest\nexport type DeviceApproveResult = ContractDeviceApproveResponse\nexport type DeviceDenyInput = ContractDeviceDenyRequest\nexport type DeviceDenyResult = ContractDeviceDenyResponse\n\n/**\n * `device` namespace — RFC 8628 device authorization grant.\n *\n * Used by headless clients (CLI, TV apps, IoT) to authorise via a\n * companion browser instead of reading credentials from env vars.\n */\nexport const device = {\n\t/**\n\t * Start a device authorization grant.\n\t *\n\t * Returns a `DeviceGrant` with `verification_uri_complete` (open this\n\t * in the user's browser) and `device_code` (use for polling).\n\t *\n\t * @example\n\t * ```typescript\n\t * const grant = await device.init({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * clientId: 'sylphx-cli',\n\t * scope: ['org:read', 'project:*'],\n\t * })\n\t * openBrowser(grant.verification_uri_complete)\n\t * ```\n\t */\n\tasync init(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly scope?: readonly string[]\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceGrant> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildDeviceHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tscope: opts.scope ?? [],\n\t\t\t}),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.init')\n\t\treturn (await res.json()) as DeviceGrant\n\t},\n\n\t/**\n\t * Poll a pending grant. Returns `status: 'pending' | 'approved' |\n\t * 'denied' | 'expired'`. On `approved`, the result carries the OAuth\n\t * pair (access_token + refresh_token).\n\t *\n\t * Callers MUST respect the `interval` returned by `init()` — polling\n\t * faster than that may return 429 slow_down (RFC 8628 §5.5).\n\t */\n\tasync poll(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly deviceCode: string\n\t\treadonly userAgent?: string\n\t}): Promise<DevicePollResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/poll`)\n\t\turl.searchParams.set('device_code', opts.deviceCode)\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildDeviceHeaders(opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.poll')\n\t\treturn (await res.json()) as DevicePollResult\n\t},\n\n\t/**\n\t * Browser leg — the approving user confirms the grant.\n\t *\n\t * Requires a valid platform-issued access token (`Authorization:\n\t * Bearer <accessToken>`) proving the user is logged in on the\n\t * Console. Typically called by the Console's `/device` verification\n\t * page server-side, forwarding the user's session JWT.\n\t */\n\tasync approve(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly userCode: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceApproveResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/approve`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t...buildDeviceHeaders(opts.userAgent),\n\t\t\t\tAuthorization: `Bearer ${opts.accessToken}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ user_code: opts.userCode }),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.approve')\n\t\treturn (await res.json()) as DeviceApproveResult\n\t},\n\n\t/**\n\t * Browser leg — the user declines the grant.\n\t *\n\t * Requires a valid platform-issued access token just like `approve`.\n\t */\n\tasync deny(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly userCode: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceDenyResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/deny`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t...buildDeviceHeaders(opts.userAgent),\n\t\t\t\tAuthorization: `Bearer ${opts.accessToken}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ user_code: opts.userCode }),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.deny')\n\t\treturn (await res.json()) as DeviceDenyResult\n\t},\n} as const\n\nfunction buildDeviceHeaders(userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\n/**\n * Normalise device-flow HTTP errors into `SylphxError`s with meaningful\n * codes so CLI-side retry logic can discriminate between `transient`\n * (5xx / network) and `invalid_grant` (404 / 409) outcomes.\n */\nasync function deviceError(res: Response, operation: string): Promise<Error> {\n\t// Dynamic import avoids a hard dependency on the error module for the\n\t// happy path. The tree-shaker drops this when unused.\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'device_flow_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as { error?: string; error_description?: string }\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\treturn new SylphxError(message, {\n\t\tcode: res.status === 429 ? 'TOO_MANY_REQUESTS' : 'BAD_REQUEST',\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * DPoP — Demonstration of Proof-of-Possession (RFC 9449 / ADR-089 Phase 5.1e).\n *\n * Client-side helpers for sender-constrained access tokens. Built on\n * `crypto.subtle` with no runtime dependencies.\n */\n\nexport const dpop = {\n\t/**\n\t * Generate a fresh ES256 key pair. Private key is non-extractable\n\t * (`extractable: false`) so it can be stored but never serialised.\n\t */\n\tasync generateKeyPair(): Promise<{\n\t\treadonly privateKey: CryptoKey\n\t\treadonly publicKey: CryptoKey\n\t\treadonly thumbprint: string\n\t}> {\n\t\tconst { privateKey, publicKey } = (await crypto.subtle.generateKey(\n\t\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\t\tfalse,\n\t\t\t['sign', 'verify'],\n\t\t)) as CryptoKeyPair\n\t\tconst publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n\t\tconst thumbprint = await thumbprintFromJwk(sanitisePublicJwk(publicJwk))\n\t\treturn { privateKey, publicKey, thumbprint }\n\t},\n\n\t/**\n\t * Sign a DPoP proof JWT. When `accessToken` is provided, the proof\n\t * includes `ath = base64url(sha256(accessToken))` so the resource\n\t * server can bind the proof to the token being presented.\n\t */\n\tasync generateProof(opts: {\n\t\treadonly privateKey: CryptoKey\n\t\treadonly publicKey: CryptoKey\n\t\treadonly method: string\n\t\treadonly uri: string\n\t\treadonly accessToken?: string\n\t\treadonly nonce?: string\n\t}): Promise<string> {\n\t\tconst publicJwkRaw = await crypto.subtle.exportKey('jwk', opts.publicKey)\n\t\tconst publicJwk = sanitisePublicJwk(publicJwkRaw)\n\n\t\tconst header = { typ: 'dpop+jwt', alg: 'ES256', jwk: publicJwk }\n\t\tconst payload: Record<string, unknown> = {\n\t\t\tjti: randomJti(),\n\t\t\thtm: opts.method.toUpperCase(),\n\t\t\thtu: stripQueryAndFragment(opts.uri),\n\t\t\tiat: Math.floor(Date.now() / 1000),\n\t\t}\n\t\tif (opts.accessToken) {\n\t\t\tpayload.ath = await sha256Base64Url(new TextEncoder().encode(opts.accessToken))\n\t\t}\n\t\tif (opts.nonce) payload.nonce = opts.nonce\n\n\t\tconst headerB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)))\n\t\tconst payloadB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)))\n\t\tconst signingInput = `${headerB64}.${payloadB64}`\n\n\t\tconst signingBytes = new TextEncoder().encode(signingInput)\n\t\tconst signingBuf = new Uint8Array(signingBytes.byteLength)\n\t\tsigningBuf.set(signingBytes)\n\t\tconst sigBuf = await crypto.subtle.sign(\n\t\t\t{ name: 'ECDSA', hash: 'SHA-256' },\n\t\t\topts.privateKey,\n\t\t\tsigningBuf.buffer,\n\t\t)\n\t\tconst sigB64 = base64UrlEncode(new Uint8Array(sigBuf))\n\t\treturn `${signingInput}.${sigB64}`\n\t},\n} as const\n\nfunction sanitisePublicJwk(jwk: JsonWebKey): { kty: string; crv: string; x: string; y: string } {\n\tif (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.x || !jwk.y) {\n\t\tthrow new Error('DPoP expects ES256 (EC P-256) JWK')\n\t}\n\treturn { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }\n}\n\nasync function thumbprintFromJwk(jwk: {\n\tkty: string\n\tcrv: string\n\tx: string\n\ty: string\n}): Promise<string> {\n\tconst canonical = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y })\n\treturn sha256Base64Url(new TextEncoder().encode(canonical))\n}\n\nasync function sha256Base64Url(data: Uint8Array): Promise<string> {\n\tconst copy = new Uint8Array(data.byteLength)\n\tcopy.set(data)\n\tconst digest = await crypto.subtle.digest('SHA-256', copy.buffer)\n\treturn base64UrlEncode(new Uint8Array(digest))\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet s = ''\n\tfor (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]!)\n\treturn btoa(s).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction stripQueryAndFragment(uri: string): string {\n\ttry {\n\t\tconst u = new URL(uri)\n\t\treturn `${u.protocol}//${u.host}${u.pathname}`\n\t} catch {\n\t\tconst q = uri.indexOf('?')\n\t\tconst h = uri.indexOf('#')\n\t\tconst cut = [q, h].filter((i) => i >= 0).sort((a, b) => a - b)[0]\n\t\treturn cut === undefined ? uri : uri.slice(0, cut)\n\t}\n}\n\nfunction randomJti(): string {\n\tconst bytes = new Uint8Array(16)\n\tcrypto.getRandomValues(bytes)\n\treturn base64UrlEncode(bytes)\n}\n","/**\n * Platform refresh-token rotation and logout SDK namespace.\n */\n\nimport type { LogoutInput, RefreshTokenInput, RefreshTokenResult } from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformRefreshInput = RefreshTokenInput\nexport type PlatformRefreshResult = RefreshTokenResult\nexport type PlatformLogoutInput = LogoutInput\n\nexport const platformAuth = {\n\tasync refresh(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly refreshToken: string\n\t\treadonly userAgent?: string\n\t\t/**\n\t\t * Path prefix between `baseUrl` and the resource path. Defaults\n\t\t * to `/api/v1` for back-compat with the admin-override host\n\t\t * (`sylphx.com`). Pass `/v1` when targeting the canonical host\n\t\t * (`api.sylphx.com`) per Rule 17.\n\t\t */\n\t\treadonly urlPrefix?: string\n\t}): Promise<PlatformRefreshResult> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' }\n\t\tif (opts.userAgent) headers['User-Agent'] = opts.userAgent\n\t\tconst prefix = opts.urlPrefix ?? '/api/v1'\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}${prefix}/auth/refresh`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({ refresh_token: opts.refreshToken } satisfies PlatformRefreshInput),\n\t\t})\n\t\tif (!res.ok) throw await platformAuthError(res, 'platformAuth.refresh')\n\t\treturn (await res.json()) as PlatformRefreshResult\n\t},\n\n\tasync logout(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly refreshToken: string\n\t\treadonly userAgent?: string\n\t\t/** See `refresh.urlPrefix`. */\n\t\treadonly urlPrefix?: string\n\t}): Promise<void> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' }\n\t\tif (opts.userAgent) headers['User-Agent'] = opts.userAgent\n\t\tconst prefix = opts.urlPrefix ?? '/api/v1'\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}${prefix}/auth/logout`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({ refresh_token: opts.refreshToken } satisfies PlatformLogoutInput),\n\t\t})\n\t\tif (!res.ok) throw await platformAuthError(res, 'platformAuth.logout')\n\t},\n} as const\n\nasync function platformAuthError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_auth_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string | { code?: string; message?: string }\n\t\t\tmessage?: string\n\t\t}\n\t\tif (typeof parsed.error === 'string') code = parsed.error\n\t\telse if (parsed.error && typeof parsed.error === 'object') {\n\t\t\tif (parsed.error.code) code = parsed.error.code\n\t\t\tif (parsed.error.message) message = parsed.error.message\n\t\t} else if (parsed.message) {\n\t\t\tmessage = parsed.message\n\t\t}\n\t} catch {\n\t\tif (body) message = body\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform impersonation SDK namespace.\n *\n * Covers the ADR-089 Phase 3b legacy helpers and Phase 5.9 WebAuthn\n * step-up + target-consent workflow.\n */\n\nimport { SylphxError } from './errors'\n\nexport interface ImpersonationStartResult {\n\treadonly success: true\n\treadonly token: string\n\treadonly sessionId: string\n\treadonly expiresAt: string\n}\n\nexport interface ImpersonationEndResult {\n\treadonly success: boolean\n\treadonly sessionsEnded: number\n}\n\nexport interface ImpersonationInfo {\n\treadonly isImpersonation: true\n\treadonly adminUserId: string\n\treadonly adminEmail: string\n\treadonly adminName: string | null\n\treadonly impersonatedAt: string\n}\n\nexport interface ImpersonationActive {\n\treadonly sessionId: string\n\treadonly adminUserId: string\n\treadonly adminEmail: string\n\treadonly adminName: string | null\n\treadonly targetUserId: string\n\treadonly targetEmail: string\n\treadonly targetName: string | null\n\treadonly impersonatedAt: string\n\treadonly lastActiveAt: string\n}\n\nexport interface ImpersonationStartChallengeInput {\n\treadonly baseUrl: string\n\treadonly accessToken: string\n\treadonly targetUserId: string\n\treadonly reason: string\n\treadonly userAgent?: string\n}\n\nexport interface ImpersonationChallenge {\n\treadonly requestId: string\n\treadonly challengeKey: string\n\treadonly webauthnOptions: {\n\t\treadonly challenge: string\n\t\treadonly rpId?: string\n\t\treadonly allowCredentials: ReadonlyArray<{\n\t\t\treadonly id: string\n\t\t\treadonly type: 'public-key'\n\t\t\treadonly transports?: readonly string[]\n\t\t}>\n\t\treadonly userVerification: 'required'\n\t\treadonly timeout: number\n\t}\n}\n\nexport interface ImpersonationStartStepupInput {\n\treadonly baseUrl: string\n\treadonly accessToken: string\n\treadonly requestId: string\n\treadonly challengeKey: string\n\treadonly assertion: unknown\n\treadonly emergencyBypass?: boolean\n\treadonly userAgent?: string\n}\n\nexport type ImpersonationStartStepupResult =\n\t| {\n\t\t\treadonly branch: 'emergency'\n\t\t\treadonly requestId: string\n\t\t\treadonly token: string\n\t\t\treadonly sessionId: string\n\t\t\treadonly expiresAt: string\n\t }\n\t| {\n\t\t\treadonly branch: 'awaiting-consent'\n\t\t\treadonly requestId: string\n\t\t\treadonly consentDeadline: string\n\t }\n\nexport type ImpersonationConsentDecision = 'approve' | 'deny'\n\nexport type ImpersonationConsentResponse =\n\t| {\n\t\t\treadonly branch: 'approved'\n\t\t\treadonly requestId: string\n\t\t\treadonly token: string\n\t\t\treadonly sessionId: string\n\t\t\treadonly expiresAt: string\n\t }\n\t| {\n\t\t\treadonly branch: 'denied'\n\t\t\treadonly requestId: string\n\t }\n\nexport interface ImpersonationRequestRow {\n\treadonly id: string\n\treadonly operatorId: string\n\treadonly targetUserId: string\n\treadonly reason: string\n\treadonly status:\n\t\t| 'awaiting-stepup'\n\t\t| 'awaiting-consent'\n\t\t| 'active'\n\t\t| 'denied'\n\t\t| 'expired'\n\t\t| 'ended'\n\t\t| 'revoked'\n\treadonly emergencyBypass: boolean\n\treadonly sessionId: string | null\n\treadonly consentDeadline: string | null\n\treadonly startedAt: string | null\n\treadonly endedAt: string | null\n\treadonly createdAt: string\n}\n\nexport const impersonation = {\n\tasync start(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly targetUserId: string\n\t\treadonly ipAddress?: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationStartResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\ttargetUserId: opts.targetUserId,\n\t\t\t\t\t...(opts.ipAddress !== undefined && { ipAddress: opts.ipAddress }),\n\t\t\t\t\t...(opts.userAgent !== undefined && { userAgent: opts.userAgent }),\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.start')\n\t\treturn (await res.json()) as ImpersonationStartResult\n\t},\n\n\tasync end(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId?: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationEndResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/end`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}),\n\t\t})\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.end')\n\t\treturn (await res.json()) as ImpersonationEndResult\n\t},\n\n\tasync info(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationInfo | null> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/info/${encodeURIComponent(opts.sessionId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.info')\n\t\treturn (await res.json()) as ImpersonationInfo | null\n\t},\n\n\tasync active(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<readonly ImpersonationActive[]> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/active`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.active')\n\t\treturn (await res.json()) as readonly ImpersonationActive[]\n\t},\n\n\tasync startChallenge(opts: ImpersonationStartChallengeInput): Promise<ImpersonationChallenge> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start-challenge`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\ttargetUserId: opts.targetUserId,\n\t\t\t\t\treason: opts.reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.startChallenge')\n\t\treturn (await res.json()) as ImpersonationChallenge\n\t},\n\n\tasync startStepup(opts: ImpersonationStartStepupInput): Promise<ImpersonationStartStepupResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\trequestId: opts.requestId,\n\t\t\t\t\tchallengeKey: opts.challengeKey,\n\t\t\t\t\tassertion: opts.assertion,\n\t\t\t\t\t...(opts.emergencyBypass ? { emergencyBypass: true } : {}),\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.startStepup')\n\t\treturn (await res.json()) as ImpersonationStartStepupResult\n\t},\n\n\tasync respondConsent(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly requestId: string\n\t\treadonly decision: ImpersonationConsentDecision\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationConsentResponse> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/impersonation-consent/${encodeURIComponent(opts.requestId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({ decision: opts.decision }),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.respondConsent')\n\t\treturn (await res.json()) as ImpersonationConsentResponse\n\t},\n\n\tasync listRequests(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: {\n\t\t\treadonly operatorId?: string\n\t\t\treadonly targetUserId?: string\n\t\t\treadonly status?: ImpersonationRequestRow['status']\n\t\t\treadonly limit?: number\n\t\t}\n\t\treadonly userAgent?: string\n\t}): Promise<readonly ImpersonationRequestRow[]> {\n\t\tconst params = new URLSearchParams()\n\t\tif (opts.filter?.operatorId) params.set('operatorId', opts.filter.operatorId)\n\t\tif (opts.filter?.targetUserId) params.set('targetUserId', opts.filter.targetUserId)\n\t\tif (opts.filter?.status) params.set('status', opts.filter.status)\n\t\tif (opts.filter?.limit != null) params.set('limit', String(opts.filter.limit))\n\t\tconst qs = params.toString()\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/requests${qs ? `?${qs}` : ''}`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.listRequests')\n\t\treturn (await res.json()) as readonly ImpersonationRequestRow[]\n\t},\n\n\tasync endSession(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly requestId: string\n\t\treadonly userAgent?: string\n\t}): Promise<{ success: true; requestId: string; sessionId: string | null }> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/end/${encodeURIComponent(opts.requestId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.endSession')\n\t\treturn (await res.json()) as { success: true; requestId: string; sessionId: string | null }\n\t},\n} as const\n\nfunction buildImpersonationHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function impersonationError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_impersonation_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\t\t| 'CONFLICT'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 409) errorCode = 'CONFLICT'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform JWT verification and cookie-session resolution.\n *\n * This module owns the SDK's hot-path Platform auth helpers: cached JWKS\n * verification for bearer tokens and cached cookie-based user resolution.\n */\n\n// ---------------------------------------------------------------------------\n// JWKS cache — module scope\n// ---------------------------------------------------------------------------\n\ninterface JwksCache {\n\tkeys: unknown[]\n\texpiresAt: number\n}\n\nlet jwtJwksCache: JwksCache | null = null\n\n/**\n * Reset the platform-JWKS cache. Tests should call this between cases\n * to avoid state bleed. Production code relies on the TTL-based\n * expiry.\n */\nexport function resetPlatformJwksCache(): void {\n\tjwtJwksCache = null\n}\n\nconst PLATFORM_JWKS_TTL_MS = 60 * 60 * 1000 // 1 hour\n\nasync function fetchPlatformJwks(baseUrl: string): Promise<unknown[]> {\n\tconst now = Date.now()\n\tif (jwtJwksCache && jwtJwksCache.expiresAt > now) return jwtJwksCache.keys\n\n\tconst root = baseUrl.replace(/\\/$/, '').replace(/\\/v1$/, '')\n\tconst res = await fetch(`${root}/.well-known/jwks.json`)\n\tif (!res.ok) throw new Error(`JWKS fetch failed: HTTP ${res.status}`)\n\tconst data = (await res.json()) as { keys?: unknown[] }\n\tif (!Array.isArray(data.keys)) throw new Error('JWKS response missing keys[]')\n\n\tjwtJwksCache = { keys: data.keys, expiresAt: now + PLATFORM_JWKS_TTL_MS }\n\treturn data.keys\n}\n\nexport interface PlatformAccessTokenClaims {\n\treadonly sub: string\n\treadonly pid?: string\n\treadonly email: string\n\treadonly name?: string\n\treadonly picture?: string\n\treadonly email_verified: boolean\n\treadonly app_id: string\n\treadonly role: string\n\treadonly org_id?: string\n\treadonly org_slug?: string\n\treadonly org_role?: string\n\treadonly iat?: number\n\treadonly exp?: number\n\t/**\n\t * RFC 7800 confirmation claim — present when the token is sender-\n\t * constrained. Today we emit this for DPoP-bound tokens (RFC 9449)\n\t * where `cnf.jkt` is the SHA-256 thumbprint of the client's DPoP\n\t * public key.\n\t *\n\t * Resource servers (e.g. apps/api Management plane) that want to\n\t * enforce DPoP MUST:\n\t * 1. Look up `oauth_clients.dpop_bound_access_tokens` on the\n\t * issuing client to know whether DPoP is required.\n\t * 2. If required AND `cnf.jkt` is absent, reject 401.\n\t * 3. If `cnf.jkt` is present, verify the inbound `DPoP` header's\n\t * proof JWT and assert its public-key thumbprint matches `jkt`.\n\t *\n\t * Pre-Wave-5.3 this field was stripped from `verifyAccessToken`'s\n\t * return value, making resource-side enforcement impossible without\n\t * decoding the JWT a second time. Exposing it preserves the wire\n\t * format and unlocks the resource-server DPoP middleware.\n\t */\n\treadonly cnf?: {\n\t\treadonly jkt?: string\n\t}\n}\n\n/**\n * `verifyAccessToken` — local JWT verification against cached JWKS.\n *\n * Designed for the Platform API's hot-path auth middleware: JWKS is\n * fetched once per process (1h TTL), signature/iss/aud/exp\n * verification is local `jose` — no per-request HTTPS hop.\n *\n * @example\n * ```typescript\n * const claims = await auth.verifyAccessToken(bearer, {\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * audience: 'platform',\n * })\n * ```\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: {\n\t\treadonly baseUrl: string\n\t\treadonly audience: string\n\t},\n): Promise<PlatformAccessTokenClaims> {\n\tconst { importJWK, jwtVerify, decodeProtectedHeader } = await import('jose')\n\n\tlet keys = await fetchPlatformJwks(opts.baseUrl)\n\n\t// Kid-aware selection — if the token's kid isn't in cache, invalidate\n\t// and refetch once (handles key-rotation windows).\n\tlet kid: string | undefined\n\ttry {\n\t\tkid = decodeProtectedHeader(token).kid\n\t} catch {\n\t\t// Fall through — jwtVerify below will surface the malformed error.\n\t}\n\n\tconst hasKid = (keyList: unknown[]) =>\n\t\tkeyList.some((k) => (k as { kid?: string } | null)?.kid === kid)\n\n\tif (kid && !hasKid(keys)) {\n\t\tresetPlatformJwksCache()\n\t\tkeys = await fetchPlatformJwks(opts.baseUrl)\n\t}\n\n\tlet lastError: unknown = null\n\tfor (const key of keys) {\n\t\tconst candidate = key as { kid?: string }\n\t\tif (kid && candidate.kid && candidate.kid !== kid) continue\n\t\ttry {\n\t\t\tconst jwk = await importJWK(key as Parameters<typeof importJWK>[0], 'RS256')\n\t\t\tconst { payload } = await jwtVerify(token, jwk, {\n\t\t\t\taudience: opts.audience,\n\t\t\t})\n\t\t\t// Wave 5.3 — extract the optional RFC 7800 `cnf` confirmation\n\t\t\t// claim. Today we only support the DPoP `jkt` thumbprint\n\t\t\t// (RFC 9449); future sender-constrained schemes (mTLS\n\t\t\t// certificate thumbprint per RFC 8705) plug into the same\n\t\t\t// claim with a different sub-key. We intentionally narrow\n\t\t\t// to `{ jkt? }` rather than passing the raw object through\n\t\t\t// — the resource-server middleware should NEVER trust an\n\t\t\t// unparsed claim shape.\n\t\t\tconst cnfRaw = payload.cnf\n\t\t\tconst cnf =\n\t\t\t\tcnfRaw && typeof cnfRaw === 'object' && 'jkt' in cnfRaw\n\t\t\t\t\t? { jkt: typeof cnfRaw.jkt === 'string' ? cnfRaw.jkt : undefined }\n\t\t\t\t\t: undefined\n\n\t\t\treturn {\n\t\t\t\tsub: payload.sub as string,\n\t\t\t\tpid: payload.pid as string | undefined,\n\t\t\t\temail: payload.email as string,\n\t\t\t\tname: payload.name as string | undefined,\n\t\t\t\tpicture: payload.picture as string | undefined,\n\t\t\t\temail_verified: Boolean(payload.email_verified),\n\t\t\t\tapp_id: payload.app_id as string,\n\t\t\t\trole: (payload.role as string) ?? 'user',\n\t\t\t\torg_id: payload.org_id as string | undefined,\n\t\t\t\torg_slug: payload.org_slug as string | undefined,\n\t\t\t\torg_role: payload.org_role as string | undefined,\n\t\t\t\tiat: payload.iat,\n\t\t\t\texp: payload.exp,\n\t\t\t\t...(cnf ? { cnf } : {}),\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlastError = err\n\t\t}\n\t}\n\n\tthrow lastError instanceof Error ? lastError : new Error('Access token verification failed')\n}\n\n// ---------------------------------------------------------------------------\n// cookies.resolvePlatformUser\n// ---------------------------------------------------------------------------\n\nexport interface PlatformUserRecord {\n\treadonly id: string\n\treadonly email: string\n\treadonly name: string | null\n\treadonly image: string | null\n\treadonly emailVerified: boolean\n\treadonly role: string\n\treadonly twoFactorEnabled: boolean\n}\n\nexport interface PlatformUserResolution {\n\treadonly user: PlatformUserRecord\n\treadonly sessionId: string\n}\n\n// 30s per-cookie cache for `resolvePlatformUser`. Hot-path middleware\n// calls this on every request; the upstream BaaS hop shouldn't run\n// more than once per user-session per 30s window. Cache is keyed on\n// the full cookie header string — any cookie change (login,\n// rotation, logout) invalidates automatically.\nconst COOKIE_RESOLVER_TTL_MS = 30 * 1000\nconst cookieResolverCache = new Map<\n\tstring,\n\t{ expiresAt: number; result: PlatformUserResolution | null }\n>()\n\nexport function resetPlatformCookieCache(): void {\n\tcookieResolverCache.clear()\n}\n\n/**\n * `cookies` namespace — Platform cookie / session resolution for the\n * Platform API's hot-path auth middleware (ADR-089 Phase 3b).\n */\nexport const cookies = {\n\t/**\n\t * Resolve a platform user from a forwarded `Cookie:` header.\n\t *\n\t * Delegates to BaaS `/auth/platform-sessions/whoami`. Caches each\n\t * unique cookie string for 30s to avoid hammering BaaS on every\n\t * SSR request.\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await auth.cookies.resolvePlatformUser({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * cookieHeader: req.headers.get('cookie') ?? '',\n\t * })\n\t * if (!result) // unauthenticated\n\t * ```\n\t */\n\tasync resolvePlatformUser(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly cookieHeader: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserResolution | null> {\n\t\tif (!opts.cookieHeader) return null\n\n\t\tconst now = Date.now()\n\t\tconst cached = cookieResolverCache.get(opts.cookieHeader)\n\t\tif (cached && cached.expiresAt > now) return cached.result\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/whoami`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tCookie: opts.cookieHeader,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t})\n\n\t\tif (!res.ok) {\n\t\t\t// Cache negative result briefly to avoid hammering on persistent\n\t\t\t// 5xx; 5s window balances responsiveness against amplification.\n\t\t\tcookieResolverCache.set(opts.cookieHeader, {\n\t\t\t\tresult: null,\n\t\t\t\texpiresAt: now + 5_000,\n\t\t\t})\n\t\t\treturn null\n\t\t}\n\n\t\tconst body = (await res.json()) as PlatformUserResolution | null\n\t\tcookieResolverCache.set(opts.cookieHeader, {\n\t\t\tresult: body,\n\t\t\texpiresAt: now + COOKIE_RESOLVER_TTL_MS,\n\t\t})\n\t\treturn body\n\t},\n} as const\n","/**\n * Platform OAuth namespace.\n *\n * Backs `auth.oauth.*` while keeping OAuth AS protocol handling out of the\n * monolithic auth module. Public exports are re-exported from `auth.ts`.\n */\n\nimport type {\n\tOAuthIntrospectRequest as ContractOAuthIntrospectRequest,\n\tOAuthIntrospectResponse as ContractOAuthIntrospectResponse,\n\tOAuthRevokeRequest as ContractOAuthRevokeRequest,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\nimport {\n\tdecodeOAuthClientCredentialsResult,\n\tdecodeOAuthTokenError,\n\tdecodeOAuthTokenResult,\n\ttype OAuthClientCredentialsResult,\n\ttype OAuthPollError,\n\ttype OAuthPollResult,\n\ttype OAuthTokenResult,\n\toauthTokenError,\n\toauthTokenFormBody,\n} from './oauth-token'\n\nexport type OAuthRevokeInput = ContractOAuthRevokeRequest\nexport type OAuthIntrospectInput = ContractOAuthIntrospectRequest\nexport type OAuthIntrospectResult = ContractOAuthIntrospectResponse\n\nexport interface MintAccessTokenClaims {\n\treadonly sub: string\n\treadonly email: string\n\treadonly name?: string\n\treadonly email_verified: boolean\n\treadonly app_id: string\n\treadonly role: string\n\treadonly org_id?: string\n\treadonly org_slug?: string\n\treadonly org_role?: string\n\treadonly picture?: string\n\treadonly pid?: string\n}\n\nexport interface MintAccessTokenResult {\n\treadonly accessToken: string\n\treadonly expiresIn: number\n}\n\ninterface OAuthClientCallOpts {\n\treadonly baseUrl: string\n\treadonly clientId: string\n\treadonly clientSecret?: string\n\treadonly token: string\n\treadonly tokenTypeHint?: 'access_token' | 'refresh_token'\n\treadonly userAgent?: string\n}\n\n/**\n * `oauth` namespace — Platform OAuth operations backed by BaaS.\n *\n * Phase 3b adds `mintAccessToken` for the refresh handler migration;\n * Phase 5.1 layered in full authorization-server verbs\n * (`/oauth/token`, `/oauth/revoke`, `/oauth/introspect`).\n */\nexport const oauth = {\n\t/**\n\t * Mint a platform-audience access token from supplied claims.\n\t *\n\t * Service-to-service call — authenticated via\n\t * `SYLPHX_INTERNAL_TOKEN` shared secret until ADR-068's\n\t * SPIFFE SVID mTLS platform-auth flip makes workload identity the\n\t * only accepted internal caller credential.\n\t */\n\tasync mintAccessToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly internalToken: string\n\t\treadonly claims: MintAccessTokenClaims\n\t\treadonly userAgent?: string\n\t}): Promise<MintAccessTokenResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-jwt/mint`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${opts.internalToken}`,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t\tbody: JSON.stringify(opts.claims),\n\t\t})\n\t\tif (!res.ok) throw await platformJwtError(res, 'oauth.mintAccessToken')\n\t\treturn (await res.json()) as MintAccessTokenResult\n\t},\n\n\tasync exchangeAuthorizationCode(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret?: string\n\t\treadonly code: string\n\t\treadonly redirectUri: string\n\t\treadonly codeVerifier: string\n\t}): Promise<OAuthTokenResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode: opts.code,\n\t\t\tredirect_uri: opts.redirectUri,\n\t\t\tclient_id: opts.clientId,\n\t\t\tcode_verifier: opts.codeVerifier,\n\t\t\t...(opts.clientSecret ? { client_secret: opts.clientSecret } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.exchangeAuthorizationCode')\n\t\treturn decodeOAuthTokenResult(await res.json())\n\t},\n\n\tasync refreshAccessToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret?: string\n\t\treadonly refreshToken: string\n\t\treadonly scope?: string\n\t}): Promise<OAuthTokenResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: opts.refreshToken,\n\t\t\tclient_id: opts.clientId,\n\t\t\t...(opts.clientSecret ? { client_secret: opts.clientSecret } : {}),\n\t\t\t...(opts.scope ? { scope: opts.scope } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.refreshAccessToken')\n\t\treturn decodeOAuthTokenResult(await res.json())\n\t},\n\n\tasync pollDeviceToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly deviceCode: string\n\t}): Promise<OAuthPollResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\tdevice_code: opts.deviceCode,\n\t\t\tclient_id: opts.clientId,\n\t\t})\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (res.ok) return { ok: true as const, tokens: decodeOAuthTokenResult(await res.json()) }\n\n\t\tconst text = await res.text().catch(() => '')\n\t\tlet code: OAuthPollError = 'oauth_error'\n\t\ttry {\n\t\t\tcode = decodeOAuthTokenError(JSON.parse(text)).error\n\t\t} catch {\n\t\t\t// Non-JSON or non-contract body — keep default.\n\t\t}\n\t\treturn { ok: false as const, error: code, status: res.status }\n\t},\n\n\tasync clientCredentialsToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret: string\n\t\treadonly scope?: string\n\t}): Promise<OAuthClientCredentialsResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'client_credentials',\n\t\t\tclient_id: opts.clientId,\n\t\t\tclient_secret: opts.clientSecret,\n\t\t\t...(opts.scope ? { scope: opts.scope } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.clientCredentialsToken')\n\t\treturn decodeOAuthClientCredentialsResult(await res.json())\n\t},\n\n\tasync revokeToken(opts: OAuthClientCallOpts): Promise<void> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildJsonHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttoken: opts.token,\n\t\t\t\ttoken_type_hint: opts.tokenTypeHint,\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tclient_secret: opts.clientSecret,\n\t\t\t} satisfies OAuthRevokeInput),\n\t\t})\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.text().catch(() => '')\n\t\t\tthrow new Error(`oauth.revokeToken failed (${res.status}): ${body}`)\n\t\t}\n\t},\n\n\tasync introspectToken(opts: OAuthClientCallOpts): Promise<OAuthIntrospectResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/introspect`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildJsonHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttoken: opts.token,\n\t\t\t\ttoken_type_hint: opts.tokenTypeHint,\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tclient_secret: opts.clientSecret,\n\t\t\t} satisfies OAuthIntrospectInput),\n\t\t})\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.text().catch(() => '')\n\t\t\tthrow new Error(`oauth.introspectToken failed (${res.status}): ${body}`)\n\t\t}\n\t\treturn (await res.json()) as OAuthIntrospectResult\n\t},\n} as const\n\nfunction buildJsonHeaders(userAgent: string | undefined): Record<string, string> {\n\treturn {\n\t\t'Content-Type': 'application/json',\n\t\t...(userAgent ? { 'User-Agent': userAgent } : {}),\n\t}\n}\n\nasync function platformJwtError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_jwt_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode: 'UNAUTHORIZED' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR' | 'TOO_MANY_REQUESTS'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * OAuth token endpoint contract helpers.\n *\n * Keeps RFC 6749/8628 request encoding, success decoding, and error decoding\n * type-bound to `@sylphx/contract` while using SDK-local runtime guards so the\n * published Promise SDK does not import Effect internals.\n */\n\nimport type {\n\tOAuthClientCredentialsResponse as ContractOAuthClientCredentialsResponse,\n\tOAuthTokenErrorResponse as ContractOAuthTokenErrorResponse,\n\tOAuthTokenRequest as ContractOAuthTokenRequest,\n\tOAuthTokenResponse as ContractOAuthTokenResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type OAuthTokenResult = ContractOAuthTokenResponse\nexport type OAuthClientCredentialsResult = ContractOAuthClientCredentialsResponse\nexport type OAuthTokenEndpointError = ContractOAuthTokenErrorResponse['error']\nexport type OAuthPollError = OAuthTokenEndpointError | 'oauth_error'\nexport type OAuthPollResult =\n\t| { readonly ok: true; readonly tokens: OAuthTokenResult }\n\t| { readonly ok: false; readonly error: OAuthPollError; readonly status: number }\n\nconst OAUTH_TOKEN_ERROR_CODES = new Set<OAuthTokenEndpointError>([\n\t'invalid_request',\n\t'invalid_client',\n\t'invalid_grant',\n\t'unauthorized_client',\n\t'unsupported_grant_type',\n\t'invalid_scope',\n\t'authorization_pending',\n\t'slow_down',\n\t'access_denied',\n\t'expired_token',\n])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null\n}\n\nfunction requireString(record: Record<string, unknown>, key: string): string {\n\tconst value = record[key]\n\tif (typeof value !== 'string') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction optionalString(record: Record<string, unknown>, key: string): string | undefined {\n\tconst value = record[key]\n\tif (value === undefined) return undefined\n\tif (typeof value !== 'string') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction optionalField(key: string, value: string | undefined): Record<string, string> {\n\treturn value === undefined ? {} : { [key]: value }\n}\n\nfunction requireNumber(record: Record<string, unknown>, key: string): number {\n\tconst value = record[key]\n\tif (typeof value !== 'number') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction assertGrantType<T extends ContractOAuthTokenRequest['grant_type']>(\n\trecord: Record<string, unknown>,\n\tgrantType: T,\n): asserts record is Record<string, unknown> & { grant_type: T } {\n\tif (record.grant_type !== grantType) {\n\t\tthrow new Error(`Invalid OAuth grant_type: expected ${grantType}`)\n\t}\n}\n\nfunction decodeOAuthTokenRequest(input: ContractOAuthTokenRequest): ContractOAuthTokenRequest {\n\tif (!isRecord(input)) throw new Error('Invalid OAuth token request')\n\tswitch (input.grant_type) {\n\t\tcase 'authorization_code':\n\t\t\tassertGrantType(input, 'authorization_code')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'authorization_code',\n\t\t\t\tcode: requireString(input, 'code'),\n\t\t\t\tredirect_uri: requireString(input, 'redirect_uri'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\tcode_verifier: requireString(input, 'code_verifier'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t}\n\t\tcase 'refresh_token':\n\t\t\tassertGrantType(input, 'refresh_token')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'refresh_token',\n\t\t\t\trefresh_token: requireString(input, 'refresh_token'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t\t...optionalField('scope', optionalString(input, 'scope')),\n\t\t\t}\n\t\tcase 'urn:ietf:params:oauth:grant-type:device_code':\n\t\t\tassertGrantType(input, 'urn:ietf:params:oauth:grant-type:device_code')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\t\tdevice_code: requireString(input, 'device_code'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t}\n\t\tcase 'client_credentials':\n\t\t\tassertGrantType(input, 'client_credentials')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'client_credentials',\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\tclient_secret: requireString(input, 'client_secret'),\n\t\t\t\t...optionalField('scope', optionalString(input, 'scope')),\n\t\t\t}\n\t\tdefault:\n\t\t\tthrow new Error('Unsupported OAuth grant_type')\n\t}\n}\n\nexport function oauthTokenFormBody(input: ContractOAuthTokenRequest): string {\n\tconst request = decodeOAuthTokenRequest(input)\n\treturn new URLSearchParams(\n\t\tObject.entries(request).filter(\n\t\t\t(entry): entry is [string, string] => typeof entry[1] === 'string',\n\t\t),\n\t).toString()\n}\n\nexport function decodeOAuthTokenResult(value: unknown): OAuthTokenResult {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth token response')\n\tconst tokenType = requireString(value, 'token_type')\n\tif (tokenType !== 'Bearer') throw new Error('Invalid OAuth token_type')\n\treturn {\n\t\taccess_token: requireString(value, 'access_token'),\n\t\ttoken_type: 'Bearer',\n\t\texpires_in: requireNumber(value, 'expires_in'),\n\t\trefresh_token: requireString(value, 'refresh_token'),\n\t\trefresh_expires_at: requireString(value, 'refresh_expires_at'),\n\t\tscope: requireString(value, 'scope'),\n\t}\n}\n\nexport function decodeOAuthClientCredentialsResult(value: unknown): OAuthClientCredentialsResult {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth client credentials response')\n\tconst tokenType = requireString(value, 'token_type')\n\tif (tokenType !== 'Bearer') throw new Error('Invalid OAuth token_type')\n\treturn {\n\t\taccess_token: requireString(value, 'access_token'),\n\t\ttoken_type: 'Bearer',\n\t\texpires_in: requireNumber(value, 'expires_in'),\n\t\tscope: requireString(value, 'scope'),\n\t}\n}\n\nexport function decodeOAuthTokenError(value: unknown): ContractOAuthTokenErrorResponse {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth token error response')\n\tconst error = requireString(value, 'error')\n\tif (!OAUTH_TOKEN_ERROR_CODES.has(error as OAuthTokenEndpointError)) {\n\t\tthrow new Error('Invalid OAuth token error code')\n\t}\n\treturn {\n\t\terror: error as OAuthTokenEndpointError,\n\t\t...optionalField('error_description', optionalString(value, 'error_description')),\n\t\t...optionalField('error_uri', optionalString(value, 'error_uri')),\n\t}\n}\n\nexport async function oauthTokenError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code: OAuthPollError = 'oauth_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = decodeOAuthTokenError(JSON.parse(body))\n\t\tcode = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t} catch {\n\t\t// Non-JSON or non-contract body — keep default message.\n\t}\n\n\tlet errorCode: 'UNAUTHORIZED' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { oauthError: code },\n\t})\n}\n","/**\n * Platform password management SDK namespace.\n *\n * Backed by `/auth/platform-password/*` on the BaaS runtime. Crypto\n * primitives and breach checks stay server-side; callers only pass tokens\n * and plaintext password inputs over the established HTTPS boundary.\n */\n\nimport type {\n\tPlatformPasswordChangeRequest,\n\tPlatformPasswordChangeResponse,\n\tPlatformPasswordSetRequest,\n\tPlatformPasswordSetResponse,\n\tPlatformPasswordStatusResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformPasswordStatusResult = PlatformPasswordStatusResponse\nexport type PlatformPasswordSetInput = PlatformPasswordSetRequest\nexport type PlatformPasswordSetResult = PlatformPasswordSetResponse\nexport type PlatformPasswordChangeInput = PlatformPasswordChangeRequest\nexport type PlatformPasswordChangeResult = PlatformPasswordChangeResponse\n\nexport const password = {\n\tasync status(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordStatusResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/status`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.status')\n\t\treturn (await res.json()) as PlatformPasswordStatusResult\n\t},\n\n\tasync set(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly password: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordSetResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/set`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tpassword: opts.password,\n\t\t\t} satisfies PlatformPasswordSetInput),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.set')\n\t\treturn (await res.json()) as PlatformPasswordSetResult\n\t},\n\n\tasync change(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly currentPassword: string\n\t\treadonly newPassword: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordChangeResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/change`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tcurrentPassword: opts.currentPassword,\n\t\t\t\tnewPassword: opts.newPassword,\n\t\t\t} satisfies PlatformPasswordChangeInput),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.change')\n\t\treturn (await res.json()) as PlatformPasswordChangeResult\n\t},\n} as const\n\nfunction buildPlatformPasswordHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformPasswordError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_password_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform session management SDK namespace.\n *\n * Backed by `/auth/platform-sessions/*` on the BaaS runtime. These helpers\n * accept platform-audience access tokens, not project `pk_`/`sk_` credentials.\n */\n\nimport type {\n\tPlatformSessionRenameRequest,\n\tPlatformSessionRenameResponse,\n\tPlatformSessionRevokeAllResponse,\n\tPlatformSessionRevokeOtherResponse,\n\tPlatformSessionRevokeRequest,\n\tPlatformSessionRevokeResponse,\n\tPlatformSessionsListResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformSessionsListResult = PlatformSessionsListResponse\nexport type PlatformSessionRevokeInput = PlatformSessionRevokeRequest\nexport type PlatformSessionRevokeResult = PlatformSessionRevokeResponse\nexport type PlatformSessionRevokeOtherResult = PlatformSessionRevokeOtherResponse\nexport type PlatformSessionRevokeAllResult = PlatformSessionRevokeAllResponse\nexport type PlatformSessionRenameInput = PlatformSessionRenameRequest\nexport type PlatformSessionRenameResult = PlatformSessionRenameResponse\n\nexport const sessions = {\n\tasync list(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionsListResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.list')\n\t\treturn (await res.json()) as PlatformSessionsListResult\n\t},\n\n\tasync revoke(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({ sessionId: opts.sessionId } satisfies PlatformSessionRevokeInput),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revoke')\n\t\treturn (await res.json()) as PlatformSessionRevokeResult\n\t},\n\n\tasync revokeOther(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeOtherResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke-other`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revokeOther')\n\t\treturn (await res.json()) as PlatformSessionRevokeOtherResult\n\t},\n\n\tasync revokeAll(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeAllResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke-all`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revokeAll')\n\t\treturn (await res.json()) as PlatformSessionRevokeAllResult\n\t},\n\n\tasync rename(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly name: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRenameResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/rename`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tsessionId: opts.sessionId,\n\t\t\t\tname: opts.name,\n\t\t\t} satisfies PlatformSessionRenameInput),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.rename')\n\t\treturn (await res.json()) as PlatformSessionRenameResult\n\t},\n} as const\n\nfunction buildPlatformSessionsHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformSessionError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_sessions_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform user GDPR export and erasure SDK namespace.\n *\n * These helpers are backed by `/auth/platform-user/*` on the BaaS runtime\n * and keep account data operations separate from generic auth/session helpers.\n */\n\nimport type {\n\tAuthUserDeleteRequest,\n\tAuthUserDeleteResponse,\n\tAuthUserExportResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformUserExportResult = AuthUserExportResponse\nexport type PlatformUserDeleteInput = AuthUserDeleteRequest\nexport type PlatformUserDeleteResult = AuthUserDeleteResponse\n\n/**\n * `user` namespace — Platform-plane (Console / CLI) GDPR operations.\n * Backed by `/auth/platform-user/*` on the BaaS runtime (ADR-089 Phase\n * 2d). See module header for the full rationale.\n */\nexport const user = {\n\t/**\n\t * Export every piece of personal data the platform holds about the\n\t * authenticated user (GDPR Article 20 — right to data portability).\n\t *\n\t * The returned record is deliberately loose — it contains the user\n\t * row, sessions, OAuth accounts, login history, security alerts,\n\t * organization memberships, subscriptions, per-project memberships,\n\t * and storage file metadata. Shape varies with customer provisioning.\n\t */\n\tasync exportData(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserExportResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformUserError(res, 'user.exportData')\n\t\treturn (await res.json()) as PlatformUserExportResult\n\t},\n\n\t/**\n\t * Permanently delete the authenticated user's account (GDPR Article\n\t * 17 — right to erasure). Cascades through every provisioned project\n\t * DB, cancels Stripe subscriptions, deletes S3 blobs, and anonymises\n\t * billing transactions.\n\t */\n\tasync deleteAccount(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly reason?: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserDeleteResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/account`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\t...(opts.reason !== undefined && { reason: opts.reason }),\n\t\t\t} satisfies PlatformUserDeleteInput),\n\t\t})\n\t\tif (!res.ok) throw await platformUserError(res, 'user.deleteAccount')\n\t\treturn (await res.json()) as PlatformUserDeleteResult\n\t},\n\n\t/**\n\t * Async GDPR Article 20 export job API (ADR-089 Phase 5.5).\n\t *\n\t * `user.exportData` is the Phase 2d synchronous shortcut; production\n\t * callers should prefer the async flow for large accounts.\n\t */\n\texports: {\n\t\t/**\n\t\t * Kick off an export job. Poll `status({ id })` until\n\t\t * `status === 'complete'`.\n\t\t */\n\t\tasync initiate(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly format?: 'json' | 'json-ld'\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<DataExportJob> {\n\t\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify(opts.format !== undefined ? { format: opts.format } : {}),\n\t\t\t})\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.initiate')\n\t\t\treturn (await res.json()) as DataExportJob\n\t\t},\n\n\t\t/**\n\t\t * Read the current state of an in-flight or completed export job.\n\t\t */\n\t\tasync status(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly id: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<DataExportJob> {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export/${encodeURIComponent(\n\t\t\t\t\topts.id,\n\t\t\t\t)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t},\n\t\t\t)\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.status')\n\t\t\treturn (await res.json()) as DataExportJob\n\t\t},\n\n\t\t/**\n\t\t * Download the completed export payload. The BaaS route returns a\n\t\t * 302 to a freshly-signed object-storage URL; `fetch` follows it\n\t\t * and resolves to the raw `Blob`.\n\t\t */\n\t\tasync download(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly id: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<{ blob: Blob; sha256: string | null; sizeBytes: number | null }> {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export/${encodeURIComponent(\n\t\t\t\t\topts.id,\n\t\t\t\t)}/download`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t},\n\t\t\t)\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.download')\n\t\t\tconst sha256 = res.headers.get('X-Sylphx-Export-Sha256')\n\t\t\tconst sizeHeader = res.headers.get('X-Sylphx-Export-Size')\n\t\t\tconst sizeBytes = sizeHeader ? Number.parseInt(sizeHeader, 10) : null\n\t\t\tconst blob = await res.blob()\n\t\t\treturn { blob, sha256, sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : null }\n\t\t},\n\t},\n} as const\n\n/**\n * Wire shape of a data-export job. `status` progresses through pending,\n * running, complete, or failed.\n */\nexport interface DataExportJob {\n\treadonly id: string\n\treadonly status: 'pending' | 'running' | 'complete' | 'failed'\n\treadonly format: 'json' | 'json-ld'\n\treadonly requestedAt: string\n\treadonly completedAt: string | null\n\treadonly downloadUrl: string | null\n\treadonly sizeBytes: number | null\n\treadonly sha256: string | null\n\treadonly errorMessage: string | null\n}\n\nfunction buildPlatformUserHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformUserError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_user_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Audit namespace — BaaS audit-log reader (ADR-089 Phase 5.3b,\n * Σ1 SoC rename).\n *\n * Scope-filtered reader for the tamper-evident audit-log chain. The\n * runtime enforces role-scoped visibility server-side so the caller\n * only needs to present their platform JWT. All filter fields are\n * optional; `limit` caps at 500 (default 100).\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `audit` and spoke to `/auth/platform-audit/*`. The server-side\n * surface moved to `/v1/audit/*` (audit is a cross-cutting BaaS\n * primitive — compliance / observability — not an auth verb); this\n * SDK module mirrors the move.\n *\n * @example\n * ```typescript\n * import { audit } from '@sylphx/sdk'\n * const { events, nextCursor } = await audit.query({\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * accessToken: platformJwt,\n * filter: { scope: 'platform-ops', limit: 200 },\n * })\n * ```\n */\n\nimport type {\n\tPlatformAuditQueryRequest as ContractPlatformAuditQueryRequest,\n\tPlatformAuditQueryResponse as ContractPlatformAuditQueryResponse,\n} from '@sylphx/contract'\n\nexport type AuditQueryFilter = ContractPlatformAuditQueryRequest\nexport type AuditQueryResult = ContractPlatformAuditQueryResponse\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\tAccept: 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function auditError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'audit_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'FORBIDDEN'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'FORBIDDEN'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\nexport const audit = {\n\tasync query(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: AuditQueryFilter\n\t\treadonly userAgent?: string\n\t}): Promise<AuditQueryResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/audit/query`)\n\t\tconst f = opts.filter ?? {}\n\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\tif (f.actor) url.searchParams.set('actor', f.actor)\n\t\tif (f.resourceType) url.searchParams.set('resource_type', f.resourceType)\n\t\tif (f.resourceId) url.searchParams.set('resource_id', f.resourceId)\n\t\tif (f.action) url.searchParams.set('action', f.action)\n\t\tif (f.from) url.searchParams.set('from', f.from)\n\t\tif (f.to) url.searchParams.set('to', f.to)\n\t\tif (f.cursor) url.searchParams.set('cursor', f.cursor)\n\t\tif (f.limit !== undefined) url.searchParams.set('limit', String(f.limit))\n\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await auditError(res, 'audit.query')\n\t\treturn (await res.json()) as AuditQueryResult\n\t},\n} as const\n","/**\n * Rate-Limits namespace — BaaS operator surface (ADR-089 Phase 5.2,\n * Σ1 SoC rename).\n *\n * Platform operators inspect + tune rate-limit enforcement without a\n * code deploy. All write paths are role-gated server-side\n * (`rate-limits.ts` on the runtime): super_admin/admin touch any\n * scope, project admins are narrowed to their project, regular users\n * to their own user row. Scope escalation returns 403.\n *\n * Phase Σ1 SoC rename: this was previously exported out of\n * `./auth` as `rateLimits` and spoke to `/auth/platform-rate-limits/*`.\n * The server-side surface moved to `/v1/rate-limits/*` (rate-limiting\n * is a cross-cutting BaaS primitive, not an auth verb); this SDK\n * module mirrors the move.\n *\n * @example\n * ```typescript\n * import { rateLimits } from '@sylphx/sdk'\n * await rateLimits.strategies.set({\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * accessToken: platformJwt,\n * namespace: 'login',\n * body: {\n * scope: 'project',\n * scope_id: 'proj_abc',\n * strategy: 'fixed-window',\n * limit: 50,\n * windowSeconds: 300,\n * },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tPlatformRateLimitStatusRequest as ContractPlatformRateLimitStatusRequest,\n\tPlatformRateLimitStatusResponse as ContractPlatformRateLimitStatusResponse,\n\tPlatformRateLimitStrategiesListRequest as ContractPlatformRateLimitStrategiesListRequest,\n\tPlatformRateLimitStrategiesListResponse as ContractPlatformRateLimitStrategiesListResponse,\n\tPlatformRateLimitStrategyDeleteRequest as ContractPlatformRateLimitStrategyDeleteRequest,\n\tPlatformRateLimitStrategyDeleteResponse as ContractPlatformRateLimitStrategyDeleteResponse,\n\tPlatformRateLimitStrategyUpsertRequest as ContractPlatformRateLimitStrategyUpsertRequest,\n\tPlatformRateLimitStrategyUpsertResponse as ContractPlatformRateLimitStrategyUpsertResponse,\n} from '@sylphx/contract'\n\nexport type RateLimitStatusFilter = ContractPlatformRateLimitStatusRequest\nexport type RateLimitStatusResult = ContractPlatformRateLimitStatusResponse\nexport type RateLimitStrategiesFilter = ContractPlatformRateLimitStrategiesListRequest\nexport type RateLimitStrategiesResult = ContractPlatformRateLimitStrategiesListResponse\nexport type RateLimitStrategyUpsertInput = ContractPlatformRateLimitStrategyUpsertRequest\nexport type RateLimitStrategyUpsertResult = ContractPlatformRateLimitStrategyUpsertResponse\nexport type RateLimitStrategyDeleteInput = ContractPlatformRateLimitStrategyDeleteRequest\nexport type RateLimitStrategyDeleteResult = ContractPlatformRateLimitStrategyDeleteResponse\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\tAccept: 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function rateLimitError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'rate_limit_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'FORBIDDEN'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'FORBIDDEN'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\nexport const rateLimits = {\n\tasync status(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: RateLimitStatusFilter\n\t\treadonly userAgent?: string\n\t}): Promise<RateLimitStatusResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/status`)\n\t\tconst f = opts.filter ?? {}\n\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\tif (f.scope_id) url.searchParams.set('scope_id', f.scope_id)\n\t\tif (f.namespace) url.searchParams.set('namespace', f.namespace)\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.status')\n\t\treturn (await res.json()) as RateLimitStatusResult\n\t},\n\n\tstrategies: {\n\t\tasync list(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly filter?: RateLimitStrategiesFilter\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategiesResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies`)\n\t\t\tconst f = opts.filter ?? {}\n\t\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\t\tif (f.scope_id) url.searchParams.set('scope_id', f.scope_id)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.list')\n\t\t\treturn (await res.json()) as RateLimitStrategiesResult\n\t\t},\n\n\t\tasync set(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly namespace: string\n\t\t\treadonly body: RateLimitStrategyUpsertInput\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategyUpsertResult> {\n\t\t\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies/${encodeURIComponent(opts.namespace)}`\n\t\t\tconst res = await fetch(url, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t\t'content-type': 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(opts.body),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.set')\n\t\t\treturn (await res.json()) as RateLimitStrategyUpsertResult\n\t\t},\n\n\t\tasync delete(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly namespace: string\n\t\t\treadonly body: RateLimitStrategyDeleteInput\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategyDeleteResult> {\n\t\t\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies/${encodeURIComponent(opts.namespace)}`\n\t\t\tconst res = await fetch(url, {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t\t'content-type': 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(opts.body),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.delete')\n\t\t\treturn (await res.json()) as RateLimitStrategyDeleteResult\n\t\t},\n\t},\n} as const\n","/**\n * Functions Admin namespace — Platform-plane function-bundle admin\n * (ADR-089 Phase 3a, Σ1 SoC rename).\n *\n * Function-bundle download for the Sylphx-internal edge-runtime\n * orchestrator. Unlike the sibling Platform namespaces this one\n * authenticates with a shared `internalToken` (service-internal secret)\n * rather than a platform-audience JWT, because the caller is another\n * Sylphx service (the edge-runtime that spawns V8 isolates to invoke\n * user functions) not an end user. The BaaS runtime\n * (`apps/runtime/src/server/runtime/routes/functions/admin.ts`)\n * owns the object-storage implementation — Platform callers dogfood through\n * this SDK surface and never touch backend storage credentials.\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `functions` (re-exported at the package root as `functionsInternal`)\n * and spoke to `/auth/platform-functions/*`. The server-side surface\n * moved to `/v1/functions/admin/*` (function bundle admin is a\n * cross-cutting BaaS primitive — function bundle storage — not an auth\n * verb); this SDK module nests the admin verbs under\n * `functions.admin.*` at the package root.\n */\n\nexport interface PlatformFunctionsDownloadBundleResult {\n\treadonly code: string\n}\n\nasync function functionsAdminError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'functions_admin_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode: 'UNAUTHORIZED' | 'NOT_FOUND' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\n/**\n * `functionsAdmin` namespace — Platform-plane (edge-runtime\n * orchestrator) function-bundle fetch. Re-exported at the package\n * root as part of `functions.admin.*`.\n */\nexport const functionsAdmin = {\n\t/**\n\t * Download a function bundle (UTF-8 source) by its storage key.\n\t *\n\t * The storage key is an opaque string chosen by `sylphx deploy`\n\t * when the bundle was uploaded — callers read it from the\n\t * `functions.storagePath` column after resolving a function row\n\t * by `(projectId, name)`.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { functions } from '@sylphx/sdk'\n\t * const { code } = await functions.admin.downloadBundle({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * internalToken: process.env.SYLPHX_INTERNAL_TOKEN!,\n\t * storagePath: 'bundles/proj_abc/my-fn/v3.js',\n\t * })\n\t * ```\n\t */\n\tasync downloadBundle(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly internalToken: string\n\t\treadonly storagePath: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformFunctionsDownloadBundleResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/functions/admin/download-bundle`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${opts.internalToken}`,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t\tbody: JSON.stringify({ storagePath: opts.storagePath }),\n\t\t})\n\t\tif (!res.ok) throw await functionsAdminError(res, 'functions.admin.downloadBundle')\n\t\treturn (await res.json()) as PlatformFunctionsDownloadBundleResult\n\t},\n} as const\n","/**\n * Realtime Functions\n *\n * Pure functions for real-time messaging via managed durable streams.\n * Supports channel-based pub/sub with SSE delivery to browsers.\n *\n * @example\n * ```ts\n * import { createConfig, realtimeEmit } from '@sylphx/sdk'\n *\n * // Server: emit events to connected clients\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n * await realtimeEmit(config, {\n * channel: 'orders',\n * event: 'order.created',\n * data: { orderId: '123', amount: 99 },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tRealtimeEmitInput as ContractRealtimeEmitInput,\n\tRealtimeEmitResult as ContractRealtimeEmitResult,\n} from '@sylphx/contract'\nimport { realtimeEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport type { StreamMessage } from './realtime-types'\n\n// Re-export shared types\nexport type { StreamMessage } from './realtime-types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RealtimeEmitRequest {\n\t/** Channel to emit the event to */\n\tchannel: string\n\t/** Event type/name */\n\tevent: string\n\t/** Event data (any JSON-serializable value) */\n\tdata: unknown\n}\n\nexport interface RealtimeEmitResponse {\n\t/** Stream entry ID */\n\tid: string\n\t/** Channel the event was emitted to */\n\tchannel: string\n}\n\nexport interface RealtimeHistoryRequest {\n\t/** Channel to get history for */\n\tchannel: string\n\t/** Maximum number of messages to return (default: 50) */\n\tlimit?: number\n\t/** Return messages after this stream entry ID */\n\tafter?: string\n}\n\nexport interface RealtimeHistoryResponse {\n\t/** List of historical messages */\n\tmessages: StreamMessage[]\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Emit an event to a realtime channel.\n *\n * All clients subscribed to the channel (via `useRealtime` hook or SSE)\n * will receive the event instantly.\n *\n * @example\n * ```ts\n * // Notify all clients watching a document\n * await realtimeEmit(config, {\n * channel: `doc:${documentId}`,\n * event: 'doc.updated',\n * data: { updatedBy: userId, timestamp: Date.now() },\n * })\n * ```\n */\nexport async function realtimeEmit(\n\tconfig: SylphxConfig,\n\trequest: RealtimeEmitRequest,\n): Promise<RealtimeEmitResponse> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractRealtimeEmitInput\n\tconst endpoint = realtimeEndpoints.emit\n\treturn callApi<ContractRealtimeEmitResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Get historical messages from a channel.\n *\n * Useful for initializing state when a client first connects,\n * or for resuming from a known stream position.\n *\n * @example\n * ```ts\n * // Get last 20 messages when a user joins a chat\n * const { messages } = await getRealtimeHistory(config, {\n * channel: 'chat:general',\n * limit: 20,\n * })\n * ```\n */\nexport async function getRealtimeHistory(\n\tconfig: SylphxConfig,\n\trequest: RealtimeHistoryRequest,\n): Promise<RealtimeHistoryResponse> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// Public SDK compatibility keeps `messages` mutable; runtime payload is\n\t// the same contract-owned array shape.\n\tconst endpoint = realtimeEndpoints.history\n\treturn callApi<RealtimeHistoryResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: {\n\t\t\tchannel: request.channel,\n\t\t\t...(request.limit !== undefined && { limit: String(request.limit) }),\n\t\t\t...(request.after !== undefined && { after: request.after }),\n\t\t},\n\t})\n}\n","/**\n * Realtime Admin namespace — Platform-plane channel-registration\n * admin (ADR-089 Phase 3a, Σ1 SoC rename).\n *\n * Channel-registration admin for the Platform plane (Console / CLI\n * operators). Like the sibling device / platform-sessions / platform-\n * password / platform-user namespaces, these endpoints accept a\n * `baseUrl + accessToken` rather than a `SylphxConfig` — the caller\n * authenticates with a platform-audience JWT, not a customer-app\n * `pk_`/`sk_` pair. The BaaS runtime\n * (`apps/runtime/src/server/runtime/routes/realtime/admin.ts`)\n * verifies the token against audience `'platform'`, confirms\n * `verifyProjectAccess(userId, projectId)`, and owns the channel\n * registration operations.\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `realtime` (re-exported at the package root as `realtimeAdmin`)\n * and spoke to `/auth/platform-realtime/*`. The server-side surface\n * moved to `/v1/realtime/admin/*` (realtime admin is a cross-cutting\n * BaaS primitive, not an auth verb); this SDK module nests the admin\n * verbs under `realtime.admin.channels.*` at the package root so it\n * no longer collides with the customer-app `realtimeEmit` /\n * `getRealtimeHistory` data-plane surface.\n *\n * For the Sylphx platform itself, `baseUrl` is\n * `https://your-app.api.sylphx.com/v1` (configurable via SYLPHX_BAAS_URL\n * for dev/staging).\n */\n\nexport interface PlatformRealtimeChannel {\n\treadonly name: string\n\treadonly activeConnections: number\n\treadonly messagesPerHour: number\n\treadonly status: 'active' | 'empty'\n}\n\nexport interface PlatformRealtimeStatusResult {\n\treadonly available: boolean\n}\n\nexport interface PlatformRealtimeListChannelsResult {\n\treadonly channels: readonly PlatformRealtimeChannel[]\n\treadonly count: number\n}\n\nexport interface PlatformRealtimeCreateChannelResult {\n\treadonly name: string\n}\n\nexport interface PlatformRealtimeDeleteChannelResult {\n\treadonly success: boolean\n}\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function realtimeAdminError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'realtime_admin_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\t\t| 'CONFLICT'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 409) errorCode = 'CONFLICT'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\n/**\n * `realtimeAdmin` namespace — Platform-plane realtime channel-\n * registration admin. Re-exported at the package root as part of\n * `realtime.admin.*` (nested under the existing `realtime` surface in\n * `./index.ts`).\n */\nexport const realtimeAdmin = {\n\tchannels: {\n\t\t/**\n\t\t * Get realtime service status for a project.\n\t\t */\n\t\tasync status(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeStatusResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/status`)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.status')\n\t\t\treturn (await res.json()) as PlatformRealtimeStatusResult\n\t\t},\n\n\t\t/**\n\t\t * List registered channels for a project.\n\t\t *\n\t\t * Returns an empty list when the project's hidden realtime store has\n\t\t * not been provisioned yet — BaaS side soft-fails because absent\n\t\t * registrations are semantically equivalent to \"none registered\".\n\t\t */\n\t\tasync list(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeListChannelsResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels`)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.list')\n\t\t\treturn (await res.json()) as PlatformRealtimeListChannelsResult\n\t\t},\n\n\t\t/**\n\t\t * Register a named realtime channel for a project.\n\t\t *\n\t\t * Returns 409 if already registered — registration is\n\t\t * idempotent per name.\n\t\t */\n\t\tasync create(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly name: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeCreateChannelResult> {\n\t\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({ projectId: opts.projectId, name: opts.name }),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.create')\n\t\t\treturn (await res.json()) as PlatformRealtimeCreateChannelResult\n\t\t},\n\n\t\t/**\n\t\t * Unregister a named realtime channel for a project. Idempotent.\n\t\t */\n\t\tasync delete(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly channelName: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeDeleteChannelResult> {\n\t\t\tconst url = new URL(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels/${encodeURIComponent(\n\t\t\t\t\topts.channelName,\n\t\t\t\t)}`,\n\t\t\t)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.delete')\n\t\t\treturn (await res.json()) as PlatformRealtimeDeleteChannelResult\n\t\t},\n\t},\n} as const\n","/**\n * Admin Functions — Server-side user management\n * Requires secretKey (PLATFORM_TOKEN). Never use on client-side.\n */\nimport { callApi, type SylphxConfig } from './config'\n\nexport interface AdminUser {\n\tid: string\n\temail: string\n\tname: string | null\n\timage: string | null\n\temailVerified: boolean\n\trole: string\n\tstatus: 'active' | 'suspended' | 'deleted'\n\tmetadata: Record<string, unknown> | null\n\tfirstSeenAt: string\n\tlastActiveAt: string\n\tcreatedAt: string\n}\n\nexport interface ListUsersOptions {\n\temail?: string\n\tstatus?: 'active' | 'suspended'\n\tlimit?: number\n\toffset?: number\n}\n\nexport interface ListUsersResult {\n\tusers: AdminUser[]\n\ttotal: number\n\tlimit: number\n\toffset: number\n}\n\n/**\n * List users in this project (paginated).\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const { users, total } = await listUsers(config, { status: 'active', limit: 20 })\n * ```\n */\nexport async function listUsers(\n\tconfig: SylphxConfig,\n\topts?: ListUsersOptions,\n): Promise<ListUsersResult> {\n\tconst params = new URLSearchParams()\n\tif (opts?.email) params.set('email', opts.email)\n\tif (opts?.status) params.set('status', opts.status)\n\tif (opts?.limit) params.set('limit', String(opts.limit))\n\tif (opts?.offset) params.set('offset', String(opts.offset))\n\tconst qs = params.toString()\n\treturn callApi<ListUsersResult>(config, `/admin/users${qs ? `?${qs}` : ''}`)\n}\n\n/**\n * Get a single user by ID.\n * Server-side only (requires secretKey).\n */\nexport async function getUser(config: SylphxConfig, userId: string): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}`)\n}\n\n/**\n * Look up a user by email address. Returns null if not found.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const user = await getUserByEmail(config, 'user@example.com')\n * if (!user) console.log('User not found')\n * ```\n */\nexport async function getUserByEmail(\n\tconfig: SylphxConfig,\n\temail: string,\n): Promise<AdminUser | null> {\n\tconst result = await listUsers(config, { email, limit: 1 })\n\treturn result.users[0] ?? null\n}\n\n/**\n * Update a user's profile fields.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const updated = await updateUser(config, userId, { role: 'admin', name: 'Jane' })\n * ```\n */\nexport async function updateUser(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tinput: { name?: string; metadata?: Record<string, unknown>; role?: string },\n): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}`, {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Update only the metadata for a user (merge-style update).\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await updateUserMetadata(config, userId, { employeeId: 'EMP-001', department: 'Engineering' })\n * ```\n */\nexport async function updateUserMetadata(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tmetadata: Record<string, unknown>,\n): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}/metadata`, {\n\t\tmethod: 'PUT',\n\t\tbody: metadata,\n\t})\n}\n\n/**\n * Suspend a user account.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await suspendUser(config, userId, 'Violation of terms of service')\n * ```\n */\nexport async function suspendUser(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\treason?: string,\n): Promise<void> {\n\tawait callApi<void>(config, `/admin/users/${userId}/suspend`, {\n\t\tmethod: 'POST',\n\t\tbody: { reason },\n\t})\n}\n\n/**\n * Delete a user account.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await deleteUser(config, userId)\n * ```\n */\nexport async function deleteUser(config: SylphxConfig, userId: string): Promise<void> {\n\tawait callApi<void>(config, `/admin/users/${userId}/delete`, {\n\t\tmethod: 'POST',\n\t})\n}\n","/**\n * Analytics Functions\n *\n * Pure functions for event tracking - no hidden state.\n * Events are sent directly to the platform.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/analytics/track`,\n * `/analytics/identify`, `/analytics/page`, and `/analytics/batch`. SDK-\n * specific convenience shapes (`BatchEvent`) stay local since they model\n * the multi-event tracker ergonomics, not the platform wire.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tBatchTrackInput as ContractBatchTrackInput,\n\tBatchTrackResult as ContractBatchTrackResult,\n\tConversionData as ContractConversionData,\n\tIdentifyInput as ContractIdentifyInput,\n\tPageInput as ContractPageInput,\n\tTrackEvent as ContractTrackEvent,\n\tTrackInput as ContractTrackInput,\n} from '@sylphx/contract'\nimport { analyticsEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type TrackEventItem = ContractTrackEvent\nexport type BatchTrackRequest = ContractBatchTrackInput\nexport type BatchTrackResponse = ContractBatchTrackResult\nexport type ConversionData = ContractConversionData\n\n// SDK-specific types for convenience\nexport interface TrackInput {\n\t/** Event name */\n\tevent: string\n\t/** Event properties */\n\tproperties?: Record<string, unknown>\n\t/** User ID (optional, for server-side tracking) */\n\tuserId?: string\n\t/** Anonymous ID (for tracking before user signs in) */\n\tanonymousId?: string\n\t/** Timestamp (defaults to now) */\n\ttimestamp?: string\n}\n\nexport interface PageInput {\n\t/** Page name or title */\n\tname: string\n\t/** Page properties */\n\tproperties?: Record<string, unknown>\n\t/** User ID (optional) */\n\tuserId?: string\n\t/** Anonymous ID */\n\tanonymousId?: string\n}\n\nexport interface IdentifyInput {\n\t/** User ID */\n\tuserId: string\n\t/** User traits */\n\ttraits?: Record<string, unknown>\n\t/** Anonymous ID to link */\n\tanonymousId?: string\n}\n\nexport interface BatchEvent {\n\ttype: 'track' | 'page' | 'identify'\n\tevent?: string\n\tname?: string\n\tuserId?: string\n\tanonymousId?: string\n\tproperties?: Record<string, unknown>\n\ttraits?: Record<string, unknown>\n\ttimestamp?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Track a custom event\n *\n * @example\n * ```typescript\n * await track(config, {\n * event: 'purchase_completed',\n * properties: { amount: 99.99, currency: 'USD' },\n * userId: 'user-123',\n * })\n * ```\n */\nexport async function track(config: SylphxConfig, input: TrackInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.track\n\tconst body = {\n\t\tevent: input.event,\n\t\tproperties: input.properties ?? {},\n\t\tuserId: input.userId,\n\t\tanonymousId: input.anonymousId,\n\t\ttimestamp: input.timestamp ?? new Date().toISOString(),\n\t} satisfies ContractTrackInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Track a page view\n *\n * @example\n * ```typescript\n * await page(config, {\n * name: 'Home',\n * properties: { path: '/', referrer: document.referrer },\n * })\n * ```\n */\nexport async function page(config: SylphxConfig, input: PageInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084). Contract `PageInput`\n\t// now carries the optional `timestamp` field (ADR-084 Wave 2d), so the\n\t// page + track envelopes are symmetric.\n\tconst endpoint = analyticsEndpoints.page\n\tconst body = {\n\t\tname: input.name,\n\t\tproperties: input.properties ?? {},\n\t\tuserId: input.userId,\n\t\tanonymousId: input.anonymousId,\n\t} satisfies ContractPageInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: { ...body, timestamp: new Date().toISOString() },\n\t})\n}\n\n/**\n * Identify a user with traits\n *\n * @example\n * ```typescript\n * await identify(config, {\n * userId: 'user-123',\n * traits: { email: 'user@example.com', plan: 'pro' },\n * anonymousId: 'anon-456', // Links anonymous activity to user\n * })\n * ```\n */\nexport async function identify(config: SylphxConfig, input: IdentifyInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.identify\n\tconst body = {\n\t\tuserId: input.userId,\n\t\ttraits: input.traits ?? {},\n\t\tanonymousId: input.anonymousId,\n\t} satisfies ContractIdentifyInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Send multiple events in a single request (batch)\n *\n * @example\n * ```typescript\n * await trackBatch(config, [\n * { type: 'track', event: 'item_viewed', properties: { id: '1' } },\n * { type: 'track', event: 'item_added', properties: { id: '1' } },\n * { type: 'track', event: 'checkout_started' },\n * ])\n * ```\n */\nexport async function trackBatch(config: SylphxConfig, events: BatchEvent[]): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.batch\n\tconst body = {\n\t\tevents: events.map((e) => ({\n\t\t\tevent: e.type === 'track' ? (e.event ?? '') : e.type === 'page' ? `$pageview` : '$identify',\n\t\t\tproperties: {\n\t\t\t\t...e.properties,\n\t\t\t\t...(e.type === 'page' && e.name ? { name: e.name } : {}),\n\t\t\t\t...(e.type === 'identify' && e.traits ? { traits: e.traits } : {}),\n\t\t\t},\n\t\t\tuserId: e.userId,\n\t\t\tanonymousId: e.anonymousId,\n\t\t\ttimestamp: e.timestamp ?? new Date().toISOString(),\n\t\t})),\n\t} satisfies ContractBatchTrackInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Generate a random anonymous ID (Segment pattern: pure UUID)\n *\n * Uses UUID v4 format without timestamp component to prevent collision risk\n * in high-traffic applications where multiple users might generate IDs at\n * the same millisecond.\n *\n * @example\n * ```typescript\n * const anonId = generateAnonymousId()\n * await track(config, { event: 'page_view', anonymousId: anonId })\n * ```\n */\nexport function generateAnonymousId(): string {\n\t// Use crypto.randomUUID if available (standard UUID v4)\n\tif (typeof crypto !== 'undefined' && crypto.randomUUID) {\n\t\treturn crypto.randomUUID()\n\t}\n\t// Fallback for older browsers: generate UUID v4 manually\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n\t\tconst r = (Math.random() * 16) | 0\n\t\tconst v = c === 'x' ? r : (r & 0x3) | 0x8\n\t\treturn v.toString(16)\n\t})\n}\n\n/**\n * Create a tracker bound to a specific config\n *\n * For convenience when making many calls with the same config.\n * This is optional - you can always use the individual functions.\n *\n * @example\n * ```typescript\n * const analytics = createTracker(config)\n *\n * // No need to pass config each time\n * analytics.track('event', { prop: 'value' })\n * analytics.page('Home')\n * analytics.identify('user-123', { email: 'user@example.com' })\n * ```\n */\nexport function createTracker(config: SylphxConfig, defaultAnonymousId?: string) {\n\tconst anonymousId = defaultAnonymousId ?? generateAnonymousId()\n\n\treturn {\n\t\ttrack: (event: string, properties?: Record<string, unknown>, userId?: string) =>\n\t\t\ttrack(config, { event, properties, userId, anonymousId }),\n\n\t\tpage: (name: string, properties?: Record<string, unknown>, userId?: string) =>\n\t\t\tpage(config, { name, properties, userId, anonymousId }),\n\n\t\tidentify: (userId: string, traits?: Record<string, unknown>) =>\n\t\t\tidentify(config, { userId, traits, anonymousId }),\n\n\t\tbatch: (events: BatchEvent[]) =>\n\t\t\ttrackBatch(\n\t\t\t\tconfig,\n\t\t\t\tevents.map((e) => ({\n\t\t\t\t\t...e,\n\t\t\t\t\tanonymousId: e.anonymousId ?? anonymousId,\n\t\t\t\t})),\n\t\t\t),\n\n\t\t/** Get the anonymous ID for this tracker */\n\t\tgetAnonymousId: () => anonymousId,\n\t}\n}\n","/**\n * AI Functions\n *\n * Pure functions for AI completions - Vercel AI SDK style.\n * Direct API calls with natural tree-shaking.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /ai/models`,\n * `/ai/usage`, `/ai/rate-limit`. Chat-completion / embedding envelopes\n * remain local to this module — they pass through the Vercel AI SDK\n * bridge and evolve independently of the platform contract.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tAIModel as ContractAIModel,\n\tGetModelsResponse,\n\tGetRateLimitResponse,\n\tGetUsageResponse,\n} from '@sylphx/contract'\nimport { buildApiUrl, buildHeaders, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type AIUsageResponse = GetUsageResponse\nexport type AIRateLimitResponse = GetRateLimitResponse\nexport type AIModelsResponse = GetModelsResponse\nexport type AIModel = ContractAIModel\n\n// ============================================================================\n// SDK-specific Types (OpenAI-compatible chat format)\n// ============================================================================\n\nexport interface ChatMessage {\n\trole: 'system' | 'user' | 'assistant' | 'tool'\n\tcontent: string | ContentPart[]\n\tname?: string\n\ttool_call_id?: string\n\ttool_calls?: ToolCall[]\n\t/** Timestamp for UI display */\n\ttimestamp?: Date\n}\n\nexport interface ContentPart {\n\ttype: 'text' | 'image_url'\n\ttext?: string\n\timage_url?: { url: string; detail?: 'auto' | 'low' | 'high' }\n}\n\nexport interface ToolCall {\n\tid: string\n\ttype: 'function'\n\tfunction: { name: string; arguments: string }\n}\n\nexport interface Tool {\n\ttype: 'function'\n\tfunction: {\n\t\tname: string\n\t\tdescription?: string\n\t\tparameters?: Record<string, unknown>\n\t}\n}\n\nexport interface ChatInput {\n\t/** Model ID (e.g., 'gpt-4o', 'claude-sonnet-4-20250514') */\n\tmodel: string\n\t/** Messages */\n\tmessages: ChatMessage[]\n\t/** Temperature (0-2) */\n\ttemperature?: number\n\t/** Max tokens to generate */\n\tmaxTokens?: number\n\t/** Top P sampling */\n\ttopP?: number\n\t/** Frequency penalty */\n\tfrequencyPenalty?: number\n\t/** Presence penalty */\n\tpresencePenalty?: number\n\t/** Stop sequences */\n\tstop?: string[]\n\t/** Tools for function calling */\n\ttools?: Tool[]\n\t/** Tool choice */\n\ttoolChoice?: 'auto' | 'none' | { type: 'function'; function: { name: string } }\n}\n\nexport interface ChatResult {\n\tid: string\n\tmodel: string\n\tchoices: Array<{\n\t\tindex: number\n\t\tmessage: {\n\t\t\trole: 'assistant'\n\t\t\tcontent: string | null\n\t\t\ttool_calls?: ToolCall[]\n\t\t}\n\t\tfinishReason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null\n\t}>\n\tusage: {\n\t\tpromptTokens: number\n\t\tcompletionTokens: number\n\t\ttotalTokens: number\n\t}\n}\n\nexport interface ChatStreamChunk {\n\tid: string\n\tmodel: string\n\tchoices: Array<{\n\t\tindex: number\n\t\tdelta: {\n\t\t\trole?: 'assistant'\n\t\t\tcontent?: string\n\t\t\ttool_calls?: ToolCall[]\n\t\t}\n\t\tfinishReason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null\n\t}>\n}\n\nexport interface EmbedInput {\n\t/** Model ID (e.g., 'text-embedding-3-small') */\n\tmodel: string\n\t/** Text(s) to embed */\n\tinput: string | string[]\n\t/** Dimensions (for models that support it) */\n\tdimensions?: number\n}\n\nexport interface EmbedResult {\n\tmodel: string\n\tdata: Array<{\n\t\tindex: number\n\t\tembedding: number[]\n\t}>\n\tusage: {\n\t\tpromptTokens: number\n\t\ttotalTokens: number\n\t}\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Create a chat completion\n *\n * @example\n * ```typescript\n * const response = await chat(config, {\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Hello!' },\n * ],\n * })\n *\n * console.log(response.choices[0].message.content)\n * ```\n */\nexport async function chat(config: SylphxConfig, input: ChatInput): Promise<ChatResult> {\n\tconst response = await fetch(buildApiUrl(config, '/chat/completions'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t...buildHeaders(config),\n\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tmodel: input.model,\n\t\t\tmessages: input.messages,\n\t\t\ttemperature: input.temperature,\n\t\t\tmax_tokens: input.maxTokens,\n\t\t\ttop_p: input.topP,\n\t\t\tfrequency_penalty: input.frequencyPenalty,\n\t\t\tpresence_penalty: input.presencePenalty,\n\t\t\tstop: input.stop,\n\t\t\ttools: input.tools,\n\t\t\ttool_choice: input.toolChoice,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tconst error = await response.json().catch(() => ({ error: { message: 'Chat request failed' } }))\n\t\tthrow new SylphxError(error?.error?.message ?? 'Chat request failed', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\tconst data = await response.json()\n\n\treturn {\n\t\tid: data.id,\n\t\tmodel: data.model,\n\t\tchoices: data.choices.map((c: Record<string, unknown>) => ({\n\t\t\tindex: c.index as number,\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant' as const,\n\t\t\t\tcontent: (c.message as Record<string, unknown>)?.content as string | null,\n\t\t\t\ttool_calls: (c.message as Record<string, unknown>)?.tool_calls as ToolCall[] | undefined,\n\t\t\t},\n\t\t\tfinishReason: c.finish_reason as ChatResult['choices'][0]['finishReason'],\n\t\t})),\n\t\tusage: {\n\t\t\tpromptTokens: data.usage.prompt_tokens,\n\t\t\tcompletionTokens: data.usage.completion_tokens,\n\t\t\ttotalTokens: data.usage.total_tokens,\n\t\t},\n\t}\n}\n\n/**\n * Create a streaming chat completion\n *\n * @example\n * ```typescript\n * const stream = chatStream(config, {\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Write a poem' }],\n * })\n *\n * for await (const chunk of stream) {\n * process.stdout.write(chunk.choices[0].delta.content ?? '')\n * }\n * ```\n */\nexport function chatStream(config: SylphxConfig, input: ChatInput): AsyncIterable<ChatStreamChunk> {\n\treturn {\n\t\t[Symbol.asyncIterator]: async function* () {\n\t\t\tconst response = await fetch(buildApiUrl(config, '/chat/completions'), {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(config),\n\t\t\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: input.model,\n\t\t\t\t\tmessages: input.messages,\n\t\t\t\t\ttemperature: input.temperature,\n\t\t\t\t\tmax_tokens: input.maxTokens,\n\t\t\t\t\ttop_p: input.topP,\n\t\t\t\t\tfrequency_penalty: input.frequencyPenalty,\n\t\t\t\t\tpresence_penalty: input.presencePenalty,\n\t\t\t\t\tstop: input.stop,\n\t\t\t\t\ttools: input.tools,\n\t\t\t\t\ttool_choice: input.toolChoice,\n\t\t\t\t\tstream: true,\n\t\t\t\t}),\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst error = await response\n\t\t\t\t\t.json()\n\t\t\t\t\t.catch(() => ({ error: { message: 'Stream request failed' } }))\n\t\t\t\tthrow new SylphxError(error?.error?.message ?? 'Stream request failed', {\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tthrow new SylphxError('No response body', {\n\t\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = ''\n\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\t\tif (done) break\n\n\t\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\t\tbuffer = lines.pop() ?? ''\n\n\t\t\t\t\tfor (const line of lines) {\n\t\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\t\tconst data = line.slice(6).trim()\n\t\t\t\t\t\t\tif (data === '[DONE]') return\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\t\t\tid: chunk.id ?? '',\n\t\t\t\t\t\t\t\t\tmodel: chunk.model ?? input.model,\n\t\t\t\t\t\t\t\t\tchoices: (chunk.choices ?? []).map((c: Record<string, unknown>) => ({\n\t\t\t\t\t\t\t\t\t\tindex: typeof c.index === 'number' ? c.index : 0,\n\t\t\t\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\t\t\t\trole: (c.delta as Record<string, unknown>)?.role as 'assistant' | undefined,\n\t\t\t\t\t\t\t\t\t\t\tcontent: (c.delta as Record<string, unknown>)?.content as string | undefined,\n\t\t\t\t\t\t\t\t\t\t\ttool_calls: (c.delta as Record<string, unknown>)?.tool_calls as\n\t\t\t\t\t\t\t\t\t\t\t\t| ToolCall[]\n\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfinishReason:\n\t\t\t\t\t\t\t\t\t\t\t(c.finish_reason as ChatStreamChunk['choices'][0]['finishReason']) ?? null,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Skip malformed JSON\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treader.releaseLock()\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Create embeddings\n *\n * @example\n * ```typescript\n * const result = await embed(config, {\n * model: 'text-embedding-3-small',\n * input: ['Hello world', 'Goodbye world'],\n * })\n *\n * console.log(result.data[0].embedding) // [0.123, -0.456, ...]\n * ```\n */\nexport async function embed(config: SylphxConfig, input: EmbedInput): Promise<EmbedResult> {\n\tconst response = await fetch(buildApiUrl(config, '/embeddings'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t...buildHeaders(config),\n\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tmodel: input.model,\n\t\t\tinput: input.input,\n\t\t\tdimensions: input.dimensions,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tconst error = await response\n\t\t\t.json()\n\t\t\t.catch(() => ({ error: { message: 'Embedding request failed' } }))\n\t\tthrow new SylphxError(error?.error?.message ?? 'Embedding request failed', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\tconst data = await response.json()\n\n\treturn {\n\t\tmodel: data.model,\n\t\tdata: data.data,\n\t\tusage: {\n\t\t\tpromptTokens: data.usage.prompt_tokens,\n\t\t\ttotalTokens: data.usage.total_tokens,\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Simple text completion (convenience wrapper)\n *\n * @example\n * ```typescript\n * const text = await complete(config, 'gpt-4o', 'Explain quantum computing in one sentence.')\n * ```\n */\nexport async function complete(\n\tconfig: SylphxConfig,\n\tmodel: string,\n\tprompt: string,\n\toptions?: Omit<ChatInput, 'model' | 'messages'>,\n): Promise<string> {\n\tconst response = await chat(config, {\n\t\tmodel,\n\t\tmessages: [{ role: 'user', content: prompt }],\n\t\t...options,\n\t})\n\treturn response.choices[0]?.message.content ?? ''\n}\n\n/**\n * Stream text to string (collects all chunks)\n *\n * @example\n * ```typescript\n * const text = await streamToString(config, {\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Write a haiku' }],\n * })\n * ```\n */\nexport async function streamToString(config: SylphxConfig, input: ChatInput): Promise<string> {\n\tlet result = ''\n\tfor await (const chunk of chatStream(config, input)) {\n\t\tresult += chunk.choices[0]?.delta.content ?? ''\n\t}\n\treturn result\n}\n","/**\n * Billing Functions\n *\n * Pure functions for billing and subscriptions.\n * Uses REST API at /api/sdk/billing/* for all operations.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /billing/plans`,\n * `/billing/subscription`, `POST /billing/checkout`, `/billing/portal`,\n * `GET /billing/balance`, `/billing/usage`.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tBillingBalanceResponse,\n\tBillingCheckoutRequest,\n\tBillingCheckoutResponse,\n\tBillingPortalRequest,\n\tBillingPortalResponse,\n\tBillingUsageResponse,\n\tSdkBillingPlan,\n\tSdkBillingSubscription,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type Plan = SdkBillingPlan\nexport type Subscription = SdkBillingSubscription\nexport type { BillingCheckoutRequest as CheckoutRequest }\nexport type { BillingCheckoutResponse as CheckoutResponse }\nexport type { BillingPortalRequest as PortalRequest }\nexport type { BillingPortalResponse as PortalResponse }\nexport type { BillingBalanceResponse as BalanceResponse }\nexport type { BillingUsageResponse as UsageResponse }\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get available plans\n *\n * @example\n * ```typescript\n * const plans = await getPlans(config)\n * plans.forEach(plan => console.log(plan.name, plan.monthlyPrice))\n * ```\n */\nexport async function getPlans(config: SylphxConfig): Promise<Plan[]> {\n\treturn callApi<Plan[]>(config, '/billing/plans')\n}\n\n/**\n * Get user's subscription\n *\n * @example\n * ```typescript\n * const sub = await getSubscription(config, 'user-123')\n * if (sub?.status === 'active') {\n * console.log(`Active plan: ${sub.planSlug}`)\n * }\n * ```\n */\nexport async function getSubscription(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<Subscription | null> {\n\treturn callApi<Subscription | null>(config, '/billing/subscription', {\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Create a checkout session\n *\n * @example\n * ```typescript\n * const { checkoutUrl } = await createCheckout(config, {\n * userId: 'user-123',\n * planSlug: 'pro',\n * interval: 'monthly',\n * successUrl: 'https://myapp.com/success',\n * cancelUrl: 'https://myapp.com/pricing',\n * })\n *\n * window.location.href = checkoutUrl\n * ```\n */\nexport async function createCheckout(\n\tconfig: SylphxConfig,\n\tinput: BillingCheckoutRequest,\n): Promise<BillingCheckoutResponse> {\n\treturn callApi<BillingCheckoutResponse>(config, '/billing/checkout', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Create a billing portal session\n *\n * @example\n * ```typescript\n * const { portalUrl } = await createPortalSession(config, {\n * userId: 'user-123',\n * returnUrl: window.location.href,\n * })\n *\n * window.location.href = portalUrl\n * ```\n */\nexport async function createPortalSession(\n\tconfig: SylphxConfig,\n\tinput: BillingPortalRequest,\n): Promise<BillingPortalResponse> {\n\treturn callApi<BillingPortalResponse>(config, '/billing/portal', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get billing balance (credits, etc.)\n *\n * @example\n * ```typescript\n * const balance = await getBillingBalance(config)\n * console.log(`Balance: ${balance.balance.currentFormatted}`)\n * ```\n */\nexport async function getBillingBalance(config: SylphxConfig): Promise<BillingBalanceResponse> {\n\treturn callApi<BillingBalanceResponse>(config, '/billing/balance')\n}\n\n/**\n * Get billing usage\n *\n * @example\n * ```typescript\n * const usage = await getBillingUsage(config, { month: '2024-01' })\n * ```\n */\nexport async function getBillingUsage(\n\tconfig: SylphxConfig,\n\toptions?: { month?: string },\n): Promise<BillingUsageResponse> {\n\treturn callApi<BillingUsageResponse>(config, '/billing/usage', {\n\t\tquery: options?.month ? { month: options.month } : undefined,\n\t})\n}\n","/**\n * Storage SDK — pure functional, namespaced. Per ADR-100.\n *\n * Wire is the contract in `@sylphx/contract` (`schemas/storage.ts` +\n * `endpoints/storage.ts`). This module is the only public surface for\n * uploads / files; consumers import the `storage` namespace.\n *\n * Features (built-in defaults, ADR-100 §2.8):\n * - Idempotency-Key auto-generated (UUIDv7) on every POST\n * - Single-part PUT or multipart, picked server-side from `size`\n * - Streaming SHA-256 via the Web Crypto API\n * - Resumable: persists `(uploadId, completedParts[])` to localStorage when available\n * - AbortSignal cancellation; auto-DELETE upload session on abort\n * - Exponential backoff with full jitter (5 retries, 1s base, 30s cap)\n * - Progress: byte-accurate via XHR (browser) or stream sampling (node)\n *\n * No vendor SDKs. Pure `fetch` + `XMLHttpRequest`.\n */\n\nimport type {\n\tFile as ContractFile,\n\tCopyFileRequest,\n\tFileId,\n\tFileVersion,\n\tFileVersionId,\n\tListFilesQuery,\n\tSignedUrlRequest,\n\tTakedownFileRequest,\n\tTakedownFileResult,\n\tUploadCompleteResult,\n\tUploadCreateRequest,\n\tUploadCreateResult,\n\tUploadId,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\nimport {\n\tcalculateBackoffDelay,\n\tisAbortError,\n\tisRetryableError,\n\tsleep,\n\ttoAbortError,\n\tuuidv7,\n\twithRetry,\n} from './lib/retry'\n\n// ============================================================================\n// Public types — sourced from contract (ADR-084 / ADR-100)\n// ============================================================================\n\nexport type {\n\tCopyFileRequest,\n\tFile,\n\tFileId,\n\tFileVersion,\n\tFileVersionId,\n\tFileVisibility,\n\tListFilesQuery,\n\tSignedUrlDisposition,\n\tSignedUrlRequest,\n\tSignedUrlResult,\n\tStorageTakedownReason,\n\tTakedownFileRequest,\n\tTakedownFileResult,\n\tUploadId,\n} from '@sylphx/contract'\n\n// SDK-local helper types (input shapes — not on the wire)\n\nexport interface UploadProgressEvent {\n\tloaded: number\n\ttotal: number\n\tpartsCompleted: number\n\tpartsTotal: number\n}\n\nexport interface UploadCreateOptions {\n\t/** Logical name; preserved as metadata. Defaults to `blob.name` if `File`. */\n\tfilename?: string\n\t/** MIME type override; defaults to `blob.type` or `application/octet-stream`. */\n\tcontentType?: string\n\t/** Logical folder path within the project namespace. */\n\tfolder?: string\n\t/** Defaults to `'private'`. */\n\tvisibility?: 'public' | 'private'\n\t/** Arbitrary user-attached metadata. */\n\tmetadata?: Record<string, unknown>\n\t/** Pre-computed SHA-256 (hex). If absent, the SDK computes it. */\n\tchecksumSha256?: string\n\t/** Fail when a file already exists at `(folder, filename)`. */\n\tifNoneMatch?: '*'\n\t/** Cancellation. Aborts in-flight PUTs and triggers `DELETE /uploads/{id}`. */\n\tsignal?: AbortSignal\n\t/** Override the auto-generated UUIDv7 idempotency key. */\n\tidempotencyKey?: string\n\t/** Progress callback. */\n\tonProgress?: (event: UploadProgressEvent) => void\n}\n\nexport interface ListFilesOptions {\n\tfolder?: string\n\tcursor?: string\n\tlimit?: number\n\tincludeDeleted?: boolean\n}\n\nexport interface SignedUrlOptions {\n\texpiresIn?: number\n\tdisposition?: 'attachment' | 'inline'\n\tuserId?: string\n}\n\nexport interface CopyFileOptions {\n\tfolder?: string\n\tfilename?: string\n\tvisibility?: 'public' | 'private'\n\tmetadata?: Record<string, unknown>\n}\n\nexport type TakedownFileOptions = TakedownFileRequest\n\n// ============================================================================\n// Endpoints (path builders)\n// ============================================================================\n\nconst PATHS = {\n\tuploads: '/storage/uploads',\n\tupload: (id: UploadId | string) => `/storage/uploads/${encodeURIComponent(String(id))}`,\n\tuploadComplete: (id: UploadId | string) =>\n\t\t`/storage/uploads/${encodeURIComponent(String(id))}:complete`,\n\tuploadPart: (id: UploadId | string, n: number) =>\n\t\t`/storage/uploads/${encodeURIComponent(String(id))}/parts/${n}`,\n\tfiles: '/storage/files',\n\tfile: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}`,\n\tfileTakedown: (id: FileId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}:takedown`,\n\tfileRestore: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}:restore`,\n\tfileSignedUrl: (id: FileId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}:signedUrl`,\n\tfileCopy: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}:copy`,\n\tversions: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}/versions`,\n\tversionRestore: (id: FileId | string, vid: FileVersionId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}/versions/${encodeURIComponent(String(vid))}:restore`,\n} as const\n\n// ============================================================================\n// Runtime feature detection\n// ============================================================================\n\nconst hasXhr = (): boolean =>\n\ttypeof (globalThis as { XMLHttpRequest?: unknown }).XMLHttpRequest !== 'undefined'\n\nconst hasLocalStorage = (): boolean => {\n\ttry {\n\t\tconst ls = (globalThis as { localStorage?: Storage }).localStorage\n\t\treturn Boolean(ls && typeof ls.getItem === 'function')\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// ============================================================================\n// SHA-256 (streaming)\n// ============================================================================\n\nasync function computeSha256Hex(blob: Blob): Promise<string> {\n\tconst subtle = (globalThis as { crypto?: { subtle?: { digest?: SubtleCrypto['digest'] } } })\n\t\t.crypto?.subtle\n\tif (!subtle?.digest) {\n\t\tthrow new SylphxError('Web Crypto SHA-256 support is required for storage uploads', {\n\t\t\tcode: 'NOT_IMPLEMENTED',\n\t\t})\n\t}\n\n\tconst buf = await blob.arrayBuffer()\n\tconst digest = await subtle.digest('SHA-256', buf)\n\treturn bytesToHex(new Uint8Array(digest))\n}\n\nfunction bytesToHex(b: Uint8Array): string {\n\tlet s = ''\n\tfor (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, '0')\n\treturn s\n}\n\n// ============================================================================\n// Resume persistence\n// ============================================================================\n\ninterface ResumeRecord {\n\tuploadId: string\n\tcompletedParts: Array<{ partNumber: number; etag: string }>\n\tupdatedAt: number\n}\n\nconst RESUME_KEY_PREFIX = 'sylphx_upload_'\n\nfunction resumeKey(uploadId: string): string {\n\treturn `${RESUME_KEY_PREFIX}${uploadId}`\n}\n\nfunction persistResume(rec: ResumeRecord): void {\n\tif (hasLocalStorage()) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(resumeKey(rec.uploadId), JSON.stringify(rec))\n\t\t} catch {\n\t\t\t/* quota or sandbox; ignore */\n\t\t}\n\t}\n}\n\nfunction clearResume(uploadId: string): void {\n\tif (hasLocalStorage()) {\n\t\ttry {\n\t\t\tlocalStorage.removeItem(resumeKey(uploadId))\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Low-level HTTP helpers (PUT to presigned URLs)\n// ============================================================================\n\ninterface PutResult {\n\tetag: string\n\tstatus: number\n}\n\n/**\n * PUT a blob (or slice) to a presigned URL. Captures `ETag` from the\n * response. Uses XHR when available (browser progress) and falls back\n * to `fetch` (node).\n */\nfunction putBlob(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\tif (hasXhr()) return putBlobXhr(url, body, headers, signal, onProgress)\n\treturn putBlobFetch(url, body, headers, signal, onProgress)\n}\n\nfunction putBlobXhr(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\treturn new Promise<PutResult>((resolve, reject) => {\n\t\tconst xhr = new XMLHttpRequest()\n\n\t\tconst handleAbort = () => {\n\t\t\ttry {\n\t\t\t\txhr.abort()\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t}\n\n\t\tif (signal?.aborted) {\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t\treturn\n\t\t}\n\t\tsignal?.addEventListener('abort', handleAbort, { once: true })\n\n\t\tif (onProgress) {\n\t\t\txhr.upload.addEventListener('progress', (e) => {\n\t\t\t\tif (e.lengthComputable) onProgress(e.loaded)\n\t\t\t})\n\t\t}\n\n\t\txhr.addEventListener('load', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\tif (xhr.status >= 200 && xhr.status < 300) {\n\t\t\t\tconst etag = resolveXhrEtag(xhr)\n\t\t\t\tresolve({ etag: stripQuotes(etag), status: xhr.status })\n\t\t\t} else {\n\t\t\t\tconst err = new Error(`PUT failed with status ${xhr.status}`) as Error & {\n\t\t\t\t\tstatus: number\n\t\t\t\t}\n\t\t\t\terr.status = xhr.status\n\t\t\t\treject(err)\n\t\t\t}\n\t\t})\n\t\txhr.addEventListener('error', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\treject(new TypeError('Network error during upload'))\n\t\t})\n\t\txhr.addEventListener('abort', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t})\n\n\t\txhr.open('PUT', url)\n\t\tfor (const [k, v] of Object.entries(headers)) {\n\t\t\ttry {\n\t\t\t\txhr.setRequestHeader(k, v)\n\t\t\t} catch {\n\t\t\t\t/* unsafe header — ignore */\n\t\t\t}\n\t\t}\n\t\txhr.send(body)\n\t})\n}\n\nfunction resolveXhrEtag(xhr: XMLHttpRequest): string {\n\tconst canonical = xhr.getResponseHeader('ETag')\n\tif (canonical !== null) return canonical\n\treturn xhr.getResponseHeader('etag') ?? ''\n}\n\nasync function putBlobFetch(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\t// Sample progress via TransformStream byte counter where available.\n\tlet stream: BodyInit = body\n\tif (onProgress && typeof TransformStream !== 'undefined' && typeof body.stream === 'function') {\n\t\tlet loaded = 0\n\t\tconst counter = new TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tloaded += chunk.byteLength\n\t\t\t\tonProgress(loaded)\n\t\t\t\tcontroller.enqueue(chunk)\n\t\t\t},\n\t\t})\n\t\tstream = body.stream().pipeThrough(counter) as unknown as BodyInit\n\t}\n\n\tconst res = await fetch(url, {\n\t\tmethod: 'PUT',\n\t\tbody: stream,\n\t\theaders,\n\t\tsignal,\n\t\t// Required when streaming a request body in Chromium / undici.\n\t\t// Cast: TS lib lacks `duplex`.\n\t\t...({ duplex: 'half' } as Record<string, unknown>),\n\t})\n\tif (!res.ok) {\n\t\tconst err = new Error(`PUT failed with status ${res.status}`) as Error & { status: number }\n\t\terr.status = res.status\n\t\tthrow err\n\t}\n\tconst etag = resolveFetchEtag(res.headers)\n\tif (onProgress) onProgress(body.size)\n\treturn { etag: stripQuotes(etag), status: res.status }\n}\n\nfunction resolveFetchEtag(headers: Headers): string {\n\tconst lowerCase = headers.get('etag')\n\tif (lowerCase !== null) return lowerCase\n\treturn headers.get('ETag') ?? ''\n}\n\nfunction stripQuotes(s: string): string {\n\tif (s.length >= 2 && s.startsWith('\"') && s.endsWith('\"')) return s.slice(1, -1)\n\treturn s\n}\n\n// ============================================================================\n// Upload retry wrapper around PUT\n// ============================================================================\n\nasync function putWithRetry(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\tlet lastErr: unknown\n\tconst maxRetries = 5\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\tif (signal?.aborted) throw toAbortError()\n\t\ttry {\n\t\t\treturn await putBlob(url, body, headers, signal, onProgress)\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) throw err\n\t\t\tlastErr = err\n\t\t\tif (attempt === maxRetries || !isRetryableError(err)) throw err\n\t\t\tawait sleep(calculateBackoffDelay(attempt), signal)\n\t\t}\n\t}\n\tthrow lastErr\n}\n\n// ============================================================================\n// uploads.create — the main flow\n// ============================================================================\n\nasync function uploadsCreate(\n\tconfig: SylphxConfig,\n\tblob: Blob | File,\n\toptions: UploadCreateOptions,\n): Promise<ContractFile> {\n\tconst signal = options.signal\n\tif (signal?.aborted) throw toAbortError()\n\n\tconst filename = options.filename ?? (isFile(blob) ? blob.name : undefined) ?? 'upload.bin'\n\tconst contentType =\n\t\toptions.contentType ??\n\t\t(blob.type && blob.type.length > 0 ? blob.type : 'application/octet-stream')\n\tconst size = blob.size\n\n\tconst checksumSha256 = options.checksumSha256 ?? (await computeSha256Hex(blob))\n\n\tconst idempotencyKey = options.idempotencyKey ?? uuidv7()\n\n\tconst createBody: UploadCreateRequest = {\n\t\tfilename,\n\t\tcontentType,\n\t\tsize,\n\t\tfolder: options.folder,\n\t\tvisibility: options.visibility,\n\t\tmetadata: options.metadata as Record<string, unknown> | undefined,\n\t\tchecksumSha256,\n\t\tifNoneMatch: options.ifNoneMatch,\n\t}\n\n\t// 1. Mint upload session.\n\tconst session = await callApi<UploadCreateResult>(config, PATHS.uploads, {\n\t\tmethod: 'POST',\n\t\tbody: createBody,\n\t\tidempotencyKey,\n\t\tsignal,\n\t})\n\n\tconst uploadId = session.uploadId\n\n\t// Helper: abort the session on cancellation, then propagate.\n\tconst onAborted = async () => {\n\t\ttry {\n\t\t\tawait callApi(config, PATHS.upload(uploadId), { method: 'DELETE' })\n\t\t} catch {\n\t\t\t/* best-effort */\n\t\t}\n\t\tclearResume(String(uploadId))\n\t}\n\n\ttry {\n\t\tif (session.method === 'PUT') {\n\t\t\treturn await runSinglePart(config, blob, session, options, idempotencyKey)\n\t\t}\n\t\treturn await runMultipart(config, blob, session, options, idempotencyKey)\n\t} catch (err) {\n\t\tif (isAbortError(err)) {\n\t\t\tawait onAborted()\n\t\t}\n\t\tthrow err\n\t}\n}\n\nfunction isFile(b: Blob | File): b is File {\n\treturn typeof (b as File).name === 'string'\n}\n\nasync function runSinglePart(\n\tconfig: SylphxConfig,\n\tblob: Blob,\n\tsession: Extract<UploadCreateResult, { method: 'PUT' }>,\n\toptions: UploadCreateOptions,\n\tidempotencyKey: string,\n): Promise<ContractFile> {\n\tconst { onProgress, signal } = options\n\tconst total = blob.size\n\n\tconst trackProgress = onProgress\n\t\t? (loaded: number) => {\n\t\t\t\tonProgress({ loaded, total, partsCompleted: 0, partsTotal: 1 })\n\t\t\t}\n\t\t: undefined\n\n\tconst put = await putWithRetry(session.url, blob, session.headers, signal, trackProgress)\n\n\tif (onProgress) onProgress({ loaded: total, total, partsCompleted: 1, partsTotal: 1 })\n\n\tconst completion = await callApi<UploadCompleteResult>(\n\t\tconfig,\n\t\tPATHS.uploadComplete(session.uploadId),\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: { parts: [{ partNumber: 1, etag: put.etag }] },\n\t\t\tidempotencyKey: `${idempotencyKey}:complete`,\n\t\t\tsignal,\n\t\t},\n\t)\n\n\tclearResume(String(session.uploadId))\n\treturn await fetchFile(config, completion.fileId)\n}\n\nasync function runMultipart(\n\tconfig: SylphxConfig,\n\tblob: Blob,\n\tsession: Extract<UploadCreateResult, { method: 'MULTIPART' }>,\n\toptions: UploadCreateOptions,\n\tidempotencyKey: string,\n): Promise<ContractFile> {\n\tconst { onProgress, signal } = options\n\tconst partSize = session.partSize\n\tconst total = blob.size\n\tconst partsTotal = session.partCount\n\tconst completedParts: Array<{ partNumber: number; etag: string }> = []\n\tconst partLoaded = new Map<number, number>()\n\n\tconst reportProgress = () => {\n\t\tif (!onProgress) return\n\t\tlet loaded = 0\n\t\tfor (const v of partLoaded.values()) loaded += v\n\t\tonProgress({ loaded, total, partsCompleted: completedParts.length, partsTotal })\n\t}\n\n\tfor (const part of session.parts) {\n\t\tif (signal?.aborted) throw toAbortError()\n\t\tconst offset = (part.partNumber - 1) * partSize\n\t\tconst end = Math.min(offset + partSize, total)\n\t\tconst slice = blob.slice(offset, end)\n\n\t\tconst trackProgress = onProgress\n\t\t\t? (loaded: number) => {\n\t\t\t\t\tpartLoaded.set(part.partNumber, loaded)\n\t\t\t\t\treportProgress()\n\t\t\t\t}\n\t\t\t: undefined\n\n\t\tconst result = await putWithRetry(part.url, slice, {}, signal, trackProgress)\n\t\tcompletedParts.push({ partNumber: part.partNumber, etag: result.etag })\n\t\tpartLoaded.set(part.partNumber, slice.size)\n\t\treportProgress()\n\n\t\t// Persist after each part for resume.\n\t\tpersistResume({\n\t\t\tuploadId: String(session.uploadId),\n\t\t\tcompletedParts: [...completedParts],\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tconst completion = await callApi<UploadCompleteResult>(\n\t\tconfig,\n\t\tPATHS.uploadComplete(session.uploadId),\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: { parts: completedParts },\n\t\t\tidempotencyKey: `${idempotencyKey}:complete`,\n\t\t\tsignal,\n\t\t},\n\t)\n\n\tclearResume(String(session.uploadId))\n\treturn await fetchFile(config, completion.fileId)\n}\n\nasync function fetchFile(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.file(fileId), { method: 'GET' })\n}\n\n// ============================================================================\n// uploads.abort\n// ============================================================================\n\nasync function uploadsAbort(config: SylphxConfig, uploadId: UploadId | string): Promise<void> {\n\tawait withRetry(() => callApi(config, PATHS.upload(uploadId), { method: 'DELETE' }), {\n\t\tmaxRetries: 3,\n\t})\n\tclearResume(String(uploadId))\n}\n\n// ============================================================================\n// files.* — list / get / delete / restore / signedUrl / copy\n// ============================================================================\n\ninterface ListPage {\n\tfiles: ContractFile[]\n\tnextCursor: string | null\n}\n\nfunction filesListPage(\n\tconfig: SylphxConfig,\n\toptions: ListFilesOptions,\n\tcursor: string | undefined,\n): Promise<ListPage> {\n\tconst query: ListFilesQuery = {\n\t\tfolder: options.folder,\n\t\tcursor: cursor ?? options.cursor,\n\t\tlimit: options.limit,\n\t\tincludeDeleted: options.includeDeleted,\n\t}\n\treturn callApi<ListPage>(config, PATHS.files, {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tfolder: query.folder,\n\t\t\tcursor: query.cursor,\n\t\t\tlimit: query.limit,\n\t\t\tincludeDeleted: query.includeDeleted,\n\t\t},\n\t})\n}\n\nfunction filesList(\n\tconfig: SylphxConfig,\n\toptions: ListFilesOptions = {},\n): AsyncIterable<ContractFile> & {\n\tfetchPage: (cursor?: string) => Promise<ListPage>\n} {\n\tconst fetchPage = (cursor?: string) => filesListPage(config, options, cursor)\n\tconst iter: AsyncIterable<ContractFile> = {\n\t\t[Symbol.asyncIterator]: async function* () {\n\t\t\tlet cursor: string | null | undefined = options.cursor\n\t\t\tdo {\n\t\t\t\tconst page = await fetchPage(cursor ?? undefined)\n\t\t\t\tfor (const f of page.files) yield f\n\t\t\t\tcursor = page.nextCursor\n\t\t\t} while (cursor)\n\t\t},\n\t}\n\treturn Object.assign(iter, { fetchPage })\n}\n\nasync function filesGet(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.file(fileId), { method: 'GET' })\n}\n\nasync function filesDelete(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n): Promise<{ id: FileId; isDeleted: true }> {\n\treturn callApi(config, PATHS.file(fileId), { method: 'DELETE' })\n}\n\nasync function filesTakedown(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: TakedownFileOptions,\n): Promise<TakedownFileResult> {\n\treturn callApi<TakedownFileResult>(config, PATHS.fileTakedown(fileId), {\n\t\tmethod: 'POST',\n\t\tbody: options,\n\t})\n}\n\nasync function filesRestore(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.fileRestore(fileId), { method: 'POST' })\n}\n\nasync function filesSignedUrl(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: SignedUrlOptions = {},\n): Promise<{ url: string; expiresAt: string; file: ContractFile }> {\n\tconst body: SignedUrlRequest = {\n\t\texpiresIn: options.expiresIn,\n\t\tdisposition: options.disposition,\n\t\tuserId: options.userId,\n\t}\n\treturn callApi(config, PATHS.fileSignedUrl(fileId), {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\nasync function filesCopy(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: CopyFileOptions,\n): Promise<ContractFile> {\n\tconst body: CopyFileRequest = {\n\t\tfolder: options.folder,\n\t\tfilename: options.filename,\n\t\tvisibility: options.visibility,\n\t\tmetadata: options.metadata as Record<string, unknown> | undefined,\n\t}\n\treturn callApi<ContractFile>(config, PATHS.fileCopy(fileId), {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\nasync function filesVersionsList(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n): Promise<FileVersion[]> {\n\tconst data = await callApi<{ versions: FileVersion[] }>(config, PATHS.versions(fileId), {\n\t\tmethod: 'GET',\n\t})\n\treturn data.versions\n}\n\nasync function filesVersionsRestore(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\tversionId: FileVersionId | string,\n): Promise<{ file: ContractFile; version: FileVersion }> {\n\treturn callApi(config, PATHS.versionRestore(fileId, versionId), { method: 'POST' })\n}\n\n// ============================================================================\n// Public namespace\n// ============================================================================\n\n/**\n * `storage` namespace — the only public surface for storage in `@sylphx/sdk`.\n *\n * @example\n * ```ts\n * import { storage } from '@sylphx/sdk'\n *\n * const file = await storage.uploads.create(config, blob, {\n * filename: 'report.pdf',\n * folder: 'documents',\n * onProgress: (e) => console.log(`${e.loaded}/${e.total}`),\n * })\n *\n * for await (const f of storage.files.list(config, { folder: 'documents' })) {\n * console.log(f.id, f.filename)\n * }\n * ```\n */\nexport const storage = {\n\tuploads: {\n\t\tcreate: uploadsCreate,\n\t\tabort: uploadsAbort,\n\t},\n\tfiles: {\n\t\tlist: filesList,\n\t\tget: filesGet,\n\t\tdelete: filesDelete,\n\t\ttakedown: filesTakedown,\n\t\trestore: filesRestore,\n\t\tsignedUrl: filesSignedUrl,\n\t\tcopy: filesCopy,\n\t\tversions: {\n\t\t\tlist: filesVersionsList,\n\t\t\trestore: filesVersionsRestore,\n\t\t},\n\t},\n} as const\n\n// Re-export `SylphxError` for catch-handlers in user code.\nexport { SylphxError }\n","/**\n * Retry & idempotency primitives — shared across SDK modules.\n *\n * Per ADR-100 §2.8, every BaaS write is retried with exponential backoff\n * + full jitter (AWS S3 pattern), respects `Retry-After`, and carries an\n * auto-generated UUIDv7 `Idempotency-Key`. Pulled out of `storage.ts`\n * into a shared module so other features can opt in.\n */\n\nimport { BASE_RETRY_DELAY_MS, MAX_RETRY_DELAY_MS } from '../constants'\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface RetryConfig {\n\t/** Maximum retry attempts (initial attempt + retries). Default: 5 (AWS S3). */\n\tmaxRetries: number\n\t/** Base delay in milliseconds. Default: `BASE_RETRY_DELAY_MS` (1s). */\n\tbaseDelayMs: number\n\t/** Maximum delay cap in milliseconds. Default: `MAX_RETRY_DELAY_MS` (30s). */\n\tmaxDelayMs: number\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n\tmaxRetries: 5,\n\tbaseDelayMs: BASE_RETRY_DELAY_MS,\n\tmaxDelayMs: MAX_RETRY_DELAY_MS,\n}\n\n// ============================================================================\n// Backoff\n// ============================================================================\n\n/**\n * Full-jitter exponential backoff (AWS recommended).\n * Formula: random(0, min(cap, base * 2^attempt))\n */\nexport function calculateBackoffDelay(\n\tattempt: number,\n\tconfig: RetryConfig = DEFAULT_RETRY_CONFIG,\n): number {\n\tconst exp = config.baseDelayMs * 2 ** attempt\n\tconst capped = Math.min(exp, config.maxDelayMs)\n\treturn Math.random() * capped\n}\n\n/** Sleep for `ms`, respecting an optional `AbortSignal`. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(toAbortError())\n\t\t\treturn\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms)\n\t\tsignal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\treject(toAbortError())\n\t\t\t},\n\t\t\t{ once: true },\n\t\t)\n\t})\n}\n\n// ============================================================================\n// Retry classification\n// ============================================================================\n\n/** HTTP status codes that should be retried (per ADR-100 §2.8). */\nexport const RETRYABLE_STATUSES: ReadonlySet<number> = new Set([408, 425, 429])\n\n/**\n * Should this error trigger a retry?\n * - `AbortError`: never (user cancelled)\n * - `TypeError` / fetch network error: yes\n * - HTTP status: 408, 425, 429, 500–599\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (isAbortError(error)) return false\n\tif (error instanceof TypeError) return true\n\tif (error instanceof Error && 'status' in error) {\n\t\tconst status = (error as { status: number }).status\n\t\treturn status >= 500 || RETRYABLE_STATUSES.has(status)\n\t}\n\treturn false\n}\n\n/** True if the error is an `AbortError` (DOMException or named Error). */\nexport function isAbortError(error: unknown): boolean {\n\tif (error instanceof DOMException && error.name === 'AbortError') return true\n\tif (error instanceof Error && error.name === 'AbortError') return true\n\treturn false\n}\n\n/** Build a portable AbortError that works in node and browser. */\nexport function toAbortError(message = 'Aborted'): Error {\n\tif (typeof DOMException !== 'undefined') {\n\t\treturn new DOMException(message, 'AbortError')\n\t}\n\tconst err = new Error(message)\n\terr.name = 'AbortError'\n\treturn err\n}\n\n// ============================================================================\n// Retry wrapper\n// ============================================================================\n\nexport interface RetryOptions extends Partial<RetryConfig> {\n\tsignal?: AbortSignal\n\t/** Hook fired before each retry; receives 0-based attempt count + delay (ms). */\n\tonRetry?: (attempt: number, delayMs: number, error: unknown) => void\n}\n\n/**\n * Run `fn` with full-jitter exponential backoff. Honours `Retry-After`\n * if the thrown error carries it (e.g. `SylphxError`).\n *\n * `fn` should throw on error; the retry loop inspects and re-runs.\n */\nexport async function withRetry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {\n\tconst cfg: RetryConfig = {\n\t\tmaxRetries: options.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,\n\t\tbaseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,\n\t\tmaxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,\n\t}\n\tlet lastError: unknown\n\n\tfor (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {\n\t\tif (options.signal?.aborted) throw toAbortError('Aborted')\n\t\ttry {\n\t\t\treturn await fn()\n\t\t} catch (err) {\n\t\t\tlastError = err\n\t\t\tif (isAbortError(err)) throw err\n\t\t\tif (attempt === cfg.maxRetries) break\n\t\t\tif (!isRetryableError(err)) throw err\n\n\t\t\tconst retryAfter = extractRetryAfter(err)\n\t\t\tconst delay = retryAfter ?? calculateBackoffDelay(attempt, cfg)\n\t\t\toptions.onRetry?.(attempt, delay, err)\n\t\t\tawait sleep(delay, options.signal)\n\t\t}\n\t}\n\tthrow lastError\n}\n\nfunction extractRetryAfter(err: unknown): number | undefined {\n\tif (err && typeof err === 'object' && 'retryAfter' in err) {\n\t\tconst v = (err as { retryAfter?: number }).retryAfter\n\t\tif (typeof v === 'number' && v > 0) return v * 1000\n\t}\n\treturn undefined\n}\n\n// ============================================================================\n// Idempotency keys (UUIDv7)\n// ============================================================================\n\n/**\n * Generate a UUIDv7 — 48-bit Unix-ms timestamp prefix + random tail.\n * Time-sortable, monotonic-ish; matches Stripe/RFC 9562. Falls back to\n * v4 randomness via `crypto.randomUUID` if available; final fallback is\n * a tiny PRNG-based generator for environments without WebCrypto.\n *\n * Spec: https://www.rfc-editor.org/rfc/rfc9562#section-5.7\n */\nexport function uuidv7(): string {\n\tconst ms = BigInt(Date.now())\n\t// 16 random bytes\n\tconst bytes = randomBytes(16)\n\t// Time hi/lo: 48 bits → bytes 0-5\n\tbytes[0] = Number((ms >> 40n) & 0xffn)\n\tbytes[1] = Number((ms >> 32n) & 0xffn)\n\tbytes[2] = Number((ms >> 24n) & 0xffn)\n\tbytes[3] = Number((ms >> 16n) & 0xffn)\n\tbytes[4] = Number((ms >> 8n) & 0xffn)\n\tbytes[5] = Number(ms & 0xffn)\n\t// Version 7 in upper nibble of byte 6\n\tbytes[6] = (bytes[6] & 0x0f) | 0x70\n\t// Variant (RFC 9562) in upper bits of byte 8\n\tbytes[8] = (bytes[8] & 0x3f) | 0x80\n\n\tconst hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n\treturn `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n\nfunction randomBytes(n: number): Uint8Array {\n\tconst out = new Uint8Array(n)\n\tconst c = (globalThis as { crypto?: { getRandomValues?: (a: Uint8Array) => Uint8Array } }).crypto\n\tif (c?.getRandomValues) {\n\t\tc.getRandomValues(out)\n\t\treturn out\n\t}\n\tfor (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256)\n\treturn out\n}\n","/**\n * Push Notification Service Worker Template\n *\n * This module provides a service worker implementation for handling\n * push notifications. Apps should copy or import this into their\n * service worker file.\n *\n * ## Industry Patterns Implemented (OneSignal/FCM)\n * - Push event handling with notification display\n * - Notification click with deep link navigation\n * - Notification close tracking\n * - Token refresh handling\n * - Background sync for offline actions\n *\n * ## Usage\n *\n * Create a service worker file in your app's public directory:\n *\n * ```typescript\n * // public/sw.ts or src/service-worker.ts\n * import { initPushServiceWorker } from '@sylphx/platform-sdk/notifications'\n *\n * initPushServiceWorker({\n * defaultIcon: '/icon-192.png',\n * defaultBadge: '/badge-72.png',\n * onNotificationClick: (data) => {\n * // Custom click handling\n * console.log('Notification clicked:', data)\n * },\n * })\n * ```\n */\n\n/**\n * Service Worker type definitions\n * These are minimal type definitions for Service Worker APIs.\n * Full types available with `lib: [\"WebWorker\"]` in tsconfig.\n */\ninterface PushEventData {\n\tjson(): unknown\n\ttext(): string\n}\n\ninterface PushEvent extends ExtendableEvent {\n\tdata: PushEventData | null\n}\n\ninterface NotificationEvent extends ExtendableEvent {\n\tnotification: Notification & {\n\t\tdata?: Record<string, unknown>\n\t\tclose(): void\n\t}\n\taction?: string\n}\n\ninterface WindowClient {\n\turl: string\n\tfocus(): Promise<WindowClient>\n}\n\ninterface Clients {\n\tmatchAll(options: { type: 'window'; includeUncontrolled: boolean }): Promise<WindowClient[]>\n\topenWindow(url: string): Promise<WindowClient | null>\n\tclaim(): Promise<void>\n}\n\ninterface ExtendableEvent extends Event {\n\twaitUntil(promise: Promise<unknown>): void\n}\n\ninterface ServiceWorkerRegistration {\n\tshowNotification(title: string, options?: NotificationOptions): Promise<void>\n}\n\ninterface ServiceWorkerGlobalScopeSubset {\n\treadonly registration: ServiceWorkerRegistration\n\treadonly clients: Clients\n\taddEventListener(type: 'push', listener: (event: PushEvent) => void): void\n\taddEventListener(\n\t\ttype: 'notificationclick' | 'notificationclose',\n\t\tlistener: (event: NotificationEvent) => void,\n\t): void\n\taddEventListener(type: 'activate', listener: (event: ExtendableEvent) => void): void\n}\n\ndeclare const self: ServiceWorkerGlobalScopeSubset\n\n/**\n * Notification payload from Sylphx platform\n */\nexport interface PushNotificationPayload {\n\t/** Notification title */\n\ttitle: string\n\t/** Notification body text */\n\tbody: string\n\t/** Icon URL (optional, falls back to default) */\n\ticon?: string\n\t/** Badge URL for Android (optional) */\n\tbadge?: string\n\t/** Image URL for expanded notification (optional) */\n\timage?: string\n\t/** Click action URL (optional) */\n\turl?: string\n\t/** Action buttons (optional) */\n\tactions?: Array<{\n\t\taction: string\n\t\ttitle: string\n\t\ticon?: string\n\t}>\n\t/** Custom data payload */\n\tdata?: Record<string, unknown>\n\t/** Notification tag for grouping (optional) */\n\ttag?: string\n\t/** Whether to require interaction (optional) */\n\trequireInteraction?: boolean\n\t/** Vibration pattern (optional) */\n\tvibrate?: number[]\n\t/** Silent notification (optional) */\n\tsilent?: boolean\n}\n\n/**\n * Service worker configuration options\n */\nexport interface PushServiceWorkerConfig {\n\t/** Default icon for notifications without an icon */\n\tdefaultIcon?: string\n\t/** Default badge for notifications without a badge */\n\tdefaultBadge?: string\n\t/** Called when notification is clicked */\n\tonNotificationClick?: (data: PushNotificationPayload) => void\n\t/** Called when notification is closed without clicking */\n\tonNotificationClose?: (data: PushNotificationPayload) => void\n\t/** Platform API URL for analytics/token refresh */\n\tplatformUrl?: string\n\t/** App ID for API calls */\n\tappId?: string\n}\n\n/**\n * Initialize push notification handling in service worker\n *\n * Call this in your service worker file to enable push notification handling.\n *\n * NOTE: This function should only be called from within a service worker context.\n * The types are loosely defined to work in both browser and service worker contexts.\n *\n * @example\n * ```typescript\n * // In your service worker (e.g., public/sw.ts)\n * initPushServiceWorker({\n * defaultIcon: '/icon-192.png',\n * defaultBadge: '/badge-72.png',\n * })\n * ```\n */\nexport function initPushServiceWorker(config: PushServiceWorkerConfig = {}): void {\n\tconst { defaultIcon, defaultBadge, onNotificationClick, onNotificationClose } = config\n\n\t// Handle push events (when notification arrives)\n\tself.addEventListener('push', (event) => {\n\t\tif (!event.data) {\n\t\t\tconsole.warn('[Sylphx SW] Push event received without data')\n\t\t\treturn\n\t\t}\n\n\t\tlet payload: PushNotificationPayload\n\t\ttry {\n\t\t\tpayload = event.data.json() as PushNotificationPayload\n\t\t} catch {\n\t\t\t// Fallback for plain text payloads\n\t\t\tpayload = {\n\t\t\t\ttitle: 'Notification',\n\t\t\t\tbody: event.data.text(),\n\t\t\t}\n\t\t}\n\n\t\t// Build notification options (compatible with both browser and SW contexts)\n\t\tconst notificationOptions = {\n\t\t\tbody: payload.body,\n\t\t\ticon: payload.icon || defaultIcon,\n\t\t\tbadge: payload.badge || defaultBadge,\n\t\t\timage: payload.image,\n\t\t\tdata: {\n\t\t\t\t...payload.data,\n\t\t\t\turl: payload.url,\n\t\t\t\t_sylphxPayload: payload,\n\t\t\t},\n\t\t\ttag: payload.tag,\n\t\t\trequireInteraction: payload.requireInteraction ?? false,\n\t\t\tvibrate: payload.vibrate,\n\t\t\tsilent: payload.silent ?? false,\n\t\t\tactions: payload.actions,\n\t\t}\n\n\t\tevent.waitUntil(self.registration.showNotification(payload.title, notificationOptions))\n\t})\n\n\t// Handle notification click events\n\tself.addEventListener('notificationclick', (event) => {\n\t\tevent.notification.close()\n\n\t\tconst data = event.notification.data\n\t\tconst payload = data?._sylphxPayload as PushNotificationPayload | undefined\n\t\tconst url = data?.url as string | undefined\n\n\t\t// Call custom handler if provided\n\t\tif (onNotificationClick && payload) {\n\t\t\tonNotificationClick(payload)\n\t\t}\n\n\t\t// Handle action button clicks\n\t\tif (event.action) {\n\t\t}\n\n\t\t// Navigate to URL if provided\n\t\tif (url) {\n\t\t\tevent.waitUntil(\n\t\t\t\tself.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {\n\t\t\t\t\t// Try to focus an existing window with this URL\n\t\t\t\t\tfor (const client of clientList) {\n\t\t\t\t\t\tif (client.url === url && 'focus' in client) {\n\t\t\t\t\t\t\treturn client.focus()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Open a new window if no existing window found\n\t\t\t\t\treturn self.clients.openWindow(url)\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t})\n\n\t// Handle notification close events (for analytics)\n\tself.addEventListener('notificationclose', (event) => {\n\t\tconst data = event.notification.data\n\t\tconst payload = data?._sylphxPayload as PushNotificationPayload | undefined\n\n\t\tif (onNotificationClose && payload) {\n\t\t\tonNotificationClose(payload)\n\t\t}\n\t})\n\n\t// Handle service worker activation\n\tself.addEventListener('activate', (event) => {\n\t\tevent.waitUntil(\n\t\t\t// Claim all clients immediately\n\t\t\tself.clients.claim(),\n\t\t)\n\t})\n}\n\n/**\n * Helper to create a simple service worker script content\n *\n * For apps that want to dynamically generate their service worker,\n * this returns the JavaScript content as a string.\n *\n * @example\n * ```typescript\n * // In a route handler\n * export function GET() {\n * const content = createServiceWorkerScript({\n * defaultIcon: '/icon-192.png',\n * })\n * return new Response(content, {\n * headers: { 'Content-Type': 'application/javascript' },\n * })\n * }\n * ```\n */\nexport function createServiceWorkerScript(config: PushServiceWorkerConfig = {}): string {\n\tconst { defaultIcon = '/icon-192.png', defaultBadge = '/badge-72.png' } = config\n\n\treturn `\n// Sylphx Push Notification Service Worker\n// Auto-generated - do not edit directly\n\nconst DEFAULT_ICON = '${defaultIcon}';\nconst DEFAULT_BADGE = '${defaultBadge}';\n\nself.addEventListener('push', (event) => {\n if (!event.data) return;\n\n let payload;\n try {\n payload = event.data.json();\n } catch {\n payload = { title: 'Notification', body: event.data.text() };\n }\n\n const options = {\n body: payload.body,\n icon: payload.icon || DEFAULT_ICON,\n badge: payload.badge || DEFAULT_BADGE,\n image: payload.image,\n data: { ...payload.data, url: payload.url },\n tag: payload.tag,\n requireInteraction: payload.requireInteraction || false,\n vibrate: payload.vibrate,\n silent: payload.silent || false,\n actions: payload.actions,\n };\n\n event.waitUntil(\n self.registration.showNotification(payload.title, options)\n );\n});\n\nself.addEventListener('notificationclick', (event) => {\n event.notification.close();\n const url = event.notification.data?.url;\n\n if (url) {\n event.waitUntil(\n clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {\n for (const client of clientList) {\n if (client.url === url && 'focus' in client) {\n return client.focus();\n }\n }\n if (clients.openWindow) {\n return clients.openWindow(url);\n }\n })\n );\n }\n});\n\nself.addEventListener('activate', (event) => {\n event.waitUntil(clients.claim());\n});\n\nconsole.log('[Sylphx SW] Push notification service worker active');\n`.trim()\n}\n\n/**\n * Register the service worker from the client side\n *\n * Call this in your app's entry point to register the service worker.\n *\n * @example\n * ```typescript\n * // In your app's entry point (e.g., _app.tsx or layout.tsx)\n * import { registerPushServiceWorker } from '@sylphx/platform-sdk/notifications'\n *\n * useEffect(() => {\n * registerPushServiceWorker('/sw.js')\n * }, [])\n * ```\n */\nexport async function registerPushServiceWorker(\n\tswPath = '/sw.js',\n): Promise<ServiceWorkerRegistration | null> {\n\tif (typeof window === 'undefined') return null\n\tif (!('serviceWorker' in navigator)) {\n\t\tconsole.warn('[Sylphx] Service workers not supported')\n\t\treturn null\n\t}\n\n\ttry {\n\t\tconst registration = await navigator.serviceWorker.register(swPath)\n\t\treturn registration\n\t} catch (error) {\n\t\tconsole.error('[Sylphx] Service worker registration failed:', error)\n\t\treturn null\n\t}\n}\n","/**\n * Notifications Functions\n *\n * Pure functions for push notifications.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/notifications/register`,\n * `/unregister`, `/send`, `/preferences`, `/messages`, `/mobile/config`.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tInAppMessagesResponse as ContractInAppMessagesResponse,\n\tMobileConfigResponse as ContractMobileConfigResponse,\n\tMobileDevice as ContractMobileDevice,\n\tPushPreferencesResponse as ContractPushPreferencesResponse,\n\tRegisterPushResponse as ContractRegisterPushResponse,\n\tSendPushInput as ContractSendPushInput,\n\tInAppMessageFull,\n\tRegisterPushInput,\n\tUnregisterPushInput,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type RegisterPushRequest = RegisterPushInput\nexport type RegisterPushResponse = ContractRegisterPushResponse\nexport type UnregisterPushRequest = UnregisterPushInput\nexport type PushPreferencesResponse = ContractPushPreferencesResponse\n/**\n * Full in-app message envelope the REST API emits under\n * `GET /notifications/messages`. Aliased to the contract's rich shape\n * (`InAppMessageFull`) — the contract also exports a leaner `InAppMessage`\n * primitive keyed off `userId`, which endpoint modules use; the SDK needs\n * the full presentation envelope.\n */\nexport type InAppMessage = InAppMessageFull\nexport type InAppMessagesResponse = ContractInAppMessagesResponse\nexport type MobileConfigResponse = ContractMobileConfigResponse\nexport type MobileDevice = ContractMobileDevice\n\n// SDK-specific types for convenience\nexport interface PushSubscription {\n\tendpoint: string\n\tkeys: {\n\t\tp256dh: string\n\t\tauth: string\n\t}\n}\n\nexport interface PushNotification {\n\ttitle: string\n\tbody: string\n\ticon?: string\n\turl?: string\n}\n\nexport interface PushDeliveryPlatformCounts {\n\treadonly web?: number\n\treadonly ios?: number\n\treadonly android?: number\n}\n\nexport interface SendPushWireResult {\n\treadonly status?: 'delivered' | 'queued' | 'failed'\n\treadonly messageId?: string\n\treadonly reason?: string\n\treadonly sent?: number\n\treadonly failed?: number\n\treadonly platforms?: PushDeliveryPlatformCounts\n\treadonly sentTo?: number\n\treadonly expired?: number\n}\n\nexport type SendPushResult = SendPushWireResult & {\n\treadonly sent: number\n\treadonly failed: number\n\treadonly sentTo: number\n\treadonly expired: number\n}\n\nfunction resolveSentCount(result: SendPushWireResult): number {\n\tif (result.sent !== undefined) return result.sent\n\tif (result.sentTo !== undefined) return result.sentTo\n\treturn result.status === 'delivered' ? 1 : 0\n}\n\nfunction resolveFailedCount(result: SendPushWireResult): number {\n\tif (result.failed !== undefined) return result.failed\n\tif (result.expired !== undefined) return result.expired\n\treturn result.status === 'failed' ? 1 : 0\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Register a push subscription\n *\n * @example\n * ```typescript\n * // Get subscription from browser\n * const registration = await navigator.serviceWorker.ready\n * const sub = await registration.pushManager.subscribe({\n * userVisibleOnly: true,\n * applicationServerKey: vapidPublicKey,\n * })\n *\n * // Register with platform\n * await registerPush(config, {\n * endpoint: sub.endpoint,\n * keys: {\n * p256dh: sub.toJSON().keys!.p256dh,\n * auth: sub.toJSON().keys!.auth,\n * },\n * })\n * ```\n */\nexport async function registerPush(\n\tconfig: SylphxConfig,\n\tsubscription: PushSubscription,\n): Promise<void> {\n\t// Contract path (`/notifications/register`) is now the SSOT after\n\t// ADR-084 Wave 2d reconciliation — matches production SDK.\n\tawait callApi(config, '/notifications/register', {\n\t\tmethod: 'POST',\n\t\tbody: { subscription },\n\t})\n}\n\n/**\n * Unregister a push subscription\n *\n * @example\n * ```typescript\n * await unregisterPush(config, subscription.endpoint)\n * ```\n */\nexport async function unregisterPush(config: SylphxConfig, endpoint: string): Promise<void> {\n\tawait callApi(config, '/notifications/unregister', {\n\t\tmethod: 'POST',\n\t\tbody: { endpoint },\n\t})\n}\n\n/**\n * Send a push notification to a user (admin only)\n *\n * @example\n * ```typescript\n * await sendPush(config, 'user-123', {\n * title: 'New message',\n * body: 'You have a new message',\n * url: '/messages',\n * })\n * ```\n */\nexport async function sendPush(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tnotification: PushNotification,\n): Promise<SendPushResult> {\n\t// Contract path + `PushDeliveryResult` shape both widened to match the\n\t// SDK production surface (`/notifications/send` + counter fields) in\n\t// ADR-084 Wave 2d. The contract type is now a superset that accepts\n\t// the runtime aggregate shape (`sent`/`failed`/`platforms`), the legacy\n\t// counter aliases (`sentTo`/`expired`), or the status union\n\t// (`status`/`messageId`/`reason`). Normalize aliases so old consumers\n\t// and new analytics both get stable fields.\n\tconst body: ContractSendPushInput = {\n\t\tuserId,\n\t\ttitle: notification.title,\n\t\tbody: notification.body,\n\t\t...(notification.icon !== undefined && { icon: notification.icon }),\n\t\t...(notification.url !== undefined && { url: notification.url }),\n\t}\n\tconst result = await callApi<SendPushWireResult>(config, '/notifications/send', {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n\tconst sent = resolveSentCount(result)\n\tconst failed = resolveFailedCount(result)\n\treturn {\n\t\t...result,\n\t\tsent,\n\t\tfailed,\n\t\tsentTo: result.sentTo ?? sent,\n\t\texpired: result.expired ?? failed,\n\t}\n}\n\n/**\n * Get push notification preferences\n *\n * @example\n * ```typescript\n * const prefs = await getPushPreferences(config)\n * ```\n */\nexport async function getPushPreferences(\n\tconfig: SylphxConfig,\n): Promise<{ enabled: boolean; categories: Record<string, boolean> }> {\n\treturn callApi(config, '/notifications/preferences', { method: 'GET' })\n}\n\n/**\n * Update push notification preferences\n *\n * @example\n * ```typescript\n * await updatePushPreferences(config, {\n * enabled: true,\n * categories: { marketing: false, updates: true },\n * })\n * ```\n */\nexport async function updatePushPreferences(\n\tconfig: SylphxConfig,\n\tpreferences: { enabled?: boolean; categories?: Record<string, boolean> },\n): Promise<void> {\n\tawait callApi(config, '/notifications/preferences', {\n\t\tmethod: 'PUT',\n\t\tbody: preferences,\n\t})\n}\n\n// ============================================================================\n// Phase 5.7 — Advanced push: segments, campaigns, scheduled + A/B sends\n//\n// Namespaced under `notifications.segments.*` + `notifications.campaigns.*`\n// to match the Firebase-style SDK pattern elsewhere in this file. Types\n// are intentionally structural (not re-exported from @sylphx/contract\n// yet — contract additions land separately as part of Wave 3 migration).\n// ============================================================================\n\n/**\n * Structured filter DSL for segments. Matches the server-side shape\n * defined in `@sylphx/core/lib/push/filter-dsl.ts`.\n */\nexport type PushSegmentFilter =\n\t| { field: string; op: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte'; value: unknown }\n\t| { field: string; op: 'in'; value: readonly unknown[] }\n\t| { field: string; op: 'contains'; value: unknown }\n\t| { all: readonly PushSegmentFilter[] }\n\t| { any: readonly PushSegmentFilter[] }\n\t| { not: PushSegmentFilter }\n\nexport type PushSegment = {\n\tid: string\n\tname: string\n\tdescription: string | null\n\tfilter: PushSegmentFilter\n\tmemberCount: number | null\n\tcomputedAt: string | null\n\tcreatedAt: string\n\tcreatedBy: string\n}\n\nexport type PushCampaignVariant = {\n\tname: string\n\tweight: number\n\ttitle: string\n\tbody: string\n\turl?: string\n\ticon?: string\n\tdata?: Record<string, unknown>\n}\n\nexport type PushCampaign = {\n\tid: string\n\tname: string\n\tsegmentId: string | null\n\tstatus: 'draft' | 'scheduled' | 'sending' | 'sent' | 'cancelled' | 'failed'\n\tvariants: readonly PushCampaignVariant[]\n\tscheduledAt: string | null\n\tsentAt: string | null\n\tcreatedAt: string\n}\n\nexport type PushCampaignStats = {\n\tcampaignId: string\n\ttotalDeliveries: number\n\tbyVariant: Record<\n\t\tstring,\n\t\t{ pending: number; sent: number; delivered: number; failed: number; clicked: number }\n\t>\n}\n\n/**\n * Segment-management namespace. Requires secret-key auth — call from\n * trusted server contexts only.\n */\nexport const segments = {\n\tasync create(\n\t\tconfig: SylphxConfig,\n\t\tinput: { name: string; description?: string; filter: PushSegmentFilter },\n\t): Promise<PushSegment> {\n\t\treturn callApi(config, '/notifications/segments', { method: 'POST', body: input })\n\t},\n\tasync list(config: SylphxConfig): Promise<{ segments: PushSegment[] }> {\n\t\treturn callApi(config, '/notifications/segments', { method: 'GET' })\n\t},\n\tasync get(config: SylphxConfig, id: string): Promise<PushSegment> {\n\t\treturn callApi(config, `/notifications/segments/${id}`, { method: 'GET' })\n\t},\n\tasync delete(config: SylphxConfig, id: string): Promise<void> {\n\t\tawait callApi(config, `/notifications/segments/${id}`, { method: 'DELETE' })\n\t},\n}\n\n/**\n * Campaign-management namespace. A/B variants assigned deterministically\n * per-user on the server; scheduled campaigns are picked up by the\n * scheduled-send worker.\n */\nexport const campaigns = {\n\tasync create(\n\t\tconfig: SylphxConfig,\n\t\tinput: {\n\t\t\tname: string\n\t\t\tsegmentId?: string\n\t\t\tvariants: readonly PushCampaignVariant[]\n\t\t\tscheduledAt?: string\n\t\t},\n\t): Promise<PushCampaign> {\n\t\treturn callApi(config, '/notifications/campaigns', { method: 'POST', body: input })\n\t},\n\tasync get(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}`, { method: 'GET' })\n\t},\n\tasync schedule(config: SylphxConfig, id: string, scheduledAt: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/schedule`, {\n\t\t\tmethod: 'POST',\n\t\t\tbody: { scheduledAt },\n\t\t})\n\t},\n\tasync send(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/send`, { method: 'POST' })\n\t},\n\tasync cancel(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/cancel`, { method: 'POST' })\n\t},\n\tasync stats(config: SylphxConfig, id: string): Promise<PushCampaignStats> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/stats`, { method: 'GET' })\n\t},\n}\n","/**\n * Triggers Client (ADR-040)\n *\n * Unified scheduling + event dispatch API.\n * Create cron schedules and event triggers that dispatch to Tasks, Runs, or HTTP URLs.\n *\n * ## Usage\n *\n * ### Cron → Task\n * ```typescript\n * import { createServerClient, TriggersClient } from '@sylphx/sdk'\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * const trigger = await TriggersClient.create(config, {\n * name: 'daily-cleanup',\n * source: { type: 'cron', expression: '0 2 * * *' },\n * target: { type: 'task', taskName: 'daily-cleanup' },\n * })\n * ```\n *\n * ### Event → Task (fires when event is published via publishEvent)\n * ```typescript\n * const trigger = await TriggersClient.create(config, {\n * name: 'welcome-email-on-signup',\n * source: { type: 'event', eventName: 'user.signup' },\n * target: { type: 'task', taskName: 'send-welcome-email' },\n * })\n * // Publish from your app:\n * await TriggersClient.publishEvent(config, 'user.signup', { userId: '123' })\n * ```\n *\n * ### Cron → HTTP URL (any language, no code required)\n * ```typescript\n * const trigger = await TriggersClient.create(config, {\n * name: 'nightly-backup',\n * source: { type: 'cron', expression: '0 3 * * *' },\n * target: { type: 'http', url: 'https://myapp.com/api/backup', payload: { type: 'full' } },\n * })\n * ```\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport type { SylphxConfig } from '../../config'\nimport { callApi } from '../../config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TriggerTargetType = 'task' | 'run' | 'http'\nexport type TriggerSourceType = 'cron' | 'event'\nexport type TriggerStatus = 'active' | 'paused' | 'deleted'\nexport type TriggerRunMachineSize = MachineSize\n\nexport interface TaskTarget {\n\ttype: 'task'\n\ttaskName: string\n\thandlerPath?: string\n\tpayload?: Record<string, unknown>\n}\n\nexport interface HttpTarget {\n\ttype: 'http'\n\turl: string\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\theaders?: Record<string, string>\n\tpayload?: Record<string, unknown>\n}\n\nexport interface RunTarget {\n\ttype: 'run'\n\timage: string\n\tcommand: string[]\n\tmachine?: TriggerRunMachineSize\n}\n\nexport type TriggerTarget = TaskTarget | HttpTarget | RunTarget\n\nexport interface CronSource {\n\ttype: 'cron'\n\texpression: string\n}\n\nexport interface EventSource {\n\ttype: 'event'\n\t/** The event name to listen for. e.g. 'user.signup', 'order.paid' */\n\teventName: string\n}\n\nexport type TriggerSource = CronSource | EventSource\n\nexport interface CreateTriggerOptions {\n\tname?: string\n\tsource: TriggerSource\n\ttarget: TriggerTarget\n\tpaused?: boolean\n\t/** Idempotency key — prevents duplicate trigger creation per project+environment */\n\tidempotencyKey?: string\n}\n\nexport interface UpdateTriggerOptions {\n\tname?: string\n\tsource?: TriggerSource\n\tpaused?: boolean\n}\n\nexport interface Trigger {\n\tid: string\n\tname: string\n\ttargetType: TriggerTargetType\n\tsourceType: TriggerSourceType\n\tcronExpression: string | null\n\teventName: string | null\n\thandlerPath: string | null\n\tcallbackUrl: string | null\n\tpayload: unknown\n\tstatus: TriggerStatus\n\tnextRunAt: string | null\n\tlastRunAt: string | null\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\nexport interface ListTriggersResult {\n\ttriggers: Trigger[]\n}\n\nexport interface PublishEventResult {\n\tdispatched: number\n\twaitResolved: number\n\teventName: string\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\n/** Create a new trigger (cron or event source, task/run/http target) */\nasync function createTrigger(\n\tconfig: SylphxConfig,\n\toptions: CreateTriggerOptions,\n): Promise<Trigger> {\n\treturn callApi<Trigger>(config, '/triggers', {\n\t\tmethod: 'POST',\n\t\tbody: options,\n\t})\n}\n\n/** List all triggers for the project */\nasync function listTriggers(config: SylphxConfig): Promise<ListTriggersResult> {\n\treturn callApi<ListTriggersResult>(config, '/triggers')\n}\n\n/** Get a trigger by ID */\nasync function getTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}`)\n}\n\n/** Update a trigger */\nasync function updateTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n\toptions: UpdateTriggerOptions,\n): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}`, {\n\t\tmethod: 'PATCH',\n\t\tbody: options,\n\t})\n}\n\n/** Delete a trigger */\nasync function deleteTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/triggers/${triggerId}`, { method: 'DELETE' })\n}\n\n/** Pause a trigger */\nasync function pauseTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}/pause`, { method: 'POST' })\n}\n\n/** Resume a paused trigger */\nasync function resumeTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}/resume`, { method: 'POST' })\n}\n\n/** Fire a trigger immediately (one-shot, regardless of schedule) */\nasync function fireTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n): Promise<{ success: boolean; message: string }> {\n\treturn callApi<{ success: boolean; message: string }>(config, `/triggers/${triggerId}/fire`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Publish an event — dispatches all active event triggers matching the event name.\n * Endpoint: POST /triggers/events\n *\n * @example\n * ```typescript\n * await TriggersClient.publishEvent(config, 'user.signup', { userId: '123', plan: 'pro' })\n * ```\n */\nasync function publishEvent(\n\tconfig: SylphxConfig,\n\teventName: string,\n\tpayload?: Record<string, unknown>,\n): Promise<PublishEventResult> {\n\treturn callApi<PublishEventResult>(config, '/triggers/events', {\n\t\tmethod: 'POST',\n\t\tbody: { eventName, payload: payload ?? {} },\n\t})\n}\n\n/**\n * Triggers client — namespace object exposing all trigger operations.\n * Use via `TriggersClient.create(...)`, `TriggersClient.publishEvent(...)`, etc.\n */\nexport const TriggersClient = {\n\tcreate: createTrigger,\n\tlist: listTriggers,\n\tget: getTrigger,\n\tupdate: updateTrigger,\n\tdelete: deleteTrigger,\n\tpause: pauseTrigger,\n\tresume: resumeTrigger,\n\tfire: fireTrigger,\n\tpublishEvent,\n} as const\n","/**\n * Tasks Handler\n *\n * Server-side endpoint factory for user task definitions.\n * Implements the Sylphx stateless-replay model:\n *\n * 1. Platform dispatches POST to the app's /api/tasks endpoint.\n * 2. The handler replays from the beginning.\n * 3. Completed steps hit a cache and return immediately.\n * 4. The first uncached step executes, then throws StepCompleteSignal.\n * 5. The platform saves the result and re-dispatches.\n * 6. After all steps complete the handler returns normally.\n *\n * @example\n * ```typescript\n * // app/api/tasks/route.ts\n * import { sylphx } from '@/lib/sylphx'\n *\n * const sendEmail = sylphx.tasks.define('send-email', async (payload, { step }) => {\n * const validated = await step.run('validate', () => validateEmail(payload.to))\n * await step.sleep('cool-down', '5 minutes')\n * return step.run('send', () => sendEmail(validated))\n * })\n *\n * export const { GET, POST } = sylphx.tasks.handler([sendEmail])\n * ```\n */\n\nimport { createHmac, timingSafeEqual } from 'node:crypto'\nimport type { NativeStepContext, NativeTaskDefinition } from './types'\n\n// ============================================================================\n// Signals (control flow via throw)\n// ============================================================================\n\n/**\n * Thrown by step.run() when a step's result is not yet cached.\n * The handler aborts; the platform saves the result and re-dispatches.\n */\nexport class StepCompleteSignal {\n\treadonly _isStepCompleteSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly result: unknown,\n\t) {}\n}\n\n/**\n * Thrown by step.sleep() when the sleep is not yet resolved.\n * The handler aborts; the platform waits and re-dispatches.\n */\nexport class StepSleepSignal {\n\treadonly _isStepSleepSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly duration: string,\n\t) {}\n}\n\n/**\n * Thrown by step.waitForEvent() when the event has not yet been received.\n * The handler aborts; the platform persists the wait record.\n * When the event arrives (via TriggersClient.publishEvent()), the platform\n * resolves the wait and re-dispatches with the event payload in context.waits.\n */\nexport class StepWaitEventSignal {\n\treadonly _isStepWaitEventSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly eventName: string,\n\t\tpublic readonly options: {\n\t\t\t/** Timeout duration, e.g. '24h', '7d'. Task fails if event not received in time. */\n\t\t\ttimeout?: string\n\t\t\t/** Optional JSON filter — only resolve if event payload matches */\n\t\t\tfilter?: Record<string, unknown>\n\t\t} = {},\n\t) {}\n}\n\n// ============================================================================\n// Step context factory\n// ============================================================================\n\n/**\n * Build the step proxy for a single handler invocation.\n *\n * @param completedSteps Map of stepName → cached result\n * @param resolvedWaits Map of stepName → wait result (sleep = undefined, event = event payload)\n */\nexport function createStepContext(\n\tcompletedSteps: Map<string, unknown>,\n\tresolvedWaits: Map<string, unknown>,\n): NativeStepContext {\n\treturn {\n\t\t/**\n\t\t * Execute a named step.\n\t\t *\n\t\t * - If cached: return the cached value immediately (replay).\n\t\t * - If not cached: execute fn, then throw StepCompleteSignal with the result.\n\t\t */\n\t\tasync run<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n\t\t\tif (completedSteps.has(name)) {\n\t\t\t\treturn completedSteps.get(name) as T\n\t\t\t}\n\n\t\t\tconst result = await fn()\n\t\t\tthrow new StepCompleteSignal(name, result)\n\t\t},\n\n\t\t/**\n\t\t * Sleep for the given duration.\n\t\t *\n\t\t * - If resolved (platform woke us up): return immediately.\n\t\t * - If not resolved: throw StepSleepSignal to pause execution.\n\t\t */\n\t\tasync sleep(name: string, duration: string): Promise<void> {\n\t\t\tif (resolvedWaits.has(name)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthrow new StepSleepSignal(name, duration)\n\t\t},\n\n\t\t/**\n\t\t * Pause execution until a named event is published via TriggersClient.publishEvent().\n\t\t *\n\t\t * - If event already arrived (platform re-dispatched with result): return event payload.\n\t\t * - If not yet arrived: throw StepWaitEventSignal to pause execution.\n\t\t *\n\t\t * @param name Step identifier (unique within handler).\n\t\t * @param eventName The event name to listen for (e.g. 'user.approved').\n\t\t * @param options Optional timeout ('24h', '7d') and payload filter.\n\t\t *\n\t\t * @example Human-in-the-loop approval\n\t\t * ```typescript\n\t\t * const approval = await step.waitForEvent('wait-approval', 'order.approved', {\n\t\t * timeout: '48h',\n\t\t * filter: { orderId: payload.orderId },\n\t\t * })\n\t\t * if (!approval) throw new Error('Approval timed out')\n\t\t * await sendConfirmation(approval.approvedBy)\n\t\t * ```\n\t\t */\n\t\tasync waitForEvent<T = unknown>(\n\t\t\tname: string,\n\t\t\teventName: string,\n\t\t\toptions: { timeout?: string; filter?: Record<string, unknown> } = {},\n\t\t): Promise<T | null> {\n\t\t\tif (resolvedWaits.has(name)) {\n\t\t\t\t// Platform resolved the wait — return the event payload (null if timed out)\n\t\t\t\treturn (resolvedWaits.get(name) as T | null) ?? null\n\t\t\t}\n\n\t\t\tthrow new StepWaitEventSignal(name, eventName, options)\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Signature verification\n// ============================================================================\n\n/**\n * Constant-time HMAC-SHA256 signature verification.\n * Accepts both raw hex and 'sha256={hex}' formats.\n */\nexport function verifySignature(body: string, signature: string, secret: string): boolean {\n\ttry {\n\t\tconst hmac = createHmac('sha256', secret).update(body, 'utf8').digest('hex')\n\t\tconst normalised = signature.replace(/^sha256=/, '')\n\n\t\tconst expected = Buffer.from(hmac, 'hex')\n\t\tconst provided = Buffer.from(normalised, 'hex')\n\n\t\tif (expected.length !== provided.length) return false\n\t\treturn timingSafeEqual(expected, provided)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// ============================================================================\n// Request / response types\n// ============================================================================\n\ninterface DispatchStepEntry {\n\tname: string\n\tresult: unknown\n\tcompletedAt: string\n}\n\ninterface DispatchWaitEntry {\n\tname: string\n\tresolvedAt: string\n\tresult?: unknown\n}\n\ninterface DispatchContext {\n\tattempt: number\n\tsteps: DispatchStepEntry[]\n\twaits: DispatchWaitEntry[]\n}\n\ninterface DispatchBody {\n\ttaskRunId: string\n\ttaskName: string\n\tpayload: unknown\n\tcontext: DispatchContext\n}\n\n// ============================================================================\n// Handler factory\n// ============================================================================\n\nexport interface TasksHandlerOptions {\n\t/** Platform signing secret. If not set, signature verification is skipped. */\n\tsigningSecret?: string\n}\n\nexport interface TasksHandlerResult {\n\tGET: (req: Request) => Promise<Response>\n\tPOST: (req: Request) => Promise<Response>\n}\n\n/**\n * Create a Next.js-compatible route handler for a list of task definitions.\n *\n * @example\n * ```typescript\n * export const { GET, POST } = createTasksHandler([sendEmail, processOrder])\n * ```\n */\nexport function createTasksHandler(\n\ttaskDefs: NativeTaskDefinition[],\n\toptions: TasksHandlerOptions = {},\n): TasksHandlerResult {\n\tconst taskMap = new Map<string, NativeTaskDefinition>()\n\tfor (const def of taskDefs) {\n\t\ttaskMap.set(def.name, def)\n\t}\n\n\t// GET: health/discovery — returns registered task names\n\tconst GET = async (_req: Request): Promise<Response> => {\n\t\treturn Response.json({\n\t\t\tok: true,\n\t\t\ttasks: Array.from(taskMap.keys()),\n\t\t})\n\t}\n\n\t// POST: dispatch handler\n\tconst POST = async (req: Request): Promise<Response> => {\n\t\t// ------------------------------------------------------------------\n\t\t// 1. Read raw body as text\n\t\t// ------------------------------------------------------------------\n\t\tlet rawBody: string\n\t\ttry {\n\t\t\trawBody = await req.text()\n\t\t} catch {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Failed to read request body' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 2. Verify signature if configured\n\t\t// ------------------------------------------------------------------\n\t\tconst signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ?? ''\n\n\t\tif (signingSecret) {\n\t\t\tconst signature = req.headers.get('x-sylphx-signature') ?? ''\n\t\t\tif (!signature) {\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{ status: 'error', message: 'Missing X-Sylphx-Signature header' },\n\t\t\t\t\t{ status: 401 },\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!verifySignature(rawBody, signature, signingSecret)) {\n\t\t\t\treturn Response.json({ status: 'error', message: 'Invalid signature' }, { status: 401 })\n\t\t\t}\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 3. Parse body\n\t\t// ------------------------------------------------------------------\n\t\tlet dispatch: DispatchBody\n\t\ttry {\n\t\t\tdispatch = JSON.parse(rawBody) as DispatchBody\n\t\t} catch {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Request body is not valid JSON' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\tconst { taskName, payload, context } = dispatch\n\n\t\tif (!taskName || typeof taskName !== 'string') {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Missing or invalid \"taskName\" field' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 4. Find task definition\n\t\t// ------------------------------------------------------------------\n\t\tconst taskDef = taskMap.get(taskName)\n\t\tif (!taskDef) {\n\t\t\treturn Response.json(\n\t\t\t\t{\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage: `Task \"${taskName}\" not found. Registered tasks: ${Array.from(taskMap.keys()).join(', ')}`,\n\t\t\t\t},\n\t\t\t\t{ status: 404 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 5. Build step context from dispatch context\n\t\t// ------------------------------------------------------------------\n\t\tconst completedSteps = new Map<string, unknown>()\n\t\tfor (const step of context?.steps ?? []) {\n\t\t\tcompletedSteps.set(step.name, step.result)\n\t\t}\n\n\t\t// resolvedWaits: stepName → result (sleep=undefined, event=payload, timeout=null)\n\t\tconst resolvedWaits = new Map<string, unknown>()\n\t\tfor (const wait of context?.waits ?? []) {\n\t\t\tresolvedWaits.set(wait.name, wait.result ?? undefined)\n\t\t}\n\n\t\tconst stepCtx = createStepContext(completedSteps, resolvedWaits)\n\n\t\t// ------------------------------------------------------------------\n\t\t// 6. Invoke the handler\n\t\t// ------------------------------------------------------------------\n\t\ttry {\n\t\t\tconst result = await taskDef.handler(payload, { step: stepCtx })\n\n\t\t\t// Handler returned normally → task is complete\n\t\t\treturn Response.json({ status: 'complete', result })\n\t\t} catch (err: unknown) {\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7a. Step completed — save result and re-dispatch\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (err instanceof StepCompleteSignal || (err as StepCompleteSignal)?._isStepCompleteSignal) {\n\t\t\t\tconst signal = err as StepCompleteSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_complete',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\tresult: signal.result,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7b. Step sleep — create wait record\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (err instanceof StepSleepSignal || (err as StepSleepSignal)?._isStepSleepSignal) {\n\t\t\t\tconst signal = err as StepSleepSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_sleep',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\tduration: signal.duration,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7c. Step waitForEvent — suspend until named event arrives\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (\n\t\t\t\terr instanceof StepWaitEventSignal ||\n\t\t\t\t(err as StepWaitEventSignal)?._isStepWaitEventSignal\n\t\t\t) {\n\t\t\t\tconst signal = err as StepWaitEventSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_wait_event',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\teventName: signal.eventName,\n\t\t\t\t\ttimeout: signal.options.timeout ?? null,\n\t\t\t\t\tfilter: signal.options.filter ?? null,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7d. Unexpected error\n\t\t\t// ------------------------------------------------------------------\n\t\t\tconst message = err instanceof Error ? err.message : String(err)\n\t\t\tconsole.error(`[sylphx/tasks] Task \"${taskName}\" threw an error:`, err)\n\t\t\treturn Response.json(\n\t\t\t\t{\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage,\n\t\t\t\t\tretriable: true,\n\t\t\t\t},\n\t\t\t\t{ status: 500 },\n\t\t\t)\n\t\t}\n\t}\n\n\treturn { GET, POST }\n}\n","/**\n * Task API Functions\n *\n * Standalone functions for scheduling tasks and managing crons via the Sylphx API.\n * These are backward-compat exports preserved from the pre-refactor tasks.ts.\n */\n\nimport { callApi, type SylphxConfig } from '../../config'\nimport type { CronSchedule } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TaskInput {\n\t/** Callback URL to call when task executes */\n\tcallbackUrl: string\n\t/** Task name/type */\n\tname?: string\n\t/** Task type for categorization */\n\ttype?: string\n\t/** Task payload sent to callback */\n\tpayload?: Record<string, unknown>\n\t/** HTTP method for callback (default: POST) */\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'DELETE'\n\t/** Additional headers for callback */\n\theaders?: Record<string, string>\n\t/** Delay before executing (in seconds, max 604800 = 7 days) */\n\tdelay?: number\n\t/** Schedule for later (ISO timestamp) */\n\tscheduledFor?: string\n\t/** Number of retries on failure (0-5, default: 3) */\n\tretries?: number\n\t/** Request timeout in seconds (1-300, default: 30) */\n\ttimeout?: number\n\t/**\n\t * Idempotency key for safe retries (Stripe/Inngest pattern).\n\t *\n\t * When provided, prevents duplicate task execution if the same\n\t * key is used within a 24-hour window.\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface TaskResult {\n\t/** Task ID */\n\ttaskId: string\n\t/** Scheduled execution time */\n\tscheduledFor?: string\n}\n\nexport interface TaskStatus {\n\tid: string\n\tname?: string\n\tstatus: 'pending' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'\n\tpayload?: Record<string, unknown>\n\tresult?: unknown\n\terror?: string\n\tcreatedAt: string\n\tqueuedAt?: string\n\tstartedAt?: string\n\tcompletedAt?: string\n}\n\nexport interface CronInput {\n\t/** Callback URL to call on each cron trigger */\n\tcallbackUrl: string\n\t/** Cron expression (e.g., '0 0 * * *' for daily at midnight) */\n\tcron: string\n\t/** Task name (required, max 200 chars) */\n\tname: string\n\t/** Task type for categorization */\n\ttype?: string\n\t/** Task payload sent to callback */\n\tpayload?: Record<string, unknown>\n\t/** HTTP method for callback (default: POST) */\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'DELETE'\n\t/** Additional headers for callback */\n\theaders?: Record<string, string>\n\t/** Number of retries on failure (0-5, default: 3) */\n\tretries?: number\n\t/** Start in paused state */\n\tpaused?: boolean\n\t/**\n\t * Idempotency key for safe cron creation.\n\t *\n\t * When provided, prevents duplicate cron schedule creation if\n\t * the same key is used.\n\t */\n\tidempotencyKey?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Schedule a one-time task for execution\n */\nexport async function scheduleTask(config: SylphxConfig, input: TaskInput): Promise<TaskResult> {\n\treturn callApi<TaskResult>(config, '/tasks/schedule', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get a task's status by ID\n */\nexport async function getTask(config: SylphxConfig, taskId: string): Promise<TaskStatus> {\n\treturn callApi<TaskStatus>(config, `/tasks/${taskId}`, { method: 'GET' })\n}\n\n/**\n * Cancel a pending task\n */\nexport async function cancelTask(config: SylphxConfig, taskId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/${taskId}/cancel`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * List tasks with optional filters\n */\nexport async function listTasks(\n\tconfig: SylphxConfig,\n\toptions?: { status?: TaskStatus['status']; limit?: number; offset?: number },\n): Promise<{ tasks: TaskStatus[]; total: number }> {\n\treturn callApi(config, '/tasks', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Create a recurring cron task\n */\nexport async function createCron(config: SylphxConfig, input: CronInput): Promise<CronSchedule> {\n\treturn callApi<CronSchedule>(config, '/tasks/cron', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Pause a cron schedule\n */\nexport async function pauseCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}/pause`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * Resume a cron schedule\n */\nexport async function resumeCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}/resume`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * Delete a cron schedule\n */\nexport async function deleteCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}`, {\n\t\tmethod: 'DELETE',\n\t})\n\treturn result.success\n}\n","/**\n * Feature Flags Functions\n *\n * Pure functions for feature flag evaluation.\n *\n * Pattern: LaunchDarkly/Statsig server-side evaluation\n * - Server-side: POST /flags/evaluate with context\n * - Returns evaluated results (enabled/disabled for this context)\n *\n * Types are derived from the OpenAPI spec (generated/api.d.ts).\n * Run `bun run generate:types:local` to regenerate after API changes.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type { EvaluateFlagsInput as ContractEvaluateFlagsInput } from '@sylphx/contract'\nimport { flagsEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (SDK-specific - no direct API schema for flag results)\n// ============================================================================\n\nexport interface FlagResult {\n\t/** Flag key */\n\tkey: string\n\t/** Whether the flag is enabled for this context */\n\tenabled: boolean\n\t/** Variant value (for multivariate flags) */\n\tvariant?: string\n\t/** Reason for the evaluation result */\n\treason?: string\n\t/** Additional payload data */\n\tpayload?: Record<string, unknown>\n}\n\nexport interface FlagContext {\n\t/** User ID for consistent targeting */\n\tuserId?: string\n\t/** Anonymous ID for pre-auth targeting */\n\tanonymousId?: string\n\t/** User properties for targeting rules (plan, isAdmin, etc.) */\n\tproperties?: Record<string, unknown>\n}\n\n/** Response from the evaluate endpoint */\ninterface EvaluateFlagsResponse {\n\tdata: Record<string, FlagResult>\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Check a single feature flag (server-side evaluation)\n *\n * Uses POST /flags/evaluate for consistent, server-side targeting.\n * The server evaluates rollout percentage, premium targeting, etc.\n *\n * @example\n * ```typescript\n * const flag = await checkFlag(config, 'new-checkout', {\n * userId: 'user-123',\n * properties: { plan: 'pro' },\n * })\n *\n * if (flag.enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function checkFlag(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<FlagResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\tkeys: [flagKey],\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\t// Return the evaluated flag, or a disabled default if not found\n\treturn (\n\t\tresponse.data[flagKey] ?? {\n\t\t\tkey: flagKey,\n\t\t\tenabled: false,\n\t\t\treason: 'flag_not_found',\n\t\t}\n\t)\n}\n\n/**\n * Get multiple feature flags at once (batch evaluation)\n *\n * Evaluates all requested flags in a single API call.\n * More efficient than calling checkFlag() multiple times.\n *\n * @example\n * ```typescript\n * const flags = await getFlags(config, ['new-checkout', 'dark-mode', 'ai-features'], {\n * userId: 'user-123',\n * })\n *\n * if (flags['new-checkout'].enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function getFlags(\n\tconfig: SylphxConfig,\n\tflagKeys: string[],\n\tcontext?: FlagContext,\n): Promise<Record<string, FlagResult>> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\tkeys: flagKeys,\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\treturn response.data\n}\n\n/**\n * Get all feature flags for a context (bootstrap)\n *\n * Evaluates ALL flags for the app in a single API call.\n * Useful for bootstrapping the flag state on app load.\n *\n * @example\n * ```typescript\n * // Bootstrap all flags on app load\n * const allFlags = await getAllFlags(config, { userId: 'user-123' })\n *\n * // Use throughout the app\n * if (allFlags['new-checkout']?.enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function getAllFlags(\n\tconfig: SylphxConfig,\n\tcontext?: FlagContext,\n): Promise<Record<string, FlagResult>> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\t// Omit keys to get all flags\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\treturn response.data\n}\n\n/**\n * Check if a flag is enabled (boolean helper)\n *\n * @example\n * ```typescript\n * if (await isEnabled(config, 'new-checkout', { userId: 'user-123' })) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function isEnabled(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<boolean> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.enabled\n}\n\n/**\n * Get flag variant (for A/B tests)\n *\n * @example\n * ```typescript\n * const variant = await getVariant(config, 'checkout-experiment', {\n * userId: 'user-123',\n * })\n *\n * switch (variant) {\n * case 'control':\n * // Show original checkout\n * break\n * case 'variant-a':\n * // Show variant A\n * break\n * case 'variant-b':\n * // Show variant B\n * break\n * }\n * ```\n */\nexport async function getVariant(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<string | undefined> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.variant\n}\n\n/**\n * Get flag payload (for remote config)\n *\n * @example\n * ```typescript\n * const payload = await getFlagPayload<{ maxItems: number }>(config, 'cart-config', {\n * userId: 'user-123',\n * })\n *\n * console.log(payload?.maxItems) // 10\n * ```\n */\nexport async function getFlagPayload<T extends Record<string, unknown>>(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<T | undefined> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.payload as T | undefined\n}\n","/**\n * Webhooks Functions\n *\n * Pure functions for webhook configuration and delivery management.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for every `/webhooks/*` endpoint\n * under the Console / Management plane. This module keeps a handful of\n * SDK-specific ergonomic envelopes (`WebhookConfig`, `WebhookEnvironment`,\n * `WebhookStats`, `WebhookConfigUpdate`, `ListDeliveriesOptions`,\n * `WebhookDeliveriesResult`) that are looser or richer than the strict\n * contract shapes — they predate the contract and stay put to avoid\n * breaking downstream consumers (SDK tests, apps/web hooks layer).\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tGetWebhookConfigResult,\n\tListWebhookDeliveriesResult,\n\tReplayDeliveryResult,\n\tUpdateWebhookConfigInput,\n\tUpdateWebhookConfigResult,\n\tWebhookDelivery as ContractWebhookDelivery,\n\tWebhookEnvironment as ContractWebhookEnvironment,\n\tWebhookStatsResult,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type WebhookConfigResponse = GetWebhookConfigResult\nexport type WebhookEnvironmentConfig = ContractWebhookEnvironment\nexport type UpdateWebhookConfigRequest = UpdateWebhookConfigInput\nexport type UpdateWebhookConfigResponse = UpdateWebhookConfigResult\nexport type ListWebhookDeliveriesResponse = ListWebhookDeliveriesResult\nexport type WebhookDelivery = ContractWebhookDelivery\nexport type ReplayDeliveryResponse = ReplayDeliveryResult\nexport type WebhookStatsResponse = WebhookStatsResult\n\n// SDK-specific types for convenience\nexport interface WebhookEnvironment {\n\tid: string\n\tname: string\n\twebhookUrl: string | null\n\twebhookSecret?: string | null\n\thasSecret?: boolean\n\tevents?: string[]\n\tcreatedAt: string\n\tupdatedAt: string | null\n}\n\nexport interface WebhookConfig {\n\tenvironments: WebhookEnvironment[]\n\tsupportedEvents?: string[]\n\tenabled?: boolean\n\turl?: string | null\n\tsecret?: string | null\n\tevents?: string[]\n}\n\nexport interface WebhookConfigUpdate {\n\tenvironmentId: string\n\twebhookUrl: string | null\n}\n\nexport interface WebhookDeliveriesResult {\n\tdeliveries: WebhookDelivery[]\n\ttotal: number\n\thasMore: boolean\n}\n\nexport interface WebhookStats {\n\t// Summary totals\n\ttotal: number\n\tdelivered: number\n\tfailed: number\n\tpending: number\n\tdeliveryRate: number\n\tavgLatencyMs: number | null\n\t// Extended stats (for UI)\n\tperiod?: string\n\ttotals?: {\n\t\ttotal: number\n\t\tdelivered: number\n\t\tfailed: number\n\t\tpending: number\n\t\tdeliveryRate: number | string\n\t}\n\tbyEvent?: Array<{ event: string; count: number }>\n\tbyStatus?: Array<{ status: string; count: number }>\n}\n\nexport interface ListDeliveriesOptions {\n\tenvironmentId?: string\n\tstatus?: 'pending' | 'queued' | 'delivered' | 'failed'\n\tlimit?: number\n\toffset?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get webhook configuration for the app\n *\n * @example\n * ```typescript\n * const config = await getWebhookConfig(sylphxConfig)\n * console.log(config.environments)\n * ```\n */\nexport async function getWebhookConfig(config: SylphxConfig): Promise<WebhookConfig> {\n\treturn callApi(config, '/webhooks/config', { method: 'GET' })\n}\n\n/**\n * Update webhook URL for an environment\n *\n * @example\n * ```typescript\n * await updateWebhookConfig(config, {\n * environmentId: 'env-123',\n * webhookUrl: 'https://myapp.com/webhooks',\n * })\n * ```\n */\nexport async function updateWebhookConfig(\n\tconfig: SylphxConfig,\n\tdata: WebhookConfigUpdate,\n): Promise<void> {\n\treturn callApi(config, '/webhooks/config', { method: 'PUT', body: data })\n}\n\n/**\n * Get webhook delivery history\n *\n * @example\n * ```typescript\n * const { deliveries, total } = await getWebhookDeliveries(config, {\n * status: 'failed',\n * limit: 20,\n * })\n * ```\n */\nexport async function getWebhookDeliveries(\n\tconfig: SylphxConfig,\n\toptions?: ListDeliveriesOptions,\n): Promise<WebhookDeliveriesResult> {\n\treturn callApi(config, '/webhooks/deliveries', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Get a single webhook delivery by ID\n *\n * @example\n * ```typescript\n * const delivery = await getWebhookDelivery(config, 'del-123')\n * console.log(delivery.payload)\n * ```\n */\nexport async function getWebhookDelivery(\n\tconfig: SylphxConfig,\n\tdeliveryId: string,\n): Promise<WebhookDelivery> {\n\treturn callApi(config, `/webhooks/deliveries/${deliveryId}`, {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Replay a failed webhook delivery\n *\n * @example\n * ```typescript\n * await replayWebhookDelivery(config, 'del-123')\n * ```\n */\nexport async function replayWebhookDelivery(\n\tconfig: SylphxConfig,\n\tdeliveryId: string,\n): Promise<void> {\n\treturn callApi(config, `/webhooks/deliveries/${deliveryId}/replay`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Get webhook statistics\n *\n * @example\n * ```typescript\n * const stats = await getWebhookStats(config)\n * console.log(`Delivery rate: ${stats.deliveryRate}%`)\n * ```\n */\nexport async function getWebhookStats(\n\tconfig: SylphxConfig,\n\tenvironmentId?: string,\n): Promise<WebhookStats> {\n\treturn callApi(config, '/webhooks/stats', {\n\t\tmethod: 'GET',\n\t\tquery: environmentId ? { environmentId } : undefined,\n\t})\n}\n","/**\n * Email Functions\n *\n * Pure functions for transactional email operations.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/email/configured`,\n * `/email/send`, `/email/send-templated`, and `/email/send-to-user`. SDK-\n * specific convenience shapes (scheduling options, domain management) stay\n * local — their routes are served by the Console plane and live alongside\n * the BaaS `emailEndpoints` in the contract's `emailAdmin` namespace.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tSendEmailInput as ContractSendEmailInput,\n\tSendEmailResult as ContractSendEmailResult,\n\tSendEmailToUserInput as ContractSendEmailToUserInput,\n\tSendTemplatedEmailInput as ContractSendTemplatedEmailInput,\n} from '@sylphx/contract'\nimport { emailEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type { ContractSendEmailInput as SendEmailRequest }\nexport type { ContractSendEmailResult as SendEmailResponse }\nexport type { ContractSendTemplatedEmailInput as SendTemplatedEmailRequest }\nexport type { ContractSendEmailResult as SendTemplatedEmailResponse }\nexport type { ContractSendEmailToUserInput as SendToUserRequest }\nexport type { ContractSendEmailResult as SendToUserResponse }\n\n// SDK-specific types for convenience\nexport interface SendEmailOptions {\n\t/** Recipient email address */\n\tto: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml: string\n\t/** Plain text content (optional fallback) */\n\ttext?: string\n\t/** Reply-to address */\n\treplyTo?: string\n\t/**\n\t * Sender email address (must be from a verified domain).\n\t * Falls back to the app environment default, then the platform FROM_EMAIL.\n\t *\n\t * @example `support@yourdomain.com`\n\t */\n\tfromEmail?: string\n\t/**\n\t * Sender display name (used together with fromEmail).\n\t *\n\t * @example `Acme Support`\n\t */\n\tfromName?: string\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * When provided, prevents duplicate email sends if the same request\n\t * is retried within 24 hours. Use a unique key per logical operation.\n\t *\n\t * @example `welcome-email-${userId}`\n\t */\n\tidempotencyKey?: string\n\t/**\n\t * Custom email headers passed directly to the provider.\n\t * Use for email threading (In-Reply-To, References) or other RFC 5322 headers.\n\t * Do not override From, To, or Subject here.\n\t *\n\t * @example `{ 'In-Reply-To': '<abc@cubeage.com>', References: '<abc@cubeage.com>' }`\n\t */\n\theaders?: Record<string, string>\n}\n\nexport interface SendTemplatedEmailOptions {\n\t/** Template name: 'welcome', 'verification', 'password_reset', 'security_alert' */\n\ttemplate: 'welcome' | 'verification' | 'password_reset' | 'security_alert'\n\t/** Recipient email address */\n\tto: string\n\t/** Template variables */\n\tdata?: Record<string, unknown>\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * @example `verification-email-${userId}`\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface SendToUserOptions {\n\t/** User ID to send to */\n\tuserId: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml: string\n\t/** Plain text content (optional fallback) */\n\ttext?: string\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * @example `notification-${userId}-${Date.now()}`\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface ScheduleEmailOptions {\n\t/** Recipient email address */\n\tto: string\n\t/** Recipient name (optional) */\n\ttoName?: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml?: string\n\t/** Plain text content */\n\ttext?: string\n\t/** Reply-to address */\n\treplyTo?: string\n\t/** From email (defaults to app's configured sender) */\n\tfromEmail?: string\n\t/** From name */\n\tfromName?: string\n\t/** ISO timestamp for when to send */\n\tscheduledFor: string\n\t/** Template key for templated emails */\n\ttemplateKey?: string\n\t/** Template variables */\n\ttemplateData?: Record<string, unknown>\n\t/** Idempotency key to prevent duplicates */\n\tidempotencyKey?: string\n\t/** Custom metadata */\n\tmetadata?: Record<string, unknown>\n}\n\nexport interface ScheduledEmail {\n\tid: string\n\tto: string\n\ttoName: string | null\n\tsubject: string\n\tstatus: 'pending' | 'queued' | 'sent' | 'cancelled' | 'failed'\n\tscheduledFor: string\n\tsentAt: string | null\n\tcreatedAt: string\n}\n\nexport interface ScheduledEmailsResult {\n\temails: ScheduledEmail[]\n\ttotal: number\n\thasMore: boolean\n}\n\nexport interface ScheduledEmailStats {\n\ttotal: number\n\tpending: number\n\tqueued: number\n\tsent: number\n\tcancelled: number\n\tfailed: number\n}\n\nexport interface ListScheduledEmailsOptions {\n\tstatus?: 'pending' | 'queued' | 'sent' | 'cancelled' | 'failed' | 'all'\n\tlimit?: number\n\toffset?: number\n}\n\nexport interface SendResult {\n\tid: string\n\tsuccess: boolean\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Check if email service is configured for the app\n *\n * @example\n * ```typescript\n * const configured = await isEmailConfigured(config)\n * if (!configured) console.log('Please configure email settings')\n * ```\n */\nexport async function isEmailConfigured(config: SylphxConfig): Promise<boolean> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = emailEndpoints.isConfigured\n\treturn callApi(config, endpoint.path, { method: endpoint.method })\n}\n\n/**\n * Send a custom email\n *\n * @example\n * ```typescript\n * const result = await sendEmail(config, {\n * to: 'user@example.com',\n * subject: 'Hello!',\n * html: '<p>Welcome to our app!</p>',\n * idempotencyKey: `welcome-${userId}`, // Safe retry\n * })\n * ```\n */\nexport async function sendEmail(\n\tconfig: SylphxConfig,\n\toptions: SendEmailOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// SDK's `SendEmailOptions` adds transport-level `idempotencyKey`; strip\n\t// it before sending the contract-owned request body.\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.send\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body as ContractSendEmailInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Send a templated email\n *\n * @example\n * ```typescript\n * await sendTemplatedEmail(config, {\n * template: 'welcome',\n * to: 'user@example.com',\n * data: { name: 'John' },\n * idempotencyKey: `welcome-${userId}`, // Safe retry\n * })\n * ```\n */\nexport async function sendTemplatedEmail(\n\tconfig: SylphxConfig,\n\toptions: SendTemplatedEmailOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.sendTemplated\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body satisfies ContractSendTemplatedEmailInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Send email to a user by their ID\n *\n * @example\n * ```typescript\n * await sendEmailToUser(config, {\n * userId: 'user-123',\n * subject: 'Account Update',\n * html: '<p>Your account has been updated.</p>',\n * idempotencyKey: `update-${userId}-${timestamp}`, // Safe retry\n * })\n * ```\n */\nexport async function sendEmailToUser(\n\tconfig: SylphxConfig,\n\toptions: SendToUserOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.sendToUser\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body satisfies ContractSendEmailToUserInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Schedule an email for future delivery\n *\n * @example\n * ```typescript\n * const scheduled = await scheduleEmail(config, {\n * to: 'user@example.com',\n * subject: 'Reminder',\n * html: '<p>Don\\'t forget!</p>',\n * scheduledFor: new Date(Date.now() + 86400000).toISOString(), // 24 hours\n * })\n * ```\n */\nexport async function scheduleEmail(\n\tconfig: SylphxConfig,\n\toptions: ScheduleEmailOptions,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, '/email/schedule', { method: 'POST', body: options })\n}\n\n/**\n * List scheduled emails\n *\n * @example\n * ```typescript\n * const { emails, total } = await listScheduledEmails(config, {\n * status: 'pending',\n * limit: 20,\n * })\n * ```\n */\nexport async function listScheduledEmails(\n\tconfig: SylphxConfig,\n\toptions?: ListScheduledEmailsOptions,\n): Promise<ScheduledEmailsResult> {\n\treturn callApi(config, '/email/scheduled', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Get a scheduled email by ID\n *\n * @example\n * ```typescript\n * const email = await getScheduledEmail(config, 'email-123')\n * console.log(email.status)\n * ```\n */\nexport async function getScheduledEmail(\n\tconfig: SylphxConfig,\n\temailId: string,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, `/email/scheduled/${emailId}`, { method: 'GET' })\n}\n\n/**\n * Cancel a scheduled email\n *\n * @example\n * ```typescript\n * await cancelScheduledEmail(config, 'email-123')\n * ```\n */\nexport async function cancelScheduledEmail(config: SylphxConfig, emailId: string): Promise<void> {\n\treturn callApi(config, `/email/scheduled/${emailId}/cancel`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Reschedule an email\n *\n * @example\n * ```typescript\n * await rescheduleEmail(config, 'email-123', new Date(Date.now() + 3600000).toISOString())\n * ```\n */\nexport async function rescheduleEmail(\n\tconfig: SylphxConfig,\n\temailId: string,\n\tscheduledFor: string,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, `/email/scheduled/${emailId}/reschedule`, {\n\t\tmethod: 'POST',\n\t\tbody: { scheduledFor },\n\t})\n}\n\n/**\n * Get scheduled email statistics\n *\n * @example\n * ```typescript\n * const stats = await getScheduledEmailStats(config)\n * console.log(`${stats.pending} emails pending`)\n * ```\n */\nexport async function getScheduledEmailStats(config: SylphxConfig): Promise<ScheduledEmailStats> {\n\treturn callApi(config, '/email/scheduled/stats', { method: 'GET' })\n}\n\n// ============================================================================\n// Email Domain Management\n// ============================================================================\n\n/**\n * DNS record required for domain verification\n */\nexport interface DnsRecord {\n\t/** DNS record type */\n\ttype: 'MX' | 'TXT' | 'CNAME'\n\t/** DNS record name (hostname) */\n\tname: string\n\t/** DNS record value */\n\tvalue: string\n\t/** MX priority (only for MX records) */\n\tpriority?: number\n\t/** TTL in seconds */\n\tttl: number\n}\n\n/**\n * A registered custom sending domain\n */\nexport interface EmailDomain {\n\tid: string\n\tdomain: string\n\tstatus: 'pending' | 'verifying' | 'verified' | 'failed'\n\tdefaultFromEmail: string | null\n\tdefaultFromName: string | null\n\tdnsRecords: DnsRecord[]\n\tcreatedAt: string\n\tverifiedAt: string | null\n}\n\nexport interface RegisterEmailDomainOptions {\n\t/** Default from address for this domain (e.g. support@cubeage.com) */\n\tdefaultFromEmail?: string\n\t/** Default sender display name (e.g. Cubeage Support) */\n\tdefaultFromName?: string\n}\n\nexport interface SetDefaultEmailDomainOptions {\n\t/** Override the default from address */\n\tdefaultFromEmail?: string\n\t/** Override the default sender name */\n\tdefaultFromName?: string\n}\n\n/**\n * Register a custom sending domain\n *\n * Enables email sending for this domain. Returns DNS records to add to your DNS provider.\n * After adding DNS records, call verifyEmailDomain to confirm ownership.\n *\n * @example\n * ```typescript\n * const domain = await registerEmailDomain(config, 'cubeage.com', {\n * defaultFromEmail: 'support@cubeage.com',\n * defaultFromName: 'Cubeage Support',\n * })\n * console.log('Add these DNS records:', domain.dnsRecords)\n * ```\n */\nexport async function registerEmailDomain(\n\tconfig: SylphxConfig,\n\tdomain: string,\n\topts?: RegisterEmailDomainOptions,\n): Promise<EmailDomain> {\n\treturn callApi(config, '/email/domains', {\n\t\tmethod: 'POST',\n\t\tbody: { domain, ...opts },\n\t})\n}\n\n/**\n * List all custom sending domains for this app\n *\n * @example\n * ```typescript\n * const { domains } = await listEmailDomains(config)\n * for (const d of domains) {\n * console.log(d.domain, d.status)\n * }\n * ```\n */\nexport async function listEmailDomains(config: SylphxConfig): Promise<{ domains: EmailDomain[] }> {\n\treturn callApi(config, '/email/domains', { method: 'GET' })\n}\n\n/**\n * Get a specific domain by ID\n *\n * @example\n * ```typescript\n * const domain = await getEmailDomain(config, 'domain-uuid')\n * console.log(domain.dnsRecords)\n * ```\n */\nexport async function getEmailDomain(config: SylphxConfig, domainId: string): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}`, { method: 'GET' })\n}\n\n/**\n * Delete a custom sending domain\n *\n * Disables email sending for this domain and removes DKIM signing configuration.\n *\n * @example\n * ```typescript\n * await deleteEmailDomain(config, 'domain-uuid')\n * ```\n */\nexport async function deleteEmailDomain(config: SylphxConfig, domainId: string): Promise<void> {\n\treturn callApi(config, `/email/domains/${domainId}`, { method: 'DELETE' })\n}\n\n/**\n * Trigger DNS verification for a domain\n *\n * Checks if your DNS records have propagated. Status changes to 'verified' when DKIM is confirmed.\n * Status changes to 'verified' on success or 'failed' on error.\n *\n * @example\n * ```typescript\n * const domain = await verifyEmailDomain(config, 'domain-uuid')\n * if (domain.status === 'verified') {\n * console.log('Domain verified!')\n * }\n * ```\n */\nexport async function verifyEmailDomain(\n\tconfig: SylphxConfig,\n\tdomainId: string,\n): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}/verify`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Set a domain as the default sender for this app\n *\n * @example\n * ```typescript\n * const domain = await setDefaultEmailDomain(config, 'domain-uuid', {\n * defaultFromEmail: 'support@cubeage.com',\n * defaultFromName: 'Cubeage Support',\n * })\n * ```\n */\nexport async function setDefaultEmailDomain(\n\tconfig: SylphxConfig,\n\tdomainId: string,\n\topts?: SetDefaultEmailDomainOptions,\n): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}/set-default`, {\n\t\tmethod: 'POST',\n\t\tbody: opts ?? {},\n\t})\n}\n","/**\n * Consent Functions\n *\n * Pure functions for GDPR/CCPA consent management.\n *\n * ## Architecture (ADR-004)\n *\n * Consent uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when checking consent\n * - Platform auto-discovers/creates consent types when first referenced\n * - Console can override names, descriptions, requirements without deployment\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /consent/types`,\n * `GET /consent`, `POST /consent`, and friends. SDK-specific input types\n * (history pagination, anonymous linking) remain local — they describe\n * ergonomic helpers on top of the wire shapes rather than the wire\n * shapes themselves.\n *\n * @example\n * ```typescript\n * import { hasConsent, getUserConsents, setConsents } from '@sylphx/sdk'\n *\n * // Check consent with inline defaults (auto-discovered if doesn't exist)\n * if (await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })) {\n * track('pageview')\n * }\n *\n * // Get user's current consents\n * const consents = await getUserConsents(config, { userId: 'user-123' })\n *\n * // Set specific consents\n * await setConsents(config, {\n * userId: 'user-123',\n * consents: { analytics: true, marketing: false }\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tSdkConsentType as ContractConsentType,\n\tSetConsentsRequest as ContractSetConsentsRequest,\n\tSetConsentsResponse as ContractSetConsentsResponse,\n\tUserConsent as ContractUserConsent,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type ConsentType = ContractConsentType\nexport type UserConsent = ContractUserConsent\nexport type SetConsentRequest = ContractSetConsentsRequest\nexport type SetConsentResponse = ContractSetConsentsResponse\n\n// SDK-specific types (not directly from API schema)\n/** Consent category for grouping */\nexport type ConsentCategory = 'necessary' | 'analytics' | 'marketing' | 'functional' | 'preferences'\n\nexport interface SetConsentsInput {\n\t/** User ID (optional for anonymous users) */\n\tuserId?: string\n\t/** Anonymous ID (for guest users) */\n\tanonymousId?: string\n\t/** Consent settings by type slug */\n\tconsents: Record<string, boolean>\n}\n\nexport interface LinkAnonymousConsentsInput {\n\t/** The authenticated user ID to link to */\n\tuserId: string\n\t/** The anonymous ID whose consents should be linked */\n\tanonymousId: string\n}\n\nexport interface GetConsentHistoryInput {\n\t/** User ID (for authenticated users) */\n\tuserId?: string\n\t/** Anonymous ID (for anonymous users) */\n\tanonymousId?: string\n\t/** Maximum records to return (default: 50) */\n\tlimit?: number\n\t/**\n\t * Opaque pagination cursor returned by a previous response.\n\t * Omit (or pass undefined) to fetch the first page.\n\t */\n\tcursor?: string\n}\n\n/** A single consent change history entry */\nexport interface ConsentHistoryEntry {\n\t/** Unique entry ID */\n\tid: string\n\t/** Consent type slug (e.g., 'analytics') */\n\tconsentType: string\n\t/** Display name of the consent type */\n\tconsentTypeName: string\n\t/** Previous consent state (null = initial consent) */\n\tpreviousGranted: boolean | null\n\t/** New consent state */\n\tnewGranted: boolean\n\t/** Source of the change (banner, settings, api) */\n\tsource: string\n\t/** Reason for change (user_action, policy_update, etc.) */\n\treason: string | null\n\t/** ISO timestamp when change occurred */\n\tcreatedAt: string\n}\n\nexport interface ConsentHistoryResult {\n\t/** List of consent change entries */\n\tentries: ConsentHistoryEntry[]\n\t/**\n\t * Opaque cursor for the next page. Pass as `cursor` to the next call.\n\t * Null when this is the last page.\n\t */\n\tnextCursor: string | null\n\t/** Whether there are more entries */\n\thasMore: boolean\n}\n\nexport interface GetConsentsInput {\n\t/** User ID (optional for anonymous users) */\n\tuserId?: string\n\t/** Anonymous ID (for guest users) */\n\tanonymousId?: string\n}\n\n/**\n * Inline defaults for consent purpose auto-discovery\n *\n * @example\n * ```typescript\n * await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })\n * ```\n */\nexport interface ConsentPurposeDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Category */\n\tcategory?: ConsentCategory\n\t/** Whether consent is required (always granted) */\n\trequired?: boolean\n\t/** Whether enabled by default */\n\tdefaultEnabled?: boolean\n\t/** Sort order in UI */\n\tsortOrder?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get all consent types configured for the app\n *\n * Returns GDPR-standard defaults if none configured.\n *\n * @example\n * ```typescript\n * const types = await getConsentTypes(config)\n * types.forEach(t => console.log(`${t.name}: ${t.required ? 'Required' : 'Optional'}`))\n * ```\n */\nexport async function getConsentTypes(config: SylphxConfig): Promise<ConsentType[]> {\n\treturn callApi(config, '/consent/types', { method: 'GET' })\n}\n\n/**\n * Check if user has granted consent for a specific purpose\n *\n * If the consent type doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param purposeSlug - Consent purpose slug (e.g., 'analytics', 'marketing')\n * @param input - User identification (userId or anonymousId)\n * @param defaults - Optional inline defaults for auto-discovery\n * @returns Whether consent is granted\n *\n * @example\n * ```typescript\n * // Check analytics consent with inline defaults\n * if (await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })) {\n * track('pageview')\n * }\n *\n * // Required consent always returns true\n * const hasNecessary = await hasConsent(config, 'necessary', { userId }, {\n * name: 'Essential Cookies',\n * description: 'Required for the website to function',\n * category: 'necessary',\n * required: true,\n * })\n * ```\n */\nexport async function hasConsent(\n\tconfig: SylphxConfig,\n\tpurposeSlug: string,\n\tinput: GetConsentsInput,\n\tdefaults?: ConsentPurposeDefaults,\n): Promise<boolean> {\n\treturn callApi(config, '/consent/check', {\n\t\tmethod: 'POST',\n\t\tbody: { purposeSlug, ...input, defaults },\n\t})\n}\n\n/**\n * Get user's current consent settings\n *\n * @example\n * ```typescript\n * // For authenticated user\n * const consents = await getUserConsents(config, { userId: 'user-123' })\n *\n * // For anonymous user\n * const consents = await getUserConsents(config, { anonymousId: 'anon-456' })\n * ```\n */\nexport async function getUserConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<UserConsent[]> {\n\treturn callApi(config, '/consent/user', {\n\t\tmethod: 'GET',\n\t\tquery: input as Record<string, string | undefined>,\n\t})\n}\n\n/**\n * Set user's consent preferences\n *\n * @example\n * ```typescript\n * await setConsents(config, {\n * userId: 'user-123',\n * consents: {\n * analytics: true,\n * marketing: false,\n * },\n * })\n * ```\n */\nexport async function setConsents(config: SylphxConfig, input: SetConsentsInput): Promise<void> {\n\treturn callApi(config, '/consent/set', { method: 'POST', body: input })\n}\n\n/**\n * Accept all consent types\n *\n * @example\n * ```typescript\n * await acceptAllConsents(config, { userId: 'user-123' })\n * ```\n */\nexport async function acceptAllConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/accept-all', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Decline all optional consent types (keeps required ones)\n *\n * @example\n * ```typescript\n * await declineOptionalConsents(config, { anonymousId: 'anon-456' })\n * ```\n */\nexport async function declineOptionalConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/decline-optional', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Link anonymous user's consents to authenticated user\n *\n * Call this after user signs up/logs in to merge their anonymous consent history.\n *\n * @example\n * ```typescript\n * await linkAnonymousConsents(config, {\n * userId: 'user-123',\n * anonymousId: 'anon-456',\n * })\n * ```\n */\nexport async function linkAnonymousConsents(\n\tconfig: SylphxConfig,\n\tinput: LinkAnonymousConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/link-anonymous', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get consent change history for GDPR audit trail\n *\n * Returns a paginated list of all consent state changes for a user.\n * Required for GDPR compliance - provides complete audit trail of consent decisions.\n *\n * @example\n * ```typescript\n * // Get consent history for authenticated user (first page)\n * const history = await getConsentHistory(config, { userId: 'user-123', limit: 20 })\n * history.entries.forEach(entry => {\n * console.log(`${entry.consentType}: ${entry.previousGranted} → ${entry.newGranted}`)\n * })\n *\n * // Fetch next page using the cursor\n * if (history.nextCursor) {\n * const page2 = await getConsentHistory(config, {\n * userId: 'user-123',\n * limit: 20,\n * cursor: history.nextCursor,\n * })\n * }\n * ```\n */\nexport async function getConsentHistory(\n\tconfig: SylphxConfig,\n\tinput: GetConsentHistoryInput,\n): Promise<ConsentHistoryResult> {\n\treturn callApi(config, '/consent/history', {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tuserId: input.userId,\n\t\t\tanonymousId: input.anonymousId,\n\t\t\tlimit: input.limit?.toString(),\n\t\t\tcursor: input.cursor,\n\t\t},\n\t})\n}\n","/**\n * Referrals Functions\n *\n * Pure functions for referral code management and tracking.\n *\n * ## Architecture (ADR-004)\n *\n * Referrals uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when redeeming referral codes\n * - Platform uses defaults if no Console override exists\n * - Console can override reward values without deployment\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /referrals/code`,\n * `/referrals/code/regenerate`, `/referrals/stats`, `/referrals/redeem`,\n * `/referrals/leaderboard`.\n *\n * @example\n * ```typescript\n * import { redeemReferralCode } from '@sylphx/sdk'\n *\n * // Redeem with inline defaults (overridable in Console)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * }, {\n * referrerReward: { type: 'premium_trial', days: 7 },\n * refereeReward: { type: 'premium_trial', days: 7 },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tReferralRewardConfig as ContractReferralRewardConfig,\n\tReferralRewardDefaults as ContractReferralRewardDefaults,\n\tRegenerateCodeResponse as ContractRegenerateCodeResponse,\n\tGetCodeResponse,\n\tGetStatsResponse,\n\tRedeemRequest,\n\tRedeemResponse,\n\tReferralLeaderboardEntry,\n\tReferralLeaderboardResponse,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type ReferralCodeResponse = GetCodeResponse\nexport type RegenerateCodeResponse = ContractRegenerateCodeResponse\nexport type ReferralStatsResponse = GetStatsResponse\nexport type RedeemReferralRequest = RedeemRequest\nexport type RedeemReferralResponse = RedeemResponse\nexport type ReferralRewardDefaults = ContractReferralRewardDefaults\nexport type ReferralRewardConfig = ContractReferralRewardConfig\nexport type LeaderboardResponse = ReferralLeaderboardResponse\nexport type LeaderboardEntry = ReferralLeaderboardEntry\n\n// SDK-specific types for convenience\nexport interface ReferralCode {\n\tcode: string\n\tcreatedAt: string\n}\n\nexport interface ReferralStats {\n\t/** User's referral code */\n\tcode: string\n\t/** Total referrals made */\n\ttotalReferrals: number\n\t/** Successful (redeemed) referrals */\n\tsuccessfulReferrals: number\n\t/** Pending referrals */\n\tpendingReferrals: number\n\t/** Total rewards earned */\n\ttotalRewards: number\n}\n\ntype LeaderboardPeriod = 'all' | 'month' | 'week'\n\nexport interface LeaderboardResult {\n\t/** Time period for the leaderboard */\n\tperiod?: LeaderboardPeriod\n\tentries: LeaderboardEntry[]\n\t/** Current user's position (may not be in top entries) */\n\tcurrentUserRank: number | null\n\t/** Total participants */\n\ttotalParticipants: number\n}\n\nexport interface RedeemReferralInput {\n\t/** Referral code to redeem */\n\tcode: string\n\t/** User ID of the person redeeming (optional for anonymous) */\n\tuserId?: string\n}\n\nexport interface RedeemResult {\n\tsuccess: boolean\n\t/** Reward type - platform types or app-specific types */\n\trewardType: 'points' | 'premium_trial' | 'discount' | 'credit' | (string & {})\n\treferredReward?: Record<string, unknown>\n\treferrerReward?: Record<string, unknown>\n}\n\nexport interface LeaderboardOptions {\n\t/** Number of entries to return (default: 10) */\n\tlimit?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get current user's referral code\n *\n * Creates one if it doesn't exist.\n *\n * @example\n * ```typescript\n * const { code } = await getMyReferralCode(config, 'user-123')\n * console.log(`Share your code: ${code}`)\n * ```\n */\nexport async function getMyReferralCode(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralCode> {\n\treturn callApi(config, '/referrals/code', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Get referral statistics for a user\n *\n * @example\n * ```typescript\n * const stats = await getReferralStats(config, 'user-123')\n * console.log(`${stats.successfulReferrals} successful referrals`)\n * ```\n */\nexport async function getReferralStats(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralStats> {\n\treturn callApi(config, '/referrals/stats', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Redeem a referral code\n *\n * If the referral program rewards aren't configured in Console, the provided\n * defaults will be used. Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Referral redemption input (code, userId)\n * @param defaults - Optional inline defaults for reward configuration\n *\n * @example\n * ```typescript\n * // Basic redemption (uses Console-configured rewards)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * })\n *\n * // With inline defaults (auto-discovered if not in Console)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * }, {\n * referrerReward: { type: 'premium_trial', days: 7 },\n * refereeReward: { type: 'premium_trial', days: 7 },\n * })\n *\n * if (result.success) {\n * console.log(`Reward: ${result.reward?.type}`)\n * }\n * ```\n */\nexport async function redeemReferralCode(\n\tconfig: SylphxConfig,\n\tinput: RedeemReferralInput,\n\tdefaults?: ReferralRewardDefaults,\n): Promise<RedeemResult> {\n\treturn callApi(config, '/referrals/redeem', {\n\t\tmethod: 'POST',\n\t\tbody: { ...input, defaults },\n\t})\n}\n\n/**\n * Get referral leaderboard\n *\n * @example\n * ```typescript\n * const { entries, currentUserRank } = await getReferralLeaderboard(config, 'user-123')\n *\n * entries.forEach(e => {\n * console.log(`#${e.rank} ${e.displayName}: ${e.completedReferrals} referrals`)\n * })\n * ```\n */\nexport async function getReferralLeaderboard(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\toptions?: LeaderboardOptions,\n): Promise<LeaderboardResult> {\n\treturn callApi(config, '/referrals/leaderboard', {\n\t\tmethod: 'GET',\n\t\tquery: { userId, ...options } as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Regenerate user's referral code\n *\n * Use this if the current code has been compromised or user wants a fresh start.\n *\n * @example\n * ```typescript\n * const { code } = await regenerateReferralCode(config, 'user-123')\n * console.log(`New code: ${code}`)\n * ```\n */\nexport async function regenerateReferralCode(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralCode> {\n\treturn callApi(config, '/referrals/code/regenerate', {\n\t\tmethod: 'POST',\n\t\tbody: { userId },\n\t})\n}\n","/**\n * Engagement Service Types\n *\n * Core types for streaks, leaderboards, and achievements.\n *\n * ## Architecture (ADR-004)\n *\n * Engagement uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when calling APIs\n * - Platform auto-discovers/creates entities when first referenced\n * - Console can override names, descriptions, values without deployment\n */\n\n// ============================================================================\n// Streaks\n// ============================================================================\n\n/** Streak activity frequency */\nexport type StreakFrequency = 'daily' | 'weekly' | 'custom'\n\n/** Streak definition (auto-discovered or from Console) */\nexport interface StreakDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description */\n\tdescription?: string\n\t/** Activity frequency */\n\tfrequency: StreakFrequency\n\t/** Grace period in hours (default: 0) */\n\tgracePeriodHours?: number\n\t/** Whether streak resets on miss (default: true) */\n\tresetOnMiss?: boolean\n\t/** Maximum streak value (optional cap) */\n\tmaxValue?: number\n\t/** Custom interval in hours (only for 'custom' frequency) */\n\tcustomIntervalHours?: number\n}\n\n/** User's streak state (from platform) */\nexport interface StreakState {\n\t/** Streak definition ID */\n\tstreakId: string\n\t/** Current streak count */\n\tcurrent: number\n\t/** Longest streak ever */\n\tlongest: number\n\t/** Last activity timestamp */\n\tlastActivityAt: string | null\n\t/** When current streak will expire */\n\texpiresAt: string | null\n\t/** Whether streak can be recovered (within grace period) */\n\tcanRecover: boolean\n\t/** Time remaining until expiry in ms */\n\ttimeRemainingMs: number | null\n\t/** User's timezone preference for streak expiry (IANA timezone, e.g., 'America/New_York') */\n\tuserTimezone: string | null\n}\n\n/** Activity recording input */\nexport interface RecordActivityInput {\n\t/** Streak ID */\n\tstreakId: string\n\t/** User's IANA timezone (e.g., 'America/New_York') for calculating streak expiry at user's local midnight */\n\tuserTimezone?: string\n\t/** Optional metadata */\n\tmetadata?: Record<string, unknown>\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * Prevents duplicate streak recordings if the same request is retried.\n\t * Use a unique key per logical activity (e.g., `streak-${userId}-${date}`).\n\t */\n\tidempotencyKey?: string\n}\n\n/** Activity recording result */\nexport interface RecordActivityResult {\n\t/** Updated streak state */\n\tstreak: StreakState\n\t/** Whether this activity extended the streak */\n\textended: boolean\n\t/** Whether a new personal best was achieved */\n\tnewPersonalBest: boolean\n\t/** Previous streak value (for animation) */\n\tpreviousValue: number\n}\n\n// ============================================================================\n// Leaderboards\n// ============================================================================\n\n/** Leaderboard sort direction */\nexport type LeaderboardSortDirection = 'asc' | 'desc'\n\n/** Leaderboard reset period */\nexport type LeaderboardResetPeriod = 'hourly' | 'daily' | 'weekly' | 'monthly' | 'never'\n\n/** Score aggregation method */\nexport type LeaderboardAggregation = 'max' | 'sum' | 'latest' | 'count' | 'min' | 'avg'\n\n/** Leaderboard definition (auto-discovered or from Console) */\nexport interface LeaderboardDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description */\n\tdescription?: string\n\t/** Sort direction (desc = higher is better) */\n\tsortDirection: LeaderboardSortDirection\n\t/** Reset period */\n\tresetPeriod: LeaderboardResetPeriod\n\t/** How to aggregate multiple scores from same user */\n\taggregation: LeaderboardAggregation\n\t/** Default privacy for entries */\n\tdefaultPrivacy?: 'public' | 'friends' | 'anonymous'\n\t/** Maximum entries to keep per period */\n\tmaxEntries?: number\n}\n\n/** Leaderboard entry */\nexport interface LeaderboardEntry {\n\t/** Rank (1-indexed) */\n\trank: number\n\t/** User ID (may be null for anonymous) */\n\tuserId: string | null\n\t/** Display name */\n\tdisplayName: string\n\t/** Avatar URL */\n\tavatarUrl: string | null\n\t/** Score/value */\n\tvalue: number\n\t/** Whether this is the current user */\n\tisCurrentUser: boolean\n\t/** Entry metadata */\n\tmetadata?: Record<string, unknown>\n\t/** When the score was submitted */\n\tsubmittedAt: string\n}\n\n/** Leaderboard query options */\nexport interface LeaderboardQueryOptions {\n\t/** Number of entries to return (default: 10) */\n\tlimit?: number\n\t/** Offset for pagination */\n\toffset?: number\n\t/** Include surrounding entries for current user */\n\tincludeSurrounding?: boolean\n\t/** Number of surrounding entries (default: 2) */\n\tsurroundingCount?: number\n}\n\n/** Leaderboard query result */\nexport interface LeaderboardResult {\n\t/** Leaderboard definition ID */\n\tleaderboardId: string\n\t/** Period (for periodic leaderboards) */\n\tperiod?: string\n\t/** Entries (top N or paginated) */\n\tentries: LeaderboardEntry[]\n\t/** Current user's entry (may not be in top entries) */\n\tcurrentUserEntry: LeaderboardEntry | null\n\t/** Entries surrounding the current user (when includeSurrounding=true and user is outside main entries) */\n\tsurroundingEntries?: LeaderboardEntry[]\n\t/** Total participants */\n\ttotalParticipants: number\n\t/** Next reset time (for periodic leaderboards) */\n\tnextResetAt: string | null\n}\n\n/** Score submission input */\nexport interface SubmitScoreInput {\n\t/** Leaderboard ID */\n\tleaderboardId: string\n\t/** Score value */\n\tvalue: number\n\t/** Optional metadata */\n\tmetadata?: Record<string, unknown>\n}\n\n/** Score submission result */\nexport interface SubmitScoreResult {\n\t/** Whether submission was accepted */\n\taccepted: boolean\n\t/** New rank (if in leaderboard) */\n\trank: number | null\n\t/** Previous best (if any) */\n\tpreviousBest: number | null\n\t/** Whether this is a new personal best */\n\tnewPersonalBest: boolean\n\t/** Rank change (positive = improved) */\n\trankChange: number | null\n}\n\n// ============================================================================\n// Achievements\n// ============================================================================\n\n/** Achievement type */\nexport type AchievementType = 'standard' | 'hidden' | 'incremental'\n\n/** Achievement tier */\nexport type AchievementTier = 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond'\n\n/** Achievement category */\nexport type AchievementCategory = string // App-defined\n\n/** Achievement criteria operator */\nexport type CriteriaOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains'\n\n/** Single criterion */\nexport interface AchievementCriterion {\n\t/** Property to check (e.g., 'event', 'count', 'streak.daily') */\n\tproperty: string\n\t/** Comparison operator */\n\toperator: CriteriaOperator\n\t/** Value to compare against */\n\tvalue: string | number | boolean | string[] | number[]\n}\n\n/** Achievement criteria (AND logic within, OR between arrays) */\nexport interface AchievementCriteria {\n\t/** Event name to track (for event-based achievements) */\n\tevent?: string\n\t/** Required count of events */\n\tcount?: number\n\t/** Additional conditions */\n\tconditions?: AchievementCriterion[]\n}\n\n/** Achievement definition (auto-discovered or from Console) */\nexport interface AchievementDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description (shown before unlock) */\n\tdescription: string\n\t/** Description shown after unlock (optional) */\n\tunlockedDescription?: string\n\t/** Achievement type */\n\ttype: AchievementType\n\t/** Tier/rarity */\n\ttier: AchievementTier\n\t/** Category (app-defined) */\n\tcategory: AchievementCategory\n\t/** Icon (Iconify name or URL) */\n\ticon: string\n\t/** Points awarded */\n\tpoints?: number\n\t/** Unlock criteria */\n\tcriteria: AchievementCriteria\n\t/** Target value for incremental achievements */\n\ttarget?: number\n\t/** Whether to show in list before unlock */\n\tsecret?: boolean\n\t/** Order in list */\n\torder?: number\n}\n\n/** User's achievement state */\nexport interface UserAchievement {\n\t/** Achievement definition ID */\n\tachievementId: string\n\t/** Whether unlocked */\n\tunlocked: boolean\n\t/** Unlock timestamp */\n\tunlockedAt: string | null\n\t/** Progress (for incremental) */\n\tprogress: number\n\t/** Target (for incremental) */\n\ttarget: number | null\n\t/** Progress percentage (0-100) */\n\tprogressPercent: number\n}\n\n/** Achievement unlock event */\nexport interface AchievementUnlockEvent {\n\t/** Achievement definition */\n\tachievement: AchievementDefinition\n\t/** User achievement state */\n\tuserAchievement: UserAchievement\n\t/** Whether this is a new unlock (vs already unlocked) */\n\tisNew: boolean\n}\n\n// ============================================================================\n// Engagement Config (from Platform)\n// ============================================================================\n\n/** Complete engagement configuration (fetched from platform) */\nexport interface EngagementConfig {\n\t/** Streak definitions */\n\tstreaks?: StreakDefinition[]\n\t/** Leaderboard definitions */\n\tleaderboards?: LeaderboardDefinition[]\n\t/** Achievement definitions */\n\tachievements?: AchievementDefinition[]\n\t/** Achievement categories (for UI grouping) */\n\tachievementCategories?: {\n\t\tid: string\n\t\tname: string\n\t\ticon?: string\n\t\torder?: number\n\t}[]\n}\n\n// ============================================================================\n// Tier Metadata (for UI)\n// ============================================================================\n\nexport const ACHIEVEMENT_TIER_CONFIG = {\n\tbronze: { color: '#CD7F32', points: 10 },\n\tsilver: { color: '#C0C0C0', points: 25 },\n\tgold: { color: '#FFD700', points: 50 },\n\tplatinum: { color: '#00CED1', points: 100 },\n\tdiamond: { color: '#B9F2FF', points: 200 },\n} as const\n\n// ============================================================================\n// Inline Defaults (Auto-Discovery)\n// ============================================================================\n// These types define the optional inline defaults that can be passed when\n// calling engagement functions. If the entity doesn't exist, the platform\n// will auto-create it with these defaults. Console can override any values.\n\n/**\n * Inline defaults for streak auto-discovery\n *\n * @example\n * ```typescript\n * await recordStreakActivity(config, { streakId: 'daily-login' }, userId, {\n * name: 'Daily Login',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n * ```\n */\nexport interface StreakDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Activity frequency */\n\tfrequency?: StreakFrequency\n\t/** Grace period in hours (default: 0) */\n\tgracePeriodHours?: number\n\t/** Whether streak resets on miss (default: true) */\n\tresetOnMiss?: boolean\n\t/** Maximum streak value (optional cap) */\n\tmaxValue?: number\n\t/** Custom interval in hours (only for 'custom' frequency) */\n\tcustomIntervalHours?: number\n}\n\n/**\n * Inline defaults for leaderboard auto-discovery\n *\n * @example\n * ```typescript\n * await submitScore(config, { leaderboardId: 'high-scores', value: 1500 }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * })\n * ```\n */\nexport interface LeaderboardDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Sort direction (desc = higher is better) */\n\tsortDirection?: LeaderboardSortDirection\n\t/** Reset period */\n\tresetPeriod?: LeaderboardResetPeriod\n\t/** How to aggregate multiple scores from same user */\n\taggregation?: LeaderboardAggregation\n\t/** Maximum entries to keep per period */\n\tmaxEntries?: number\n}\n\n/**\n * Inline defaults for achievement auto-discovery\n *\n * @example\n * ```typescript\n * await unlockAchievement(config, 'first-purchase', userId, {\n * name: 'First Purchase',\n * description: 'Made your first purchase',\n * points: 100,\n * tier: 'bronze',\n * })\n * ```\n */\nexport interface AchievementDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description (shown before unlock) */\n\tdescription?: string\n\t/** Description shown after unlock */\n\tunlockedDescription?: string\n\t/** Achievement type */\n\ttype?: AchievementType\n\t/** Tier/rarity */\n\ttier?: AchievementTier\n\t/** Category (app-defined) */\n\tcategory?: AchievementCategory\n\t/** Icon (Iconify name or URL) */\n\ticon?: string\n\t/** Points awarded */\n\tpoints?: number\n\t/** Target value for incremental achievements */\n\ttarget?: number\n\t/** Whether to show in list before unlock */\n\tsecret?: boolean\n}\n","/**\n * Engagement Functions\n *\n * Pure functions for streaks, leaderboards, and achievements.\n *\n * ## Architecture (ADR-004)\n *\n * Engagement uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when calling functions\n * - Platform auto-discovers/creates entities when first referenced\n * - Console can override names, descriptions, values without deployment\n *\n * @example\n * ```typescript\n * import { unlockAchievement, recordStreakActivity, submitScore } from '@sylphx/sdk'\n *\n * // Unlock achievement with inline defaults (auto-discovered if doesn't exist)\n * await unlockAchievement(config, 'first-win', userId, {\n * name: 'First Win',\n * description: 'Won your first game',\n * points: 100,\n * tier: 'bronze',\n * })\n *\n * // Record streak activity with inline defaults\n * await recordStreakActivity(config, { streakId: 'daily-login' }, userId, {\n * name: 'Daily Login',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n *\n * // Submit leaderboard score with inline defaults\n * await submitScore(config, { leaderboardId: 'high-scores', value: 1500 }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * })\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// Re-export types from types file\nexport type {\n\tAchievementCategory,\n\tAchievementCriteria,\n\tAchievementCriterion,\n\t// Achievements\n\tAchievementDefinition,\n\tAchievementTier,\n\tAchievementType,\n\tAchievementUnlockEvent,\n\tCriteriaOperator,\n\tLeaderboardAggregation,\n\t// Leaderboards\n\tLeaderboardDefinition,\n\tLeaderboardEntry,\n\tLeaderboardQueryOptions,\n\tLeaderboardResetPeriod,\n\tLeaderboardResult,\n\tLeaderboardSortDirection,\n\tRecordActivityInput,\n\tRecordActivityResult,\n\t// Streaks\n\tStreakDefinition,\n\tStreakFrequency,\n\tStreakState,\n\tSubmitScoreInput,\n\tSubmitScoreResult,\n\tUserAchievement,\n} from './lib/engagement/types'\n\nexport { ACHIEVEMENT_TIER_CONFIG } from './lib/engagement/types'\n\n// ============================================================================\n// Streak Functions\n// ============================================================================\n\nimport type {\n\tRecordActivityInput,\n\tRecordActivityResult,\n\tStreakDefaults,\n\tStreakState,\n} from './lib/engagement/types'\n\n/**\n * Get current streak state for a user\n *\n * @example\n * ```typescript\n * const streak = await getStreak(config, 'daily-challenge', userId)\n * console.log(`Current streak: ${streak.current}`)\n * console.log(`Expires in: ${streak.timeRemainingMs}ms`)\n * ```\n */\nexport async function getStreak(\n\tconfig: SylphxConfig,\n\tstreakId: string,\n\tuserId: string,\n): Promise<StreakState> {\n\treturn callApi(config, '/engagement/streaks/get', {\n\t\tmethod: 'GET',\n\t\tquery: { streakId, userId },\n\t})\n}\n\n/**\n * Get all streak states for a user\n *\n * @example\n * ```typescript\n * const streaks = await getAllStreaks(config, userId)\n * for (const streak of streaks) {\n * console.log(`${streak.streakId}: ${streak.current}`)\n * }\n * ```\n */\nexport async function getAllStreaks(config: SylphxConfig, userId: string): Promise<StreakState[]> {\n\treturn callApi(config, '/engagement/streaks', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Record an activity to extend/maintain a streak\n *\n * If the streak doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Activity input (streakId required)\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await recordStreakActivity(config, {\n * streakId: 'daily-challenge',\n * }, userId, {\n * name: 'Daily Challenge',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n *\n * if (result.extended) {\n * console.log(`Streak extended to ${result.streak.current}!`)\n * }\n * if (result.newPersonalBest) {\n * console.log('New personal best!')\n * }\n * ```\n */\nexport async function recordStreakActivity(\n\tconfig: SylphxConfig,\n\tinput: RecordActivityInput,\n\tuserId: string,\n\tdefaults?: StreakDefaults,\n): Promise<RecordActivityResult> {\n\tconst { idempotencyKey, ...inputBody } = input\n\treturn callApi(config, '/engagement/streaks/record', {\n\t\tmethod: 'POST',\n\t\tbody: { ...inputBody, userId, defaults },\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Recover a streak within grace period (may require payment/reward)\n *\n * @example\n * ```typescript\n * const result = await recoverStreak(config, 'daily-challenge', userId)\n * if (result.success) {\n * console.log(`Streak recovered at ${result.streak.current}`)\n * }\n * ```\n */\nexport async function recoverStreak(\n\tconfig: SylphxConfig,\n\tstreakId: string,\n\tuserId: string,\n): Promise<{ success: boolean; streak: StreakState }> {\n\treturn callApi(config, '/engagement/streaks/recover', {\n\t\tmethod: 'POST',\n\t\tbody: { streakId, userId },\n\t})\n}\n\n// ============================================================================\n// Leaderboard Functions\n// ============================================================================\n\nimport type {\n\tLeaderboardDefaults,\n\tLeaderboardQueryOptions,\n\tLeaderboardResult,\n\tSubmitScoreInput,\n\tSubmitScoreResult,\n} from './lib/engagement/types'\n\n/**\n * Get leaderboard entries\n *\n * @example\n * ```typescript\n * const result = await getLeaderboard(config, 'high-scores', userId, {\n * limit: 10,\n * includeSurrounding: true,\n * })\n *\n * for (const entry of result.entries) {\n * console.log(`#${entry.rank} ${entry.displayName}: ${entry.value}`)\n * }\n *\n * if (result.currentUserEntry) {\n * console.log(`Your rank: #${result.currentUserEntry.rank}`)\n * }\n * ```\n */\nexport async function getLeaderboard(\n\tconfig: SylphxConfig,\n\tleaderboardId: string,\n\tuserId: string | null,\n\toptions?: LeaderboardQueryOptions,\n): Promise<LeaderboardResult> {\n\treturn callApi(config, '/engagement/leaderboards/get', {\n\t\tmethod: 'GET',\n\t\tquery: { leaderboardId, userId: userId ?? undefined, ...options } as Record<\n\t\t\tstring,\n\t\t\tstring | number | boolean | undefined\n\t\t>,\n\t})\n}\n\n/**\n * Submit a score to a leaderboard\n *\n * If the leaderboard doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Score submission input (leaderboardId, value required)\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await submitScore(config, {\n * leaderboardId: 'high-scores',\n * value: 1500,\n * metadata: { level: 'hard' },\n * }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * aggregation: 'max',\n * })\n *\n * if (result.newPersonalBest) {\n * console.log('New personal best!')\n * }\n * if (result.rank !== null) {\n * console.log(`Ranked #${result.rank}`)\n * }\n * ```\n */\nexport async function submitScore(\n\tconfig: SylphxConfig,\n\tinput: SubmitScoreInput,\n\tuserId: string,\n\tdefaults?: LeaderboardDefaults,\n): Promise<SubmitScoreResult> {\n\treturn callApi(config, '/engagement/leaderboards/submit', {\n\t\tmethod: 'POST',\n\t\tbody: { ...input, userId, defaults },\n\t})\n}\n\n/**\n * Get user's rank on a leaderboard (even if not in top entries)\n *\n * @example\n * ```typescript\n * const rank = await getUserRank(config, 'high-scores', userId)\n * if (rank) {\n * console.log(`You are ranked #${rank.rank} with score ${rank.value}`)\n * }\n * ```\n */\nexport async function getUserLeaderboardRank(\n\tconfig: SylphxConfig,\n\tleaderboardId: string,\n\tuserId: string,\n): Promise<{ rank: number; value: number } | null> {\n\treturn callApi(config, '/engagement/leaderboards/rank', {\n\t\tmethod: 'GET',\n\t\tquery: { leaderboardId, userId },\n\t})\n}\n\n// ============================================================================\n// Achievement Functions\n// ============================================================================\n\nimport type {\n\tAchievementDefaults,\n\tAchievementUnlockEvent,\n\tUserAchievement,\n} from './lib/engagement/types'\n\n/**\n * Get all achievements with user progress\n *\n * @example\n * ```typescript\n * const achievements = await getAchievements(config, userId)\n *\n * const unlocked = achievements.filter(a => a.unlocked)\n * console.log(`${unlocked.length} achievements unlocked`)\n *\n * const inProgress = achievements.filter(a => !a.unlocked && a.progress > 0)\n * for (const a of inProgress) {\n * console.log(`${a.achievementId}: ${a.progress}/${a.target}`)\n * }\n * ```\n */\nexport async function getAchievements(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<UserAchievement[]> {\n\treturn callApi(config, '/engagement/achievements', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Get a single achievement with user progress\n *\n * @example\n * ```typescript\n * const achievement = await getAchievement(config, 'first-win', userId)\n * if (achievement?.unlocked) {\n * console.log(`Unlocked at ${achievement.unlockedAt}`)\n * }\n * ```\n */\nexport async function getAchievement(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tuserId: string,\n): Promise<UserAchievement | null> {\n\treturn callApi(config, '/engagement/achievements/get', {\n\t\tmethod: 'GET',\n\t\tquery: { achievementId, userId },\n\t})\n}\n\n/**\n * Manually unlock an achievement\n *\n * If the achievement doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param achievementId - Achievement ID\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await unlockAchievement(config, 'first-purchase', userId, {\n * name: 'First Purchase',\n * description: 'Made your first purchase',\n * points: 100,\n * tier: 'bronze',\n * })\n * if (result.isNew) {\n * showAchievementToast(result.achievement)\n * }\n * ```\n */\nexport async function unlockAchievement(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tuserId: string,\n\tdefaults?: AchievementDefaults,\n): Promise<AchievementUnlockEvent> {\n\treturn callApi(config, '/engagement/achievements/unlock', {\n\t\tmethod: 'POST',\n\t\tbody: { achievementId, userId, defaults },\n\t})\n}\n\n/**\n * Increment progress on an incremental achievement\n *\n * If the achievement doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param achievementId - Achievement ID\n * @param amount - Amount to increment\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * // User collected an item\n * const result = await incrementAchievementProgress(config, 'collector-100', 1, userId, {\n * name: 'Collector',\n * description: 'Collect 100 items',\n * type: 'incremental',\n * target: 100,\n * tier: 'silver',\n * })\n *\n * if (result.unlocked) {\n * console.log('Achievement unlocked!')\n * } else {\n * console.log(`Progress: ${result.progress}/${result.target}`)\n * }\n * ```\n */\nexport async function incrementAchievementProgress(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tamount: number,\n\tuserId: string,\n\tdefaults?: AchievementDefaults,\n): Promise<UserAchievement> {\n\treturn callApi(config, '/engagement/achievements/progress', {\n\t\tmethod: 'POST',\n\t\tbody: { achievementId, amount, userId, defaults },\n\t})\n}\n\n/**\n * Get total achievement points for a user\n *\n * @example\n * ```typescript\n * const points = await getAchievementPoints(config, userId)\n * console.log(`Total points: ${points.total}`)\n * console.log(`This month: ${points.thisMonth}`)\n * ```\n */\nexport async function getAchievementPoints(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<{ total: number; thisMonth: number; rank: number | null }> {\n\treturn callApi(config, '/engagement/achievements/points', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n","/**\n * Organization Functions\n *\n * Pure functions for organization management - no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /api/sdk/orgs/* for all operations.\n *\n * Types are re-exported from `@sylphx/contract` (ADR-084). The contract is\n * the single source of truth for every wire shape — this module only adds\n * ergonomic aliases (`Organization*` wrappers) and inline envelopes for\n * request bodies + member role updates.\n */\n\n// Contract is the SSOT for both endpoint paths and wire types.\nimport {\n\ttype CreateOrgInput as ContractCreateOrgInput,\n\ttype InviteMemberInput as ContractInviteMemberInput,\n\ttype UpdateOrgInput as ContractUpdateOrgInput,\n\ttype MembershipInfo,\n\ttype Organization,\n\ttype OrgInvitation,\n\ttype OrgMember,\n\ttype OrgSdkRole,\n\torganizationsEndpoints,\n\ttype UserOrganizationMembership,\n\ttype UserOrganizationsResponse,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type { Organization }\n/**\n * The contract types `OrgMember.role` as a permissive `string` so it can\n * absorb any role stored in the DB (including legacy `'member'`). The SDK\n * surface is narrower — components key off the six-value role union — so\n * re-narrow here to keep `member.role` valid in existing call sites.\n */\nexport type OrganizationMember = Omit<OrgMember, 'role'> & {\n\trole: OrgSdkRole\n}\nexport type OrganizationInvitation = OrgInvitation\n/**\n * Membership info returned inline with `GET /orgs/:idOrSlug`. The endpoint\n * may return `null` when the caller has no role on the org (platform-admin\n * path) — SDK callers get the union on the response envelope, not here.\n */\nexport type OrganizationMembership = MembershipInfo\nexport type UserOrganization = UserOrganizationMembership\nexport type OrganizationsListResult = {\n\torganizations: Organization[]\n\ttotal: number\n\tlimit: number\n\toffset: number\n}\nexport type OrgRole = OrgSdkRole\nexport type CreateOrgInput = ContractCreateOrgInput\nexport type UpdateOrgInput = ContractUpdateOrgInput & {\n\tmetadata?: Record<string, unknown> | null\n}\nexport type InviteMemberInput = ContractInviteMemberInput\n\n// ============================================================================\n// Organization CRUD\n// ============================================================================\n\n/**\n * Get all organizations the current user belongs to\n *\n * @example\n * ```typescript\n * const { organizations } = await getOrganizations(config)\n * ```\n */\nexport async function getOrganizations(config: SylphxConfig): Promise<UserOrganizationsResponse> {\n\tconst endpoint = organizationsEndpoints.memberships\n\treturn callApi<UserOrganizationsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Admin-only organization list.\n *\n * `getOrganizations()` is intentionally user-scoped. Use this function only\n * from Platform operator contexts that are allowed to enumerate every org.\n *\n * @example\n * ```typescript\n * const { organizations, total } = await listOrganizations(adminConfig)\n * ```\n */\nexport async function listOrganizations(\n\tconfig: SylphxConfig,\n\toptions: { limit?: number; offset?: number } = {},\n): Promise<OrganizationsListResult> {\n\tconst endpoint = organizationsEndpoints.list\n\treturn callApi<OrganizationsListResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: options,\n\t})\n}\n\n/**\n * Get organization by ID or slug\n *\n * @example\n * ```typescript\n * const { organization, membership } = await getOrganization(config, 'my-org')\n * ```\n */\nexport async function getOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{\n\torganization: Organization\n\tmembership: OrganizationMembership | null\n}> {\n\treturn callApi<{\n\t\torganization: Organization\n\t\tmembership: OrganizationMembership | null\n\t}>(config, `/orgs/${orgIdOrSlug}`)\n}\n\n/**\n * Create a new organization\n *\n * @example\n * ```typescript\n * const { organization } = await createOrganization(config, {\n * name: 'My Company',\n * slug: 'my-company',\n * })\n * ```\n */\nexport async function createOrganization(\n\tconfig: SylphxConfig,\n\tinput: CreateOrgInput,\n): Promise<{ organization: Organization }> {\n\tconst endpoint = organizationsEndpoints.create\n\tconst organization = await callApi<Organization>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: input,\n\t})\n\treturn { organization }\n}\n\n/**\n * Update an organization\n *\n * @example\n * ```typescript\n * const { organization } = await updateOrganization(config, 'my-org', {\n * name: 'New Name',\n * })\n * ```\n */\nexport async function updateOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinput: UpdateOrgInput,\n): Promise<{ organization: Organization }> {\n\treturn callApi<{ organization: Organization }>(config, `/orgs/${orgIdOrSlug}`, {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Delete an organization\n *\n * Requires super_admin role.\n *\n * @example\n * ```typescript\n * await deleteOrganization(config, 'my-org')\n * ```\n */\nexport async function deleteOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Members\n// ============================================================================\n\n/**\n * Get organization members\n *\n * @example\n * ```typescript\n * const { members } = await getOrganizationMembers(config, 'my-org')\n * ```\n */\nexport async function getOrganizationMembers(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ members: OrganizationMember[] }> {\n\treturn callApi<{ members: OrganizationMember[] }>(config, `/orgs/${orgIdOrSlug}/members`)\n}\n\n/**\n * Invite a member to an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { invitation } = await inviteOrganizationMember(config, 'my-org', {\n * email: 'user@example.com',\n * role: 'developer',\n * })\n * ```\n */\nexport async function inviteOrganizationMember(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinput: InviteMemberInput,\n): Promise<{ invitation: OrganizationInvitation }> {\n\treturn callApi<{ invitation: OrganizationInvitation }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/invite`,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: input,\n\t\t},\n\t)\n}\n\n/**\n * Update a member's role\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { member } = await updateOrganizationMemberRole(config, 'my-org', userId, 'admin')\n * ```\n */\nexport async function updateOrganizationMemberRole(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n\trole: OrgRole,\n): Promise<{ member: OrganizationMember }> {\n\treturn callApi<{ member: OrganizationMember }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/role`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { role },\n\t\t},\n\t)\n}\n\n/**\n * Remove a member from an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * await removeOrganizationMember(config, 'my-org', userId)\n * ```\n */\nexport async function removeOrganizationMember(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/members/${memberId}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n/**\n * Leave an organization\n *\n * @example\n * ```typescript\n * await leaveOrganization(config, 'my-org')\n * ```\n */\nexport async function leaveOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/leave`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n// ============================================================================\n// Invitations\n// ============================================================================\n\n/**\n * Get pending invitations for an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { invitations } = await getOrganizationInvitations(config, 'my-org')\n * ```\n */\nexport async function getOrganizationInvitations(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ invitations: OrganizationInvitation[] }> {\n\treturn callApi<{ invitations: OrganizationInvitation[] }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/invitations`,\n\t)\n}\n\n/**\n * Accept an organization invitation\n *\n * @example\n * ```typescript\n * const { organization } = await acceptOrganizationInvitation(config, invitationToken)\n * ```\n */\nexport async function acceptOrganizationInvitation(\n\tconfig: SylphxConfig,\n\ttoken: string,\n): Promise<{ organization: Organization }> {\n\treturn callApi<{ organization: Organization }>(config, '/orgs/invitations/accept', {\n\t\tmethod: 'POST',\n\t\tbody: { token },\n\t})\n}\n\n/**\n * Revoke a pending invitation\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * await revokeOrganizationInvitation(config, 'my-org', invitationId)\n * ```\n */\nexport async function revokeOrganizationInvitation(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinvitationId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/invitations/${invitationId}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Check if user has a specific role or higher in the organization\n */\nexport function hasRole(membership: OrganizationMembership | null, minimumRole: OrgRole): boolean {\n\tif (!membership) return false\n\n\tconst roleHierarchy: OrgRole[] = [\n\t\t'viewer',\n\t\t'analytics',\n\t\t'developer',\n\t\t'billing',\n\t\t'admin',\n\t\t'super_admin',\n\t]\n\n\tconst userRoleIndex = roleHierarchy.indexOf(membership.role)\n\tconst requiredRoleIndex = roleHierarchy.indexOf(minimumRole)\n\n\treturn userRoleIndex >= requiredRoleIndex\n}\n\n/**\n * Check if user can manage members (invite, remove, change roles)\n */\nexport function canManageMembers(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'admin')\n}\n\n/**\n * Check if user can manage organization settings\n */\nexport function canManageSettings(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'admin')\n}\n\n/**\n * Check if user can delete the organization\n */\nexport function canDeleteOrganization(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'super_admin')\n}\n","/**\n * Permission Functions\n *\n * Pure functions for permission management — no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /permissions/* for project-scoped operations.\n * Uses REST API at /orgs/{orgId}/members/{memberId}/permissions for member checks.\n *\n * Types are self-contained (not dependent on generated OpenAPI spec) because\n * the RBAC routes were added after the last spec generation.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A permission definition within a project.\n *\n * Permissions are the atomic building blocks of RBAC roles.\n * They use colon-separated keys (e.g. \"org:members:read\", \"payroll:approve\").\n */\nexport interface Permission {\n\t/** Prefixed permission ID (e.g. \"perm_xxx\") */\n\tid: string\n\t/** Unique key within the project (e.g. \"org:members:read\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription: string | null\n\t/** Whether this is a system-defined permission (immutable) */\n\tisSystem: boolean\n\t/** ISO 8601 creation timestamp */\n\tcreatedAt: string\n}\n\n/**\n * Input for creating a custom permission.\n */\nexport interface CreatePermissionInput {\n\t/** Unique key — colon-separated lowercase segments (e.g. \"org:leave:approve\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription?: string\n}\n\n/**\n * Resolved permissions for a member, including their assigned role.\n */\nexport interface MemberPermissionsResult {\n\t/** Prefixed user ID of the member */\n\tmemberId: string\n\t/** Assigned role (null if no role assigned) */\n\trole: { key: string; name: string } | null\n\t/** Flattened, deduplicated permission keys from all assigned roles */\n\tpermissions: string[]\n}\n\n// ============================================================================\n// Permission CRUD (project-scoped, requires secretKey)\n// ============================================================================\n\n/**\n * List all permissions for the current project.\n *\n * Returns both system-defined and custom permissions.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { permissions } = await listPermissions(config)\n * console.log(permissions.map(p => p.key))\n * // ['org:members:read', 'org:members:manage', 'payroll:view', ...]\n * ```\n */\nexport async function listPermissions(\n\tconfig: SylphxConfig,\n): Promise<{ permissions: Permission[] }> {\n\treturn callApi<{ permissions: Permission[] }>(config, '/permissions')\n}\n\n/**\n * Create a custom permission for the project.\n *\n * Permission keys must be colon-separated lowercase segments\n * (e.g. \"org:leave:approve\", \"payroll:run\").\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { permission } = await createPermission(config, {\n * key: 'payroll:approve',\n * name: 'Approve Payroll',\n * description: 'Can approve payroll runs for the organization',\n * })\n * ```\n */\nexport async function createPermission(\n\tconfig: SylphxConfig,\n\tinput: CreatePermissionInput,\n): Promise<{ permission: Permission }> {\n\treturn callApi<{ permission: Permission }>(config, '/permissions', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Delete a custom permission by key.\n *\n * System permissions cannot be deleted.\n * Role-permission assignments are removed automatically via cascading delete.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { success } = await deletePermission(config, 'payroll:approve')\n * ```\n */\nexport async function deletePermission(\n\tconfig: SylphxConfig,\n\tpermissionKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/permissions/${permissionKey}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Member Permissions (org-scoped, requires accessToken)\n// ============================================================================\n\n/**\n * Get a member's resolved permissions within an organization.\n *\n * Returns the flattened, deduplicated set of permission keys from all\n * roles assigned to the member. Also returns their current role info.\n * Requires the caller to be a member of the same organization.\n *\n * @example\n * ```typescript\n * const result = await getMemberPermissions(config, 'my-org', 'usr_abc123')\n * console.log(result.permissions)\n * // ['org:members:read', 'payroll:view', 'payroll:approve']\n * console.log(result.role)\n * // { key: 'hr_manager', name: 'HR Manager' }\n * ```\n */\nexport async function getMemberPermissions(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n): Promise<MemberPermissionsResult> {\n\treturn callApi<MemberPermissionsResult>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/permissions`,\n\t)\n}\n\n// ============================================================================\n// Pure Helpers (no API calls — client-side permission checks)\n// ============================================================================\n\n/**\n * Check if a permission set includes a specific permission.\n *\n * Pure function — no API call. Use with permissions from JWT claims\n * (org_permissions) or from getMemberPermissions().\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view']\n * hasPermission(permissions, 'payroll:view') // true\n * hasPermission(permissions, 'payroll:approve') // false\n * ```\n */\nexport function hasPermission(permissions: string[], required: string): boolean {\n\treturn permissions.includes(required)\n}\n\n/**\n * Check if a permission set includes ANY of the required permissions.\n *\n * Pure function — no API call. Returns true if at least one of the\n * required permissions is present.\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view']\n * hasAnyPermission(permissions, ['payroll:view', 'payroll:approve']) // true\n * hasAnyPermission(permissions, ['admin:full', 'super:admin']) // false\n * ```\n */\nexport function hasAnyPermission(permissions: string[], required: string[]): boolean {\n\treturn required.some((perm) => permissions.includes(perm))\n}\n\n/**\n * Check if a permission set includes ALL of the required permissions.\n *\n * Pure function — no API call. Returns true only if every required\n * permission is present.\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view', 'payroll:approve']\n * hasAllPermissions(permissions, ['payroll:view', 'payroll:approve']) // true\n * hasAllPermissions(permissions, ['payroll:view', 'admin:full']) // false\n * ```\n */\nexport function hasAllPermissions(permissions: string[], required: string[]): boolean {\n\treturn required.every((perm) => permissions.includes(perm))\n}\n","/**\n * Role Functions\n *\n * Pure functions for role management — no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /roles/* for project-scoped operations.\n * Uses REST API at /orgs/{orgId}/members/{memberId}/assign-role for assignment.\n *\n * Types are self-contained (not dependent on generated OpenAPI spec) because\n * the RBAC routes were added after the last spec generation.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A role definition within a project.\n *\n * Roles bundle permissions into named groups that can be assigned to\n * organization members (e.g. \"HR Manager\", \"Payroll Admin\").\n */\nexport interface Role {\n\t/** Prefixed role ID (e.g. \"role_xxx\") */\n\tid: string\n\t/** Unique key within the project (e.g. \"hr_manager\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription: string | null\n\t/** Whether this is a system-defined role (metadata immutable) */\n\tisSystem: boolean\n\t/** Whether this role is automatically assigned to new org members */\n\tisDefault: boolean\n\t/** Display order (lower = higher priority) */\n\tsortOrder: number\n\t/** Permission keys assigned to this role */\n\tpermissions: string[]\n\t/** ISO 8601 creation timestamp */\n\tcreatedAt: string\n\t/** ISO 8601 update timestamp (present on roles route response) */\n\tupdatedAt?: string\n}\n\n/**\n * Input for creating a custom role.\n */\nexport interface CreateRoleInput {\n\t/** Unique key — lowercase alphanumeric with underscores (e.g. \"hr_manager\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription?: string\n\t/** Permission keys to assign to this role */\n\tpermissions?: string[]\n\t/** Whether to auto-assign to new org members */\n\tisDefault?: boolean\n\t/** Display order (lower = higher priority) */\n\tsortOrder?: number\n}\n\n/**\n * Input for updating an existing role.\n *\n * System role metadata (name, description) is immutable, but their\n * permissions can be changed.\n */\nexport interface UpdateRoleInput {\n\t/** Human-readable name (ignored for system roles) */\n\tname?: string\n\t/** Description (ignored for system roles) */\n\tdescription?: string | null\n\t/** Permission keys to assign (replaces existing) */\n\tpermissions?: string[]\n\t/** Whether to auto-assign to new org members */\n\tisDefault?: boolean\n\t/** Display order */\n\tsortOrder?: number\n}\n\n// ============================================================================\n// Role CRUD (project-scoped, requires secretKey)\n// ============================================================================\n\n/**\n * List all roles for the current project.\n *\n * Returns both system-defined and custom roles, each with their\n * assigned permission keys. Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { roles } = await listRoles(config)\n * for (const role of roles) {\n * console.log(`${role.name}: ${role.permissions.join(', ')}`)\n * }\n * ```\n */\nexport async function listRoles(config: SylphxConfig): Promise<{ roles: Role[] }> {\n\treturn callApi<{ roles: Role[] }>(config, '/roles')\n}\n\n/**\n * Get a single role by key, including its assigned permission keys.\n *\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await getRole(config, 'hr_manager')\n * console.log(role.permissions)\n * // ['org:members:read', 'payroll:view', 'payroll:approve']\n * ```\n */\nexport async function getRole(config: SylphxConfig, roleKey: string): Promise<{ role: Role }> {\n\treturn callApi<{ role: Role }>(config, `/roles/${roleKey}`)\n}\n\n/**\n * Create a custom role with optional permission assignments.\n *\n * Role keys must be lowercase alphanumeric with underscores\n * (e.g. \"hr_manager\", \"payroll_admin\").\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await createRole(config, {\n * key: 'hr_manager',\n * name: 'HR Manager',\n * description: 'Can manage employees and approve leave',\n * permissions: ['org:members:read', 'leave:approve', 'payroll:view'],\n * })\n * ```\n */\nexport async function createRole(\n\tconfig: SylphxConfig,\n\tinput: CreateRoleInput,\n): Promise<{ role: Role }> {\n\t// Map SDK field names to API field names\n\tconst body: Record<string, unknown> = {\n\t\tkey: input.key,\n\t\tname: input.name,\n\t}\n\tif (input.description !== undefined) body.description = input.description\n\tif (input.permissions !== undefined) body.permissionKeys = input.permissions\n\tif (input.isDefault !== undefined) body.isDefault = input.isDefault\n\tif (input.sortOrder !== undefined) body.sortOrder = input.sortOrder\n\n\treturn callApi<{ role: Role }>(config, '/roles', {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\n/**\n * Update a role's metadata and/or permission assignments.\n *\n * System role metadata (name, description) is immutable, but their\n * permissions can be changed. Passing `permissions` replaces the\n * entire permission set for the role.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await updateRole(config, 'hr_manager', {\n * permissions: ['org:members:read', 'org:members:manage', 'leave:approve'],\n * })\n * ```\n */\nexport async function updateRole(\n\tconfig: SylphxConfig,\n\troleKey: string,\n\tinput: UpdateRoleInput,\n): Promise<{ role: Role }> {\n\t// Map SDK field names to API field names\n\tconst body: Record<string, unknown> = {}\n\tif (input.name !== undefined) body.name = input.name\n\tif (input.description !== undefined) body.description = input.description\n\tif (input.permissions !== undefined) body.permissionKeys = input.permissions\n\tif (input.isDefault !== undefined) body.isDefault = input.isDefault\n\tif (input.sortOrder !== undefined) body.sortOrder = input.sortOrder\n\n\treturn callApi<{ role: Role }>(config, `/roles/${roleKey}`, {\n\t\tmethod: 'PUT',\n\t\tbody,\n\t})\n}\n\n/**\n * Delete a custom role by key.\n *\n * System roles cannot be deleted. Roles with active member assignments\n * cannot be deleted — reassign members first.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { success } = await deleteRole(config, 'hr_manager')\n * ```\n */\nexport async function deleteRole(\n\tconfig: SylphxConfig,\n\troleKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/roles/${roleKey}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Member Role Assignment (org-scoped, requires accessToken)\n// ============================================================================\n\n/**\n * Assign an RBAC role to an organization member.\n *\n * Replaces any existing role assignment (single-role mode).\n * Requires admin access to the organization.\n *\n * @example\n * ```typescript\n * const { success } = await assignMemberRole(config, 'my-org', 'usr_abc123', 'hr_manager')\n * ```\n */\nexport async function assignMemberRole(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n\troleKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/assign-role`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { roleKey },\n\t\t},\n\t)\n}\n","/**\n * Secrets SDK\n *\n * Secure secrets management for applications.\n * Secrets are encrypted at rest with AES-256-GCM.\n *\n * @example\n * ```typescript\n * import { createConfig, getSecret, getSecrets, listSecretKeys } from '@sylphx/sdk'\n *\n * const config = createConfig({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Get a single secret\n * const dbUrl = await getSecret(config, { key: 'DATABASE_URL' })\n * console.log(dbUrl.value) // postgres://...\n *\n * // Get multiple secrets at once\n * const secrets = await getSecrets(config, {\n * keys: ['DATABASE_URL', 'API_KEY', 'JWT_SECRET']\n * })\n * console.log(secrets.DATABASE_URL) // postgres://...\n *\n * // List all secret keys (without values)\n * const keys = await listSecretKeys(config)\n * keys.forEach(k => console.log(k.key, k.description))\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GetSecretInput {\n\t/** Secret key (uppercase, underscores allowed) */\n\tkey: string\n\t/** Optional environment ID override */\n\tenvironmentId?: string\n}\n\nexport interface GetSecretResult {\n\t/** Secret key */\n\tkey: string\n\t/** Decrypted secret value */\n\tvalue: string\n\t/** Version number */\n\tversion: string\n}\n\nexport interface GetSecretsInput {\n\t/** Array of secret keys to retrieve */\n\tkeys: string[]\n\t/** Optional environment ID override */\n\tenvironmentId?: string\n}\n\n/** Map of key -> decrypted value */\nexport type GetSecretsResult = Record<string, string>\n\nexport interface ListSecretKeysInput {\n\t/** Optional environment ID filter */\n\tenvironmentId?: string\n}\n\nexport interface SecretKeyInfo {\n\t/** Secret key name */\n\tkey: string\n\t/** Human-readable description */\n\tdescription: string | null\n\t/** Current version */\n\tversion: string\n\t/** Whether this is environment-specific */\n\tisEnvironmentSpecific: boolean\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get a single secret value by key.\n *\n * @param config - SDK configuration\n * @param input - Secret key and optional environment ID\n * @returns Decrypted secret value\n * @throws Error if secret not found or access denied\n *\n * @example\n * ```typescript\n * const secret = await getSecret(config, { key: 'DATABASE_URL' })\n * const dbConnection = createPool(secret.value)\n * ```\n */\nexport async function getSecret(\n\tconfig: SylphxConfig,\n\tinput: GetSecretInput,\n): Promise<GetSecretResult> {\n\treturn callApi<GetSecretResult>(config, '/secrets/get', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tkey: input.key,\n\t\t\tenvironmentId: input.environmentId,\n\t\t},\n\t})\n}\n\n/**\n * Get multiple secrets at once.\n *\n * More efficient than multiple getSecret calls.\n *\n * @param config - SDK configuration\n * @param input - Array of secret keys\n * @returns Map of key -> decrypted value\n *\n * @example\n * ```typescript\n * const secrets = await getSecrets(config, {\n * keys: ['DATABASE_URL', 'CACHE_URL', 'JWT_SECRET']\n * })\n *\n * const db = createPool(secrets.DATABASE_URL)\n * const cache = createCacheClient(secrets.CACHE_URL)\n * ```\n */\nexport async function getSecrets(\n\tconfig: SylphxConfig,\n\tinput: GetSecretsInput,\n): Promise<GetSecretsResult> {\n\treturn callApi<GetSecretsResult>(config, '/secrets/getMany', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tkeys: input.keys,\n\t\t\tenvironmentId: input.environmentId,\n\t\t},\n\t})\n}\n\n/**\n * List all secret keys (without values).\n *\n * Useful for showing available secrets in UI or debugging.\n *\n * @param config - SDK configuration\n * @param input - Optional environment filter\n * @returns Array of secret key info\n *\n * @example\n * ```typescript\n * const keys = await listSecretKeys(config)\n * console.log('Available secrets:')\n * keys.forEach(k => console.log(` ${k.key}: ${k.description}`))\n * ```\n */\nexport async function listSecretKeys(\n\tconfig: SylphxConfig,\n\tinput: ListSecretKeysInput = {},\n): Promise<SecretKeyInfo[]> {\n\treturn callApi<SecretKeyInfo[]>(config, '/secrets/listKeys', {\n\t\tmethod: 'GET',\n\t\tquery: input.environmentId ? { environmentId: input.environmentId } : undefined,\n\t})\n}\n\n/**\n * Check if a secret exists without retrieving its value.\n *\n * @param config - SDK configuration\n * @param key - Secret key to check\n * @returns true if the secret exists\n *\n * @example\n * ```typescript\n * if (await hasSecret(config, 'STRIPE_SECRET_KEY')) {\n * // Stripe is configured, enable payment features\n * }\n * ```\n */\nexport async function hasSecret(config: SylphxConfig, key: string): Promise<boolean> {\n\ttry {\n\t\tconst keys = await listSecretKeys(config)\n\t\treturn keys.some((k) => k.key === key)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Get all secrets for an environment as an object.\n *\n * Useful for loading all secrets into process.env at startup.\n *\n * @param config - SDK configuration\n * @param environmentId - Optional environment ID\n * @returns Object with all secrets\n *\n * @example\n * ```typescript\n * // Load all secrets into process.env at app startup\n * const secrets = await getAllSecrets(config)\n * Object.assign(process.env, secrets)\n * ```\n */\nexport async function getAllSecrets(\n\tconfig: SylphxConfig,\n\tenvironmentId?: string,\n): Promise<GetSecretsResult> {\n\tconst keys = await listSecretKeys(config, { environmentId })\n\tif (keys.length === 0) {\n\t\treturn {}\n\t}\n\treturn getSecrets(config, { keys: keys.map((k) => k.key), environmentId })\n}\n","/**\n * Search SDK\n *\n * State-of-the-art search with full-text, semantic, and hybrid modes.\n * Powered by PostgreSQL tsvector + pgvector.\n *\n * @example\n * ```typescript\n * import { createConfig, indexDocument, search, batchIndex } from '@sylphx/sdk'\n *\n * const config = createConfig({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Index a document\n * await indexDocument(config, {\n * content: 'How to reset your password...',\n * title: 'Password Reset Guide',\n * namespace: 'help-articles',\n * category: 'account',\n * tags: ['password', 'security'],\n * })\n *\n * // Search (hybrid mode by default)\n * const results = await search(config, {\n * query: 'forgot my login credentials',\n * namespace: 'help-articles',\n * searchType: 'hybrid',\n * highlight: true,\n * })\n *\n * results.results.forEach(r => console.log(r.title, r.score, r.highlight))\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface IndexDocumentInput {\n\t/** Document title (weighted higher in search) */\n\ttitle?: string\n\t/** Document content to index */\n\tcontent: string\n\t/** Namespace for data isolation (e.g., 'products', 'articles') */\n\tnamespace?: string\n\t/** External document ID (your system's ID) */\n\texternalId?: string\n\t/** URL or path */\n\turl?: string\n\t/** Searchable metadata */\n\tmetadata?: Record<string, unknown>\n\t/** Category facet for filtering */\n\tcategory?: string\n\t/** Type facet for filtering */\n\ttype?: string\n\t/** Tags facet for filtering */\n\ttags?: string[]\n\t/** Language for full-text search (default: english) */\n\tlanguage?: string\n\t/** Skip embedding generation (for keyword-only search) */\n\tskipEmbedding?: boolean\n\t/** Embedding model to use */\n\tembeddingModel?: string\n}\n\nexport interface IndexDocumentResult {\n\t/** Generated document ID */\n\tid: string\n\t/** External ID if provided */\n\texternalId: string | null\n\t/** Namespace */\n\tnamespace: string\n}\n\nexport interface BatchIndexInput {\n\t/** Documents to index (max 100) */\n\tdocuments: Array<{\n\t\ttitle?: string\n\t\tcontent: string\n\t\texternalId?: string\n\t\turl?: string\n\t\tmetadata?: Record<string, unknown>\n\t\tcategory?: string\n\t\ttype?: string\n\t\ttags?: string[]\n\t}>\n\t/** Namespace for all documents */\n\tnamespace?: string\n\t/** Language for full-text search */\n\tlanguage?: string\n\t/** Skip embedding generation */\n\tskipEmbedding?: boolean\n\t/** Embedding model to use */\n\tembeddingModel?: string\n}\n\nexport interface BatchIndexResult {\n\t/** Number of documents indexed */\n\tindexed: number\n\t/** Generated document IDs */\n\tids: string[]\n}\n\nexport type SearchType = 'keyword' | 'semantic' | 'hybrid'\n\nexport interface SearchInput {\n\t/** Search query text */\n\tquery: string\n\t/** Namespace to search within */\n\tnamespace?: string\n\t/** Search type: keyword, semantic, or hybrid (default) */\n\tsearchType?: SearchType\n\t/** Maximum results to return (default: 10, max: 100) */\n\tlimit?: number\n\t/** Offset for pagination */\n\toffset?: number\n\t/** Minimum similarity threshold (0-1) for semantic search */\n\tminSimilarity?: number\n\t/** Enable typo tolerance (default: true) */\n\ttypoTolerance?: boolean\n\t/** Language for full-text search */\n\tlanguage?: string\n\t/** Facet filters */\n\tfilters?: {\n\t\tcategory?: string\n\t\ttype?: string\n\t\ttags?: string[]\n\t\tmetadata?: Record<string, unknown>\n\t}\n\t/** Include highlighted snippets (default: true) */\n\thighlight?: boolean\n\t/** Embedding model for semantic search */\n\tembeddingModel?: string\n\t/** Track this query for analytics (default: true) */\n\ttrackQuery?: boolean\n\t/** Session ID for analytics */\n\tsessionId?: string\n\t/** User ID for analytics */\n\tuserId?: string\n}\n\nexport interface SearchResultItem {\n\t/** Document ID */\n\tid: string\n\t/** External ID if set */\n\texternalId: string | null\n\t/** Document title */\n\ttitle: string | null\n\t/** Document content */\n\tcontent: string\n\t/** Document URL */\n\turl: string | null\n\t/** Document metadata */\n\tmetadata: Record<string, unknown> | null\n\t/** Category facet */\n\tcategory: string | null\n\t/** Type facet */\n\ttype: string | null\n\t/** Tags facet */\n\ttags: string[] | null\n\t/** Combined score */\n\tscore: number\n\t/** Keyword search score (if hybrid) */\n\tkeywordScore?: number\n\t/** Semantic search score (if hybrid) */\n\tsemanticScore?: number\n\t/** Highlighted snippet (if enabled) */\n\thighlight?: string\n}\n\nexport interface SearchResponse {\n\t/** Search results */\n\tresults: SearchResultItem[]\n\t/** Total results found */\n\ttotal: number\n\t/** Original query */\n\tquery: string\n\t/** Search type used */\n\tsearchType: SearchType\n\t/** Query processing time in ms */\n\tlatencyMs: number\n}\n\nexport interface GetFacetsInput {\n\t/** Namespace to get facets from */\n\tnamespace?: string\n\t/** Facets to retrieve */\n\tfacets?: Array<'category' | 'type' | 'tags'>\n\t/** Filter facets by category or type */\n\tfilters?: {\n\t\tcategory?: string\n\t\ttype?: string\n\t}\n}\n\nexport interface FacetsResponse {\n\tfacets: {\n\t\tcategory?: Array<{ value: string; count: number }>\n\t\ttype?: Array<{ value: string; count: number }>\n\t\ttags?: Array<{ value: string; count: number }>\n\t}\n}\n\nexport interface DeleteDocumentInput {\n\t/** Document ID to delete */\n\tid?: string\n\t/** Or delete by external ID */\n\texternalId?: string\n\t/** Namespace (required if using externalId) */\n\tnamespace?: string\n}\n\nexport interface UpsertDocumentInput extends IndexDocumentInput {\n\t/** External ID is required for upsert */\n\texternalId: string\n}\n\nexport interface UpsertDocumentResult extends IndexDocumentResult {\n\t/** Whether the document was created (true) or updated (false) */\n\tcreated: boolean\n}\n\nexport interface SearchStatsResult {\n\t/** Total documents indexed */\n\ttotalDocuments: number\n\t/** Documents with embeddings */\n\tdocumentsWithEmbedding: number\n\t/** Documents by namespace */\n\tbyNamespace: Array<{ namespace: string; count: number }>\n}\n\nexport interface TrackClickInput {\n\t/** Search query ID */\n\tqueryId: string\n\t/** Clicked document ID */\n\tdocumentId: string\n\t/** Position in results (1-indexed) */\n\tposition: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Index a document for search.\n *\n * Automatically generates tsvector for full-text search and\n * optional embedding for semantic search.\n *\n * @param config - SDK configuration\n * @param input - Document to index\n * @returns Indexed document info\n *\n * @example\n * ```typescript\n * const result = await indexDocument(config, {\n * title: 'Getting Started Guide',\n * content: 'Welcome to our platform...',\n * namespace: 'docs',\n * category: 'tutorials',\n * tags: ['beginner', 'setup'],\n * })\n * ```\n */\nexport async function indexDocument(\n\tconfig: SylphxConfig,\n\tinput: IndexDocumentInput,\n): Promise<IndexDocumentResult> {\n\treturn callApi<IndexDocumentResult>(config, '/search/index', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\ttitle: input.title,\n\t\t\tcontent: input.content,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\texternalId: input.externalId,\n\t\t\turl: input.url,\n\t\t\tmetadata: input.metadata,\n\t\t\tcategory: input.category,\n\t\t\ttype: input.type,\n\t\t\ttags: input.tags,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Index multiple documents in a single batch.\n *\n * More efficient than multiple indexDocument calls.\n * Max 100 documents per batch.\n *\n * @param config - SDK configuration\n * @param input - Documents to index\n * @returns Batch index result\n *\n * @example\n * ```typescript\n * const result = await batchIndex(config, {\n * namespace: 'products',\n * documents: products.map(p => ({\n * title: p.name,\n * content: p.description,\n * externalId: p.id,\n * category: p.category,\n * }))\n * })\n * console.log(`Indexed ${result.indexed} products`)\n * ```\n */\nexport async function batchIndex(\n\tconfig: SylphxConfig,\n\tinput: BatchIndexInput,\n): Promise<BatchIndexResult> {\n\treturn callApi<BatchIndexResult>(config, '/search/batchIndex', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tdocuments: input.documents,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Search documents.\n *\n * Supports three search modes:\n * - `keyword`: Full-text search with typo tolerance\n * - `semantic`: AI-powered vector search\n * - `hybrid`: Combined ranking (default, best results)\n *\n * @param config - SDK configuration\n * @param input - Search query and options\n * @returns Search results with scores\n *\n * @example\n * ```typescript\n * // Hybrid search (recommended)\n * const results = await search(config, {\n * query: 'how to change email address',\n * namespace: 'help',\n * searchType: 'hybrid',\n * highlight: true,\n * })\n *\n * results.results.forEach(r => {\n * console.log(`[${r.score.toFixed(3)}] ${r.title}`)\n * console.log(r.highlight)\n * })\n * ```\n */\nexport async function search(config: SylphxConfig, input: SearchInput): Promise<SearchResponse> {\n\treturn callApi<SearchResponse>(config, '/search/search', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tquery: input.query,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tsearchType: input.searchType ?? 'hybrid',\n\t\t\tlimit: input.limit ?? 10,\n\t\t\toffset: input.offset ?? 0,\n\t\t\tminSimilarity: input.minSimilarity,\n\t\t\ttypoTolerance: input.typoTolerance ?? true,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tfilters: input.filters,\n\t\t\thighlight: input.highlight ?? true,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t\ttrackQuery: input.trackQuery ?? true,\n\t\t\tsessionId: input.sessionId,\n\t\t\tuserId: input.userId,\n\t\t},\n\t})\n}\n\n/**\n * Get facet counts for filtering.\n *\n * Returns counts of documents by category, type, and tags.\n *\n * @param config - SDK configuration\n * @param input - Facet options\n * @returns Facet counts\n *\n * @example\n * ```typescript\n * const facets = await getFacets(config, {\n * namespace: 'products',\n * facets: ['category', 'type'],\n * })\n *\n * facets.facets.category?.forEach(f => {\n * console.log(`${f.value}: ${f.count} products`)\n * })\n * ```\n */\nexport async function getFacets(\n\tconfig: SylphxConfig,\n\tinput: GetFacetsInput = {},\n): Promise<FacetsResponse> {\n\treturn callApi<FacetsResponse>(config, '/search/getFacets', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tfacets: input.facets ?? ['category', 'type'],\n\t\t\tfilters: input.filters,\n\t\t},\n\t})\n}\n\n/**\n * Delete a document from the search index.\n *\n * @param config - SDK configuration\n * @param input - Document ID or external ID\n * @returns Deletion result\n *\n * @example\n * ```typescript\n * // Delete by internal ID\n * await deleteDocument(config, { id: 'doc-uuid-123' })\n *\n * // Delete by external ID\n * await deleteDocument(config, {\n * externalId: 'product-456',\n * namespace: 'products'\n * })\n * ```\n */\nexport async function deleteDocument(\n\tconfig: SylphxConfig,\n\tinput: DeleteDocumentInput,\n): Promise<{ deleted: number }> {\n\treturn callApi<{ deleted: number }>(config, '/search/delete', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tid: input.id,\n\t\t\texternalId: input.externalId,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t},\n\t})\n}\n\n/**\n * Upsert a document (insert or update by externalId).\n *\n * If a document with the same externalId exists, it will be replaced.\n * Otherwise, a new document is created.\n *\n * @param config - SDK configuration\n * @param input - Document to upsert (externalId required)\n * @returns Upsert result\n *\n * @example\n * ```typescript\n * const result = await upsertDocument(config, {\n * externalId: 'product-123',\n * title: 'Updated Product Name',\n * content: 'New description...',\n * namespace: 'products',\n * })\n * console.log(result.created ? 'Created' : 'Updated')\n * ```\n */\nexport async function upsertDocument(\n\tconfig: SylphxConfig,\n\tinput: UpsertDocumentInput,\n): Promise<UpsertDocumentResult> {\n\treturn callApi<UpsertDocumentResult>(config, '/search/upsert', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\ttitle: input.title,\n\t\t\tcontent: input.content,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\texternalId: input.externalId,\n\t\t\turl: input.url,\n\t\t\tmetadata: input.metadata,\n\t\t\tcategory: input.category,\n\t\t\ttype: input.type,\n\t\t\ttags: input.tags,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Get search index statistics.\n *\n * @param config - SDK configuration\n * @param namespace - Optional namespace filter\n * @returns Index statistics\n *\n * @example\n * ```typescript\n * const stats = await getSearchStats(config)\n * console.log(`Total docs: ${stats.totalDocuments}`)\n * console.log(`With embeddings: ${stats.documentsWithEmbedding}`)\n * ```\n */\nexport async function getSearchStats(\n\tconfig: SylphxConfig,\n\tnamespace?: string,\n): Promise<SearchStatsResult> {\n\treturn callApi<SearchStatsResult>(config, '/search/getStats', {\n\t\tmethod: 'POST',\n\t\tbody: { namespace },\n\t})\n}\n\n/**\n * Track a click on a search result.\n *\n * Use this to improve search quality over time.\n *\n * @param config - SDK configuration\n * @param input - Click information\n * @returns Success status\n *\n * @example\n * ```typescript\n * await trackClick(config, {\n * queryId: searchResponse.queryId,\n * documentId: clickedResult.id,\n * position: 3,\n * })\n * ```\n */\nexport async function trackClick(\n\tconfig: SylphxConfig,\n\tinput: TrackClickInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/search/trackClick', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n","/**\n * Database Functions\n *\n * Pure functions for retrieving Platform-provisioned database connection strings.\n * Server-side only (requires secret key `sk_*`).\n *\n * The Platform provisions a PostgreSQL database for each app and encrypts\n * the connection string. These functions retrieve and decrypt the connection\n * string at startup, so your app never needs to store it.\n *\n * @example\n * ```ts\n * import { createConfig, getDatabaseConnectionString } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n * const { connectionString } = await getDatabaseConnectionString(config)\n *\n * const pool = new Pool({ connectionString })\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DatabaseStatus =\n\t| 'provisioning'\n\t| 'ready'\n\t| 'suspended'\n\t| 'failed'\n\t| 'deleted'\n\t| 'not_provisioned'\n\nexport interface DatabaseConnectionInfo {\n\t/** Decrypted PostgreSQL connection string */\n\tconnectionString: string\n\t/** Database name */\n\tdatabaseName: string\n\t/** Database role/user name */\n\troleName: string\n\t/** Database provisioning status */\n\tstatus: Exclude<DatabaseStatus, 'not_provisioned'>\n}\n\nexport interface DatabaseStatusInfo {\n\t/** Current database status */\n\tstatus: DatabaseStatus\n\t/** Provisioned region */\n\tregion: string | null\n\t/** PostgreSQL major version */\n\tpgVersion: number | null\n\t/** Database name */\n\tdatabaseName: string | null\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get the provisioned PostgreSQL connection string for this app.\n *\n * Requires secret key authentication (server-side only).\n * The connection string is decrypted on the Platform and returned in plaintext.\n *\n * @throws `NOT_FOUND` if no database has been provisioned for this app\n * @throws `UNPROCESSABLE_ENTITY` if database is not yet ready\n *\n * @example\n * ```ts\n * const { connectionString } = await getDatabaseConnectionString(config)\n * const pool = new Pool({ connectionString })\n * ```\n */\nexport async function getDatabaseConnectionString(\n\tconfig: SylphxConfig,\n): Promise<DatabaseConnectionInfo> {\n\treturn callApi<DatabaseConnectionInfo>(config, '/sdk/database/connection-string', {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Get the current status of the provisioned database.\n *\n * Use this to check if the database is ready before attempting to connect.\n * Requires secret key authentication (server-side only).\n *\n * @example\n * ```ts\n * const { status } = await getDatabaseStatus(config)\n * if (status === 'ready') {\n * // Connect to database\n * }\n * ```\n */\nexport async function getDatabaseStatus(config: SylphxConfig): Promise<DatabaseStatusInfo> {\n\treturn callApi<DatabaseStatusInfo>(config, '/sdk/database/status', {\n\t\tmethod: 'GET',\n\t})\n}\n","/**\n * KV (Key-Value Store) Functions\n *\n * Pure functions for managed key-value storage.\n * Supports strings, hashes, lists, sorted sets, and built-in rate limiting.\n *\n * Keys are automatically namespaced per app, so no key collisions occur\n * between different apps on the same Platform.\n *\n * @example\n * ```ts\n * import { createConfig, kvSet, kvGet, kvDelete } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n *\n * // Basic key-value operations\n * await kvSet(config, { key: 'user:123', value: { name: 'Alice' }, ex: 3600 })\n * const user = await kvGet(config, 'user:123')\n * await kvDelete(config, 'user:123')\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tKvDeleteResult as ContractKvDeleteResult,\n\tKvExistsResult as ContractKvExistsResult,\n\tKvExpireInput as ContractKvExpireInput,\n\tKvIncrInput as ContractKvIncrInput,\n\tKvIncrResult as ContractKvIncrResult,\n\tKvRateLimitInput as ContractKvRateLimitInput,\n\tKvScanResult as ContractKvScanResult,\n\tKvSetInput as ContractKvSetInput,\n\tKvSetResult as ContractKvSetResult,\n} from '@sylphx/contract'\nimport { kvEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport type { KvRateLimitResult, KvSetOptions, KvZMember } from './kv-types'\n\n// Re-export shared types\nexport type { KvRateLimitResult, KvSetOptions, KvZMember } from './kv-types'\n\n/**\n * Interpolate `:param` placeholders in a path template with URL-encoded values.\n *\n * Example: `interpolatePath('/kv/:key', { key: 'a/b' })` -> `/kv/a%2Fb`.\n */\nfunction interpolatePath(template: string, params: Record<string, string>): string {\n\tlet path = template\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tpath = path.replace(`:${key}`, encodeURIComponent(value))\n\t}\n\treturn path\n}\n\nconst KV_PATHS = {\n\tmset: '/kv/mset',\n\tmget: '/kv/mget',\n\thset: '/kv/hset',\n\thget: '/kv/hget',\n\thgetall: '/kv/hgetall',\n\tlpush: '/kv/lpush',\n\tlrange: '/kv/lrange',\n\tzadd: '/kv/zadd',\n\tzrange: '/kv/zrange',\n} as const\n\ntype SuccessWireResult = { success?: boolean; ok?: boolean }\n\nfunction toLegacyOkResult(result: SuccessWireResult): { ok: boolean } {\n\tif (result.success !== undefined) return { ok: result.success }\n\tif (result.ok !== undefined) return { ok: result.ok }\n\treturn { ok: false }\n}\n\nfunction resolveCreatedCount(result: { created?: number; count?: number }): number {\n\tif (result.created !== undefined) return result.created\n\tif (result.count !== undefined) return result.count\n\treturn 0\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface KvSetRequest extends KvSetOptions {\n\t/** Key to store */\n\tkey: string\n\t/** Value to store (any JSON-serializable value) */\n\tvalue: unknown\n\t/** @deprecated Use `ex`. Kept for compatibility with early SDK adopters. */\n\tttl?: number\n}\n\nexport interface KvMsetRequest {\n\t/** Key-value pairs to set in a single atomic operation */\n\tentries: Array<{ key: string; value: unknown }>\n}\n\nexport interface KvMgetRequest {\n\t/** Keys to retrieve */\n\tkeys: string[]\n}\n\nexport interface KvHsetRequest {\n\t/** Hash key */\n\tkey: string\n\t/** Field-value pairs to set on the hash */\n\tfields: Record<string, unknown>\n}\n\nexport interface KvHgetRequest {\n\t/** Hash key */\n\tkey: string\n\t/** Field to get */\n\tfield: string\n}\n\nexport interface KvHgetallRequest {\n\t/** Hash key */\n\tkey: string\n}\n\nexport interface KvLpushRequest {\n\t/** List key */\n\tkey: string\n\t/** Values to prepend (left push) */\n\tvalues: unknown[]\n}\n\nexport interface KvLrangeRequest {\n\t/** List key */\n\tkey: string\n\t/** Start index (0-based, negative counts from end) */\n\tstart: number\n\t/** Stop index (inclusive, negative counts from end) */\n\tstop: number\n}\n\nexport interface KvZaddRequest {\n\t/** Sorted set key */\n\tkey: string\n\t/** Members with scores to add */\n\tmembers: KvZMember[]\n}\n\nexport interface KvZrangeRequest {\n\t/** Sorted set key */\n\tkey: string\n\t/** Start index or score */\n\tstart: number | string\n\t/** Stop index or score */\n\tstop: number | string\n\t/** Return scores alongside members */\n\twithScores?: boolean\n\t/** Reverse order */\n\trev?: boolean\n\t/** Treat start/stop as scores (BYSCORE) */\n\tbyScore?: boolean\n}\n\nexport interface KvIncrRequest {\n\t/** Key to increment */\n\tkey: string\n\t/** Amount to increment by (default: 1) */\n\tby?: number\n}\n\nexport interface KvExpireRequest {\n\t/** Key to set expiry on */\n\tkey: string\n\t/** TTL in seconds */\n\tseconds: number\n}\n\nexport interface KvRateLimitRequest {\n\t/** Rate limit key (e.g., userId, IP) */\n\tkey?: string\n\t/** @deprecated Use `key`. Kept for compatibility with early SDK adopters. */\n\tidentifier?: string\n\t/** Maximum requests allowed in the window */\n\tlimit: number\n\t/** Window duration in seconds */\n\twindow: number | string\n}\n\n// ============================================================================\n// Functions — Basic Operations\n// ============================================================================\n\n/**\n * Set a key-value pair, with optional TTL and conditional flags.\n *\n * @example\n * ```ts\n * // Simple set\n * await kvSet(config, { key: 'session:abc', value: { userId: '123' }, ex: 86400 })\n *\n * // Set only if not exists (NX)\n * await kvSet(config, { key: 'lock:task', value: '1', ex: 30, nx: true })\n * ```\n */\nexport async function kvSet(config: SylphxConfig, request: KvSetRequest): Promise<{ ok: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// `KvSetRequest` is structurally compatible with `ContractKvSetInput` (key/value/ex/nx/xx).\n\tconst { ttl, ...rest } = request\n\tconst body = {\n\t\t...rest,\n\t\t...(rest.ex === undefined && ttl !== undefined ? { ex: ttl } : {}),\n\t} satisfies ContractKvSetInput\n\tconst endpoint = kvEndpoints.set\n\tconst result = await callApi<ContractKvSetResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Get a value by key.\n *\n * Returns `null` if the key does not exist or has expired.\n *\n * @example\n * ```ts\n * const session = await kvGet<{ userId: string }>(config, 'session:abc')\n * if (session) {\n * console.log(session.userId)\n * }\n * ```\n */\nexport async function kvGet<T = unknown>(config: SylphxConfig, key: string): Promise<T | null> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.get\n\tconst result = await callApi<{ value: T | null }>(\n\t\tconfig,\n\t\tinterpolatePath(endpoint.path, { key }),\n\t\t{ method: endpoint.method },\n\t)\n\treturn result.value\n}\n\n/**\n * Delete one or more keys.\n *\n * @example\n * ```ts\n * const { deleted } = await kvDelete(config, 'session:abc')\n * console.log(`Deleted ${deleted} keys`)\n * ```\n */\nexport async function kvDelete(config: SylphxConfig, key: string): Promise<{ deleted: number }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.delete\n\treturn callApi<ContractKvDeleteResult>(config, interpolatePath(endpoint.path, { key }), {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Check if a key exists.\n *\n * @example\n * ```ts\n * const { exists } = await kvExists(config, 'session:abc')\n * ```\n */\nexport async function kvExists(config: SylphxConfig, key: string): Promise<{ exists: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.exists\n\treturn callApi<ContractKvExistsResult>(config, interpolatePath(endpoint.path, { key }), {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Set expiry on an existing key.\n *\n * @example\n * ```ts\n * await kvExpire(config, { key: 'session:abc', seconds: 3600 })\n * ```\n */\nexport async function kvExpire(\n\tconfig: SylphxConfig,\n\trequest: KvExpireRequest,\n): Promise<{ ok: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractKvExpireInput\n\tconst endpoint = kvEndpoints.expire\n\tconst result = await callApi<ContractKvSetResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Increment a numeric value.\n *\n * @example\n * ```ts\n * const { value } = await kvIncr(config, { key: 'page:views', by: 1 })\n * ```\n */\nexport async function kvIncr(\n\tconfig: SylphxConfig,\n\trequest: KvIncrRequest,\n): Promise<{ value: number }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractKvIncrInput\n\tconst endpoint = kvEndpoints.incr\n\treturn callApi<ContractKvIncrResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Functions — Bulk Operations\n// ============================================================================\n\n/**\n * Set multiple key-value pairs atomically.\n */\nexport async function kvMset(\n\tconfig: SylphxConfig,\n\trequest: KvMsetRequest,\n): Promise<{ ok: boolean }> {\n\tconst result = await callApi<SuccessWireResult>(config, KV_PATHS.mset, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Get multiple values by keys in a single request.\n *\n * Returns `null` for keys that don't exist.\n */\nexport async function kvMget<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvMgetRequest,\n): Promise<Array<T | null>> {\n\tconst result = await callApi<{ values: Array<T | null> | Record<string, T | null> }>(\n\t\tconfig,\n\t\tKV_PATHS.mget,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: request,\n\t\t},\n\t)\n\tconst values = result.values\n\tif (Array.isArray(values)) {\n\t\treturn values\n\t}\n\treturn request.keys.map((key) => values[key] ?? null)\n}\n\n// ============================================================================\n// Functions — Hash Operations\n// ============================================================================\n\n/**\n * Set fields on a hash key.\n *\n * @example\n * ```ts\n * await kvHset(config, { key: 'user:123', fields: { name: 'Alice', age: 30 } })\n * ```\n */\nexport async function kvHset(\n\tconfig: SylphxConfig,\n\trequest: KvHsetRequest,\n): Promise<{ count: number }> {\n\tconst result = await callApi<{ created?: number; count?: number }>(config, KV_PATHS.hset, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn { count: resolveCreatedCount(result) }\n}\n\n/**\n * Get a single field from a hash key.\n */\nexport async function kvHget<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvHgetRequest,\n): Promise<T | null> {\n\tconst result = await callApi<{ value: T | null }>(config, KV_PATHS.hget, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn result.value\n}\n\n/**\n * Get all fields from a hash key.\n */\nexport async function kvHgetall<T extends Record<string, unknown> = Record<string, unknown>>(\n\tconfig: SylphxConfig,\n\trequest: KvHgetallRequest,\n): Promise<T | null> {\n\tconst result = await callApi<{ fields?: T | null; value?: T | null }>(config, KV_PATHS.hgetall, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\tif (result.fields !== undefined) return result.fields\n\tif (result.value !== undefined) return result.value\n\treturn null\n}\n\n// ============================================================================\n// Functions — List Operations\n// ============================================================================\n\n/**\n * Left-push values onto a list.\n *\n * @example\n * ```ts\n * const { length } = await kvLpush(config, { key: 'events', values: [event] })\n * ```\n */\nexport async function kvLpush(\n\tconfig: SylphxConfig,\n\trequest: KvLpushRequest,\n): Promise<{ length: number }> {\n\treturn callApi<{ length: number }>(config, KV_PATHS.lpush, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Get a range of elements from a list.\n *\n * @example\n * ```ts\n * // Get last 10 events\n * const items = await kvLrange(config, { key: 'events', start: 0, stop: 9 })\n * ```\n */\nexport async function kvLrange<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvLrangeRequest,\n): Promise<T[]> {\n\tconst result = await callApi<{ values?: T[]; items?: T[] }>(config, KV_PATHS.lrange, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\tif (result.values !== undefined) return result.values\n\tif (result.items !== undefined) return result.items\n\treturn []\n}\n\n// ============================================================================\n// Functions — Sorted Set Operations\n// ============================================================================\n\n/**\n * Add members to a sorted set.\n *\n * @example\n * ```ts\n * // Add to leaderboard\n * await kvZadd(config, {\n * key: 'leaderboard',\n * members: [{ member: 'user:123', score: 1500 }],\n * })\n * ```\n */\nexport async function kvZadd(\n\tconfig: SylphxConfig,\n\trequest: KvZaddRequest,\n): Promise<{ added: number }> {\n\treturn callApi<{ added: number }>(config, KV_PATHS.zadd, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Get a range of members from a sorted set.\n *\n * @example\n * ```ts\n * // Get top 10 leaderboard entries\n * const entries = await kvZrange(config, {\n * key: 'leaderboard',\n * start: 0,\n * stop: 9,\n * rev: true,\n * withScores: true,\n * })\n * ```\n */\nexport async function kvZrange(\n\tconfig: SylphxConfig,\n\trequest: KvZrangeRequest,\n): Promise<Array<{ member: string; score?: number }>> {\n\tconst result = await callApi<{\n\t\tmembers: Array<{ member: string; score?: number }>\n\t}>(config, KV_PATHS.zrange, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn result.members\n}\n\n// ============================================================================\n// Functions — Rate Limiting\n// ============================================================================\n\n/**\n * Check and consume a rate limit token using the platform sliding window.\n *\n * This is a built-in rate limiter — no external service needed.\n *\n * @example\n * ```ts\n * // 10 requests per 60 seconds per user\n * const result = await kvRateLimit(config, {\n * key: `user:${userId}`,\n * limit: 10,\n * window: '60s',\n * })\n *\n * if (!result.success) {\n * return Response.json({ error: 'Rate limit exceeded' }, { status: 429 })\n * }\n * ```\n */\nexport async function kvRateLimit(\n\tconfig: SylphxConfig,\n\trequest: KvRateLimitRequest,\n): Promise<KvRateLimitResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst key = request.key ?? request.identifier\n\tif (!key) {\n\t\tthrow new Error('kvRateLimit requires `key`')\n\t}\n\tconst body = {\n\t\tkey,\n\t\tlimit: request.limit,\n\t\twindow: typeof request.window === 'number' ? `${request.window}s` : request.window,\n\t} satisfies ContractKvRateLimitInput\n\tconst endpoint = kvEndpoints.rateLimit\n\treturn callApi<KvRateLimitResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Functions — Scan / Iteration\n// ============================================================================\n\nexport interface KvScanOptions {\n\t/** Key pattern to match (e.g. 'user:*'). Defaults to '*' (all keys). */\n\tpattern?: string\n\t/** Cursor for pagination. Use '0' to start a new scan (default). */\n\tcursor?: string\n\t/** Hint for how many keys to return per iteration (1–1000). Default: 100. */\n\tcount?: number\n}\n\nexport interface KvScanResult {\n\t/** Keys matching the pattern (namespace prefix stripped). */\n\tkeys: string[]\n\t/** Cursor for the next page. Pass this as `cursor` in the next call. */\n\tnextCursor: string\n\t/** True when the full keyspace has been scanned (nextCursor is '0'). */\n\tdone: boolean\n}\n\n/**\n * Scan keys matching a pattern using cursor-based pagination.\n *\n * Unlike `KEYS`, SCAN is safe to use in production — it iterates incrementally.\n * Call repeatedly with the returned `nextCursor` until `done` is true.\n *\n * @example\n * ```ts\n * // Iterate all user keys\n * let cursor = '0'\n * do {\n * const result = await kvScan(config, { pattern: 'user:*', cursor })\n * for (const key of result.keys) console.log(key)\n * cursor = result.nextCursor\n * } while (!result.done)\n * ```\n */\nexport async function kvScan(config: SylphxConfig, options?: KvScanOptions): Promise<KvScanResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// Public SDK compatibility keeps `keys` mutable; runtime payload is the\n\t// same contract-owned array shape.\n\tconst endpoint = kvEndpoints.scan\n\treturn callApi<ContractKvScanResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: {\n\t\t\t...(options?.pattern !== undefined && { pattern: options.pattern }),\n\t\t\t...(options?.cursor !== undefined && { cursor: options.cursor }),\n\t\t\t...(options?.count !== undefined && { count: String(options.count) }),\n\t\t},\n\t}) as Promise<KvScanResult>\n}\n\n// ============================================================================\n// Convenience — JSON helpers\n// ============================================================================\n\n/**\n * Get a JSON value by key. Automatically parses the stored JSON string.\n *\n * Returns `null` if the key does not exist or has expired.\n *\n * @example\n * ```ts\n * const profile = await kvGetJSON<UserProfile>(config, 'user:123:profile')\n * if (profile) console.log(profile.name)\n * ```\n */\nexport async function kvGetJSON<T = unknown>(config: SylphxConfig, key: string): Promise<T | null> {\n\tconst raw = await kvGet<string>(config, key)\n\tif (raw === null) return null\n\ttry {\n\t\treturn JSON.parse(raw) as T\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a JSON value by key. Automatically serializes the value to JSON.\n *\n * @example\n * ```ts\n * await kvSetJSON(config, 'user:123:profile', { name: 'Alice', plan: 'pro' }, { ex: 3600 })\n * ```\n */\nexport async function kvSetJSON<T>(\n\tconfig: SylphxConfig,\n\tkey: string,\n\tvalue: T,\n\toptions?: KvSetOptions,\n): Promise<{ ok: boolean }> {\n\treturn kvSet(config, { key, value: JSON.stringify(value), ...options })\n}\n","/**\n * Deploy Functions\n *\n * Pure functions for managing app deployments, environment variables,\n * and custom domains via the Platform Deploy API.\n *\n * Requires secret key authentication (`sk_*`).\n *\n * @example\n * ```ts\n * import { createConfig, triggerDeploy, getDeployStatus } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n *\n * // Trigger a deployment\n * const deploy = await triggerDeploy(config, { envId: 'env_prod_xxx' })\n *\n * // Poll for completion\n * const status = await getDeployStatus(config, deploy.envId)\n * console.log(status.status) // 'building' | 'deploying' | 'success' | 'failed'\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DeployStatus = 'queued' | 'building' | 'deploying' | 'success' | 'failed' | 'cancelled'\n\nexport interface DeployInfo {\n\t/** Deployment ID */\n\tdeploymentId: string\n\t/** Environment ID */\n\tenvId: string\n\t/** Current deployment status */\n\tstatus: DeployStatus\n\t/** Deployment URL */\n\turl?: string\n\t/** Git commit SHA */\n\tcommitSha?: string\n\t/** Git branch */\n\tbranch?: string\n\t/** Deployment started at (ISO timestamp) */\n\tstartedAt?: string\n\t/** Deployment completed at (ISO timestamp) */\n\tcompletedAt?: string\n\t/** Error message if failed */\n\terror?: string\n}\n\nexport interface TriggerDeployRequest {\n\t/** Environment ID to deploy */\n\tenvId: string\n\t/** Force rebuild without cache */\n\tforceRebuild?: boolean\n}\n\nexport interface RollbackDeployRequest {\n\t/** Environment ID to rollback */\n\tenvId: string\n\t/** Deployment ID to rollback to */\n\tdeploymentId: string\n}\n\nexport interface EnvVar {\n\t/** Environment variable key */\n\tkey: string\n\t/** Environment variable value */\n\tvalue: string\n\t/** Whether value is sensitive (masked in logs) */\n\tsensitive?: boolean\n}\n\nexport interface SetEnvVarRequest {\n\t/** Environment variable key */\n\tkey: string\n\t/** Environment variable value */\n\tvalue: string\n\t/** Whether value is sensitive/secret */\n\tsensitive?: boolean\n}\n\nexport interface DeployHistoryResponse {\n\t/** List of past deployments */\n\tdeployments: DeployInfo[]\n}\n\nexport interface BuildLog {\n\t/** Log line text */\n\ttext: string\n\t/** Log timestamp (ISO) */\n\ttimestamp?: string\n\t/** Log level */\n\tlevel?: 'info' | 'warn' | 'error'\n}\n\nexport interface BuildLogHistoryResponse {\n\t/** Log lines */\n\tlogs: BuildLog[]\n}\n\n// ============================================================================\n// Functions — Deployments\n// ============================================================================\n\n/**\n * Trigger a new deployment for an environment.\n *\n * @example\n * ```ts\n * const deploy = await triggerDeploy(config, { envId: 'env_prod_xxx' })\n * console.log(`Deployment ${deploy.deploymentId} started`)\n * ```\n */\nexport async function triggerDeploy(\n\tconfig: SylphxConfig,\n\trequest: TriggerDeployRequest,\n): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/trigger/${encodeURIComponent(request.envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: request.forceRebuild !== undefined ? { forceRebuild: request.forceRebuild } : undefined,\n\t})\n}\n\n/**\n * Get the current deployment status for an environment.\n *\n * @example\n * ```ts\n * const { status } = await getDeployStatus(config, 'env_prod_xxx')\n * if (status === 'success') {\n * console.log('Deployment succeeded!')\n * }\n * ```\n */\nexport async function getDeployStatus(config: SylphxConfig, envId: string): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/status/${encodeURIComponent(envId)}`, {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Get deployment history for an environment.\n *\n * @example\n * ```ts\n * const { deployments } = await getDeployHistory(config, 'env_prod_xxx')\n * const lastDeploy = deployments[0]\n * ```\n */\nexport async function getDeployHistory(\n\tconfig: SylphxConfig,\n\tenvId: string,\n): Promise<DeployHistoryResponse> {\n\treturn callApi<DeployHistoryResponse>(\n\t\tconfig,\n\t\t`/sdk/deploy/history/${encodeURIComponent(envId)}`,\n\t\t{ method: 'GET' },\n\t)\n}\n\n/**\n * Rollback an environment to a previous deployment.\n *\n * @example\n * ```ts\n * await rollbackDeploy(config, {\n * envId: 'env_prod_xxx',\n * deploymentId: 'dep_abc123',\n * })\n * ```\n */\nexport async function rollbackDeploy(\n\tconfig: SylphxConfig,\n\trequest: RollbackDeployRequest,\n): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/rollback/${encodeURIComponent(request.envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: { deploymentId: request.deploymentId },\n\t})\n}\n\n/**\n * Get stored build log history for an environment.\n *\n * For live log streaming during an active build, use the SSE endpoint\n * directly or the `useDeployLogs` React hook.\n *\n * @example\n * ```ts\n * const { logs } = await getBuildLogHistory(config, 'env_prod_xxx')\n * for (const log of logs) {\n * console.log(log.text)\n * }\n * ```\n */\nexport async function getBuildLogHistory(\n\tconfig: SylphxConfig,\n\tenvId: string,\n): Promise<BuildLogHistoryResponse> {\n\treturn callApi<BuildLogHistoryResponse>(\n\t\tconfig,\n\t\t`/sdk/deploy/logs/${encodeURIComponent(envId)}/history`,\n\t\t{ method: 'GET' },\n\t)\n}\n\n// ============================================================================\n// Functions — Environment Variables\n// ============================================================================\n\n/**\n * List environment variables for a deployment environment.\n *\n * Sensitive values are masked in the response.\n *\n * @example\n * ```ts\n * const envVars = await listEnvVars(config, 'env_prod_xxx')\n * ```\n */\nexport async function listEnvVars(config: SylphxConfig, envId: string): Promise<EnvVar[]> {\n\tconst result = await callApi<{ envVars: EnvVar[] }>(\n\t\tconfig,\n\t\t`/sdk/deploy/envvars/${encodeURIComponent(envId)}`,\n\t\t{ method: 'GET' },\n\t)\n\treturn result.envVars\n}\n\n/**\n * Set (create or update) an environment variable.\n *\n * @example\n * ```ts\n * await setEnvVar(config, 'env_prod_xxx', {\n * key: 'DATABASE_URL',\n * value: 'postgresql://...',\n * sensitive: true,\n * })\n * ```\n */\nexport async function setEnvVar(\n\tconfig: SylphxConfig,\n\tenvId: string,\n\trequest: SetEnvVarRequest,\n): Promise<EnvVar> {\n\treturn callApi<EnvVar>(config, `/sdk/deploy/envvars/${encodeURIComponent(envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Delete an environment variable.\n *\n * @example\n * ```ts\n * await deleteEnvVar(config, 'env_prod_xxx', 'DATABASE_URL')\n * ```\n */\nexport async function deleteEnvVar(\n\tconfig: SylphxConfig,\n\tenvId: string,\n\tkey: string,\n): Promise<{ deleted: boolean }> {\n\treturn callApi<{ deleted: boolean }>(\n\t\tconfig,\n\t\t`/sdk/deploy/envvars/${encodeURIComponent(envId)}/${encodeURIComponent(key)}`,\n\t\t{ method: 'DELETE' },\n\t)\n}\n","/**\n * Monitoring Functions\n *\n * Pure functions for error tracking and log capture.\n * Works client-side and server-side.\n *\n * ## Industry Patterns\n * - **Error grouping via fingerprinting** — Same error only billed once (Sentry pattern)\n * - **Adaptive sampling** — Automatic sample rate adjustment based on quota usage\n * - **Breadcrumb trails** — Contextual trail leading to errors\n *\n * @example\n * ```ts\n * import { createConfig, captureException, captureMessage } from '@sylphx/sdk'\n *\n * const config = createConfig({ appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID! })\n *\n * // Capture errors\n * try {\n * await riskyOperation()\n * } catch (err) {\n * await captureException(config, err as Error)\n * }\n *\n * // Capture log messages\n * await captureMessage(config, 'User completed onboarding', { level: 'info' })\n * ```\n */\n\n// ADR-084 Wave 2d: contract now carries the Sentry-style envelope\n// (`exception: { values: ExceptionValue[] }`) plus the richer response\n// fields (`eventId`/`isNewError`/`quotaUsage`/`recommendedSampleRate`),\n// matching this SDK's production wire shape. Path is `/sdk/monitoring/*`\n// on both sides. Full contract rewire (runtime import + path derivation)\n// is Agent J's follow-up; this file keeps its SDK-native types until\n// then.\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type MonitoringSeverity = 'fatal' | 'error' | 'warning' | 'info'\n\nexport interface ExceptionFrame {\n\t/** Source filename */\n\tfilename?: string\n\t/** Function name */\n\tfunction?: string\n\t/** Line number */\n\tlineno?: number\n\t/** Column number */\n\tcolno?: number\n}\n\nexport interface ExceptionValue {\n\t/** Exception class/type (e.g., \"TypeError\") */\n\ttype: string\n\t/** Exception message */\n\tvalue: string\n\t/** Stack trace frames (innermost first) */\n\tstacktrace?: { frames?: ExceptionFrame[] }\n}\n\nexport interface Breadcrumb {\n\t/** Breadcrumb type (e.g., \"navigation\", \"http\", \"ui.click\") */\n\ttype?: string\n\t/** Log level */\n\tlevel?: MonitoringSeverity\n\t/** Breadcrumb message */\n\tmessage?: string\n\t/** Breadcrumb data */\n\tdata?: Record<string, unknown>\n\t/** Unix timestamp (seconds) */\n\ttimestamp?: number\n}\n\nexport interface CaptureExceptionRequest {\n\t/** Exception value(s) — first is primary, rest are chained causes */\n\texception: { values: ExceptionValue[] }\n\t/** Severity level (default: \"error\") */\n\tlevel?: MonitoringSeverity\n\t/** Current page route */\n\troute?: string\n\t/** User agent string */\n\tuserAgent?: string\n\t/** App release version */\n\trelease?: string\n\t/** Environment name (e.g., \"production\", \"staging\") */\n\tenvironment?: string\n\t/** Custom tags for filtering */\n\ttags?: Record<string, string>\n\t/** Extra context data */\n\textra?: Record<string, unknown>\n\t/** Breadcrumb trail leading to the error */\n\tbreadcrumbs?: Breadcrumb[]\n\t/** Custom fingerprint for grouping (overrides automatic grouping) */\n\tfingerprint?: string[]\n}\n\nexport interface CaptureMessageRequest {\n\t/** Log message */\n\tmessage: string\n\t/** Severity level (default: \"info\") */\n\tlevel?: MonitoringSeverity\n\t/** Current page route */\n\troute?: string\n\t/** App release version */\n\trelease?: string\n\t/** Custom tags for filtering */\n\ttags?: Record<string, string>\n\t/** Extra context data */\n\textra?: Record<string, unknown>\n}\n\nexport interface MonitoringResponse {\n\t/** Internal event ID */\n\teventId: string\n\t/** Whether this is a new unique error (true = billed, false = duplicate = free) */\n\tisNewError: boolean\n\t/** Current quota usage percentage (0-100+). Present when >= 50%. */\n\tquotaUsage?: number\n\t/**\n\t * Recommended client-side sample rate (0.0-1.0).\n\t * Reduce your error capture rate to this value when quota is high.\n\t * Present when quotaUsage >= 50%.\n\t */\n\trecommendedSampleRate?: number\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Convert a native Error object to ExceptionValue format.\n */\nfunction errorToExceptionValue(error: Error): ExceptionValue {\n\tconst frames: ExceptionFrame[] = []\n\n\tif (error.stack) {\n\t\t// Parse V8/SpiderMonkey style stack traces\n\t\tconst lines = error.stack.split('\\n').slice(1)\n\t\tfor (const line of lines) {\n\t\t\t// V8: \" at functionName (file:line:col)\"\n\t\t\tconst v8Match = line.match(/^\\s+at\\s+(.+?)\\s+\\((.+):(\\d+):(\\d+)\\)$/)\n\t\t\tif (v8Match) {\n\t\t\t\tframes.push({\n\t\t\t\t\tfunction: v8Match[1],\n\t\t\t\t\tfilename: v8Match[2],\n\t\t\t\t\tlineno: Number(v8Match[3]),\n\t\t\t\t\tcolno: Number(v8Match[4]),\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// V8 (no function): \" at file:line:col\"\n\t\t\tconst v8AnonMatch = line.match(/^\\s+at\\s+(.+):(\\d+):(\\d+)$/)\n\t\t\tif (v8AnonMatch) {\n\t\t\t\tframes.push({\n\t\t\t\t\tfilename: v8AnonMatch[1],\n\t\t\t\t\tlineno: Number(v8AnonMatch[2]),\n\t\t\t\t\tcolno: Number(v8AnonMatch[3]),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: error.name || 'Error',\n\t\tvalue: error.message,\n\t\tstacktrace: frames.length > 0 ? { frames } : undefined,\n\t}\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Capture an exception for error tracking.\n *\n * Errors with the same fingerprint are grouped automatically.\n * Duplicate occurrences of the same error are FREE (only new unique\n * errors count against your quota).\n *\n * @example\n * ```ts\n * try {\n * await processPayment(amount)\n * } catch (err) {\n * const result = await captureException(config, err as Error, {\n * tags: { paymentProvider: 'stripe' },\n * extra: { amount, userId },\n * })\n * }\n * ```\n */\nexport async function captureException(\n\tconfig: SylphxConfig,\n\terror: Error,\n\toptions: Omit<CaptureExceptionRequest, 'exception'> = {},\n): Promise<MonitoringResponse> {\n\t// Contract shape (Sentry envelope + rich response) reconciled with this\n\t// SDK in ADR-084 Wave 2d. Paths are already aligned (`/sdk/monitoring/*`).\n\tconst exceptionValue = errorToExceptionValue(error)\n\tconst request: CaptureExceptionRequest = {\n\t\t...options,\n\t\texception: { values: [exceptionValue] },\n\t}\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/exception', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Capture an exception with full control over the exception payload.\n *\n * Use this for structured exception capture with custom types, chained\n * causes, or when not working with native Error objects.\n */\nexport async function captureExceptionRaw(\n\tconfig: SylphxConfig,\n\trequest: CaptureExceptionRequest,\n): Promise<MonitoringResponse> {\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/exception', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Capture a log message for monitoring.\n *\n * Like `captureException` but for non-error events (warnings, info, etc.).\n * Messages with the same content are grouped automatically.\n *\n * @example\n * ```ts\n * // Info log\n * await captureMessage(config, 'Payment webhook received', {\n * level: 'info',\n * tags: { provider: 'stripe', event: 'payment.succeeded' },\n * })\n *\n * // Warning\n * await captureMessage(config, 'Slow query detected', {\n * level: 'warning',\n * extra: { queryMs: 2400, query: sql },\n * })\n * ```\n */\nexport async function captureMessage(\n\tconfig: SylphxConfig,\n\tmessage: string,\n\toptions: Omit<CaptureMessageRequest, 'message'> = {},\n): Promise<MonitoringResponse> {\n\tconst request: CaptureMessageRequest = { ...options, message }\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/message', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n","/**\n * Sandbox Client\n *\n * Ephemeral compute sandbox API — SOTA direct-connection design.\n *\n * ## Architecture\n *\n * POST /sandboxes → Platform provisions sandbox, waits for runtime readiness,\n * returns { endpoint, token }. All subsequent exec/files/pty operations\n * go DIRECTLY to the sandbox exec-server — Platform is not in the data path.\n *\n * ```\n * SDK.create() ──→ Platform API (lifecycle only)\n * ↓ returns endpoint + token\n * SDK.exec() ──→ sandbox exec-server (direct, SSE stream)\n * SDK.files ──→ sandbox exec-server (direct, REST)\n * SDK.pty() ──→ sandbox exec-server (direct, WebSocket)\n * ```\n *\n * ## Usage\n *\n * ```typescript\n * import { createServerClient, SandboxClient } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Create sandbox (Platform waits for the runtime before returning)\n * const sandbox = await SandboxClient.create(config)\n *\n * // Stream exec output in real-time\n * for await (const event of sandbox.exec(['npm', 'install'])) {\n * if (event.type === 'stdout') process.stdout.write(event.data)\n * if (event.type === 'exit') console.log('exit', event.exitCode)\n * }\n *\n * // File operations\n * await sandbox.files.write('/workspace/app.py', 'print(\"hello\")')\n * const content = await sandbox.files.read('/workspace/output.txt')\n *\n * // Terminate when done\n * await sandbox.terminate()\n * ```\n *\n * ## Auth\n * Requires sk_* secret key (server-side only).\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport { SANDBOX_CREATE_TIMEOUT_MS } from './constants'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SandboxOptions {\n\t/** OCI image from a connected registry. Omit to use the environment default. */\n\timage?: string\n\t/**\n\t * Request timeout for the create lifecycle call.\n\t * Defaults to the SDK's sandbox-create budget, which matches the Runtime\n\t * API readiness wait plus transport margin.\n\t */\n\tcreateTimeoutMs?: number\n\t/** Idle timeout in ms before auto-termination (default: 300_000 = 5 min) */\n\tidleTimeoutMs?: number\n\t/** Scratch storage size in GiB at /data, scoped to the sandbox lifecycle. */\n\tstorageGi?: number\n\t/** Managed machine size. Defaults to `standard`. */\n\tmachine?: SandboxMachineSize\n\t/** Environment variables injected into the sandbox container */\n\tenv?: Record<string, string>\n\t/**\n\t * Shared volume mounts from org-level managed volumes.\n\t * Use volumes created with sharing=\"shared\" when multiple sandboxes need\n\t * concurrent filesystem access.\n\t */\n\tvolumeMounts?: Array<{\n\t\t/** Volume resource ID (from `sylphx volumes list`) */\n\t\tvolumeId: string\n\t\t/** Absolute path inside the container (e.g. \"/shared\") */\n\t\tmountPath: string\n\t\t/** Optional sub-path within the volume */\n\t\tsubPath?: string\n\t\t/** Read-only mount (default: false) */\n\t\treadOnly?: boolean\n\t}>\n\t/**\n\t * Platform-managed workspace mounts.\n\t * Prefer this over raw volumeMounts for agent/IDE durable filesystems: the\n\t * Platform resolves the workspace's backing volume, PVC, quota, and lineage.\n\t */\n\tworkspaceMounts?: Array<{\n\t\t/** Workspace ID from the managed workspaces API. */\n\t\tworkspaceId: string\n\t\t/** Absolute path inside the container (e.g. \"/workspace\") */\n\t\tmountPath: string\n\t\t/** Optional workspace-relative sub-path view. */\n\t\tsubPath?: string\n\t\t/** Read-only mount (default: false) */\n\t\treadOnly?: boolean\n\t}>\n}\n\nexport type SandboxMachineSize = MachineSize\n\n/** SSE event emitted by sandbox.exec() */\nexport type ExecEvent =\n\t| { type: 'stdout'; data: string }\n\t| { type: 'stderr'; data: string }\n\t| { type: 'exit'; exitCode: number; durationMs: number; timedOut?: boolean }\n\t| { type: 'error'; message: string }\n\n/** Non-streaming exec result (all output collected, returned at once) */\nexport interface ExecResult {\n\tstdout: string\n\tstderr: string\n\texitCode: number\n\tdurationMs: number\n}\n\n/** @deprecated Use ExecResult */\nexport type CommandResult = ExecResult\n\n/** @deprecated File operations are now handled by SandboxFiles class */\nexport interface SandboxFile {\n\tpath: string\n\tcontent: string\n\tencoding?: 'utf8' | 'base64'\n}\n\nexport interface ExecOptions {\n\tcwd?: string\n\tenv?: Record<string, string>\n\ttimeout?: number\n\tstdin?: string\n}\n\n// =============================================================================\n// Process API Types\n// =============================================================================\n\nexport interface ProcessStartOptions {\n\t/** Command + args (e.g. ['npm', 'install']) */\n\tcommand: string[]\n\t/** Working directory */\n\tcwd?: string\n\t/** Environment variables */\n\tenv?: Record<string, string>\n\t/** Hard timeout in seconds (0 = no timeout) */\n\ttimeoutSeconds?: number\n\t/** Open stdin pipe for writing */\n\tstdin?: boolean\n}\n\nexport interface ProcessInfo {\n\tid: string\n\tpid: number\n\tcommand: string[]\n\tcwd: string\n\tstatus: 'running' | 'exited' | 'killed' | 'timeout'\n\texitCode: number | null\n\tsignal: string | null\n\tstartedAt: string\n\texitedAt: string | null\n\tdurationMs: number | null\n\tstdout: string\n\tstderr: string\n}\n\nexport interface ProcessSummary {\n\tid: string\n\tpid: number\n\tcommand: string[]\n\tstatus: 'running' | 'exited' | 'killed' | 'timeout'\n\texitCode: number | null\n\tstartedAt: string\n\texitedAt: string | null\n\tdurationMs: number | null\n}\n\n/** SSE event from a process stream */\nexport type ProcessEvent =\n\t| { type: 'stdout'; pid: number; data: string }\n\t| { type: 'stderr'; pid: number; data: string }\n\t| { type: 'exit'; pid: number; exitCode: number }\n\n// =============================================================================\n// Watch API Types\n// =============================================================================\n\nexport interface WatchOptions {\n\t/** Path to watch (relative to /workspace or absolute) */\n\tpath: string\n\t/** Watch subdirectories recursively (default: true) */\n\trecursive?: boolean\n\t/** Additional patterns to ignore */\n\tignore?: string[]\n}\n\nexport interface WatchEntry {\n\tpath: string\n\trecursive: boolean\n\tcreatedAt: string\n}\n\n/** File change event delivered via SSE /events stream */\nexport interface FileEvent {\n\ttype: 'file'\n\tpath: string\n\tevent: 'created' | 'modified' | 'deleted'\n}\n\nexport interface SandboxRecord {\n\tid: string\n\tstatus: 'starting' | 'running' | 'idle' | 'terminated' | 'error'\n\timage: string\n\t/** Managed machine size selected for this sandbox. */\n\tmachine: SandboxMachineSize | null\n\t/** Public HTTPS endpoint: https://sbx-xxx.sandboxes.sylphx.app */\n\tendpoint: string | null\n\t/** Per-sandbox RS256 JWT for direct exec-server authentication */\n\ttoken: string | null\n\tprojectId: string\n\tidleTimeoutMs: number\n\tcreatedAt: string\n\tstartedAt: string | null\n\texpiresAt: string | null\n\tterminatedAt: string | null\n}\n\n// =============================================================================\n// Files namespace\n// =============================================================================\n\nexport class SandboxFiles {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Write a file to the sandbox filesystem. */\n\tasync write(\n\t\tpath: string,\n\t\tcontent: string | Buffer,\n\t\tencoding: 'utf8' | 'base64' = 'utf8',\n\t): Promise<void> {\n\t\tconst contentStr = Buffer.isBuffer(content) ? content.toString('base64') : content\n\t\tconst effectiveEncoding = Buffer.isBuffer(content) ? 'base64' : encoding\n\n\t\tconst res = await fetch(`${this.endpoint}/files`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ path, content: contentStr, encoding: effectiveEncoding }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.write failed: ${await res.text()}`)\n\t}\n\n\t/** Read a file from the sandbox filesystem. Returns content as string. */\n\tasync read(path: string): Promise<string> {\n\t\tconst res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.read failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { content: string }\n\t\treturn data.content\n\t}\n\n\t/** Delete a file from the sandbox filesystem. */\n\tasync delete(path: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.delete failed: ${await res.text()}`)\n\t}\n\n\t/** List files in a directory. */\n\tasync list(path = '/'): Promise<string[]> {\n\t\tconst res = await fetch(`${this.endpoint}/list?path=${encodeURIComponent(path)}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.list failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { files: string[] }\n\t\treturn data.files\n\t}\n}\n\n// =============================================================================\n// Process namespace\n// =============================================================================\n\nexport class SandboxProcesses {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Spawn a new tracked process. Returns processId + pid immediately. */\n\tasync start(opts: ProcessStartOptions): Promise<{ id: string; pid: number }> {\n\t\tconst res = await fetch(`${this.endpoint}/process/start`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(opts),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.start failed: ${await res.text()}`)\n\t\treturn (await res.json()) as { id: string; pid: number }\n\t}\n\n\t/** List all tracked processes. */\n\tasync list(): Promise<ProcessSummary[]> {\n\t\tconst res = await fetch(`${this.endpoint}/process/list`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.list failed: ${await res.text()}`)\n\t\treturn (await res.json()) as ProcessSummary[]\n\t}\n\n\t/** Get full process info including buffered output. */\n\tasync get(processId: string): Promise<ProcessInfo> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.get failed: ${await res.text()}`)\n\t\treturn (await res.json()) as ProcessInfo\n\t}\n\n\t/** Send a signal to a process. */\n\tasync kill(processId: string, signal: string = 'SIGTERM'): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/kill`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ signal }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.kill failed: ${await res.text()}`)\n\t}\n\n\t/** Write to process stdin. */\n\tasync writeStdin(processId: string, data: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/input`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ data }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.writeStdin failed: ${await res.text()}`)\n\t}\n\n\t/**\n\t * Wait for a process to complete and return its final info.\n\t * Polls every 500ms until status is no longer 'running'.\n\t *\n\t * For real-time output, use stream() instead.\n\t */\n\tasync wait(processId: string, timeoutMs = 300_000): Promise<ProcessInfo> {\n\t\tconst deadline = Date.now() + timeoutMs\n\t\twhile (Date.now() < deadline) {\n\t\t\tconst info = await this.get(processId)\n\t\t\tif (info.status !== 'running') return info\n\t\t\tawait new Promise((r) => setTimeout(r, 500))\n\t\t}\n\t\tthrow new Error(`Timed out waiting for process ${processId} to complete (${timeoutMs}ms)`)\n\t}\n\n\t/** Stream process output as async iterable SSE events. */\n\tasync *stream(processId: string): AsyncGenerator<ProcessEvent> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/stream`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.stream failed: ${await res.text()}`)\n\t\tif (!res.body) throw new Error('process.stream: no response body')\n\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? ''\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(line.slice(6)) as ProcessEvent\n\t\t\t\t\t\t\tyield event\n\t\t\t\t\t\t\tif (event.type === 'exit') return\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* skip malformed */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n}\n\n// =============================================================================\n// Watch namespace\n// =============================================================================\n\nexport class SandboxWatch {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Start watching a path. Events delivered via sandbox.events() SSE stream. */\n\tasync add(opts: WatchOptions): Promise<WatchEntry> {\n\t\tconst res = await fetch(`${this.endpoint}/watch`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(opts),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.add failed: ${await res.text()}`)\n\t\treturn (await res.json()) as WatchEntry\n\t}\n\n\t/** List active watches. */\n\tasync list(): Promise<WatchEntry[]> {\n\t\tconst res = await fetch(`${this.endpoint}/watch`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.list failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { watches: WatchEntry[] }\n\t\treturn data.watches\n\t}\n\n\t/** Stop watching a path. */\n\tasync remove(path: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/watch?path=${encodeURIComponent(path)}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.remove failed: ${await res.text()}`)\n\t}\n}\n\n// =============================================================================\n// SandboxClient\n// =============================================================================\n\nexport class SandboxClient {\n\treadonly id: string\n\tprivate readonly config: SylphxConfig\n\n\t/** Public endpoint from Platform (may be null for sandboxes from pool pre-v2) */\n\treadonly endpoint: string | null\n\t/** Per-sandbox JWT for direct exec-server auth */\n\treadonly token: string | null\n\n\t/** File operations (direct to exec-server) */\n\treadonly files: SandboxFiles | null\n\n\t/** Concurrent process management (direct to exec-server) */\n\treadonly processes: SandboxProcesses | null\n\n\t/** Filesystem watch management (direct to exec-server) */\n\treadonly watch: SandboxWatch | null\n\n\tprivate constructor(\n\t\tid: string,\n\t\tconfig: SylphxConfig,\n\t\tendpoint: string | null,\n\t\ttoken: string | null,\n\t) {\n\t\tthis.id = id\n\t\tthis.config = config\n\t\tthis.endpoint = endpoint\n\t\tthis.token = token\n\t\tthis.files = endpoint && token ? new SandboxFiles(endpoint, token) : null\n\t\tthis.processes = endpoint && token ? new SandboxProcesses(endpoint, token) : null\n\t\tthis.watch = endpoint && token ? new SandboxWatch(endpoint, token) : null\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Factory\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Create a new sandbox.\n\t *\n\t * Platform provisions the sandbox runtime, waits for readiness, and returns\n\t * { endpoint, token } once the sandbox is fully ready to accept traffic.\n\t * No client-side polling required.\n\t */\n\tstatic async create(config: SylphxConfig, options?: SandboxOptions): Promise<SandboxClient> {\n\t\tconst record = await callApi<SandboxRecord>(config, '/sandboxes', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: {\n\t\t\t\timage: options?.image,\n\t\t\t\tidleTimeoutMs: options?.idleTimeoutMs ?? 300_000,\n\t\t\t\tmachine: options?.machine ?? 'standard',\n\t\t\t\tenv: options?.env,\n\t\t\t\tstorage:\n\t\t\t\t\toptions?.storageGi !== undefined\n\t\t\t\t\t\t? { enabled: true, sizeGi: options.storageGi }\n\t\t\t\t\t\t: undefined,\n\t\t\t\tvolumeMounts: options?.volumeMounts,\n\t\t\t\tworkspaceMounts: options?.workspaceMounts,\n\t\t\t},\n\t\t\ttimeout: options?.createTimeoutMs ?? SANDBOX_CREATE_TIMEOUT_MS,\n\t\t})\n\n\t\treturn new SandboxClient(record.id, config, record.endpoint, record.token)\n\t}\n\n\t/**\n\t * Reconnect to an existing sandbox by ID.\n\t * Fetches the current status to get the endpoint + token.\n\t */\n\tstatic async fromId(config: SylphxConfig, sandboxId: string): Promise<SandboxClient> {\n\t\tconst record = await callApi<SandboxRecord>(config, `/sandboxes/${sandboxId}`, {\n\t\t\tmethod: 'GET',\n\t\t})\n\t\treturn new SandboxClient(record.id, config, record.endpoint, record.token)\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Lifecycle\n\t// ---------------------------------------------------------------------------\n\n\tasync getStatus(): Promise<SandboxRecord> {\n\t\treturn callApi<SandboxRecord>(this.config, `/sandboxes/${this.id}`, { method: 'GET' })\n\t}\n\n\tasync terminate(): Promise<void> {\n\t\tawait callApi<{ success: boolean }>(this.config, `/sandboxes/${this.id}`, { method: 'DELETE' })\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Exec — SSE streaming (primary)\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Execute a command and stream output as async iterable SSE events.\n\t *\n\t * **Stateless mode**: each exec() call runs in an isolated bash invocation.\n\t * Shell state (CWD changes, exported env vars, functions) is NOT preserved\n\t * between calls.\n\t *\n\t * For state-preserving execution (CWD, env), use `run()` which runs in the\n\t * persistent active shell and returns the result once complete.\n\t *\n\t * For streaming + state-preserving (advanced), combine `sandbox.events()` with `run()`:\n\t * ```typescript\n\t * const eventStream = sandbox.events({ type: 'stdout' })\n\t * sandbox.run(['npm', 'install']) // don't await yet\n\t * for await (const ev of eventStream) { ... }\n\t * ```\n\t *\n\t * @example\n\t * ```typescript\n\t * for await (const event of sandbox.exec(['npm', 'install'])) {\n\t * if (event.type === 'stdout') process.stdout.write(event.data)\n\t * if (event.type === 'exit') console.log('Done:', event.exitCode)\n\t * }\n\t * ```\n\t */\n\tasync *exec(command: string[], options?: ExecOptions): AsyncGenerator<ExecEvent> {\n\t\tthis.assertDirect()\n\n\t\t// POST /exec with stateless:true, stream:true → exec-server returns SSE response.\n\t\t// 'stateless' runs in an isolated bash process (not the active shell).\n\t\t// Without stateless:true, /exec runs in the active shell and returns JSON — not SSE.\n\t\tconst res = await fetch(`${this.endpoint!}/exec`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${this.token!}`,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t},\n\t\t\tbody: JSON.stringify({ command, ...options, stateless: true, stream: true }),\n\t\t})\n\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`exec failed (${res.status}): ${await res.text()}`)\n\t\t}\n\t\tif (!res.body) throw new Error('exec: no response body')\n\n\t\t// Parse SSE stream\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? '' // Keep incomplete last line\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(line.slice(6)) as ExecEvent\n\t\t\t\t\t\t\tyield event\n\t\t\t\t\t\t\tif (event.type === 'exit' || event.type === 'error') return\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Malformed SSE data — skip\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n\n\t/**\n\t * Execute a command and collect all output (non-streaming).\n\t * Convenience wrapper over exec() for simple use cases.\n\t *\n\t * For long-running commands, prefer exec() to stream output incrementally.\n\t */\n\tasync run(command: string[], options?: ExecOptions): Promise<ExecResult> {\n\t\tlet stdout = ''\n\t\tlet stderr = ''\n\t\tlet exitCode = 1\n\t\tlet durationMs = 0\n\n\t\tfor await (const event of this.exec(command, options)) {\n\t\t\tif (event.type === 'stdout') stdout += event.data\n\t\t\telse if (event.type === 'stderr') stderr += event.data\n\t\t\telse if (event.type === 'exit') {\n\t\t\t\texitCode = event.exitCode\n\t\t\t\tdurationMs = event.durationMs\n\t\t\t}\n\t\t}\n\n\t\treturn { stdout, stderr, exitCode, durationMs }\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Events — Unified SSE stream\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Subscribe to the unified event stream (SSE).\n\t *\n\t * Receives all sandbox events: stdout, stderr, exit, port, file, shell, resource.\n\t * Filter by type/pid/shellId using query params.\n\t *\n\t * @example\n\t * ```typescript\n\t * for await (const event of sandbox.events({ type: 'file' })) {\n\t * console.log('File changed:', event.path, event.event)\n\t * }\n\t * ```\n\t */\n\tasync *events(filter?: {\n\t\ttype?: 'stdout' | 'stderr' | 'exit' | 'port' | 'file' | 'shell' | 'resource'\n\t\tpid?: number\n\t\tshellId?: string\n\t}): AsyncGenerator<Record<string, unknown>> {\n\t\tthis.assertDirect()\n\n\t\tconst params = new URLSearchParams()\n\t\tif (filter?.type) params.set('type', filter.type)\n\t\tif (filter?.pid !== undefined) params.set('pid', String(filter.pid))\n\t\tif (filter?.shellId) params.set('shellId', filter.shellId)\n\n\t\tconst qs = params.toString()\n\t\tconst url = `${this.endpoint!}/events${qs ? `?${qs}` : ''}`\n\n\t\tconst res = await fetch(url, {\n\t\t\theaders: { Authorization: `Bearer ${this.token!}` },\n\t\t})\n\n\t\tif (!res.ok) throw new Error(`events failed (${res.status}): ${await res.text()}`)\n\t\tif (!res.body) throw new Error('events: no response body')\n\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? ''\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tyield JSON.parse(line.slice(6)) as Record<string, unknown>\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* skip malformed */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// PTY — Interactive terminal (WebSocket)\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Open an interactive PTY session (WebSocket).\n\t *\n\t * Returns a WebSocket connected to a bash shell in the sandbox.\n\t * Token is passed as a query param (WebSocket doesn't support custom headers in browsers).\n\t *\n\t * @example\n\t * ```typescript\n\t * const ws = await sandbox.pty()\n\t * ws.on('message', (data) => process.stdout.write(JSON.parse(data).data))\n\t * ws.send(JSON.stringify({ type: 'input', data: 'ls -la\\n' }))\n\t * ws.send(JSON.stringify({ type: 'resize', cols: 120, rows: 40 }))\n\t * ```\n\t */\n\tpty(): WebSocket {\n\t\tthis.assertDirect()\n\n\t\tconst wsEndpoint = this.endpoint!.replace(/^https:\\/\\//, 'wss://').replace(\n\t\t\t/^http:\\/\\//,\n\t\t\t'ws://',\n\t\t)\n\n\t\treturn new WebSocket(`${wsEndpoint}/pty?token=${encodeURIComponent(this.token!)}`)\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Private\n\t// ---------------------------------------------------------------------------\n\n\tprivate assertDirect(): void {\n\t\tif (!this.endpoint || !this.token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Sandbox endpoint/token not available. ' +\n\t\t\t\t\t'This sandbox was created with an older SDK version or does not have a public endpoint.',\n\t\t\t)\n\t\t}\n\t}\n}\n","/**\n * Runs Client (ADR-040, formerly Workers)\n *\n * Fire-and-forget batch compute API (Modal-style run-to-completion jobs).\n *\n * Runs are ephemeral isolated jobs that run to completion. Use them for:\n * - ML training folds (walk-forward cross-validation)\n * - Data processing pipelines\n * - Batch inference\n * - Any parallelisable CPU-heavy work\n *\n * ## Usage\n *\n * ### Single worker\n * ```typescript\n * import { createServerClient, RunsClient } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * const run = await RunsClient.run(config, {\n * image: 'ghcr.io/acme/my-trainer:sha-abc123',\n * command: ['python', 'train.py', '--fold', '0'],\n * machine: 'large',\n * timeoutSeconds: 3600,\n * })\n *\n * const result = await run.wait()\n * console.log(result.exitCode) // 0\n * console.log(result.stdout) // captured stdout\n * ```\n *\n * ### Parallel workers (walk-forward training)\n * ```typescript\n * const workers = await Promise.all(\n * folds.map((fold) =>\n * RunsClient.run(config, {\n * image: 'ghcr.io/acme/trainer:sha-abc123',\n * command: ['python', 'train.py', '--fold', String(fold.id)],\n * env: { FOLD_ID: String(fold.id), DATABASE_URL: process.env.DATABASE_URL! },\n * machine: 'large',\n * volumeMounts: [{ volumeId: sharedCacheVolumeId, mountPath: '/cache' }],\n * timeoutSeconds: 7200,\n * }),\n * ),\n * )\n *\n * const results = await Promise.all(workers.map((w) => w.wait()))\n * const failures = results.filter((r) => r.exitCode !== 0)\n * ```\n *\n * ## Architecture\n *\n * - Workers are isolated one-shot runs with no automatic restarts\n * - Images come from connected registries and are scanned before execution\n * - Volumes: org-level volume resources mounted into the run\n * - single-writer for one active writer at a time\n * - shared for concurrent runs and shared feature caches\n * - Auth: sk_* secret key (server-side only)\n * - Quota: 20 concurrent workers per org\n *\n * @module\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type RunStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'timeout'\nexport type RunMachineSize = MachineSize\n\nexport interface RunVolumeMount {\n\t/** UUID of the volumeResource to mount (must belong to this org) */\n\tvolumeId: string\n\t/** Absolute mount path inside the container (e.g. '/cache') */\n\tmountPath: string\n\t/** Optional sub-path within the volume (e.g. 'fold-3') */\n\tsubPath?: string\n\t/** Mount as read-only (default: false) */\n\treadOnly?: boolean\n}\n\nexport interface CreateRunOptions {\n\t/**\n\t * OCI image to run from a connected registry.\n\t *\n\t * @example 'ghcr.io/acme/my-trainer:sha-abc123'\n\t */\n\timage: string\n\n\t/**\n\t * Command + args to execute.\n\t *\n\t * @example ['python', 'train.py', '--fold', '3']\n\t * @example ['node', 'dist/process.js']\n\t */\n\tcommand: string[]\n\n\t/**\n\t * Environment variables to inject.\n\t * Use for fold IDs, feature flags, DB URLs, etc.\n\t */\n\tenv?: Record<string, string>\n\n\t/**\n\t * Managed machine size. The platform owns CPU, memory, scheduling, and isolation details.\n\t * Defaults to `standard`.\n\t */\n\tmachine?: RunMachineSize\n\n\t/**\n\t * Hard timeout in seconds (default: 3600 = 1 hour, max: 86400 = 24 hours).\n\t * The platform terminates the run when the deadline is reached (status: 'timeout').\n\t */\n\ttimeoutSeconds?: number\n\n\t/**\n\t * Volume mounts from org-level volumeResources.\n\t * Shared volumes allow concurrent access by multiple parallel workers.\n\t */\n\tvolumeMounts?: RunVolumeMount[]\n}\n\nexport interface Run {\n\t/** Worker run ID (e.g. 'worker_Vh3kJ9mNpQ2wXsL1') */\n\tid: string\n\t/** Current lifecycle status */\n\tstatus: RunStatus\n\t/** Docker image */\n\timage: string\n\t/** Command being executed */\n\tcommand: string[]\n\t/** Environment variables */\n\tenv: Record<string, string> | null\n\t/** Managed machine size selected for this run. */\n\tmachine: RunMachineSize | null\n\t/** Hard timeout in seconds */\n\ttimeoutSeconds: number\n\t/** Volume mounts */\n\tvolumeMounts: RunVolumeMount[] | null\n\t/** Exit code (only when succeeded or failed) */\n\texitCode: number | null\n\t/** Captured stdout (up to 1 MiB) */\n\tstdout: string | null\n\t/** Captured stderr (up to 1 MiB) */\n\tstderr: string | null\n\t/** Error message (e.g. OOMKilled, image pull failure) */\n\terrorMessage: string | null\n\t/** Duration in milliseconds (only when completed) */\n\tdurationMs: number | null\n\t/** When the worker runtime started */\n\tstartedAt: string | null\n\t/** When the worker completed */\n\tcompletedAt: string | null\n\t/** When this run was created */\n\tcreatedAt: string\n\t/** Last update timestamp */\n\tupdatedAt: string\n}\n\nexport interface RunResult {\n\t/** Exit code (0 = success) */\n\texitCode: number | null\n\t/** Status at completion */\n\tstatus: RunStatus\n\t/** Captured stdout */\n\tstdout: string | null\n\t/** Captured stderr */\n\tstderr: string | null\n\t/** Error message (OOMKilled, DeadlineExceeded, etc.) */\n\terrorMessage: string | null\n\t/** Wall-clock duration in milliseconds */\n\tdurationMs: number | null\n}\n\nexport interface RunLogsResult {\n\t/** Captured stdout (up to 1 MiB) */\n\tstdout: string\n\t/** Captured stderr (up to 1 MiB) */\n\tstderr: string\n\t/** Whether logs are still being captured (worker is running) */\n\tlive: boolean\n}\n\nexport interface ListRunsOptions {\n\t/** Filter by status */\n\tstatus?: RunStatus\n}\n\n/** OpenAI/Stripe-style list response */\nexport interface ListRunsResult {\n\tobject: 'list'\n\tdata: Run[]\n\t/** True if there are more results (limit was hit) */\n\thas_more: boolean\n}\n\n// ============================================================================\n// RunHandle — returned by RunsClient.run()\n// ============================================================================\n\nconst TERMINAL_STATUSES: ReadonlySet<RunStatus> = new Set([\n\t'succeeded',\n\t'failed',\n\t'cancelled',\n\t'timeout',\n])\nconst DEFAULT_POLL_INTERVAL_MS = 3_000\nconst DEFAULT_WAIT_TIMEOUT_MS = 7_200_000 // 2 hours\n\n/**\n * Handle to a running (or completed) worker.\n * Use `.wait()` to poll until completion, `.logs()` to stream logs, `.cancel()` to abort.\n */\nexport class RunHandle {\n\treadonly id: string\n\tprivate readonly config: SylphxConfig\n\n\tconstructor(id: string, config: SylphxConfig) {\n\t\tthis.id = id\n\t\tthis.config = config\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Status\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Get the current status of this worker.\n\t */\n\tasync status(): Promise<Run> {\n\t\treturn callApi<Run>(this.config, `/workers/${this.id}`, { method: 'GET' })\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Wait (poll to completion)\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Poll the worker until it reaches a terminal state (succeeded, failed, cancelled, timeout).\n\t *\n\t * @param options.pollIntervalMs - How often to poll in ms (default: 3000)\n\t * @param options.timeoutMs - Max time to wait before throwing (default: 7_200_000 = 2h)\n\t * @returns RunResult with exit code, status, stdout/stderr\n\t * @throws Error if waitTimeout is exceeded\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await run.wait()\n\t * if (result.exitCode !== 0) {\n\t * throw new Error(`Worker failed: ${result.errorMessage ?? result.stderr}`)\n\t * }\n\t * ```\n\t */\n\tasync wait(options?: { pollIntervalMs?: number; timeoutMs?: number }): Promise<RunResult> {\n\t\tconst pollMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS\n\t\tconst maxWaitMs = options?.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS\n\t\tconst deadline = Date.now() + maxWaitMs\n\n\t\twhile (true) {\n\t\t\tconst run = await this.status()\n\n\t\t\tif (TERMINAL_STATUSES.has(run.status)) {\n\t\t\t\treturn {\n\t\t\t\t\texitCode: run.exitCode,\n\t\t\t\t\tstatus: run.status,\n\t\t\t\t\tstdout: run.stdout,\n\t\t\t\t\tstderr: run.stderr,\n\t\t\t\t\terrorMessage: run.errorMessage,\n\t\t\t\t\tdurationMs: run.durationMs,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Worker ${this.id} did not complete within ${maxWaitMs}ms (current status: ${run.status})`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tawait sleep(pollMs)\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Logs\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Fetch captured logs for this worker.\n\t *\n\t * - For a running worker: returns live logs streamed from the runtime (may be incomplete)\n\t * - For a completed worker: returns the full captured output stored in DB\n\t *\n\t * @example\n\t * ```typescript\n\t * const { stdout, stderr, live } = await worker.logs()\n\t * console.log(stdout)\n\t * if (live) console.log('(worker still running, logs may be incomplete)')\n\t * ```\n\t */\n\tasync logs(): Promise<RunLogsResult> {\n\t\treturn callApi<RunLogsResult>(this.config, `/workers/${this.id}/logs`, { method: 'GET' })\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cancel\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Cancel this worker.\n\t *\n\t * - If still pending: immediately cancelled\n\t * - If running: runtime is terminated\n\t * - If already completed: no-op\n\t */\n\tasync cancel(): Promise<void> {\n\t\tawait callApi<{ success: boolean }>(this.config, `/workers/${this.id}`, { method: 'DELETE' })\n\t}\n}\n\n// ============================================================================\n// RunsClient\n// ============================================================================\n\n/**\n * Static client for the Workers BaaS service.\n *\n * @example\n * ```typescript\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Run a worker and wait for completion\n * const result = await RunsClient.run(config, { ... }).then((run) => run.wait())\n *\n * // Run N workers in parallel, wait for all\n * const handles = await Promise.all(folds.map((fold) => RunsClient.run(config, { ... })))\n * const results = await Promise.all(handles.map(h => h.wait()))\n * ```\n */\nexport const RunsClient = {\n\t// --------------------------------------------------------------------------\n\t// Run\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Spawn a new worker and return a handle.\n\t *\n\t * The run is created immediately and starts pulling the image.\n\t * Use the returned handle to `.wait()` for completion or `.cancel()`.\n\t *\n\t * @example\n\t * ```typescript\n\t * const run = await RunsClient.run(config, {\n\t * image: 'ghcr.io/acme/trainer:sha-abc123',\n\t * command: ['python', 'train.py', '--fold', '3'],\n\t * machine: 'large',\n\t * volumeMounts: [{ volumeId: cacheVolumeId, mountPath: '/cache' }],\n\t * })\n\t * const result = await run.wait()\n\t * ```\n\t */\n\tasync run(config: SylphxConfig, options: CreateRunOptions): Promise<RunHandle> {\n\t\tconst run = await callApi<{ id: string }>(config, '/runs', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: {\n\t\t\t\timage: options.image,\n\t\t\t\tcommand: options.command,\n\t\t\t\tenv: options.env,\n\t\t\t\tmachine: options.machine ?? 'standard',\n\t\t\t\ttimeoutSeconds: options.timeoutSeconds ?? 3600,\n\t\t\t\tvolumeMounts: options.volumeMounts,\n\t\t\t},\n\t\t})\n\t\treturn new RunHandle(run.id, config)\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// Get\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Get a RunHandle for an existing run by ID.\n\t *\n\t * Useful for resuming monitoring across requests.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Store the worker ID, retrieve later\n\t * const handle = RunsClient.fromId(config, storedWorkerId)\n\t * const result = await handle.wait()\n\t * ```\n\t */\n\tfromId(config: SylphxConfig, workerId: string): RunHandle {\n\t\treturn new RunHandle(workerId, config)\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// List\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * List worker runs for this environment.\n\t *\n\t * @example\n\t * ```typescript\n\t * const { data } = await RunsClient.list(config, { status: 'running' })\n\t * console.log(`${data.length} runs currently running`)\n\t * ```\n\t */\n\tasync list(config: SylphxConfig, options?: ListRunsOptions): Promise<ListRunsResult> {\n\t\treturn callApi<ListRunsResult>(config, '/runs', {\n\t\t\tmethod: 'GET',\n\t\t\tquery: options?.status ? { status: options.status } : undefined,\n\t\t})\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// Run-and-wait convenience\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Spawn a worker and wait for it to complete in one call.\n\t *\n\t * Equivalent to `(await RunsClient.run(config, options)).wait(waitOptions)`.\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await RunsClient.runAndWait(config, {\n\t * image: 'ghcr.io/acme/process:sha-abc123',\n\t * command: ['node', 'dist/process.js'],\n\t * })\n\t * if (result.exitCode !== 0) throw new Error(result.errorMessage ?? 'worker failed')\n\t * ```\n\t */\n\tasync runAndWait(\n\t\tconfig: SylphxConfig,\n\t\toptions: CreateRunOptions,\n\t\twaitOptions?: { pollIntervalMs?: number; timeoutMs?: number },\n\t): Promise<RunResult> {\n\t\tconst handle = await RunsClient.run(config, options)\n\t\treturn handle.wait(waitOptions)\n\t},\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// ── Backward-compat aliases (ADR-040) ────────────────────────────────────────\n/** @deprecated Use RunsClient */\nexport const WorkersClient = RunsClient\n/** @deprecated Use RunHandle */\nexport { RunHandle as WorkerHandle }\n/** @deprecated Use CreateRunOptions */\nexport type { CreateRunOptions as RunWorkerOptions }\n/** @deprecated Use Run */\nexport type { Run as WorkerRun }\n/** @deprecated Use RunLogsResult */\nexport type { RunLogsResult as WorkerLogsResult }\n/** @deprecated Use RunResult */\nexport type { RunResult as WorkerResult }\n/** @deprecated Use RunStatus */\nexport type { RunStatus as WorkerStatus }\n/** @deprecated Use RunVolumeMount */\nexport type { RunVolumeMount as WorkerVolumeMount }\n","/**\n * Application Logs SDK (ADR-089 Phase 4a)\n *\n * Pure functions for runtime application log ingestion and querying,\n * backed by `apps/runtime/src/server/runtime/routes/logs.ts`:\n *\n * POST /logs — ingest log entries (requires secret key / serverAuth)\n * GET /logs — query logs (requires any key / configAuth)\n *\n * Logs are persisted in the project's `appLogs` table. Ingestion is\n * batched via `{ logs: [...] }` — do not call once per line in a loop.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n\nexport interface LogEntry {\n\treadonly level: LogLevel\n\treadonly message: string\n\treadonly timestamp?: string\n\treadonly metadata?: Record<string, unknown>\n\treadonly source?: string\n\treadonly traceId?: string\n}\n\nexport interface StoredLogEntry {\n\treadonly id: string\n\treadonly level: LogLevel\n\treadonly message: string\n\treadonly timestamp: string\n\treadonly metadata: Record<string, unknown> | null\n\treadonly traceId: string | null\n\treadonly source: string | null\n\treadonly userId: string | null\n}\n\nexport interface IngestLogsResult {\n\treadonly ingested: number\n}\n\nexport interface QueryLogsOptions {\n\t/** Minimum level to return (levels at or above this are included). */\n\treadonly level?: LogLevel\n\t/** ISO 8601 lower bound (inclusive). */\n\treadonly since?: string\n\t/** ISO 8601 upper bound (inclusive). */\n\treadonly until?: string\n\t/** Max entries (default 100, server-enforced cap 1000). */\n\treadonly limit?: number\n\t/** Full-text search against `message` (case-insensitive). */\n\treadonly search?: string\n\t/** Filter by a specific trace correlation ID. */\n\treadonly traceId?: string\n}\n\nexport interface QueryLogsResult {\n\treadonly logs: readonly StoredLogEntry[]\n\treadonly total: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Ingest one or more log entries for the current project.\n *\n * @example\n * ```typescript\n * await ingestLogs(config, [\n * { level: 'info', message: 'user signed in', metadata: { userId } },\n * { level: 'warn', message: 'stripe webhook retry' },\n * ])\n * ```\n */\nexport async function ingestLogs(\n\tconfig: SylphxConfig,\n\tentries: readonly LogEntry[],\n): Promise<IngestLogsResult> {\n\treturn callApi<IngestLogsResult>(config, '/logs', {\n\t\tmethod: 'POST',\n\t\tbody: { logs: entries },\n\t})\n}\n\n/**\n * Query stored application logs with optional filtering.\n * Returns newest-first, capped at the server's limit (1000).\n *\n * @example\n * ```typescript\n * const { logs } = await queryLogs(config, { level: 'error', limit: 50 })\n * ```\n */\nexport async function queryLogs(\n\tconfig: SylphxConfig,\n\toptions: QueryLogsOptions = {},\n): Promise<QueryLogsResult> {\n\treturn callApi<QueryLogsResult>(config, '/logs', {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tlevel: options.level,\n\t\t\tsince: options.since,\n\t\t\tuntil: options.until,\n\t\t\tlimit: options.limit,\n\t\t\tsearch: options.search,\n\t\t\ttraceId: options.traceId,\n\t\t},\n\t})\n}\n","/**\n * Miscellaneous SDK endpoints (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/misc.ts`:\n *\n * GET /app — project metadata (configAuth)\n * POST /challenge/verify — step-up identity verification (userAuth)\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProjectMetadata {\n\treadonly id: string\n\treadonly name: string\n\treadonly slug: string\n\treadonly captcha?: {\n\t\treadonly enabled: true\n\t\treadonly provider: 'turnstile' | 'hcaptcha'\n\t\treadonly siteKey: string\n\t\treadonly action: 'register'\n\t}\n\treadonly [key: string]: unknown\n}\n\nexport type ChallengeMethod = 'password' | 'email' | 'totp' | 'backup'\nexport type ChallengeType = 'identity' | 'mfa'\n\nexport interface ChallengeVerifyInput {\n\t/** Verification method to use. */\n\treadonly method: ChallengeMethod\n\t/** Verification value matching `method` (password, email code, TOTP code, backup code). */\n\treadonly value: string\n}\n\nexport interface ChallengeVerifyResult {\n\treadonly verified: boolean\n\treadonly method: ChallengeMethod\n\treadonly type: ChallengeType\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get app metadata for the current project (id, name, slug).\n * Cacheable — the BaaS emits `Cache-Control` headers for CDN reuse.\n */\nexport async function getProjectMetadata(config: SylphxConfig): Promise<ProjectMetadata> {\n\treturn callApi<ProjectMetadata>(config, '/app')\n}\n\n/**\n * Verify the authenticated user's identity for step-up authentication.\n *\n * Lockout is enforced server-side after repeated failures — respect the\n * `429` response. Methods accepted: password, email OTP, TOTP, backup code.\n *\n * @example\n * ```typescript\n * await verifyChallenge(config, { method: 'password', value: pw })\n * ```\n */\nexport async function verifyChallenge(\n\tconfig: SylphxConfig,\n\tinput: ChallengeVerifyInput,\n): Promise<ChallengeVerifyResult> {\n\treturn callApi<ChallengeVerifyResult>(config, '/challenge/verify', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n","/**\n * User SDK — customer-app-facing user management (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/user.ts`:\n *\n * GET /user/profile — full profile\n * PUT /user/profile — update profile\n * GET /user/security — 2FA / password status\n * GET /user/sessions — list active sessions\n * PUT /user/sessions/{id} — rename session device\n * DELETE /user/sessions/{id} — revoke session\n * GET /user/export — GDPR export\n * DELETE /user/account — GDPR erasure\n *\n * These routes are authenticated with a **user access token** (bearer) —\n * distinct from the platform-user namespace in `auth.ts` which targets\n * `/auth/platform-user/*` for Console / CLI operators. Customer apps\n * should use these; platform tooling should use `auth.user.*`.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UserFullProfile {\n\treadonly id: string\n\treadonly email: string\n\treadonly name: string | null\n\treadonly image: string | null\n\treadonly emailVerified: boolean\n\treadonly createdAt: string\n\treadonly updatedAt: string\n\treadonly [key: string]: unknown\n}\n\nexport interface UserUpdateProfileInput {\n\treadonly name?: string\n\treadonly image?: string\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface UserSecuritySettings {\n\treadonly hasPassword: boolean\n\treadonly twoFactorEnabled: boolean\n\treadonly passkeyCount: number\n\treadonly oauthProviders: readonly string[]\n\treadonly [key: string]: unknown\n}\n\nexport interface UserSession {\n\treadonly id: string\n\treadonly deviceName: string | null\n\treadonly ipAddress: string | null\n\treadonly userAgent: string | null\n\treadonly createdAt: string\n\treadonly lastActiveAt: string | null\n\treadonly expiresAt: string\n\treadonly current: boolean\n}\n\nexport interface UserSessionsList {\n\treadonly sessions: readonly UserSession[]\n}\n\nexport interface UserDataExport {\n\treadonly exportedAt: string\n\treadonly user: Record<string, unknown>\n\treadonly [key: string]: unknown\n}\n\nexport interface DeleteAccountResult {\n\treadonly success: boolean\n\treadonly deletedData: readonly string[]\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\nexport async function getUserProfile(config: SylphxConfig): Promise<UserFullProfile> {\n\treturn callApi<UserFullProfile>(config, '/user/profile')\n}\n\nexport async function updateUserProfile(\n\tconfig: SylphxConfig,\n\tinput: UserUpdateProfileInput,\n): Promise<UserFullProfile> {\n\treturn callApi<UserFullProfile>(config, '/user/profile', {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\nexport async function getUserSecurity(config: SylphxConfig): Promise<UserSecuritySettings> {\n\treturn callApi<UserSecuritySettings>(config, '/user/security')\n}\n\nexport async function listUserSessions(config: SylphxConfig): Promise<UserSessionsList> {\n\treturn callApi<UserSessionsList>(config, '/user/sessions')\n}\n\nexport async function revokeUserSession(\n\tconfig: SylphxConfig,\n\tsessionId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/user/sessions/${encodeURIComponent(sessionId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\nexport async function renameUserSession(\n\tconfig: SylphxConfig,\n\tsessionId: string,\n\tdeviceName: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/user/sessions/${encodeURIComponent(sessionId)}`, {\n\t\tmethod: 'PUT',\n\t\tbody: { deviceName },\n\t})\n}\n\n/**\n * Export every piece of personal data the platform holds about the\n * authenticated user (GDPR Article 20 — data portability).\n */\nexport async function exportUserData(config: SylphxConfig): Promise<UserDataExport> {\n\treturn callApi<UserDataExport>(config, '/user/export')\n}\n\n/**\n * Permanently delete the authenticated user's account and all\n * associated data (GDPR Article 17 — right to erasure). Callers\n * SHOULD gate this behind a `verifyChallenge()` step-up.\n */\nexport async function deleteUserAccount(config: SylphxConfig): Promise<DeleteAccountResult> {\n\treturn callApi<DeleteAccountResult>(config, '/user/account', {\n\t\tmethod: 'DELETE',\n\t})\n}\n","/**\n * Security SDK — customer-app-facing account security management (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/security/*`:\n *\n * /security/password/set — set or replace password\n * /security/oauth/disconnect — remove linked OAuth provider\n * /security/score — security posture score\n * /security/email/change + confirm — verified email change flow\n * /security/passkeys (+ register) — WebAuthn passkey CRUD\n * /security/2fa/setup|verify|disable — TOTP 2FA lifecycle\n * /security/backup-codes — view / regenerate recovery codes\n * /security/alerts — security alert inbox\n *\n * All routes require a user access token (bearer). Destructive verbs\n * (disable 2FA, delete passkey, disconnect OAuth) SHOULD be gated\n * behind a `verifyChallenge()` step-up from `@sylphx/sdk/misc`.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SecurityScoreResult {\n\treadonly score: number\n\treadonly maxScore: number\n\treadonly factors: readonly {\n\t\treadonly key: string\n\t\treadonly label: string\n\t\treadonly satisfied: boolean\n\t\treadonly weight: number\n\t}[]\n}\n\nexport interface PasswordSetInput {\n\treadonly password: string\n\t/** Current password — required if one is already set. */\n\treadonly currentPassword?: string\n}\n\nexport interface EmailChangeInput {\n\treadonly newEmail: string\n}\n\nexport interface EmailConfirmInput {\n\treadonly code: string\n}\n\nexport interface PasskeySummary {\n\treadonly id: string\n\treadonly name: string | null\n\treadonly createdAt: string\n\treadonly lastUsedAt: string | null\n}\n\nexport interface PasskeysList {\n\treadonly passkeys: readonly PasskeySummary[]\n}\n\nexport interface PasskeyRegistrationOptions {\n\t/** WebAuthn `PublicKeyCredentialCreationOptions` — pass to `navigator.credentials.create()`. */\n\treadonly [key: string]: unknown\n}\n\nexport interface PasskeyRegistrationInput {\n\t/** WebAuthn credential response from `navigator.credentials.create()`. */\n\treadonly credential: Record<string, unknown>\n\treadonly deviceName?: string\n}\n\nexport interface TwoFactorSetupResult {\n\treadonly secret: string\n\treadonly qrCodeUri: string\n\treadonly recoveryHint?: string\n}\n\nexport interface TwoFactorEnableResult {\n\treadonly success: boolean\n\treadonly backupCodes: readonly string[]\n}\n\nexport interface BackupCodesResult {\n\treadonly codes: readonly string[]\n\treadonly remaining: number\n}\n\nexport interface SecurityAlert {\n\treadonly id: string\n\treadonly type: string\n\treadonly severity: 'info' | 'warning' | 'critical'\n\treadonly message: string\n\treadonly createdAt: string\n\treadonly readAt: string | null\n\treadonly metadata: Record<string, unknown> | null\n}\n\nexport interface SecurityAlertsList {\n\treadonly alerts: readonly SecurityAlert[]\n\treadonly unread: number\n}\n\n// ============================================================================\n// Password & OAuth linkage\n// ============================================================================\n\nexport async function setPassword(\n\tconfig: SylphxConfig,\n\tinput: PasswordSetInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/password/set', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function disconnectOAuthProvider(\n\tconfig: SylphxConfig,\n\tprovider: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/oauth/disconnect', {\n\t\tmethod: 'POST',\n\t\tbody: { provider },\n\t})\n}\n\nexport async function getSecurityScore(config: SylphxConfig): Promise<SecurityScoreResult> {\n\treturn callApi<SecurityScoreResult>(config, '/security/score')\n}\n\n// ============================================================================\n// Email change\n// ============================================================================\n\nexport async function requestEmailChange(\n\tconfig: SylphxConfig,\n\tinput: EmailChangeInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/email/change', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function confirmEmailChange(\n\tconfig: SylphxConfig,\n\tinput: EmailConfirmInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/email/confirm', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n// ============================================================================\n// Passkeys (WebAuthn)\n// ============================================================================\n\nexport async function listPasskeys(config: SylphxConfig): Promise<PasskeysList> {\n\treturn callApi<PasskeysList>(config, '/security/passkeys')\n}\n\nexport async function startPasskeyRegistration(\n\tconfig: SylphxConfig,\n): Promise<PasskeyRegistrationOptions> {\n\treturn callApi<PasskeyRegistrationOptions>(config, '/security/passkeys/register/start', {\n\t\tmethod: 'POST',\n\t})\n}\n\nexport async function verifyPasskeyRegistration(\n\tconfig: SylphxConfig,\n\tinput: PasskeyRegistrationInput,\n): Promise<{ success: boolean; passkey: PasskeySummary }> {\n\treturn callApi(config, '/security/passkeys/register/verify', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function renamePasskey(\n\tconfig: SylphxConfig,\n\tpasskeyId: string,\n\tname: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/passkeys/${encodeURIComponent(passkeyId)}`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { name },\n\t\t},\n\t)\n}\n\nexport async function deletePasskey(\n\tconfig: SylphxConfig,\n\tpasskeyId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/passkeys/${encodeURIComponent(passkeyId)}`,\n\t\t{ method: 'DELETE' },\n\t)\n}\n\n// ============================================================================\n// Two-factor authentication\n// ============================================================================\n\nexport async function setupTwoFactor(config: SylphxConfig): Promise<TwoFactorSetupResult> {\n\treturn callApi<TwoFactorSetupResult>(config, '/security/2fa/setup', { method: 'POST' })\n}\n\nexport async function verifyTwoFactorEnable(\n\tconfig: SylphxConfig,\n\tcode: string,\n): Promise<TwoFactorEnableResult> {\n\treturn callApi<TwoFactorEnableResult>(config, '/security/2fa/verify', {\n\t\tmethod: 'POST',\n\t\tbody: { code },\n\t})\n}\n\nexport async function disableTwoFactor(config: SylphxConfig): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/2fa/disable', { method: 'POST' })\n}\n\nexport async function getBackupCodes(config: SylphxConfig): Promise<BackupCodesResult> {\n\treturn callApi<BackupCodesResult>(config, '/security/backup-codes')\n}\n\nexport async function regenerateBackupCodes(config: SylphxConfig): Promise<BackupCodesResult> {\n\treturn callApi<BackupCodesResult>(config, '/security/backup-codes/regenerate', {\n\t\tmethod: 'POST',\n\t})\n}\n\n// ============================================================================\n// Security alerts\n// ============================================================================\n\nexport async function listSecurityAlerts(config: SylphxConfig): Promise<SecurityAlertsList> {\n\treturn callApi<SecurityAlertsList>(config, '/security/alerts')\n}\n\nexport async function markSecurityAlertRead(\n\tconfig: SylphxConfig,\n\talertId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/alerts/${encodeURIComponent(alertId)}/read`,\n\t\t{ method: 'POST' },\n\t)\n}\n\nexport async function markAllSecurityAlertsRead(\n\tconfig: SylphxConfig,\n): Promise<{ success: boolean; updated: number }> {\n\treturn callApi(config, '/security/alerts/read-all', { method: 'POST' })\n}\n","/**\n * OAuth SDK — customer-app-facing social login (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/oauth.ts`:\n *\n * POST /oauth/authorize — initiate OAuth flow, returns provider URL\n *\n * The per-provider callback handler is a browser-side redirect endpoint\n * on the runtime (`GET /oauth/callback/{provider}`); SDK consumers\n * redirect the user to `authorizationUrl`, then receive a `code` on\n * their own `redirect_uri` which they exchange via `signIn()` or\n * `auth/login` with grant_type=authorization_code.\n *\n * PKCE is required per OAuth 2.1. Callers MUST generate `code_verifier`\n * + `code_challenge` and persist the verifier in session / cookie for\n * the subsequent token exchange — this SDK does not retain state.\n *\n * ## Distinction from `auth.oauth`\n *\n * `auth.oauth.*` (from `./auth`) targets platform-plane admin mint\n * (`/auth/platform-jwt/mint`) used by Console / CLI. The functions\n * here target the customer-app social-login flow.\n */\n\nimport type { TokenResponse } from './auth'\nimport { callApi, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\nimport type { OAuthProviderId } from './types'\n\n// ============================================================================\n// OIDC Discovery + UserInfo — ADR-089 Phase 5.6\n// ============================================================================\n\n/**\n * OIDC Discovery document shape (OIDC-Core §4). Every published field is\n * optional from the SDK's perspective because different providers\n * advertise different subsets; Sylphx's own doc is shipped fully\n * populated by {@link buildOidcDiscoveryDocument}.\n */\nexport interface OidcDiscoveryDocument {\n\treadonly issuer: string\n\treadonly authorization_endpoint?: string\n\treadonly token_endpoint?: string\n\treadonly userinfo_endpoint?: string\n\treadonly jwks_uri?: string\n\treadonly introspection_endpoint?: string\n\treadonly revocation_endpoint?: string\n\treadonly device_authorization_endpoint?: string\n\treadonly response_types_supported?: readonly string[]\n\treadonly subject_types_supported?: readonly string[]\n\treadonly id_token_signing_alg_values_supported?: readonly string[]\n\treadonly grant_types_supported?: readonly string[]\n\treadonly scopes_supported?: readonly string[]\n\treadonly claims_supported?: readonly string[]\n\treadonly token_endpoint_auth_methods_supported?: readonly string[]\n\treadonly code_challenge_methods_supported?: readonly string[]\n\treadonly dpop_signing_alg_values_supported?: readonly string[]\n\treadonly frontchannel_logout_supported?: boolean\n\treadonly backchannel_logout_supported?: boolean\n\treadonly [k: string]: unknown\n}\n\nexport interface OidcUserInfoResponse {\n\treadonly sub: string\n\treadonly email?: string\n\treadonly email_verified?: boolean\n\treadonly name?: string\n\treadonly picture?: string\n\treadonly updated_at?: number\n\treadonly [k: string]: unknown\n}\n\n/**\n * Fetch the provider's OIDC discovery document. Works against any\n * spec-compliant provider (Sylphx, Okta, Azure AD, etc.) — pass the\n * issuer URL (scheme + host, no path) as `baseUrl`.\n *\n * @example\n * ```ts\n * const doc = await getOidcDiscoveryDocument({ baseUrl: 'https://api.sylphx.com' })\n * console.log(doc.token_endpoint)\n * ```\n */\nexport async function getOidcDiscoveryDocument(opts: {\n\tbaseUrl: string\n}): Promise<OidcDiscoveryDocument> {\n\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/.well-known/openid-configuration`\n\tconst res = await fetch(url, { headers: { accept: 'application/json' } })\n\tif (!res.ok) {\n\t\tthrow new Error(`OIDC discovery failed: ${res.status} ${res.statusText}`)\n\t}\n\treturn (await res.json()) as OidcDiscoveryDocument\n}\n\n/**\n * Call the OIDC UserInfo endpoint (OIDC-Core §5.3). Returns identity\n * claims for the subject identified by `accessToken`. The token MUST\n * have been granted `openid` scope; otherwise the endpoint returns 403\n * `insufficient_scope`.\n *\n * @example\n * ```ts\n * const info = await userInfo({\n * baseUrl: 'https://api.sylphx.com',\n * accessToken: token.access_token,\n * })\n * console.log(info.sub, info.email)\n * ```\n */\nexport async function userInfo(opts: {\n\tbaseUrl: string\n\taccessToken: string\n\t/** Override the discovered endpoint (e.g. when testing against a staging AS). */\n\tendpoint?: string\n}): Promise<OidcUserInfoResponse> {\n\tconst url = opts.endpoint ?? `${opts.baseUrl.replace(/\\/$/, '')}/v1/auth/oauth/userinfo`\n\tconst res = await fetch(url, {\n\t\tmethod: 'GET',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${opts.accessToken}`,\n\t\t\taccept: 'application/json',\n\t\t},\n\t})\n\tif (!res.ok) {\n\t\tconst body = await res.text()\n\t\tthrow new Error(`userinfo failed: ${res.status} ${res.statusText} ${body}`)\n\t}\n\treturn (await res.json()) as OidcUserInfoResponse\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type OAuthProvider = OAuthProviderId | (string & {})\n\nexport type PkceMethod = 'S256' | 'plain'\n\nexport interface OAuthAuthorizeInput {\n\treadonly provider: OAuthProvider\n\t/** Where the OAuth provider sends the user after consent. */\n\treadonly redirectUri: string\n\t/** PKCE code challenge derived from `code_verifier` (S256 recommended). */\n\treadonly codeChallenge: string\n\treadonly codeChallengeMethod?: PkceMethod\n\t/** Override the default scopes for the provider. */\n\treadonly scopes?: readonly string[]\n\t/** Opaque state for CSRF protection. */\n\treadonly state?: string\n}\n\nexport interface OAuthAuthorizeResult {\n\t/** Fully-qualified URL to redirect the user to. */\n\treadonly authorizationUrl: string\n\t/** Opaque server-side state — echo on callback handler if present. */\n\treadonly state: string\n}\n\nexport interface OAuthProvidersResult {\n\treadonly providers: readonly OAuthProvider[]\n}\n\nexport interface OAuthCodeExchangeInput {\n\treadonly code: string\n\treadonly codeVerifier: string\n\treadonly anonymousId?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Initiate an OAuth flow with the given provider. Returns the provider's\n * authorization URL — redirect the user to it.\n *\n * @example\n * ```typescript\n * import { generatePkce } from '@sylphx/sdk'\n * const { verifier, challenge } = await generatePkce()\n * sessionStorage.setItem('pkce_verifier', verifier)\n * const { authorizationUrl } = await authorizeOAuth(config, {\n * provider: 'google',\n * redirectUri: 'https://app.example.com/auth/callback',\n * codeChallenge: challenge,\n * codeChallengeMethod: 'S256',\n * })\n * window.location.href = authorizationUrl\n * ```\n */\nexport async function authorizeOAuth(\n\tconfig: SylphxConfig,\n\tinput: OAuthAuthorizeInput,\n): Promise<OAuthAuthorizeResult> {\n\treturn callApi<OAuthAuthorizeResult>(config, '/oauth/authorize', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tprovider: input.provider,\n\t\t\tredirect_uri: input.redirectUri,\n\t\t\tcode_challenge: input.codeChallenge,\n\t\t\tcode_challenge_method: input.codeChallengeMethod ?? 'S256',\n\t\t\t...(input.scopes && { scopes: input.scopes }),\n\t\t\t...(input.state && { state: input.state }),\n\t\t},\n\t})\n}\n\n/**\n * List OAuth providers enabled for the current customer app/environment.\n *\n * Uses the same `SylphxConfig` routing surface as every other BaaS call, so\n * customer apps do not need a second app-id or platformUrl configuration path.\n */\nexport async function listOAuthProviders(config: SylphxConfig): Promise<OAuthProvidersResult> {\n\treturn callApi<OAuthProvidersResult>(config, '/auth/oauth-providers')\n}\n\n/**\n * Exchange a social-login authorization code for Platform session tokens.\n *\n * This is the customer-app counterpart to {@link authorizeOAuth}. It uses the\n * app's publishable connection URL as the OAuth client id and keeps PKCE in\n * the caller's control.\n */\nexport async function exchangeOAuthCode(\n\tconfig: SylphxConfig,\n\tinput: OAuthCodeExchangeInput,\n): Promise<TokenResponse> {\n\tif (config.credentialType !== 'pk' || !config.publicKey) {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] exchangeOAuthCode() requires a publishable connection URL (pk_*).',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\treturn callApi<TokenResponse>(config, '/auth/token', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode: input.code,\n\t\t\tclient_id: config.publicKey,\n\t\t\tcode_verifier: input.codeVerifier,\n\t\t\t...(input.anonymousId ? { anonymous_id: input.anonymousId } : {}),\n\t\t},\n\t})\n}\n\n/**\n * Parse an OAuth callback URL (e.g. `window.location.href`) into the\n * fields a customer app needs to complete sign-in.\n *\n * Pure function — no network. Does NOT validate `state` against your\n * session store; callers MUST compare `parsed.state` to the opaque\n * value they persisted before redirect (CSRF mitigation).\n */\nexport function parseOAuthCallback(url: string): {\n\treadonly code: string | null\n\treadonly state: string | null\n\treadonly error: string | null\n\treadonly errorDescription: string | null\n} {\n\tconst parsed = new URL(url)\n\treturn {\n\t\tcode: parsed.searchParams.get('code'),\n\t\tstate: parsed.searchParams.get('state'),\n\t\terror: parsed.searchParams.get('error'),\n\t\terrorDescription: parsed.searchParams.get('error_description'),\n\t}\n}\n\n/**\n * Generate a cryptographically random PKCE verifier + S256 challenge\n * pair (RFC 7636 §4.1-4.2). Works in browsers (Web Crypto) and Bun /\n * Node 18+ (`globalThis.crypto`).\n */\nexport async function generatePkce(): Promise<{ verifier: string; challenge: string }> {\n\tconst bytes = new Uint8Array(32)\n\tglobalThis.crypto.getRandomValues(bytes)\n\tconst verifier = base64UrlEncode(bytes)\n\tconst digest = await globalThis.crypto.subtle.digest(\n\t\t'SHA-256',\n\t\tnew TextEncoder().encode(verifier),\n\t)\n\tconst challenge = base64UrlEncode(new Uint8Array(digest))\n\treturn { verifier, challenge }\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number)\n\treturn btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n","/**\n * Promo Code SDK (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/promo.ts`:\n *\n * POST /promo/validate — check code validity (projectAuth)\n * POST /promo/redeem — redeem for current user (userAuth)\n * GET /admin/promo — list codes (serverAuth)\n * POST /admin/promo — create code (serverAuth)\n * GET /admin/promo/{id} — get single code (serverAuth)\n * PATCH /admin/promo/{id} — update code (serverAuth)\n * DELETE /admin/promo/{id} — archive code (serverAuth)\n * GET /admin/promo/redemptions — redemption history (serverAuth)\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type PromoType = 'percent_off' | 'fixed_off' | 'free_trial' | 'feature_unlock'\nexport type PromoStatus = 'active' | 'paused' | 'expired' | 'archived'\n\nexport interface PromoCode {\n\treadonly id: string\n\treadonly code: string\n\treadonly name: string\n\treadonly description: string | null\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly maxUses: number | null\n\treadonly maxUsesPerUser: number | null\n\treadonly usedCount: number\n\treadonly status: PromoStatus\n\treadonly expiresAt: string | null\n\treadonly startsAt: string | null\n\treadonly minPurchaseAmount: number | null\n\treadonly applicablePlanIds: readonly string[] | null\n\treadonly metadata: Record<string, unknown> | null\n\treadonly createdAt: string\n\treadonly updatedAt: string\n}\n\nexport interface PromoValidationPreview {\n\treadonly code: string\n\treadonly name: string\n\treadonly description: string | null\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly expiresAt: string | null\n}\n\nexport interface ValidatePromoInput {\n\treadonly code: string\n\treadonly planId?: string\n\treadonly purchaseAmount?: number\n}\n\nexport interface ValidatePromoResult {\n\treadonly valid: boolean\n\treadonly promo: PromoValidationPreview | null\n\treadonly error?: string\n}\n\nexport interface RedeemPromoInput {\n\treadonly code: string\n\treadonly planId?: string\n\treadonly purchaseAmount?: number\n}\n\nexport interface RedeemPromoResult {\n\treadonly success: boolean\n\treadonly redemption: {\n\t\treadonly id: string\n\t\treadonly code: string\n\t\treadonly type: PromoType\n\t\treadonly appliedValue: number\n\t\treadonly discountAmount: number | null\n\t\treadonly trialDays: number | null\n\t\treadonly featureId: string | null\n\t}\n}\n\nexport interface CreatePromoInput {\n\treadonly code: string\n\treadonly name: string\n\treadonly description?: string\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly maxUses?: number\n\treadonly maxUsesPerUser?: number\n\treadonly expiresAt?: string\n\treadonly startsAt?: string\n\treadonly minPurchaseAmount?: number\n\treadonly applicablePlanIds?: readonly string[]\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface UpdatePromoInput {\n\treadonly name?: string\n\treadonly description?: string | null\n\treadonly status?: PromoStatus\n\treadonly maxUses?: number | null\n\treadonly maxUsesPerUser?: number | null\n\treadonly expiresAt?: string | null\n\treadonly startsAt?: string | null\n\treadonly minPurchaseAmount?: number | null\n\treadonly applicablePlanIds?: readonly string[] | null\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface ListPromosOptions {\n\treadonly status?: PromoStatus\n\treadonly limit?: number\n\treadonly offset?: number\n}\n\nexport interface ListPromosResult {\n\treadonly promos: readonly PromoCode[]\n\treadonly total: number\n}\n\nexport interface PromoRedemption {\n\treadonly id: string\n\treadonly promoId: string\n\treadonly code: string\n\treadonly userId: string\n\treadonly appliedValue: number\n\treadonly createdAt: string\n}\n\nexport interface ListRedemptionsOptions {\n\treadonly promoId?: string\n\treadonly userId?: string\n\treadonly limit?: number\n\treadonly offset?: number\n}\n\nexport interface ListRedemptionsResult {\n\treadonly redemptions: readonly PromoRedemption[]\n\treadonly total: number\n}\n\n// ============================================================================\n// Client functions (projectAuth + userAuth)\n// ============================================================================\n\n/** Validate a promo code — safe to call with a publishable key. */\nexport async function validatePromo(\n\tconfig: SylphxConfig,\n\tinput: ValidatePromoInput,\n): Promise<ValidatePromoResult> {\n\treturn callApi<ValidatePromoResult>(config, '/promo/validate', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/** Redeem a promo for the authenticated user. Requires a user access token. */\nexport async function redeemPromo(\n\tconfig: SylphxConfig,\n\tinput: RedeemPromoInput,\n): Promise<RedeemPromoResult> {\n\treturn callApi<RedeemPromoResult>(config, '/promo/redeem', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n// ============================================================================\n// Admin functions (serverAuth — secret key only)\n// ============================================================================\n\nexport async function listPromos(\n\tconfig: SylphxConfig,\n\toptions: ListPromosOptions = {},\n): Promise<ListPromosResult> {\n\treturn callApi<ListPromosResult>(config, '/admin/promo', {\n\t\tquery: {\n\t\t\tstatus: options.status,\n\t\t\tlimit: options.limit,\n\t\t\toffset: options.offset,\n\t\t},\n\t})\n}\n\nexport async function getPromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`)\n}\n\nexport async function createPromo(\n\tconfig: SylphxConfig,\n\tinput: CreatePromoInput,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, '/admin/promo', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function updatePromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n\tinput: UpdatePromoInput,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`, {\n\t\tmethod: 'PATCH',\n\t\tbody: input,\n\t})\n}\n\nexport async function deletePromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\nexport async function listPromoRedemptions(\n\tconfig: SylphxConfig,\n\toptions: ListRedemptionsOptions = {},\n): Promise<ListRedemptionsResult> {\n\treturn callApi<ListRedemptionsResult>(config, '/admin/promo/redemptions', {\n\t\tquery: {\n\t\t\tpromoId: options.promoId,\n\t\t\tuserId: options.userId,\n\t\t\tlimit: options.limit,\n\t\t\toffset: options.offset,\n\t\t},\n\t})\n}\n"],"mappings":"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","names":["getErrorCode","getErrorDetails","getErrorMessage","request","response","search","user","SylphxError","buildHeaders","SylphxError","buildHeaders","SylphxError","SylphxError","buildHeaders","SylphxError","isRetryableError","isRetryableError","page","interpolatePath","sleep","base64UrlEncode"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/compute.ts","../src/config/database-pricing.ts","../src/config/referrals.ts","../src/error-extract.ts","../src/connection-url.ts","../src/config.ts","../src/csv.ts","../src/formatting.ts","../src/json.ts","../src/utils.ts","../src/utils/user-agent.ts","../src/workspaces.ts","../src/config/auth.ts","../src/config/billing.ts","../src/config/console-keys.ts","../src/config/instance-types.ts","../src/config/platform-plans.ts","../src/debug.ts","../src/rest-client.ts","../src/key-validation.ts","../src/index.ts","../src/auth.ts","../src/dpop.ts","../src/platform-auth.ts","../src/platform-impersonation.ts","../src/platform-jwt.ts","../src/platform-oauth.ts","../src/oauth-token.ts","../src/platform-password.ts","../src/platform-sessions.ts","../src/platform-user.ts","../src/audit.ts","../src/rate-limits.ts","../src/functions.ts","../src/realtime.ts","../src/realtime-admin.ts","../src/admin.ts","../src/analytics.ts","../src/ai.ts","../src/billing.ts","../src/storage.ts","../src/lib/retry.ts","../src/lib/notifications/service-worker.ts","../src/notifications.ts","../src/lib/triggers/index.ts","../src/lib/tasks/handler.ts","../src/lib/tasks/api-functions.ts","../src/flags.ts","../src/webhooks.ts","../src/email.ts","../src/consent.ts","../src/referrals.ts","../src/lib/engagement/types.ts","../src/engagement.ts","../src/orgs.ts","../src/permissions.ts","../src/roles.ts","../src/secrets.ts","../src/search.ts","../src/database.ts","../src/kv.ts","../src/deploy.ts","../src/monitoring.ts","../src/sandbox.ts","../src/workers.ts","../src/logs.ts","../src/misc.ts","../src/user.ts","../src/security.ts","../src/oauth.ts","../src/promo.ts"],"sourcesContent":["/**\n * SDK Constants — Single Source of Truth\n *\n * Shared constants used across the SDK. Centralizing these\n * prevents magic number duplication and makes changes easier.\n *\n * IMPORTANT: All time-based constants should be used consistently\n * across the SDK. Never hardcode magic numbers like 30000, 5 * 60 * 1000, etc.\n */\n\n// =============================================================================\n// API Configuration\n// =============================================================================\n\n/**\n * Canonical environment variable name for the client connection URL.\n *\n * Format: sylphx://pk_prod_{hex}@tenant-slug.api.sylphx.com\n * Browser-safe (publishable key only).\n */\nexport const ENV_URL = 'SYLPHX_URL'\n\n/**\n * Canonical environment variable name for the Next.js public connection URL.\n *\n * Same format as ENV_URL, browser-safe.\n */\nexport const ENV_PUBLIC_URL = 'NEXT_PUBLIC_SYLPHX_URL'\n\n/**\n * Canonical environment variable name for the server secret connection URL.\n *\n * Format: sylphx://sk_prod_{hex}@tenant-slug.api.sylphx.com\n * Server-side only — never expose to client.\n */\nexport const ENV_SECRET_URL = 'SYLPHX_SECRET_URL'\n\n/**\n * Resolve the connection URL from environment variables.\n *\n * Checks SYLPHX_URL first, then NEXT_PUBLIC_SYLPHX_URL.\n */\nexport function resolveUrl(explicit?: string): string | undefined {\n\treturn explicit || process.env[ENV_URL] || process.env[ENV_PUBLIC_URL]\n}\n\n/**\n * Resolve the secret connection URL from environment variables.\n */\nexport function resolveSecretUrl(explicit?: string): string | undefined {\n\treturn explicit || process.env[ENV_SECRET_URL]\n}\n\n/**\n * SDK API path for the per-project subdomain-based SDK server.\n *\n * The full base URL is built as: https://{tenant-slug}.api.sylphx.com/v1\n */\nexport const SDK_API_PATH = `/v1`\nexport const SDK_API_VERSION = 'v1'\n\n/**\n * Default SDK API host.\n *\n * Management API: https://api.sylphx.com/v1/*\n * BaaS/SDK API: https://{tenant-slug}.api.sylphx.com/v1/*\n */\nexport const DEFAULT_SDK_API_HOST = 'api.sylphx.com'\nexport const DEFAULT_PLATFORM_URL = 'https://sylphx.com'\n\n/**\n * Default auth route prefix\n *\n * Used for OAuth callbacks and signout routes.\n * Must match the middleware's authPrefix config.\n */\nexport const DEFAULT_AUTH_PREFIX = '/auth'\n\n/**\n * SDK package version\n *\n * Sent in X-SDK-Version header for debugging and analytics.\n * Update this when releasing new SDK versions.\n */\nexport const SDK_VERSION = '0.5.0'\n\n/**\n * SDK platform identifier\n *\n * Sent in X-SDK-Platform header to identify the runtime environment.\n */\ntype SdkRuntimeGlobal = typeof globalThis & {\n\treadonly EdgeRuntime?: string\n}\n\nfunction detectSdkPlatform(): 'browser' | 'edge' | 'node' {\n\tif (typeof window !== 'undefined') return 'browser'\n\tconst runtimeGlobal = globalThis as SdkRuntimeGlobal\n\tif (typeof runtimeGlobal.EdgeRuntime !== 'undefined') return 'edge'\n\treturn 'node'\n}\n\nexport const SDK_PLATFORM = detectSdkPlatform()\n\n// =============================================================================\n// Timeouts & Durations\n// =============================================================================\n\n/** Default request timeout in milliseconds (30 seconds) */\nexport const DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Sandbox create waits for the Runtime API to provision runtime resources and for\n * the exec-server readiness probe to pass. The server-side readiness budget is\n * 120s; this client budget adds transport margin without changing the default\n * timeout for ordinary API calls.\n */\nexport const SANDBOX_CREATE_TIMEOUT_MS = 150_000\n\n/**\n * Token expiry buffer in milliseconds (30 seconds)\n *\n * Refresh tokens this many milliseconds BEFORE they expire\n * to account for network latency and clock skew.\n */\nexport const TOKEN_EXPIRY_BUFFER_MS = 30_000\n\n/**\n * Session token lifetime in seconds (5 minutes)\n *\n * Matches Clerk's short-lived access token pattern.\n * Used for cookie maxAge and React Query staleTime.\n */\nexport const SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60\n\n/** Session token lifetime in milliseconds (for React Query staleTime) */\nexport const SESSION_TOKEN_LIFETIME_MS = SESSION_TOKEN_LIFETIME_SECONDS * 1000\n\n/**\n * Refresh token lifetime in seconds (30 days)\n *\n * Long-lived token for silent refresh.\n */\nexport const REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60\n\n// =============================================================================\n// Feature Flags Cache\n// =============================================================================\n\n/**\n * Feature flags cache TTL in milliseconds (5 minutes)\n *\n * How long to cache flags before fetching fresh values.\n * Matches LaunchDarkly's default streaming connection behavior.\n */\nexport const FLAGS_CACHE_TTL_MS = 5 * 60 * 1000\n\n/**\n * Feature flags stale-while-revalidate window in milliseconds (1 minute)\n *\n * Allow serving stale flags while fetching fresh values.\n */\nexport const FLAGS_STALE_WHILE_REVALIDATE_MS = 60 * 1000\n\n// =============================================================================\n// Retry & Backoff\n// =============================================================================\n\n/** Maximum retry delay for exponential backoff (30 seconds) */\nexport const MAX_RETRY_DELAY_MS = 30_000\n\n/** Base retry delay for exponential backoff (1 second) */\nexport const BASE_RETRY_DELAY_MS = 1_000\n\n/** Maximum number of retries for network requests */\nexport const MAX_RETRIES = 3\n\n// =============================================================================\n// Analytics\n// =============================================================================\n\n/**\n * Analytics session timeout in milliseconds (30 minutes)\n *\n * After this much inactivity, a new session is started.\n */\nexport const ANALYTICS_SESSION_TIMEOUT_MS = 30 * 60 * 1000\n\n// =============================================================================\n// Webhooks\n// =============================================================================\n\n/**\n * Maximum age for webhook signature validation (5 minutes)\n *\n * Reject webhooks with timestamps older than this.\n */\nexport const WEBHOOK_MAX_AGE_MS = 5 * 60 * 1000\n\n/**\n * Clock skew allowance for webhook validation (30 seconds)\n *\n * Allow timestamps this far in the future.\n */\nexport const WEBHOOK_CLOCK_SKEW_MS = 30 * 1000\n\n// =============================================================================\n// PKCE (OAuth)\n// =============================================================================\n\n/**\n * PKCE code verifier TTL in milliseconds (10 minutes)\n *\n * How long the code verifier is stored during OAuth flow.\n */\nexport const PKCE_CODE_TTL_MS = 10 * 60 * 1000\n\n// =============================================================================\n// Jobs\n// =============================================================================\n\n/**\n * Job dead-letter queue retention in milliseconds (7 days)\n */\nexport const JOBS_DLQ_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\n// =============================================================================\n// Session Replay\n// =============================================================================\n\n/**\n * Maximum session replay recording duration in milliseconds (60 minutes)\n */\nexport const SESSION_REPLAY_MAX_DURATION_MS = 60 * 60 * 1000\n\n/**\n * Session replay upload interval in milliseconds (5 seconds)\n */\nexport const SESSION_REPLAY_UPLOAD_INTERVAL_MS = 5_000\n\n/**\n * Session replay scroll event throttle interval (150 ms)\n */\nexport const SESSION_REPLAY_SCROLL_THROTTLE_MS = 150\n\n/**\n * Session replay media time update throttle interval (800 ms)\n */\nexport const SESSION_REPLAY_MEDIA_THROTTLE_MS = 800\n\n/**\n * Session replay rage click detection window (1 second)\n */\nexport const SESSION_REPLAY_RAGE_CLICK_WINDOW_MS = 1_000\n\n/**\n * Session replay dead click detection timeout (500 ms)\n */\nexport const SESSION_REPLAY_DEAD_CLICK_TIMEOUT_MS = 500\n\n/**\n * Session replay scroll heat detection window (2 seconds)\n */\nexport const SESSION_REPLAY_SCROLL_HEAT_WINDOW_MS = 2_000\n\n/**\n * Session replay status check interval (5 seconds)\n */\nexport const SESSION_REPLAY_STATUS_CHECK_MS = 5_000\n\n// =============================================================================\n// Analytics (Extended)\n// =============================================================================\n\n/**\n * Analytics event flush interval in milliseconds (5 seconds)\n */\nexport const ANALYTICS_FLUSH_INTERVAL_MS = 5_000\n\n/**\n * Analytics maximum text length for autocapture (100 characters)\n */\nexport const ANALYTICS_MAX_TEXT_LENGTH = 100\n\n/**\n * Analytics flush timeout in milliseconds (1 second)\n */\nexport const ANALYTICS_FLUSH_TIMEOUT_MS = 1_000\n\n/**\n * Analytics interval check in milliseconds (1 second)\n */\nexport const ANALYTICS_INTERVAL_CHECK_MS = 1_000\n\n/**\n * Analytics retry base delay in milliseconds (1 second)\n * Exponential backoff: delay = base * 2^retries (with jitter)\n */\nexport const ANALYTICS_RETRY_BASE_DELAY_MS = 1_000\n\n/**\n * Analytics retry max delay in milliseconds (30 seconds)\n */\nexport const ANALYTICS_RETRY_MAX_DELAY_MS = 30_000\n\n/**\n * Analytics retry jitter factor (±20%)\n * Prevents thundering herd when multiple clients retry simultaneously\n */\nexport const ANALYTICS_RETRY_JITTER = 0.2\n\n/**\n * Analytics maximum retries before dropping event (Segment pattern: 10)\n */\nexport const ANALYTICS_MAX_RETRIES = 10\n\n// =============================================================================\n// Feature Flags (Extended)\n// =============================================================================\n\n/**\n * Feature flags exposure deduplication window (1 hour)\n *\n * Prevents duplicate exposure events for A/B tests within this window.\n */\nexport const FLAGS_EXPOSURE_DEDUPE_WINDOW_MS = 60 * 60 * 1000\n\n/**\n * Flag stream initial reconnection delay (1 second)\n */\nexport const FLAGS_STREAM_INITIAL_RECONNECT_MS = 1_000\n\n/**\n * Flag stream maximum reconnection delay (30 seconds)\n */\nexport const FLAGS_STREAM_MAX_RECONNECT_MS = 30_000\n\n/**\n * Flag stream heartbeat timeout (45 seconds)\n */\nexport const FLAGS_STREAM_HEARTBEAT_TIMEOUT_MS = 45_000\n\n/**\n * Flag HTTP polling interval fallback (60 seconds)\n */\nexport const FLAGS_HTTP_POLLING_INTERVAL_MS = 60_000\n\n// =============================================================================\n// Jobs (Extended)\n// =============================================================================\n\n/**\n * Default retry delay sequence for exponential backoff (ms)\n */\nexport const DEFAULT_RETRY_DELAYS_MS = [1_000, 5_000, 15_000, 30_000, 60_000] as const\n\n/**\n * Default job timeout in milliseconds (60 seconds)\n */\nexport const JOB_DEFAULT_TIMEOUT_MS = 60_000\n\n/**\n * Default job status polling interval (2 seconds)\n */\nexport const JOB_POLL_INTERVAL_MS = 2_000\n\n// =============================================================================\n// Storage Keys & Prefixes\n// =============================================================================\n\n/**\n * Storage key prefix for SDK data\n */\nexport const STORAGE_KEY_PREFIX = 'sylphx_'\n\n/**\n * localStorage key for cached feature flags\n */\nexport const FLAGS_CACHE_KEY = 'sylphx_feature_flags'\n\n/**\n * localStorage key for feature flags cache timestamp\n */\nexport const FLAGS_CACHE_TIMESTAMP_KEY = 'sylphx_feature_flags_ts'\n\n/**\n * localStorage key for feature flags overrides\n */\nexport const FLAGS_OVERRIDES_KEY = 'sylphx_feature_flags_overrides'\n\n/**\n * localStorage key for active organization\n */\nexport const ORG_STORAGE_KEY = 'sylphx_active_org'\n\n/**\n * BroadcastChannel name for cross-tab org sync\n */\nexport const ORG_BROADCAST_CHANNEL = 'sylphx_org_sync'\n\n/**\n * Storage prefix for PKCE verifiers\n */\nexport const PKCE_STORAGE_PREFIX = 'sylphx_pkce_'\n\n/**\n * Test key for checking storage availability\n */\nexport const STORAGE_TEST_KEY = '__sylphx_test__'\n\n/**\n * Cookie/storage name for analytics sessions\n */\nexport const ANALYTICS_SESSION_KEY = 'sylphx_session'\n\n/**\n * Default storage key for flags persistence\n */\nexport const FLAGS_STORAGE_KEY = 'sylphx_flags'\n\n// =============================================================================\n// Click ID & Attribution\n// =============================================================================\n\n/**\n * Click ID attribution window in milliseconds (90 days)\n *\n * How long click IDs are stored for conversion attribution.\n */\nexport const CLICK_ID_EXPIRY_MS = 90 * 24 * 60 * 60 * 1000\n\n// =============================================================================\n// React Query Stale Times\n// =============================================================================\n\n/**\n * React Query staleTime for frequently-changing data (1 minute)\n *\n * Use for: real-time metrics, live feeds, active sessions\n */\nexport const STALE_TIME_FREQUENT_MS = 60 * 1_000\n\n/**\n * React Query staleTime for moderately-changing data (2 minutes)\n *\n * Use for: subscriptions, user profiles, preferences\n */\nexport const STALE_TIME_MODERATE_MS = 2 * 60 * 1_000\n\n/**\n * React Query staleTime for stable/config data (5 minutes)\n *\n * Use for: plans, feature flags, app config\n */\nexport const STALE_TIME_STABLE_MS = 5 * 60 * 1_000\n\n/**\n * React Query staleTime for webhook stats (30 seconds)\n */\nexport const STALE_TIME_STATS_MS = 30 * 1_000\n\n// =============================================================================\n// UI Component Timeouts\n// =============================================================================\n\n/**\n * Copy-to-clipboard feedback display duration (2 seconds)\n */\nexport const UI_COPY_FEEDBACK_MS = 2_000\n\n/**\n * Form success message display duration (3 seconds)\n */\nexport const UI_FORM_SUCCESS_MS = 3_000\n\n/**\n * General notification display duration (5 seconds)\n */\nexport const UI_NOTIFICATION_MS = 5_000\n\n/**\n * Prompt auto-show delay (3 seconds)\n */\nexport const UI_PROMPT_DELAY_MS = 3_000\n\n/**\n * Redirect delay after action (3 seconds)\n */\nexport const UI_REDIRECT_DELAY_MS = 3_000\n\n/**\n * Animation out duration (200 ms)\n */\nexport const UI_ANIMATION_OUT_MS = 200\n\n/**\n * Animation in duration (300 ms)\n */\nexport const UI_ANIMATION_IN_MS = 300\n\n// =============================================================================\n// Email & Verification\n// =============================================================================\n\n/**\n * Email resend cooldown tick interval (1 second)\n */\nexport const EMAIL_RESEND_COOLDOWN_TICK_MS = 1_000\n\n/**\n * New user detection threshold (1 minute)\n *\n * Users created within this window are considered \"new\" for signup tracking.\n */\nexport const NEW_USER_THRESHOLD_MS = 60 * 1_000\n\n// =============================================================================\n// Web Vitals Thresholds\n// =============================================================================\n\n/**\n * FCP (First Contentful Paint) \"good\" threshold (1800 ms)\n */\nexport const WEB_VITALS_FCP_GOOD_MS = 1_800\n\n/**\n * FCP (First Contentful Paint) \"poor\" threshold (3000 ms)\n */\nexport const WEB_VITALS_FCP_POOR_MS = 3_000\n\n// =============================================================================\n// Storage Sizes\n// =============================================================================\n\n/**\n * Multipart upload threshold (5 MB)\n *\n * Files larger than this use multipart upload for better reliability.\n */\nexport const STORAGE_MULTIPART_THRESHOLD_BYTES = 5 * 1024 * 1024\n\n/**\n * Default max file size for uploads (5 MB)\n */\nexport const STORAGE_DEFAULT_MAX_SIZE_BYTES = 5 * 1024 * 1024\n\n/**\n * Avatar max file size (2 MB)\n */\nexport const STORAGE_AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024\n\n/**\n * Large file max size for file uploads (10 MB)\n */\nexport const STORAGE_LARGE_MAX_SIZE_BYTES = 10 * 1024 * 1024\n\n// =============================================================================\n// Cache TTLs\n// =============================================================================\n\n/**\n * JWK cache TTL (1 hour)\n */\nexport const JWK_CACHE_TTL_MS = 60 * 60 * 1000\n\n// =============================================================================\n// Analytics Event Tracking\n// =============================================================================\n\n/**\n * Max tracked event IDs to keep in memory\n */\nexport const ANALYTICS_MAX_TRACKED_EVENT_IDS = 1000\n\n/**\n * Number of event IDs to keep after cleanup\n */\nexport const ANALYTICS_TRACKED_IDS_KEEP = 500\n\n/**\n * Analytics queue limit before force flush\n */\nexport const ANALYTICS_QUEUE_LIMIT = 100\n\n// =============================================================================\n// Session Replay (Extended)\n// =============================================================================\n\n/**\n * Session replay check interval (1 second)\n */\nexport const SESSION_REPLAY_CHECK_INTERVAL_MS = 1_000\n\n/**\n * Success feedback delay for invite/account actions (1.5 seconds)\n */\nexport const UI_SUCCESS_REDIRECT_MS = 1_500\n\n// =============================================================================\n// String Truncation Limits\n// =============================================================================\n\n/**\n * Max message length for logging (1000 chars)\n */\nexport const LOG_MESSAGE_MAX_LENGTH = 1_000\n\n/**\n * Max DOM snapshot length for debugging (1000 chars)\n */\nexport const DOM_SNAPSHOT_MAX_LENGTH = 1_000\n\n/**\n * Max stack trace length for error tracking (500 chars)\n */\nexport const STACK_TRACE_MAX_LENGTH = 500\n\n/**\n * Google Consent Mode wait for update timeout (500 ms)\n */\nexport const CONSENT_WAIT_FOR_UPDATE_MS = 500\n\n// =============================================================================\n// Time Unit Conversions\n// =============================================================================\n\n/** Milliseconds per minute (60,000) */\nexport const MS_PER_MINUTE = 60_000\n\n/** Milliseconds per hour (3,600,000) */\nexport const MS_PER_HOUR = 3_600_000\n\n/** Milliseconds per day (86,400,000) */\nexport const MS_PER_DAY = 86_400_000\n\n/** Seconds per minute (60) */\nexport const SECONDS_PER_MINUTE = 60\n\n/** Seconds per hour (3,600) */\nexport const SECONDS_PER_HOUR = 3_600\n\n// =============================================================================\n// Z-Index Values\n// =============================================================================\n\n/** Z-index for modal overlays (9999) */\nexport const Z_INDEX_OVERLAY = 9999\n\n/** Z-index for critical overlays like feature gates (99999) */\nexport const Z_INDEX_CRITICAL_OVERLAY = 99999\n\n// =============================================================================\n// API Key Expiry (seconds)\n// =============================================================================\n\n/** API key expiry: 1 day (86,400 seconds) */\nexport const API_KEY_EXPIRY_1_DAY = 86_400\n\n/** API key expiry: 7 days (604,800 seconds) */\nexport const API_KEY_EXPIRY_7_DAYS = 604_800\n\n/** API key expiry: 30 days (2,592,000 seconds) */\nexport const API_KEY_EXPIRY_30_DAYS = 2_592_000\n\n/** API key expiry: 90 days (7,776,000 seconds) */\nexport const API_KEY_EXPIRY_90_DAYS = 7_776_000\n\n/** API key expiry: 1 year (31,536,000 seconds) */\nexport const API_KEY_EXPIRY_1_YEAR = 31_536_000\n\n// =============================================================================\n// Web Vitals Thresholds (Google standards)\n// =============================================================================\n\n/** LCP (Largest Contentful Paint) \"good\" threshold (2500 ms) */\nexport const WEB_VITALS_LCP_GOOD_MS = 2_500\n\n/** LCP (Largest Contentful Paint) \"poor\" threshold (4000 ms) */\nexport const WEB_VITALS_LCP_POOR_MS = 4_000\n\n/** INP (Interaction to Next Paint) \"good\" threshold (200 ms) */\nexport const WEB_VITALS_INP_GOOD_MS = 200\n\n/** INP (Interaction to Next Paint) \"poor\" threshold (500 ms) */\nexport const WEB_VITALS_INP_POOR_MS = 500\n\n/** TTFB (Time to First Byte) \"good\" threshold (800 ms) */\nexport const WEB_VITALS_TTFB_GOOD_MS = 800\n\n/** TTFB (Time to First Byte) \"poor\" threshold (1800 ms) */\nexport const WEB_VITALS_TTFB_POOR_MS = 1_800\n\n// =============================================================================\n// Security\n// =============================================================================\n\n/** Minimum password length (NIST SP 800-63B recommends 12+) */\nexport const MIN_PASSWORD_LENGTH = 12\n\n// =============================================================================\n// AI\n// =============================================================================\n\n/** Default context window for AI models (4096 tokens) */\nexport const DEFAULT_CONTEXT_WINDOW = 4_096\n\n// =============================================================================\n// Circuit Breaker (AWS/Resilience4j pattern)\n// =============================================================================\n\n/**\n * Circuit breaker failure threshold\n *\n * Number of failures in the window before circuit opens.\n */\nexport const CIRCUIT_BREAKER_FAILURE_THRESHOLD = 5\n\n/**\n * Circuit breaker failure window in milliseconds (10 seconds)\n *\n * Time window for counting failures.\n */\nexport const CIRCUIT_BREAKER_WINDOW_MS = 10_000\n\n/**\n * Circuit breaker open duration in milliseconds (30 seconds)\n *\n * How long the circuit stays open before allowing a test request.\n */\nexport const CIRCUIT_BREAKER_OPEN_DURATION_MS = 30_000\n\n// =============================================================================\n// ETag Cache (HTTP conditional requests)\n// =============================================================================\n\n/**\n * Maximum ETag cache entries\n *\n * LRU eviction when exceeded.\n */\nexport const ETAG_CACHE_MAX_ENTRIES = 100\n\n/**\n * ETag cache TTL in milliseconds (5 minutes)\n *\n * How long cached responses are valid.\n */\nexport const ETAG_CACHE_TTL_MS = 5 * 60 * 1000\n","/**\n * Sylphx SDK Error Classes\n *\n * Typed error classes for better error handling and debugging.\n * Compatible with tRPC error codes and provides rich context.\n *\n * @example\n * ```typescript\n * import { SylphxError, isRetryableError, getErrorMessage } from '@sylphx/sdk'\n *\n * try {\n * await sylphx.auth.login.mutate({ email, password })\n * } catch (error) {\n * if (error instanceof SylphxError) {\n * console.log(error.code) // 'UNAUTHORIZED'\n * console.log(error.isRetryable) // false\n * }\n * if (isRetryableError(error)) {\n * // Safe to retry\n * }\n * }\n * ```\n */\n\nimport { BASE_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, MAX_RETRY_DELAY_MS } from './constants'\n\n// ============================================================================\n// Error Codes (aligned with tRPC and HTTP semantics)\n// ============================================================================\n\nexport type SylphxErrorCode =\n\t// Client errors (4xx)\n\t| 'BAD_REQUEST' // 400 - Invalid input\n\t| 'UNAUTHORIZED' // 401 - Not authenticated\n\t| 'FORBIDDEN' // 403 - Not authorized\n\t| 'NOT_FOUND' // 404 - Resource not found\n\t| 'CONFLICT' // 409 - Resource conflict (e.g., duplicate)\n\t| 'PAYLOAD_TOO_LARGE' // 413 - Request too large\n\t| 'UNPROCESSABLE_ENTITY' // 422 - Validation failed\n\t| 'TOO_MANY_REQUESTS' // 429 - Rate limited\n\t| 'QUOTA_EXCEEDED' // 402/429 - Quota/plan limit exceeded\n\t// Server errors (5xx)\n\t| 'INTERNAL_SERVER_ERROR' // 500 - Server error\n\t| 'NOT_IMPLEMENTED' // 501 - Feature not available\n\t| 'BAD_GATEWAY' // 502 - Upstream error\n\t| 'SERVICE_UNAVAILABLE' // 503 - Temporarily unavailable\n\t| 'GATEWAY_TIMEOUT' // 504 - Upstream timeout\n\t// Network/Client errors\n\t| 'NETWORK_ERROR' // Network failure\n\t| 'TIMEOUT' // Request timeout\n\t| 'ABORTED' // Request aborted\n\t// SDK-specific\n\t| 'PARSE_ERROR' // JSON/response parse error\n\t| 'UNKNOWN' // Unknown error\n\n/**\n * Simplified semantic error codes (DX-friendly aliases).\n * Maps to the more granular SylphxErrorCode internally.\n *\n * @example\n * ```ts\n * if (SylphxError.isRateLimited(err)) {\n * console.log(`Retry after ${err.retryAfter}s`)\n * }\n * ```\n */\nexport type ErrorCode =\n\t| 'UNAUTHORIZED'\n\t| 'FORBIDDEN'\n\t| 'NOT_FOUND'\n\t| 'RATE_LIMITED'\n\t| 'QUOTA_EXCEEDED'\n\t| 'VALIDATION_ERROR'\n\t| 'NETWORK_ERROR'\n\t| 'UPSTREAM_ERROR'\n\t| 'INTERNAL_ERROR'\n\n/**\n * HTTP status code mapping for error codes\n */\nexport const ERROR_CODE_STATUS: Record<SylphxErrorCode, number> = {\n\tBAD_REQUEST: 400,\n\tUNAUTHORIZED: 401,\n\tFORBIDDEN: 403,\n\tNOT_FOUND: 404,\n\tCONFLICT: 409,\n\tPAYLOAD_TOO_LARGE: 413,\n\tUNPROCESSABLE_ENTITY: 422,\n\tTOO_MANY_REQUESTS: 429,\n\tQUOTA_EXCEEDED: 402,\n\tINTERNAL_SERVER_ERROR: 500,\n\tNOT_IMPLEMENTED: 501,\n\tBAD_GATEWAY: 502,\n\tSERVICE_UNAVAILABLE: 503,\n\tGATEWAY_TIMEOUT: 504,\n\tNETWORK_ERROR: 0,\n\tTIMEOUT: 0,\n\tABORTED: 0,\n\tPARSE_ERROR: 0,\n\tUNKNOWN: 0,\n}\n\n/**\n * Retryable error codes (safe to retry automatically)\n */\nexport const RETRYABLE_CODES: Set<SylphxErrorCode> = new Set([\n\t'NETWORK_ERROR',\n\t'TIMEOUT',\n\t'BAD_GATEWAY',\n\t'SERVICE_UNAVAILABLE',\n\t'GATEWAY_TIMEOUT',\n\t'TOO_MANY_REQUESTS', // With backoff\n\t'INTERNAL_SERVER_ERROR', // Sometimes transient\n])\n\n// ============================================================================\n// Error Classes\n// ============================================================================\n\nexport interface SylphxErrorOptions {\n\t/** Error code for programmatic handling */\n\tcode?: SylphxErrorCode\n\t/** HTTP status code (inferred from code if not provided) */\n\tstatus?: number\n\t/** Additional context data */\n\tdata?: Record<string, unknown>\n\t/** Original error that caused this */\n\tcause?: Error\n\t/** Retry-After header value (seconds) for rate limiting */\n\tretryAfter?: number\n}\n\n/**\n * Base error class for all Sylphx SDK errors\n *\n * @example\n * ```typescript\n * throw new SylphxError('Invalid email format', {\n * code: 'BAD_REQUEST',\n * data: { field: 'email' }\n * })\n * ```\n */\nexport class SylphxError extends Error {\n\t/** Error code for programmatic handling */\n\treadonly code: SylphxErrorCode\n\n\t/** HTTP status code */\n\treadonly status: number\n\n\t/** Additional context data */\n\treadonly data?: Record<string, unknown>\n\n\t/** Whether this error is safe to retry */\n\treadonly isRetryable: boolean\n\n\t/** Retry-After value in seconds (for rate limiting) */\n\treadonly retryAfter?: number\n\n\t/** Timestamp when error occurred */\n\treadonly timestamp: Date\n\n\tconstructor(message: string, options: SylphxErrorOptions = {}) {\n\t\tsuper(message, { cause: options.cause })\n\t\tthis.name = 'SylphxError'\n\t\tthis.code = options.code ?? 'UNKNOWN'\n\t\tthis.status = options.status ?? ERROR_CODE_STATUS[this.code]\n\t\tthis.data = options.data\n\t\tthis.isRetryable = RETRYABLE_CODES.has(this.code)\n\t\tthis.retryAfter = options.retryAfter\n\t\tthis.timestamp = new Date()\n\n\t\t// Maintain proper stack trace in V8\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, SylphxError)\n\t\t}\n\t}\n\n\t// ============================================================================\n\t// Static Type Guards (DX helpers)\n\t// ============================================================================\n\n\t/**\n\t * Check if error is a rate-limit error (429 Too Many Requests)\n\t */\n\tstatic isRateLimited(err: unknown): err is SylphxError & { code: 'TOO_MANY_REQUESTS' } {\n\t\treturn err instanceof SylphxError && err.code === 'TOO_MANY_REQUESTS'\n\t}\n\n\t/**\n\t * Check if error is an account lockout error (too many failed login attempts).\n\t * When true, `error.data?.lockoutUntil` contains the ISO 8601 timestamp when the lockout expires.\n\t */\n\tstatic isAccountLocked(\n\t\terr: unknown,\n\t): err is SylphxError & { code: 'TOO_MANY_REQUESTS'; data: { lockoutUntil: string | null } } {\n\t\treturn (\n\t\t\terr instanceof SylphxError &&\n\t\t\terr.code === 'TOO_MANY_REQUESTS' &&\n\t\t\terr.data?.code === 'ACCOUNT_LOCKED'\n\t\t)\n\t}\n\n\t/**\n\t * Check if error is a quota exceeded error (plan limit reached)\n\t */\n\tstatic isQuotaExceeded(err: unknown): err is SylphxError & { code: 'QUOTA_EXCEEDED' } {\n\t\treturn err instanceof SylphxError && err.code === 'QUOTA_EXCEEDED'\n\t}\n\n\t/**\n\t * Check if error is an authentication error (401 Unauthorized)\n\t */\n\tstatic isUnauthorized(err: unknown): err is SylphxError & { code: 'UNAUTHORIZED' } {\n\t\treturn err instanceof SylphxError && err.code === 'UNAUTHORIZED'\n\t}\n\n\t/**\n\t * Check if error is a not-found error (404 Not Found)\n\t */\n\tstatic isNotFound(err: unknown): err is SylphxError & { code: 'NOT_FOUND' } {\n\t\treturn err instanceof SylphxError && err.code === 'NOT_FOUND'\n\t}\n\n\t/**\n\t * Check if error is an authorization error (403 Forbidden)\n\t */\n\tstatic isForbidden(err: unknown): err is SylphxError & { code: 'FORBIDDEN' } {\n\t\treturn err instanceof SylphxError && err.code === 'FORBIDDEN'\n\t}\n\n\t/**\n\t * Check if error is a validation error (422 Unprocessable Entity)\n\t */\n\tstatic isValidationError(err: unknown): err is SylphxError & { code: 'UNPROCESSABLE_ENTITY' } {\n\t\treturn err instanceof SylphxError && err.code === 'UNPROCESSABLE_ENTITY'\n\t}\n\n\t/**\n\t * Check if error is a network error (no response received)\n\t */\n\tstatic isNetworkError(err: unknown): err is SylphxError & { code: 'NETWORK_ERROR' } {\n\t\treturn err instanceof SylphxError && err.code === 'NETWORK_ERROR'\n\t}\n\n\t/**\n\t * Check if error is an upstream/gateway error (502/504)\n\t */\n\tstatic isUpstreamError(err: unknown): err is SylphxError {\n\t\treturn (\n\t\t\terr instanceof SylphxError &&\n\t\t\t(err.code === 'BAD_GATEWAY' ||\n\t\t\t\terr.code === 'GATEWAY_TIMEOUT' ||\n\t\t\t\terr.code === 'SERVICE_UNAVAILABLE')\n\t\t)\n\t}\n\n\t/**\n\t * Convert to JSON-serializable object\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tcode: this.code,\n\t\t\tstatus: this.status,\n\t\t\tdata: this.data,\n\t\t\tisRetryable: this.isRetryable,\n\t\t\tretryAfter: this.retryAfter,\n\t\t\ttimestamp: this.timestamp.toISOString(),\n\t\t}\n\t}\n}\n\n/**\n * Network-related errors (no response received)\n */\nexport class NetworkError extends SylphxError {\n\tconstructor(message = 'Network request failed', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'NETWORK_ERROR' })\n\t\tthis.name = 'NetworkError'\n\t}\n}\n\n/**\n * Request timeout errors\n */\nexport class TimeoutError extends SylphxError {\n\t/** Timeout duration in milliseconds */\n\treadonly timeout: number\n\n\tconstructor(timeout: number, options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(`Request timed out after ${timeout}ms`, {\n\t\t\t...options,\n\t\t\tcode: 'TIMEOUT',\n\t\t})\n\t\tthis.name = 'TimeoutError'\n\t\tthis.timeout = timeout\n\t}\n}\n\n/**\n * Authentication errors (401)\n */\nexport class AuthenticationError extends SylphxError {\n\tconstructor(message = 'Authentication required', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'UNAUTHORIZED' })\n\t\tthis.name = 'AuthenticationError'\n\t}\n}\n\n/**\n * Authorization errors (403)\n */\nexport class AuthorizationError extends SylphxError {\n\tconstructor(message = 'Permission denied', options?: Omit<SylphxErrorOptions, 'code'>) {\n\t\tsuper(message, { ...options, code: 'FORBIDDEN' })\n\t\tthis.name = 'AuthorizationError'\n\t}\n}\n\n/**\n * Validation errors (422)\n */\nexport class ValidationError extends SylphxError {\n\t/** Field-specific errors */\n\treadonly fieldErrors?: Record<string, string[]>\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & {\n\t\t\tfieldErrors?: Record<string, string[]>\n\t\t},\n\t) {\n\t\tsuper(message, { ...options, code: 'UNPROCESSABLE_ENTITY' })\n\t\tthis.name = 'ValidationError'\n\t\tthis.fieldErrors = options?.fieldErrors\n\t}\n\n\t/**\n\t * Get error message for a specific field\n\t */\n\tgetFieldError(field: string): string | undefined {\n\t\treturn this.fieldErrors?.[field]?.[0]\n\t}\n}\n\n/**\n * Rate limit metadata (Stripe SDK pattern)\n */\nexport interface RateLimitInfo {\n\t/** Maximum requests allowed in window */\n\tlimit?: number\n\t/** Remaining requests in current window */\n\tremaining?: number\n\t/** Unix timestamp (seconds) when limit resets */\n\tresetAt?: number\n\t/** Seconds until limit resets (Retry-After header) */\n\tretryAfter?: number\n}\n\n/**\n * Rate limit errors (429)\n *\n * Provides full rate limit metadata for consumer apps to implement\n * proper backoff UI (countdown timers, retry buttons, etc.)\n *\n * @example\n * ```typescript\n * try {\n * await sendEmail(config, options)\n * } catch (error) {\n * if (error instanceof RateLimitError) {\n * const waitSeconds = error.retryAfter ?? 60\n * console.log(`Rate limited. Retry after ${waitSeconds}s`)\n * console.log(`Remaining: ${error.remaining}/${error.limit}`)\n * console.log(`Resets at: ${new Date(error.resetAt! * 1000)}`)\n * }\n * }\n * ```\n */\nexport class RateLimitError extends SylphxError {\n\t/** Maximum requests allowed in window */\n\treadonly limit?: number\n\n\t/** Remaining requests in current window */\n\treadonly remaining?: number\n\n\t/** Unix timestamp (seconds) when limit resets */\n\treadonly resetAt?: number\n\n\tconstructor(\n\t\tmessage = 'Too many requests',\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & RateLimitInfo,\n\t) {\n\t\tsuper(message, { ...options, code: 'TOO_MANY_REQUESTS' })\n\t\tthis.name = 'RateLimitError'\n\t\tthis.limit = options?.limit\n\t\tthis.remaining = options?.remaining\n\t\tthis.resetAt = options?.resetAt\n\t}\n\n\t/**\n\t * Get Date when rate limit resets\n\t */\n\tgetResetDate(): Date | undefined {\n\t\treturn this.resetAt ? new Date(this.resetAt * 1000) : undefined\n\t}\n\n\t/**\n\t * Get human-readable retry message\n\t */\n\tgetRetryMessage(): string {\n\t\tif (this.retryAfter) {\n\t\t\treturn `Please retry after ${this.retryAfter} seconds`\n\t\t}\n\t\tif (this.resetAt) {\n\t\t\tconst seconds = Math.max(0, this.resetAt - Math.floor(Date.now() / 1000))\n\t\t\treturn `Rate limit resets in ${seconds} seconds`\n\t\t}\n\t\treturn 'Please wait before retrying'\n\t}\n}\n\n/**\n * Resource not found errors (404)\n */\nexport class NotFoundError extends SylphxError {\n\t/** Type of resource that wasn't found */\n\treadonly resourceType?: string\n\n\t/** ID of the resource that wasn't found */\n\treadonly resourceId?: string\n\n\tconstructor(\n\t\tmessage = 'Resource not found',\n\t\toptions?: Omit<SylphxErrorOptions, 'code'> & {\n\t\t\tresourceType?: string\n\t\t\tresourceId?: string\n\t\t},\n\t) {\n\t\tsuper(message, { ...options, code: 'NOT_FOUND' })\n\t\tthis.name = 'NotFoundError'\n\t\tthis.resourceType = options?.resourceType\n\t\tthis.resourceId = options?.resourceId\n\t}\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Check if an error is a Sylphx SDK error\n */\nexport function isSylphxError(error: unknown): error is SylphxError {\n\treturn error instanceof SylphxError\n}\n\n/**\n * Check if an error is safe to retry\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (error instanceof SylphxError) {\n\t\treturn error.isRetryable\n\t}\n\n\t// Check for network errors\n\tif (error instanceof Error) {\n\t\tconst message = error.message.toLowerCase()\n\t\tconst name = error.name.toLowerCase()\n\n\t\t// Network errors\n\t\tif (name === 'typeerror' && message.includes('fetch')) return true\n\t\tif (name === 'networkerror') return true\n\n\t\t// Timeout patterns\n\t\tif (message.includes('timeout')) return true\n\t\tif (message.includes('timed out')) return true\n\n\t\t// Connection errors\n\t\tif (message.includes('econnrefused')) return true\n\t\tif (message.includes('econnreset')) return true\n\t\tif (message.includes('socket')) return true\n\n\t\t// Server errors that might be transient\n\t\tif (message.includes('502')) return true\n\t\tif (message.includes('503')) return true\n\t\tif (message.includes('504')) return true\n\t}\n\n\treturn false\n}\n\n/**\n * Extract error message from any error type\n */\nexport function getErrorMessage(error: unknown, fallback = 'An unknown error occurred'): string {\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\tif (typeof error === 'string') {\n\t\treturn error\n\t}\n\tif (error && typeof error === 'object' && 'message' in error) {\n\t\tconst message = (error as { message: unknown }).message\n\t\tif (typeof message === 'string') return message\n\t}\n\tif (error && typeof error === 'object' && 'response' in error) {\n\t\tconst response = (error as { response?: { data?: { message?: string; error?: string } } })\n\t\t\t.response\n\t\tif (response?.data?.message) return response.data.message\n\t\tif (response?.data?.error) return response.data.error\n\t}\n\treturn fallback\n}\n\n/**\n * Get error code from any error type\n */\nexport function getErrorCode(error: unknown): SylphxErrorCode {\n\tif (error instanceof SylphxError) {\n\t\treturn error.code\n\t}\n\treturn 'UNKNOWN'\n}\n\nfunction getErrorStatus(error: unknown): number | undefined {\n\tif (!error || typeof error !== 'object') return undefined\n\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\treturn (error as { status: number }).status\n\t}\n\tif ('statusCode' in error && typeof (error as { statusCode: unknown }).statusCode === 'number') {\n\t\treturn (error as { statusCode: number }).statusCode\n\t}\n\treturn undefined\n}\n\nfunction getSafeErrorKey(error: unknown): string | undefined {\n\tif (error instanceof SylphxError) return error.code\n\tif (!error || typeof error !== 'object') return undefined\n\tif ('code' in error && typeof (error as { code: unknown }).code === 'string') {\n\t\treturn (error as { code: string }).code\n\t}\n\tconst status = getErrorStatus(error)\n\treturn status == null ? undefined : String(status)\n}\n\nexport interface ErrorDetails {\n\treadonly message: string\n\treadonly code?: string\n\treadonly name?: string\n\treadonly stack?: string\n\treadonly status?: number\n\treadonly cause?: unknown\n}\n\nexport function getErrorDetails(\n\terror: unknown,\n\tfallbackMessage = 'An unknown error occurred',\n): ErrorDetails {\n\tconst details: ErrorDetails = {\n\t\tmessage: getErrorMessage(error, fallbackMessage),\n\t}\n\tconst code = getSafeErrorKey(error)\n\tif (code) {\n\t\tObject.assign(details, { code })\n\t}\n\tconst status = getErrorStatus(error)\n\tif (status != null) {\n\t\tObject.assign(details, { status })\n\t}\n\tif (error instanceof Error) {\n\t\tObject.assign(details, {\n\t\t\tname: error.name,\n\t\t\tstack: error.stack,\n\t\t\t...(error.cause ? { cause: error.cause } : {}),\n\t\t})\n\t}\n\treturn details\n}\n\nconst INTERNAL_ERROR_PATTERNS = [\n\t/sql/i,\n\t/database/i,\n\t/postgres/i,\n\t/neon/i,\n\t/drizzle/i,\n\t/prisma/i,\n\t/constraint/i,\n\t/foreign key/i,\n\t/unique violation/i,\n\t/null value/i,\n\t/column/i,\n\t/table/i,\n\t/relation/i,\n\t/trpc/i,\n\t/internal server/i,\n\t/unexpected/i,\n\t/cannot read propert/i,\n\t/undefined is not/i,\n\t/null is not/i,\n\t/cannot find module/i,\n\t/econnrefused/i,\n\t/etimedout/i,\n\t/enotfound/i,\n]\n\nconst SAFE_ERROR_MESSAGES: Record<string, string> = {\n\t'400': 'Invalid request. Please check your input and try again.',\n\t'401': 'Please sign in to continue.',\n\t'403': \"You don't have permission to perform this action.\",\n\t'404': 'The requested resource was not found.',\n\t'409': 'This action conflicts with existing data. Please refresh and try again.',\n\t'429': 'Too many requests. Please wait a moment and try again.',\n\t'500': 'Something went wrong on our end. Please try again later.',\n\t'502': 'Service temporarily unavailable. Please try again in a moment.',\n\t'503': 'Service temporarily unavailable. Please try again in a moment.',\n\tBAD_REQUEST: 'Invalid request. Please check your input.',\n\tUNAUTHORIZED: 'Please sign in to continue.',\n\tFORBIDDEN: \"You don't have permission to perform this action.\",\n\tNOT_FOUND: 'The requested item was not found.',\n\tCONFLICT: 'This action conflicts with existing data.',\n\tTOO_MANY_REQUESTS: 'Too many requests. Please wait a moment.',\n\tINTERNAL_SERVER_ERROR: 'Something went wrong. Please try again later.',\n\tTIMEOUT: 'Request timed out. Please try again.',\n\tPRECONDITION_FAILED: 'This action cannot be completed right now.',\n\tQUOTA_EXCEEDED: \"You've reached your usage limit. Please upgrade your plan.\",\n\tPAYMENT_REQUIRED: 'Payment is required to continue.',\n\tINVALID_CREDENTIALS: 'Invalid email or password.',\n\tEMAIL_NOT_VERIFIED: 'Please verify your email address.',\n\tACCOUNT_LOCKED: 'Your account has been locked. Please contact support.',\n\tSESSION_EXPIRED: 'Your session has expired. Please sign in again.',\n}\n\nexport function getSafeErrorMessage(\n\terror: unknown,\n\tfallback = 'Something went wrong. Please try again.',\n): string {\n\tconst errorCode = getSafeErrorKey(error)\n\tif (errorCode && SAFE_ERROR_MESSAGES[errorCode]) return SAFE_ERROR_MESSAGES[errorCode]\n\n\tif (error && typeof error === 'object') {\n\t\tconst coded = error as { data?: { code?: string } }\n\t\tif (coded.data?.code && SAFE_ERROR_MESSAGES[coded.data.code]) {\n\t\t\treturn SAFE_ERROR_MESSAGES[coded.data.code]\n\t\t}\n\t}\n\n\tconst rawMessage = getErrorMessage(error, '')\n\tif (\n\t\trawMessage &&\n\t\trawMessage.length < 200 &&\n\t\t!rawMessage.includes('\\n') &&\n\t\t!rawMessage.includes('at ') &&\n\t\t!INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(rawMessage))\n\t) {\n\t\treturn rawMessage\n\t}\n\n\treturn fallback\n}\n\nexport function isChallengeRequired(err: unknown): boolean {\n\tif (!(err instanceof Error)) return false\n\tif (err.message.includes('challenge')) return true\n\tconst errWithData = err as { data?: { code?: string } }\n\treturn errWithData.data?.code === 'FORBIDDEN'\n}\n\n/**\n * Convert any error to SylphxError\n */\nexport function toSylphxError(error: unknown): SylphxError {\n\tif (error instanceof SylphxError) {\n\t\treturn error\n\t}\n\n\tif (error instanceof Error) {\n\t\t// Try to infer error type from message/name\n\t\tconst message = error.message.toLowerCase()\n\t\tconst name = error.name.toLowerCase()\n\n\t\t// Network errors\n\t\tif (name === 'typeerror' && message.includes('fetch')) {\n\t\t\treturn new NetworkError(error.message, { cause: error })\n\t\t}\n\t\tif (name === 'aborterror' || message.includes('aborted')) {\n\t\t\treturn new SylphxError(error.message, { code: 'ABORTED', cause: error })\n\t\t}\n\n\t\t// Timeout\n\t\tif (message.includes('timeout')) {\n\t\t\treturn new TimeoutError(DEFAULT_TIMEOUT_MS, { cause: error })\n\t\t}\n\n\t\t// HTTP status codes in message\n\t\tif (message.includes('401') || message.includes('unauthorized')) {\n\t\t\treturn new AuthenticationError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('403') || message.includes('forbidden')) {\n\t\t\treturn new AuthorizationError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('404') || message.includes('not found')) {\n\t\t\treturn new NotFoundError(error.message, { cause: error })\n\t\t}\n\t\tif (message.includes('429') || message.includes('rate limit')) {\n\t\t\treturn new RateLimitError(error.message, { cause: error })\n\t\t}\n\n\t\treturn new SylphxError(error.message, { cause: error })\n\t}\n\n\treturn new SylphxError(getErrorMessage(error))\n}\n\n/**\n * Calculate exponential backoff delay with jitter\n *\n * @param attempt - Retry attempt number (0-indexed)\n * @param baseDelay - Base delay in milliseconds (default: 1000)\n * @param maxDelay - Maximum delay in milliseconds (default: 30000)\n * @returns Delay in milliseconds with jitter\n */\nexport function exponentialBackoff(\n\tattempt: number,\n\tbaseDelay = BASE_RETRY_DELAY_MS,\n\tmaxDelay = MAX_RETRY_DELAY_MS,\n): number {\n\t// Calculate exponential delay: baseDelay * 2^attempt\n\tconst exponentialDelay = baseDelay * 2 ** attempt\n\n\t// Cap at maxDelay\n\tconst cappedDelay = Math.min(exponentialDelay, maxDelay)\n\n\t// Add jitter (±25% randomness)\n\tconst jitter = cappedDelay * 0.25 * (Math.random() * 2 - 1)\n\n\treturn Math.round(cappedDelay + jitter)\n}\n","export {\n\tDEFAULT_MACHINE_SIZE,\n\tisMachineSize,\n\tMACHINE_CONFIGS,\n\tMACHINE_MAX_INSTANCES,\n\tMACHINE_RESOURCE_REQUIREMENTS,\n\tMACHINE_SIZES,\n\ttype MachineConfig,\n\ttype MachineResourceRequirements,\n\ttype MachineTierResources,\n\tparseMachineSize,\n\tresolveMachineConfig,\n\tresolveMachineMaxInstances,\n\tresolveMachineResources,\n\tresolveMachineTierResources,\n\ttoPublicMachineSize,\n} from '@sylphx/contract/compute'\n","/**\n * Database Pricing Configuration (SSOT)\n *\n * All database billing constants centralized here.\n * Used by billing calculations, usage tracking, and cost display.\n *\n * Pricing Strategy:\n * - Self-hosted infra on AX162-R: flat ~$270/month\n * - Competitive pricing with 75-99% margins\n *\n * Customer Prices (updated for self-hosted, 2026-02):\n * - Compute: $0.08/hour (25% below Neon $0.106/hr)\n * - Storage: $0.25/GB-month (29% below Neon $0.35/GB)\n * - Transfer: $0.09/GB (matches Supabase)\n */\n\n// ==========================================\n// Compute Pricing\n// ==========================================\n\n/** Price per compute hour in microdollars ($0.08/hour = 80,000 microdollars) */\nexport const COMPUTE_PRICE_PER_HOUR_MICRODOLLARS = 80_000\n\n/** Free compute hours per month (platform free tier) */\nexport const FREE_COMPUTE_HOURS = 3\n\n// ==========================================\n// Storage Pricing\n// ==========================================\n\n/** Price per GB-month in microdollars ($0.25/GB-month = 250,000 microdollars) */\nexport const STORAGE_PRICE_PER_GB_MONTH_MICRODOLLARS = 250_000\n\n/** Free storage in GB (256 MB) */\nexport const FREE_STORAGE_GB = 0.25\n\n// ==========================================\n// Transfer Pricing\n// ==========================================\n\n/** Price per GB data transfer ($0.09/GB = 90,000 microdollars) */\nexport const TRANSFER_PRICE_PER_GB_MICRODOLLARS = 90_000\n\n// ==========================================\n// KV Storage Free Tier\n// ==========================================\n// Applied in code at billing time because platform_pricing.free_tier_amount\n// is an integer column and cannot represent 0.25 GB. Same pattern as\n// FREE_STORAGE_GB for databases above.\n\n/** KV free storage in GB (256 MB) */\nexport const KV_FREE_STORAGE_GB = 0.25\n\n// ==========================================\n// Time Constants\n// ==========================================\n\n/** Hours per month (AWS/GCP standard for billing) */\nexport const HOURS_PER_MONTH = 730\n","/**\n * Referrals Configuration (SSOT)\n *\n * Single source of truth for referral system configuration.\n * Used by: referral router, SDK referral endpoints\n */\n\n// ==========================================\n// Rewards\n// ==========================================\n\n/** Default points awarded per successful referral */\nexport const DEFAULT_POINTS_REWARD = 100\n\n// ==========================================\n// Discount Configuration\n// ==========================================\n\n/** Number of months the referral discount is valid */\nexport const DISCOUNT_DURATION_MONTHS = 3\n\n/** Default discount percentage */\nexport const DISCOUNT_PERCENT = 20\n\n/** Premium trial days from referral */\nexport const PREMIUM_TRIAL_DAYS = 7\n\n// ==========================================\n// Code Generation\n// ==========================================\n\n/** Default referral code length */\nconst REFERRAL_CODE_LENGTH = 8\n\n/**\n * Characters used for referral codes (uppercase alphanumeric, no confusing chars).\n * 32 characters total — divides evenly into 256, so byte % 32 has zero modulo bias.\n */\nconst REFERRAL_CODE_CHARS = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'\n\n/**\n * Generate a cryptographically secure referral code\n *\n * Uses crypto.getRandomValues for uniform random bytes (0-255).\n * Since REFERRAL_CODE_CHARS has exactly 32 characters and 256 / 32 = 8,\n * byte % 32 produces perfectly uniform distribution with zero modulo bias.\n *\n * Entropy: 8 chars * log2(32) = 40 bits (~1.1 trillion possible codes)\n *\n * Format: 8 uppercase alphanumeric characters (excluding ambiguous: 0, O, I, L, 1)\n */\nexport function generateReferralCode(): string {\n\tconst bytes = new Uint8Array(REFERRAL_CODE_LENGTH)\n\tcrypto.getRandomValues(bytes)\n\n\tlet code = ''\n\tfor (let i = 0; i < REFERRAL_CODE_LENGTH; i++) {\n\t\tcode += REFERRAL_CODE_CHARS[bytes[i] % REFERRAL_CODE_CHARS.length]\n\t}\n\treturn code\n}\n","export function getErrorMessage(error: unknown, fallback = 'Unknown error'): string {\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\n\tif (typeof error === 'string') {\n\t\treturn error\n\t}\n\n\tif (error && typeof error === 'object' && 'message' in error) {\n\t\tconst message = (error as { message: unknown }).message\n\t\tif (typeof message === 'string') {\n\t\t\treturn message\n\t\t}\n\t}\n\n\tif (error && typeof error === 'object' && 'response' in error) {\n\t\tconst response = (error as { response?: { data?: { message?: string; error?: string } } })\n\t\t\t.response\n\t\tif (response?.data?.message) return response.data.message\n\t\tif (response?.data?.error) return response.data.error\n\t}\n\n\treturn fallback\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== 'object') {\n\t\treturn undefined\n\t}\n\n\tif ('code' in error && typeof (error as { code: unknown }).code === 'string') {\n\t\treturn (error as { code: string }).code\n\t}\n\n\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\treturn String((error as { status: number }).status)\n\t}\n\n\tif ('statusCode' in error && typeof (error as { statusCode: unknown }).statusCode === 'number') {\n\t\treturn String((error as { statusCode: number }).statusCode)\n\t}\n\n\treturn undefined\n}\n\nexport interface ErrorDetails {\n\tmessage: string\n\tcode?: string\n\tname?: string\n\tstack?: string\n\tstatus?: number\n\tcause?: unknown\n}\n\nexport function getErrorDetails(error: unknown, fallbackMessage = 'Unknown error'): ErrorDetails {\n\tconst details: ErrorDetails = {\n\t\tmessage: getErrorMessage(error, fallbackMessage),\n\t}\n\n\tif (error instanceof Error) {\n\t\tdetails.name = error.name\n\t\tdetails.stack = error.stack\n\t\tif (error.cause) {\n\t\t\tdetails.cause = error.cause\n\t\t}\n\t}\n\n\tconst code = getErrorCode(error)\n\tif (code) {\n\t\tdetails.code = code\n\t}\n\n\tif (error && typeof error === 'object') {\n\t\tif ('status' in error && typeof (error as { status: unknown }).status === 'number') {\n\t\t\tdetails.status = (error as { status: number }).status\n\t\t} else if (\n\t\t\t'statusCode' in error &&\n\t\t\ttypeof (error as { statusCode: unknown }).statusCode === 'number'\n\t\t) {\n\t\t\tdetails.status = (error as { statusCode: number }).statusCode\n\t\t}\n\t}\n\n\treturn details\n}\n\nconst INTERNAL_ERROR_PATTERNS = [\n\t/sql/i,\n\t/database/i,\n\t/postgres/i,\n\t/neon/i,\n\t/drizzle/i,\n\t/prisma/i,\n\t/constraint/i,\n\t/foreign key/i,\n\t/unique violation/i,\n\t/null value/i,\n\t/column/i,\n\t/table/i,\n\t/relation/i,\n\t/trpc/i,\n\t/internal server/i,\n\t/unexpected/i,\n\t/cannot read propert/i,\n\t/undefined is not/i,\n\t/null is not/i,\n\t/cannot find module/i,\n\t/econnrefused/i,\n\t/etimedout/i,\n\t/enotfound/i,\n]\n\nconst SAFE_ERROR_MESSAGES: Record<string, string> = {\n\t'400': 'Invalid request. Please check your input and try again.',\n\t'401': 'Please sign in to continue.',\n\t'403': \"You don't have permission to perform this action.\",\n\t'404': 'The requested resource was not found.',\n\t'409': 'This action conflicts with existing data. Please refresh and try again.',\n\t'429': 'Too many requests. Please wait a moment and try again.',\n\t'500': 'Something went wrong on our end. Please try again later.',\n\t'502': 'Service temporarily unavailable. Please try again in a moment.',\n\t'503': 'Service temporarily unavailable. Please try again in a moment.',\n\tBAD_REQUEST: 'Invalid request. Please check your input.',\n\tUNAUTHORIZED: 'Please sign in to continue.',\n\tFORBIDDEN: \"You don't have permission to perform this action.\",\n\tNOT_FOUND: 'The requested item was not found.',\n\tCONFLICT: 'This action conflicts with existing data.',\n\tTOO_MANY_REQUESTS: 'Too many requests. Please wait a moment.',\n\tINTERNAL_SERVER_ERROR: 'Something went wrong. Please try again later.',\n\tTIMEOUT: 'Request timed out. Please try again.',\n\tPRECONDITION_FAILED: 'This action cannot be completed right now.',\n\tQUOTA_EXCEEDED: \"You've reached your usage limit. Please upgrade your plan.\",\n\tPAYMENT_REQUIRED: 'Payment is required to continue.',\n\tINVALID_CREDENTIALS: 'Invalid email or password.',\n\tEMAIL_NOT_VERIFIED: 'Please verify your email address.',\n\tACCOUNT_LOCKED: 'Your account has been locked. Please contact support.',\n\tSESSION_EXPIRED: 'Your session has expired. Please sign in again.',\n}\n\nexport function getSafeErrorMessage(\n\terror: unknown,\n\tfallback = 'Something went wrong. Please try again.',\n): string {\n\tconst rawMessage = getErrorMessage(error, '')\n\tconst errorCode = getErrorCode(error)\n\n\tif (errorCode && SAFE_ERROR_MESSAGES[errorCode]) {\n\t\treturn SAFE_ERROR_MESSAGES[errorCode]\n\t}\n\n\tif (error && typeof error === 'object') {\n\t\tconst trpcError = error as { data?: { code?: string } }\n\t\tif (trpcError.data?.code && SAFE_ERROR_MESSAGES[trpcError.data.code]) {\n\t\t\treturn SAFE_ERROR_MESSAGES[trpcError.data.code]\n\t\t}\n\t}\n\n\tif (rawMessage && !INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(rawMessage))) {\n\t\tif (rawMessage.length < 200 && !rawMessage.includes('\\n') && !rawMessage.includes('at ')) {\n\t\t\treturn rawMessage\n\t\t}\n\t}\n\n\treturn fallback\n}\n\nexport function isChallengeRequired(err: unknown): boolean {\n\tif (err instanceof Error) {\n\t\tif (err.message.includes('challenge')) return true\n\n\t\tconst errWithData = err as { data?: { code?: string } }\n\t\tif (errWithData.data?.code === 'FORBIDDEN') return true\n\t}\n\treturn false\n}\n","/**\n * Sylphx Connection URL — Single Source of Truth (ADR-123)\n *\n * Implements the canonical connection string format defined in ADR-055 §5.\n * This module is the SDK-owned SSOT per ADR-123 (SDK/application boundary).\n * Consuming applications MUST import from `@sylphx/sdk` rather than duplicating\n * this logic.\n *\n * Hosted format:\n * sylphx://{credential}@{tenant-slug}.api.sylphx.com[:port][/v{version}]\n *\n * Custom/self-hosted domains are also accepted as long as the first DNS label\n * is the tenant slug.\n *\n * Examples:\n * sylphx://pk_prod_f19e5cdc3cc54f7ff81bdc26ec5bfbad@bold-river-a1b2c3.api.sylphx.com\n * sylphx://sk_prod_5120bfeb5120bfeb5120bfeb5120bfeb@bold-river-a1b2c3.api.sylphx.com/v1\n * sylphx://pk_dev_abc12345abc12345abc12345abc12345@calm-peak-z9x4d5.sylphx.dev\n *\n * Invariants:\n * - Protocol is always `sylphx:` (no exceptions)\n * - Credential matches `(pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}`\n * - Host's first DNS label is the resource slug (validated by slug regex)\n * - `apiBaseUrl` is always HTTPS, with `/v{version}` appended (default `v1`)\n *\n * Parsing uses the WHATWG `URL` constructor — custom regex parsing is banned\n * because it is notoriously brittle (ADR-055 §5.3).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ConnectionCredentialType = 'pk' | 'sk'\nexport type ConnectionEnv = 'dev' | 'stg' | 'prod' | 'prev'\n\nexport interface ParsedConnectionUrl {\n\t/** Full credential string, e.g. `pk_prod_f19e...` */\n\treadonly credential: string\n\t/** Credential kind — `pk` (publishable) or `sk` (secret) */\n\treadonly credentialType: ConnectionCredentialType\n\t/** Target environment encoded in the credential */\n\treadonly env: ConnectionEnv\n\t/** First DNS label of the host — the resource slug (e.g. `bold-river-a1b2c3`) */\n\treadonly slug: string\n\t/** Full host including port when present (e.g. `bold-river-a1b2c3.api.sylphx.com`) */\n\treadonly host: string\n\t/** Ready-to-use SDK base URL, always HTTPS (e.g. `https://bold-river-a1b2c3.api.sylphx.com/v1`) */\n\treadonly apiBaseUrl: string\n}\n\nexport interface BuildConnectionUrlInput {\n\t/** Credential — must match the credential format regex */\n\treadonly credential: string\n\t/** Resource slug — validated DNS label */\n\treadonly slug: string\n\t/** SDK API domain suffix; defaults to `api.sylphx.com`. Use `sylphx.dev` for dev. */\n\treadonly domain?: string\n\t/** API version suffix, e.g. `v1`. Defaults to `v1`. Pass empty string to omit. */\n\treadonly version?: string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SYLPHX_PROTOCOL = 'sylphx:'\nconst DEFAULT_DOMAIN = 'api.sylphx.com'\nconst DEFAULT_VERSION = 'v1'\n\n/**\n * Credential format — opaque token with type, env, optional project ref, and\n * hex payload. Ref-scoped credentials are emitted by Platform app-env injection;\n * legacy credentials without the ref remain valid for existing deploys.\n */\nexport const CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32,64}$/\n\n/** Version segment, e.g. `v1`, `v2`, `v10`. */\nconst VERSION_REGEX = /^v[0-9]+$/\n\n/**\n * Slug validation regex — RFC 1035 DNS label.\n * Lowercase letters, numbers, and hyphens; must start and end with alnum.\n * Length 1–63.\n */\nconst SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/\n\n// ============================================================================\n// Errors\n// ============================================================================\n\nexport class InvalidConnectionUrlError extends Error {\n\treadonly code = 'INVALID_CONNECTION_URL' as const\n\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = 'InvalidConnectionUrlError'\n\t\tObject.setPrototypeOf(this, InvalidConnectionUrlError.prototype)\n\t}\n}\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\nfunction fail(reason: string): never {\n\tthrow new InvalidConnectionUrlError(`Invalid Sylphx connection URL: ${reason}`)\n}\n\nfunction parseCredential(raw: string): {\n\tcredentialType: ConnectionCredentialType\n\tenv: ConnectionEnv\n} {\n\tconst match = CREDENTIAL_REGEX.exec(raw)\n\tif (!match) {\n\t\tfail(`credential must match (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}, got \"${raw}\"`)\n\t}\n\treturn {\n\t\tcredentialType: match[1] as ConnectionCredentialType,\n\t\tenv: match[2] as ConnectionEnv,\n\t}\n}\n\nfunction validateSlug(candidate: string): string {\n\tif (!candidate || candidate.length > 63 || !SLUG_REGEX.test(candidate)) {\n\t\tfail(`slug \"${candidate}\" is not a valid DNS label (lowercase alnum + hyphens, 1-63 chars)`)\n\t}\n\treturn candidate\n}\n\nfunction validateDomain(domain: string): string {\n\tif (!domain || domain.includes('/') || domain.includes('@') || domain.includes(' ')) {\n\t\tfail(`domain \"${domain}\" is not a valid hostname suffix`)\n\t}\n\t// Reject schemes — domain is just a hostname suffix\n\tif (domain.includes('://')) {\n\t\tfail(`domain \"${domain}\" must not contain a scheme`)\n\t}\n\treturn domain.toLowerCase()\n}\n\nfunction normaliseVersion(version: string | undefined): string {\n\tif (version === undefined) return DEFAULT_VERSION\n\tif (version === '') return ''\n\tif (!VERSION_REGEX.test(version)) {\n\t\tfail(`version \"${version}\" must match /^v[0-9]+$/`)\n\t}\n\treturn version\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Build a canonical Sylphx connection URL.\n *\n * Throws `InvalidConnectionUrlError` if any component is malformed.\n */\nexport function buildConnectionUrl(input: BuildConnectionUrlInput): string {\n\tconst { credential, slug, domain = DEFAULT_DOMAIN, version = DEFAULT_VERSION } = input\n\n\tparseCredential(credential)\n\tconst safeSlug = validateSlug(slug)\n\tconst safeDomain = validateDomain(domain)\n\tconst safeVersion = normaliseVersion(version)\n\n\tconst host = `${safeSlug}.${safeDomain}`\n\tconst pathSuffix = safeVersion ? `/${safeVersion}` : ''\n\treturn `${SYLPHX_PROTOCOL}//${credential}@${host}${pathSuffix}`\n}\n\n/**\n * Parse a Sylphx connection URL into its structured components.\n *\n * Throws `InvalidConnectionUrlError` on any structural problem.\n */\nexport function parseConnectionUrl(url: string): ParsedConnectionUrl {\n\tif (typeof url !== 'string' || url.length === 0) {\n\t\tfail('url must be a non-empty string')\n\t}\n\n\tlet parsed: URL\n\ttry {\n\t\tparsed = new URL(url)\n\t} catch {\n\t\tfail(`not a valid URL: \"${url}\"`)\n\t}\n\n\tif (parsed.protocol !== SYLPHX_PROTOCOL) {\n\t\tfail(`protocol must be \"sylphx:\", got \"${parsed.protocol}\"`)\n\t}\n\n\t// URL parses `sylphx://cred@host/path` but places `cred` in `username` only\n\t// when the authority is parsed. For non-special schemes, browsers/Node parse\n\t// the authority uniformly — we defensively read both `username` and `host`.\n\tconst credential = decodeURIComponent(parsed.username)\n\tif (!credential) {\n\t\tfail('missing credential (expected `sylphx://<credential>@<host>`)')\n\t}\n\tif (parsed.password) {\n\t\tfail('connection URL must not contain a password component')\n\t}\n\n\tconst { credentialType, env } = parseCredential(credential)\n\n\tconst host = parsed.host\n\tif (!host) {\n\t\tfail('missing host')\n\t}\n\n\t// Extract slug from the first DNS label\n\tconst hostname = parsed.hostname\n\tconst firstDot = hostname.indexOf('.')\n\tif (firstDot <= 0) {\n\t\tfail(`host \"${hostname}\" must contain at least one dot (slug.domain)`)\n\t}\n\tconst slugCandidate = hostname.slice(0, firstDot)\n\tconst domainSuffix = hostname.slice(firstDot + 1)\n\tif (!domainSuffix) {\n\t\tfail(`host \"${hostname}\" has empty domain suffix`)\n\t}\n\tconst slug = validateSlug(slugCandidate)\n\n\t// Path is either empty, `/`, or `/v{N}`\n\tconst rawPath = parsed.pathname.replace(/^\\/+/, '').replace(/\\/+$/, '')\n\tlet version = DEFAULT_VERSION\n\tif (rawPath !== '') {\n\t\tif (!VERSION_REGEX.test(rawPath)) {\n\t\t\tfail(`path \"${parsed.pathname}\" must be empty or match /v{N}`)\n\t\t}\n\t\tversion = rawPath\n\t}\n\n\tif (parsed.search) {\n\t\tfail('connection URL must not contain a query string')\n\t}\n\tif (parsed.hash) {\n\t\tfail('connection URL must not contain a fragment')\n\t}\n\n\tconst apiBaseUrl = `https://${host}/${version}`\n\n\treturn {\n\t\tcredential,\n\t\tcredentialType,\n\t\tenv,\n\t\tslug,\n\t\thost,\n\t\tapiBaseUrl,\n\t}\n}\n","/**\n * SDK Configuration — ADR-055 Connection URL API\n *\n * v0.5.0: The primary entry point is `createClient(url)` which accepts\n * a `sylphx://` connection URL. The old `createConfig({ ref, publicKey })`\n * API is removed.\n *\n * @example\n * ```typescript\n * import { createClient } from '@sylphx/sdk'\n *\n * const sylphx = createClient(process.env.SYLPHX_URL!)\n * // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n */\n\nimport {\n\tCREDENTIAL_REGEX,\n\tInvalidConnectionUrlError,\n\ttype ParsedConnectionUrl,\n\tparseConnectionUrl,\n} from './connection-url'\nimport { DEFAULT_SDK_API_HOST, DEFAULT_TIMEOUT_MS } from './constants'\nimport {\n\tNetworkError,\n\tRateLimitError,\n\tSylphxError,\n\ttype SylphxErrorCode,\n\tTimeoutError,\n} from './errors'\n\nexport type {\n\tBuildConnectionUrlInput,\n\tConnectionCredentialType,\n\tConnectionEnv,\n\tParsedConnectionUrl,\n} from './connection-url'\n// Re-export connection URL primitives for consumers (ADR-123 SSOT)\nexport {\n\tbuildConnectionUrl,\n\tCREDENTIAL_REGEX,\n\tInvalidConnectionUrlError,\n\tparseConnectionUrl,\n} from './connection-url'\n\n// =============================================================================\n// Legacy format detection — produces clear migration errors\n// =============================================================================\n\n/** Matches a bare ref-scoped credential mistakenly passed without the `sylphx://` URL wrapper. */\nconst LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/\n\n/** Matches old app_* format: app_{env}_{anything} */\nconst LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/\n\nconst MIGRATION_MESSAGE =\n\t'API key format has changed. Use a sylphx:// connection URL instead.\\n\\n' +\n\t'New format: sylphx://pk_prod_{ref?}_{hex}@your-slug.api.sylphx.com\\n\\n' +\n\t'Generate new credentials from the Sylphx Console → Your App → Environments.\\n' +\n\t'See https://docs.sylphx.com/migration for details.'\n\n/**\n * Detect legacy key formats and throw a helpful migration error.\n */\nfunction rejectLegacyKeyFormat(input: string): void {\n\tconst trimmed = input.trim().toLowerCase()\n\n\tif (LEGACY_APP_KEY_PATTERN.test(trimmed)) {\n\t\tthrow new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: 'BAD_REQUEST' })\n\t}\n\n\tif (LEGACY_EMBEDDED_REF_PATTERN.test(trimmed)) {\n\t\tthrow new SylphxError(`[Sylphx] ${MIGRATION_MESSAGE}`, { code: 'BAD_REQUEST' })\n\t}\n}\n\n// =============================================================================\n// Config types\n// =============================================================================\n\n/**\n * SDK Configuration object — immutable, frozen.\n *\n * Created by `createClient()` or `createServerClient()`.\n * Passed to all pure SDK functions (`track()`, `signIn()`, etc.).\n */\nexport interface SylphxConfig {\n\t/** The credential string (pk_* or sk_*) */\n\treadonly credential: string\n\t/** Credential type: 'pk' (publishable) or 'sk' (secret) */\n\treadonly credentialType: 'pk' | 'sk'\n\t/** Target environment: dev, stg, prod, or prev */\n\treadonly env: 'dev' | 'stg' | 'prod' | 'prev'\n\t/** Resource slug (first DNS label), e.g. 'bold-river-a1b2c3' */\n\treadonly slug: string\n\t/** Pre-computed API base URL, e.g. 'https://bold-river-a1b2c3.api.sylphx.com/v1' */\n\treadonly baseUrl: string\n\t/** Optional access token for authenticated requests */\n\treadonly accessToken?: string\n\t/**\n\t * Secret key — populated when credentialType is 'sk'.\n\t * Backward-compatible alias for `credential` when credential is sk_*.\n\t */\n\treadonly secretKey?: string\n\t/**\n\t * Publishable key — populated when credentialType is 'pk'.\n\t * Backward-compatible alias for `credential` when credential is pk_*.\n\t */\n\treadonly publicKey?: string\n\t/**\n\t * @deprecated Use `slug`. Backward-compatible alias.\n\t */\n\treadonly ref: string\n}\n\n/**\n * Explicit components input — alternative to connection URL string.\n *\n * For multi-tenant apps or cases where components are stored separately.\n */\nexport interface SylphxClientInput {\n\t/** Resource slug, e.g. 'bold-river-a1b2c3' */\n\tslug?: string\n\t/** Publishable key (pk_*) — client-safe */\n\tpublicKey?: string\n\t/** Secret key (sk_*) — server-side only */\n\tsecretKey?: string\n\t/** Optional access token */\n\taccessToken?: string\n\t/** API domain override (default: api.sylphx.com) */\n\tdomain?: string\n\t/**\n\t * @deprecated Use `slug`. Accepted for backward compatibility during migration.\n\t */\n\tref?: string\n\t/**\n\t * @deprecated Use `domain`. Accepted for backward compatibility during migration.\n\t */\n\tplatformUrl?: string\n}\n\n/**\n * Build a frozen SylphxConfig with backward-compat fields (secretKey, publicKey, ref).\n */\nfunction freezeConfig(opts: {\n\tcredential: string\n\tcredentialType: 'pk' | 'sk'\n\tenv: 'dev' | 'stg' | 'prod' | 'prev'\n\tslug: string\n\tbaseUrl: string\n\taccessToken?: string\n}): SylphxConfig {\n\treturn Object.freeze({\n\t\tcredential: opts.credential,\n\t\tcredentialType: opts.credentialType,\n\t\tenv: opts.env,\n\t\tslug: opts.slug,\n\t\tbaseUrl: opts.baseUrl,\n\t\taccessToken: opts.accessToken,\n\t\t// Backward-compat aliases\n\t\tsecretKey: opts.credentialType === 'sk' ? opts.credential : undefined,\n\t\tpublicKey: opts.credentialType === 'pk' ? opts.credential : undefined,\n\t\tref: opts.slug,\n\t})\n}\n\n// =============================================================================\n// createClient — primary entry point (ADR-055)\n// =============================================================================\n\n/**\n * Create a Sylphx client from a connection URL or explicit components.\n *\n * This is the primary SDK entry point for client-side (browser) usage.\n * Accepts a `sylphx://` connection URL or an explicit components object.\n *\n * @example Connection URL (recommended)\n * ```typescript\n * const sylphx = createClient(process.env.NEXT_PUBLIC_SYLPHX_URL!)\n * // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n *\n * @example Explicit components\n * ```typescript\n * const sylphx = createClient({\n * slug: 'bold-river-a1b2c3',\n * publicKey: 'pk_prod_f19e...',\n * })\n * ```\n */\nexport function createClient(input: string | SylphxClientInput): SylphxConfig {\n\tif (typeof input === 'string') {\n\t\treturn createConfigFromUrl(input)\n\t}\n\treturn createConfigFromComponents(input)\n}\n\n/**\n * Create a Sylphx server client from a connection URL or explicit components.\n *\n * Equivalent to `createClient()` but validates that a secret key (sk_*) is provided.\n * Use this for server-side operations that require elevated permissions.\n *\n * @example Connection URL (recommended)\n * ```typescript\n * const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)\n * // Parses: sylphx://sk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com\n * ```\n *\n * @example Explicit components\n * ```typescript\n * const sylphx = createServerClient({\n * slug: 'bold-river-a1b2c3',\n * secretKey: 'sk_prod_5120...',\n * })\n * ```\n */\nexport function createServerClient(input: string | SylphxClientInput): SylphxConfig {\n\tconst config = createClient(input)\n\n\tif (config.credentialType !== 'sk') {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] createServerClient() requires a secret key (sk_*). ' +\n\t\t\t\t'Use a SYLPHX_SECRET_URL with an sk_ credential, or pass { secretKey } in the components object.',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\treturn config\n}\n\n// =============================================================================\n// Internal construction\n// =============================================================================\n\nfunction createConfigFromUrl(url: string): SylphxConfig {\n\tif (!url || typeof url !== 'string') {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] Connection URL is required. Set SYLPHX_URL or NEXT_PUBLIC_SYLPHX_URL environment variable.\\n\\n' +\n\t\t\t\t'Format: sylphx://pk_prod_{hex}@your-slug.api.sylphx.com',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\tconst trimmed = url.trim()\n\n\t// Detect legacy key formats passed as the URL string\n\trejectLegacyKeyFormat(trimmed)\n\n\t// If someone passes a bare key instead of a URL, give a helpful error\n\tif (!trimmed.startsWith('sylphx://')) {\n\t\t// Check if it looks like a new-format bare credential\n\t\tif (CREDENTIAL_REGEX.test(trimmed)) {\n\t\t\tthrow new SylphxError(\n\t\t\t\t'[Sylphx] Received a bare credential instead of a connection URL.\\n\\n' +\n\t\t\t\t\t'Wrap it in a connection URL: sylphx://<credential>@<slug>.api.sylphx.com\\n' +\n\t\t\t\t\t'Or use createClient({ slug, publicKey }) for explicit components.',\n\t\t\t\t{ code: 'BAD_REQUEST' },\n\t\t\t)\n\t\t}\n\t\tthrow new SylphxError(\n\t\t\t`[Sylphx] Invalid connection URL — must start with \"sylphx://\". Got: \"${trimmed.slice(0, 30)}...\"`,\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\tlet parsed: ParsedConnectionUrl\n\ttry {\n\t\tparsed = parseConnectionUrl(trimmed)\n\t} catch (err) {\n\t\tif (err instanceof InvalidConnectionUrlError) {\n\t\t\tthrow new SylphxError(err.message, { code: 'BAD_REQUEST', cause: err })\n\t\t}\n\t\tthrow err\n\t}\n\n\treturn freezeConfig({\n\t\tcredential: parsed.credential,\n\t\tcredentialType: parsed.credentialType,\n\t\tenv: parsed.env,\n\t\tslug: parsed.slug,\n\t\tbaseUrl: parsed.apiBaseUrl,\n\t})\n}\n\nfunction createConfigFromComponents(input: SylphxClientInput): SylphxConfig {\n\tconst credential = input.secretKey || input.publicKey\n\tif (!credential) {\n\t\tthrow new SylphxError('[Sylphx] Either publicKey or secretKey must be provided.', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\t// Accept deprecated `ref` as fallback for `slug` during migration\n\tconst resolvedSlug = input.slug || input.ref\n\n\tif (!resolvedSlug) {\n\t\tthrow new SylphxError('[Sylphx] slug is required when using explicit components.', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\t// Try to validate as ADR-055 credential first; if invalid, check for legacy\n\tconst trimmedCred = credential.trim().toLowerCase()\n\n\tif (CREDENTIAL_REGEX.test(trimmedCred)) {\n\t\t// Connection credential: pk/sk_{env}_{optional ref}_{hex}\n\t\tconst match = CREDENTIAL_REGEX.exec(trimmedCred)!\n\t\tconst credentialType = match[1] as 'pk' | 'sk'\n\t\tconst env = match[2] as 'dev' | 'stg' | 'prod' | 'prev'\n\n\t\tconst slug = resolvedSlug.trim().toLowerCase()\n\t\t// Default to `api.sylphx.com` (DNS-only) — `sylphx.com` is Cloudflare-\n\t\t// proxied with no per-slug origin mapping and returns 404 for any\n\t\t// `<slug>.sylphx.com` request. The legacy components path below\n\t\t// (4-segment credentials) already defaults to `api.sylphx.com`; this\n\t\t// brings the new-format path in line so both behave consistently and\n\t\t// match the ADR-055 SYLPHX_URL injected by the platform.\n\t\tconst domain = input.domain?.trim() || DEFAULT_SDK_API_HOST\n\t\tconst baseUrl = `https://${slug}.${domain}/v1`\n\n\t\treturn freezeConfig({\n\t\t\tcredential: trimmedCred,\n\t\t\tcredentialType,\n\t\t\tenv,\n\t\t\tslug,\n\t\t\tbaseUrl,\n\t\t\taccessToken: input.accessToken,\n\t\t})\n\t}\n\n\t// Backward compat: accept old-format keys (pk_{env}_{ref}_{hex} or sk_{env}_{ref}_{hex})\n\t// These have 4 underscore-delimited segments. Extract env from segment 1.\n\tconst parts = trimmedCred.split('_')\n\tconst prefix = parts[0] as 'pk' | 'sk'\n\tif ((prefix === 'pk' || prefix === 'sk') && parts.length >= 3) {\n\t\tconst envSegment = parts[1]\n\t\tconst validEnvs = ['dev', 'stg', 'prod', 'prev']\n\t\tconst env = validEnvs.includes(envSegment)\n\t\t\t? (envSegment as 'dev' | 'stg' | 'prod' | 'prev')\n\t\t\t: 'prod'\n\n\t\tconst slug = resolvedSlug.trim().toLowerCase()\n\t\t// Accept deprecated platformUrl for base URL construction\n\t\tlet baseUrl: string\n\t\tif (input.platformUrl) {\n\t\t\tconst platform = input.platformUrl.trim().replace(/\\/$/, '')\n\t\t\tbaseUrl = platform.includes('/v1') ? platform : `${platform}/v1`\n\t\t} else {\n\t\t\tconst domain = input.domain?.trim() || DEFAULT_SDK_API_HOST\n\t\t\tbaseUrl = `https://${slug}.${domain}/v1`\n\t\t}\n\n\t\treturn freezeConfig({\n\t\t\tcredential: trimmedCred,\n\t\t\tcredentialType: prefix,\n\t\t\tenv,\n\t\t\tslug,\n\t\t\tbaseUrl,\n\t\t\taccessToken: input.accessToken,\n\t\t})\n\t}\n\n\tthrow new SylphxError(\n\t\t`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}. Got: \"${trimmedCred.slice(0, 30)}...\"`,\n\t\t{ code: 'BAD_REQUEST' },\n\t)\n}\n\n// =============================================================================\n// Config utilities\n// =============================================================================\n\n/**\n * Create a new config with an updated access token.\n *\n * Returns a new frozen config — does not mutate the original.\n *\n * @example\n * ```typescript\n * const authenticatedConfig = withToken(config, 'access_token_here')\n * ```\n */\nexport function withToken(config: SylphxConfig, accessToken: string): SylphxConfig {\n\treturn Object.freeze({\n\t\t...config,\n\t\taccessToken,\n\t})\n}\n\n// =============================================================================\n// Backward compatibility — createConfig (deprecated, will be removed)\n// =============================================================================\n\n/**\n * @deprecated Use `createClient()` or `createServerClient()` instead.\n * This function is kept temporarily for migration but will be removed.\n */\nexport type SylphxConfigInput = string | SylphxClientInput\n\n/**\n * @deprecated Use `createClient()` instead. See ADR-055.\n */\nexport const createConfig = createClient\n\n// =============================================================================\n// Request helpers — internal SDK plumbing\n// =============================================================================\n\n/**\n * Map HTTP status code to SylphxErrorCode\n */\nfunction httpStatusToErrorCode(status: number): SylphxErrorCode {\n\tswitch (status) {\n\t\tcase 400:\n\t\t\treturn 'BAD_REQUEST'\n\t\tcase 401:\n\t\t\treturn 'UNAUTHORIZED'\n\t\tcase 403:\n\t\t\treturn 'FORBIDDEN'\n\t\tcase 404:\n\t\t\treturn 'NOT_FOUND'\n\t\tcase 409:\n\t\t\treturn 'CONFLICT'\n\t\tcase 413:\n\t\t\treturn 'PAYLOAD_TOO_LARGE'\n\t\tcase 422:\n\t\t\treturn 'UNPROCESSABLE_ENTITY'\n\t\tcase 429:\n\t\t\treturn 'TOO_MANY_REQUESTS'\n\t\tcase 500:\n\t\t\treturn 'INTERNAL_SERVER_ERROR'\n\t\tcase 501:\n\t\t\treturn 'NOT_IMPLEMENTED'\n\t\tcase 502:\n\t\t\treturn 'BAD_GATEWAY'\n\t\tcase 503:\n\t\t\treturn 'SERVICE_UNAVAILABLE'\n\t\tcase 504:\n\t\t\treturn 'GATEWAY_TIMEOUT'\n\t\tdefault:\n\t\t\treturn status >= 500 ? 'INTERNAL_SERVER_ERROR' : 'BAD_REQUEST'\n\t}\n}\n\n/**\n * Internal: Build headers for API requests.\n *\n * Sends the credential as `x-app-secret` header.\n */\nexport function buildHeaders(config: SylphxConfig): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t}\n\n\tif (config.credential) {\n\t\theaders['x-app-secret'] = config.credential\n\t}\n\tif (config.accessToken) {\n\t\theaders.Authorization = `Bearer ${config.accessToken}`\n\t}\n\n\treturn headers\n}\n\n/**\n * Internal: Build REST API URL.\n *\n * Appends path to the pre-computed base URL.\n */\nexport function buildApiUrl(config: SylphxConfig, path: string): string {\n\tconst base = config.baseUrl.replace(/\\/$/, '')\n\tconst cleanPath = path.startsWith('/') ? path : `/${path}`\n\treturn `${base}${cleanPath}`\n}\n\n/**\n * Internal: Call REST API endpoint.\n *\n * Features:\n * - Request timeout (default 30s) prevents infinite hangs\n * - Proper HTTP status code mapping to error codes\n * - Safe JSON parsing with error handling\n * - Idempotency key support (Stripe pattern)\n */\nexport async function callApi<TOutput>(\n\tconfig: SylphxConfig,\n\tpath: string,\n\toptions: {\n\t\tmethod?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\t\tbody?: unknown\n\t\tquery?: Record<string, string | number | boolean | undefined>\n\t\ttimeout?: number\n\t\tsignal?: AbortSignal\n\t\tidempotencyKey?: string\n\t\theaders?: Record<string, string>\n\t} = {},\n): Promise<TOutput> {\n\tconst {\n\t\tmethod = 'GET',\n\t\tbody,\n\t\tquery,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t\tsignal,\n\t\tidempotencyKey,\n\t\theaders: extraHeaders,\n\t} = options\n\n\tlet url = buildApiUrl(config, path)\n\n\t// Add query parameters\n\tif (query) {\n\t\tconst params = new URLSearchParams()\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tparams.set(key, String(value))\n\t\t\t}\n\t\t}\n\t\tconst queryString = params.toString()\n\t\tif (queryString) {\n\t\t\turl += `?${queryString}`\n\t\t}\n\t}\n\n\t// Create AbortController for timeout\n\tconst controller = new AbortController()\n\tconst timeoutId = setTimeout(() => controller.abort(), timeout)\n\n\t// Combine user signal with timeout signal\n\tconst combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal\n\n\tconst headers = buildHeaders(config)\n\n\t// Add idempotency key header for safe retries (Stripe pattern)\n\tif (idempotencyKey) {\n\t\theaders['Idempotency-Key'] = idempotencyKey\n\t}\n\n\t// Merge user-supplied headers\n\tif (extraHeaders) {\n\t\tfor (const [k, v] of Object.entries(extraHeaders)) {\n\t\t\theaders[k] = v\n\t\t}\n\t}\n\n\tconst fetchOptions: RequestInit = {\n\t\tmethod,\n\t\theaders,\n\t\tsignal: combinedSignal,\n\t}\n\n\tif (body) {\n\t\tfetchOptions.body = JSON.stringify(body)\n\t}\n\n\tlet response: Response\n\ttry {\n\t\tresponse = await fetch(url, fetchOptions)\n\t} catch (error) {\n\t\tclearTimeout(timeoutId)\n\n\t\t// Handle abort/timeout\n\t\tif (error instanceof Error) {\n\t\t\tif (error.name === 'AbortError') {\n\t\t\t\t// Check if it was our timeout or user cancellation\n\t\t\t\tif (controller.signal.aborted && !signal?.aborted) {\n\t\t\t\t\tthrow new TimeoutError(timeout)\n\t\t\t\t}\n\t\t\t\tthrow new SylphxError('Request aborted', {\n\t\t\t\t\tcode: 'ABORTED',\n\t\t\t\t\tcause: error,\n\t\t\t\t})\n\t\t\t}\n\t\t\t// Network errors\n\t\t\tthrow new NetworkError(error.message, { cause: error })\n\t\t}\n\t\tthrow new NetworkError('Network request failed')\n\t} finally {\n\t\tclearTimeout(timeoutId)\n\t}\n\n\tif (!response.ok) {\n\t\tconst errorBody = await response.text().catch(() => '')\n\t\tlet errorMessage = 'Request failed'\n\t\tlet errorData: Record<string, unknown> | undefined\n\n\t\t// Safe JSON parsing\n\t\tif (errorBody) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(errorBody) as {\n\t\t\t\t\terror?: { message?: string }\n\t\t\t\t\tmessage?: string\n\t\t\t\t}\n\t\t\t\terrorMessage = parsed.error?.message ?? parsed.message ?? errorMessage\n\t\t\t\terrorData = parsed.error as Record<string, unknown> | undefined\n\t\t\t} catch {\n\t\t\t\terrorMessage = response.statusText || errorMessage\n\t\t\t}\n\t\t}\n\n\t\tconst errorCode = httpStatusToErrorCode(response.status)\n\n\t\t// Extract rate limit headers (Stripe SDK pattern)\n\t\tconst retryAfterHeader = response.headers.get('Retry-After')\n\t\tconst rateLimitLimit = response.headers.get('X-RateLimit-Limit')\n\t\tconst rateLimitRemaining = response.headers.get('X-RateLimit-Remaining')\n\t\tconst rateLimitReset = response.headers.get('X-RateLimit-Reset')\n\n\t\tconst retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : undefined\n\n\t\t// Use specialized RateLimitError for 429 responses\n\t\tif (response.status === 429) {\n\t\t\tthrow new RateLimitError(errorMessage || 'Too many requests', {\n\t\t\t\tstatus: response.status,\n\t\t\t\tdata: errorData,\n\t\t\t\tretryAfter,\n\t\t\t\tlimit: rateLimitLimit ? Number.parseInt(rateLimitLimit, 10) : undefined,\n\t\t\t\tremaining: rateLimitRemaining ? Number.parseInt(rateLimitRemaining, 10) : undefined,\n\t\t\t\tresetAt: rateLimitReset ? Number.parseInt(rateLimitReset, 10) : undefined,\n\t\t\t})\n\t\t}\n\n\t\tthrow new SylphxError(errorMessage, {\n\t\t\tcode: errorCode,\n\t\t\tstatus: response.status,\n\t\t\tdata: errorData,\n\t\t\tretryAfter,\n\t\t})\n\t}\n\n\t// Handle empty responses (204 No Content)\n\tconst text = await response.text()\n\tif (!text) {\n\t\treturn {} as TOutput\n\t}\n\n\t// Safe JSON parsing for response body\n\ttry {\n\t\treturn JSON.parse(text) as TOutput\n\t} catch (error) {\n\t\tthrow new SylphxError('Failed to parse response', {\n\t\t\tcode: 'PARSE_ERROR',\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\tdata: { body: text.slice(0, 200) },\n\t\t})\n\t}\n}\n","/**\n * CSV utilities for browser and server SDK consumers.\n */\n\n/**\n * Escape a CSV field to handle commas, quotes, and newlines.\n *\n * Handles null/undefined by returning an empty field. Wraps values containing\n * RFC 4180 special characters in double quotes and escapes internal quotes.\n */\nexport function escapeCsvField(value: string | null | undefined): string {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n\t\treturn `\"${value.replace(/\"/g, '\"\"')}\"`\n\t}\n\treturn value\n}\n","/**\n * Formatting Utilities\n *\n * Shared formatting functions for consistent display across the project.\n */\n\n// ==========================================\n// Number Formatting\n// ==========================================\n\n/**\n * Calculate percentage with consistent rounding.\n * SSOT for all success rate, completion rate calculations.\n *\n * @param count - The numerator (e.g., successful count)\n * @param total - The denominator (e.g., total count)\n * @param decimals - Number of decimal places (default: 2)\n * @returns Percentage value (0-100)\n *\n * @example\n * ```ts\n * calculatePercentage(75, 100) // 75\n * calculatePercentage(1, 3) // 33.33\n * calculatePercentage(0, 0) // 100 (safe division)\n * ```\n */\nexport function calculatePercentage(count: number, total: number, decimals = 2): number {\n\tif (total === 0) return 100 // Default to 100% when no data\n\tconst multiplier = 10 ** (decimals + 2)\n\treturn Math.round((count / total) * multiplier) / 10 ** decimals\n}\n\n// ==========================================\n// Currency Formatting\n// ==========================================\n\n/**\n * Format microdollars to currency string\n * @param microdollars Amount in microdollars (1 dollar = 1,000,000 microdollars)\n * @param options Intl.NumberFormat options\n */\nexport function formatMicrodollars(\n\tmicrodollars: number,\n\toptions?: Intl.NumberFormatOptions,\n): string {\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency: 'USD',\n\t\t...options,\n\t}).format(microdollars / 1_000_000)\n}\n\n/**\n * Format cents to currency string\n * @param cents Amount in cents (100 cents = 1 dollar)\n *\n * @example\n * ```ts\n * formatCents(1999) // \"$19.99\"\n * formatCents(100) // \"$1.00\"\n * ```\n */\nexport function formatCents(cents: number): string {\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency: 'USD',\n\t}).format(cents / 100)\n}\n\n/**\n * Format dollars to currency string with optional compact notation\n * @param amount Amount in dollars\n * @param compact Use compact notation for large amounts (default: false)\n *\n * @example\n * ```ts\n * formatCurrency(1999.99) // \"$1,999.99\"\n * formatCurrency(1999.99, true) // \"$2.0K\"\n * formatCurrency(1999.99, { currency: 'EUR' }) // \"€1,999.99\"\n * formatCurrency(1999.99, { compact: true }) // \"$2.0K\"\n * ```\n *\n * Second argument accepts either a bare `boolean` (back-compat for\n * the historical `compact` flag) or an options object with\n * `{ currency, compact }`. The options form is required for any UI\n * surface that displays multi-currency amounts (billing, invoices,\n * usage statements) — previously two local copies of this function\n * lived in `billing-management.tsx` to work around the missing\n * currency parameter.\n */\nexport function formatCurrency(\n\tamount: number,\n\toptsOrCompact: boolean | { compact?: boolean; currency?: string; decimals?: number } = false,\n): string {\n\tconst opts = typeof optsOrCompact === 'boolean' ? { compact: optsOrCompact } : optsOrCompact\n\tconst currency = (opts.currency ?? 'USD').toUpperCase()\n\tconst decimals = opts.decimals ?? 2\n\tif (opts.compact && Math.abs(amount) >= 1000) {\n\t\treturn new Intl.NumberFormat('en-US', {\n\t\t\tstyle: 'currency',\n\t\t\tcurrency,\n\t\t\tnotation: 'compact',\n\t\t\tminimumFractionDigits: 1,\n\t\t\tmaximumFractionDigits: 1,\n\t\t}).format(amount)\n\t}\n\treturn new Intl.NumberFormat('en-US', {\n\t\tstyle: 'currency',\n\t\tcurrency,\n\t\tminimumFractionDigits: decimals,\n\t\tmaximumFractionDigits: decimals,\n\t}).format(amount)\n}\n\n/**\n * Format percentage with sign for trend display\n * @param value Percentage value (not multiplied by 100)\n *\n * @example\n * ```ts\n * formatPercent(12.5) // \"+12.5%\"\n * formatPercent(-5.2) // \"-5.2%\"\n * formatPercent(0) // \"+0.0%\"\n * ```\n */\nexport function formatPercent(value: number): string {\n\treturn `${value >= 0 ? '+' : ''}${value.toFixed(1)}%`\n}\n\n/**\n * Format number with abbreviated suffix (K, M, B) or compact notation\n * @param num Number to format\n * @param compact Use Intl compact notation (default: false, uses K/M/B suffix)\n *\n * @example\n * ```ts\n * formatNumber(1234) // \"1,234\"\n * formatNumber(1234567) // \"1.2M\"\n * formatNumber(1234, true) // \"1.2K\" (Intl compact)\n * ```\n */\nexport function formatNumber(num: number, compact = false): string {\n\tif (compact && num >= 1000) {\n\t\treturn new Intl.NumberFormat('en-US', {\n\t\t\tnotation: 'compact',\n\t\t\tmaximumFractionDigits: 1,\n\t\t}).format(num)\n\t}\n\tif (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)}B`\n\tif (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`\n\tif (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`\n\treturn num.toLocaleString()\n}\n\n/**\n * Format duration in milliseconds to human-readable string.\n * SSOT for latency display in traces, performance, and monitoring.\n *\n * @param ms Duration in milliseconds\n * @returns Formatted string (e.g., \"<1ms\", \"42ms\", \"1.23s\")\n *\n * @example\n * ```ts\n * formatDuration(0.5) // \"<1ms\"\n * formatDuration(42) // \"42ms\"\n * formatDuration(1500) // \"1.50s\"\n * ```\n */\nexport function formatDuration(ms: number): string {\n\tif (ms < 1) return '<1ms'\n\tif (ms < 1000) return `${Math.round(ms)}ms`\n\treturn `${(ms / 1000).toFixed(2)}s`\n}\n\n/**\n * Format bytes to human-readable string\n * @param bytes Number of bytes\n * @param decimals Number of decimal places (default: 1)\n */\nexport function formatBytes(bytes: number | null | undefined, decimals = 1): string {\n\tif (bytes == null || bytes === 0 || Number.isNaN(bytes)) return '0 B'\n\tconst k = 1024\n\tconst sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k))\n\treturn `${Number.parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`\n}\n\n// ==========================================\n// Status Badge Variants (Declarative)\n// ==========================================\n\n/** Badge variant type for consistency */\ntype BadgeVariant = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'outline'\n\n/**\n * Billing status to badge variant mapping.\n * Declarative configuration — add new statuses here.\n */\nconst BILLING_STATUS_VARIANTS: ReadonlyMap<string, BadgeVariant> = new Map([\n\t// Active/healthy states\n\t['healthy', 'default'],\n\t['active', 'default'],\n\t// Pending/low states\n\t['low', 'secondary'],\n\t['pending', 'secondary'],\n\t// Success states\n\t['paid', 'success'],\n\t['completed', 'success'],\n\t// Warning states\n\t['grace_period', 'warning'],\n\t['overdue', 'warning'],\n\t// Error states\n\t['critical', 'error'],\n\t['blocked', 'error'],\n\t['suspended', 'error'],\n\t['failed', 'error'],\n])\n\n/**\n * Get billing status badge variant.\n * Pure function — no side effects, deterministic output.\n *\n * @param status Billing account status\n * @returns Badge variant for display\n */\nexport function getBillingStatusVariant(status: string): BadgeVariant {\n\treturn BILLING_STATUS_VARIANTS.get(status) ?? 'outline'\n}\n\n/**\n * Invoice status to badge variant mapping.\n * Declarative configuration — add new statuses here.\n */\nconst INVOICE_STATUS_VARIANTS: ReadonlyMap<string, BadgeVariant> = new Map([\n\t['draft', 'secondary'],\n\t['pending', 'default'],\n\t['paid', 'success'],\n\t['overdue', 'warning'],\n\t['failed', 'error'],\n\t['cancelled', 'error'],\n])\n\n/**\n * Get invoice status badge variant.\n * Pure function — no side effects, deterministic output.\n *\n * @param status Invoice status\n * @returns Badge variant for display\n */\nexport function getInvoiceStatusVariant(status: string): BadgeVariant {\n\treturn INVOICE_STATUS_VARIANTS.get(status) ?? 'outline'\n}\n\n/**\n * Safely parse a date value, returning null for invalid dates.\n */\nfunction parseDate(date: Date | string): Date | null {\n\tconst d = typeof date === 'string' ? new Date(date) : date\n\t// Check for invalid date (NaN check on timestamp)\n\treturn Number.isNaN(d.getTime()) ? null : d\n}\n\n/**\n * Format date for display\n * @param date Date to format (null returns fallback)\n * @param options Intl.DateTimeFormat options\n * @param fallback Value to return when date is null (default: '-')\n */\nexport function formatDate(\n\tdate: Date | string | null,\n\toptions?: Intl.DateTimeFormatOptions,\n\tfallback = '-',\n): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleDateString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: 'numeric',\n\t\t...options,\n\t})\n}\n\n/**\n * Format date with time for display\n * @param date Date to format (null returns fallback)\n * @param options Override options\n * @param fallback Value to return when date is null (default: '-')\n */\nexport function formatDateTime(\n\tdate: Date | string | null,\n\toptions?: Intl.DateTimeFormatOptions,\n\tfallback = '-',\n): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleString('en-US', {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\thour: '2-digit',\n\t\tminute: '2-digit',\n\t\t...options,\n\t})\n}\n\n/** Relative time formatter using native Intl API */\nconst relativeTimeFormatter = new Intl.RelativeTimeFormat('en', {\n\tnumeric: 'auto',\n})\n\n/** Time divisions for relative time calculation */\nconst TIME_DIVISIONS: { amount: number; unit: Intl.RelativeTimeFormatUnit }[] = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n]\n\n/**\n * Format relative time (e.g., \"2 hours ago\")\n *\n * Uses native Intl.RelativeTimeFormat for proper localization.\n *\n * @param date Date to format (null returns 'Never')\n */\nexport function formatRelativeTime(date: Date | string | null): string {\n\tif (!date) return 'Never'\n\tconst d = parseDate(date)\n\tif (!d) return 'Never'\n\tlet seconds = (d.getTime() - Date.now()) / 1000\n\n\tfor (const { amount, unit } of TIME_DIVISIONS) {\n\t\tif (Math.abs(seconds) < amount) {\n\t\t\treturn relativeTimeFormatter.format(Math.round(seconds), unit)\n\t\t}\n\t\tseconds /= amount\n\t}\n\n\t// Fallback (shouldn't reach here)\n\treturn formatDate(d)\n}\n\n/**\n * Format relative time in compact form (e.g., \"2h ago\", \"3d ago\").\n * SSOT for dense UI contexts: tables, feeds, badges.\n *\n * Uses short suffixes (s/m/h/d/w) instead of Intl.RelativeTimeFormat words.\n * For prose contexts, use {@link formatRelativeTime} instead.\n *\n * @param date Date to format (null returns 'Never')\n *\n * @example\n * ```ts\n * formatRelativeTimeShort(new Date()) // \"Just now\"\n * formatRelativeTimeShort('2024-01-01T00:00:00Z') // \"3d ago\"\n * formatRelativeTimeShort(null) // \"Never\"\n * ```\n */\nexport function formatRelativeTimeShort(date: Date | string | null): string {\n\tif (!date) return 'Never'\n\tconst d = parseDate(date)\n\tif (!d) return 'Never'\n\tconst diffMs = Date.now() - d.getTime()\n\tconst diffSecs = Math.floor(diffMs / 1000)\n\tconst diffMins = Math.floor(diffMs / 60_000)\n\tconst diffHours = Math.floor(diffMs / 3_600_000)\n\tconst diffDays = Math.floor(diffMs / 86_400_000)\n\n\tif (diffSecs < 10) return 'Just now'\n\tif (diffSecs < 60) return `${diffSecs}s ago`\n\tif (diffMins < 60) return `${diffMins}m ago`\n\tif (diffHours < 24) return `${diffHours}h ago`\n\tif (diffDays === 1) return 'Yesterday'\n\tif (diffDays < 7) return `${diffDays}d ago`\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`\n\treturn d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n}\n\n/**\n * Format month and year (e.g., \"January 2024\")\n * SSOT for billing period display, invoice headers.\n * @param date Date to format (null returns fallback)\n * @param fallback Value to return when date is null\n */\nexport function formatMonthYear(date: Date | string | null, fallback = '-'): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })\n}\n\n/**\n * Format time only (e.g., \"2:30 PM\")\n * SSOT for log timestamps, activity feeds.\n * @param date Date to format (null returns fallback)\n * @param fallback Value to return when date is null\n */\nexport function formatTime(date: Date | string | null, fallback = '-'): string {\n\tif (!date) return fallback\n\tconst d = parseDate(date)\n\tif (!d) return fallback\n\treturn d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })\n}\n","/**\n * Safely parse a JSON string, returning a fallback value on failure instead of throwing.\n *\n * Use this when malformed input is a normal (non-exceptional) case — e.g. parsing\n * user-provided data, localStorage values, or Redis cache entries where the caller\n * simply wants a default on failure.\n *\n * For cases where parse failure is truly exceptional and the caller needs to handle\n * the error explicitly, use a standard try-catch with proper logging instead.\n */\nexport function safeJsonParse<T = unknown>(input: string, fallback?: T): T | null {\n\ttry {\n\t\treturn JSON.parse(input) as T\n\t} catch {\n\t\treturn fallback ?? null\n\t}\n}\n","/**\n * Utility Functions\n */\n\n/**\n * Get the base URL for API requests\n *\n * Use cases:\n * - getBaseUrl(): For relative URLs in browser, absolute in SSR (tRPC, API calls)\n * - getBaseUrl('origin'): For absolute URLs that need the actual origin (auth, sharing)\n *\n * Priority: NEXT_PUBLIC_APP_URL > localhost\n */\nexport function getBaseUrl(mode: 'relative' | 'origin' = 'relative'): string {\n\tif (typeof (globalThis as { window?: unknown }).window !== 'undefined') {\n\t\t// Browser: use relative or actual origin\n\t\treturn mode === 'origin'\n\t\t\t? (globalThis as { window: { location: { origin: string } } }).window.location.origin\n\t\t\t: ''\n\t}\n\n\t// SSR/Server: use environment configuration\n\tif (process.env.NEXT_PUBLIC_APP_URL) {\n\t\treturn process.env.NEXT_PUBLIC_APP_URL\n\t}\n\n\t// Fallback for development\n\tconst port = process.env.PORT ?? '3000'\n\treturn `http://localhost:${port}`\n}\n\n/**\n * HTML entity map for escaping\n */\nconst HTML_ENTITIES: Record<string, string> = {\n\t'&': '&amp;',\n\t'<': '&lt;',\n\t'>': '&gt;',\n\t'\"': '&quot;',\n\t\"'\": '&#039;',\n}\n\n/**\n * Escape HTML special characters to prevent XSS\n *\n * Uses single-pass regex replacement for efficiency.\n */\nexport function escapeHtml(str: string): string {\n\treturn str.replace(/[&<>\"']/g, (char) => HTML_ENTITIES[char])\n}\n\n// ==========================================\n// Slug Utilities\n// ==========================================\n\n/**\n * Generate a URL-friendly slug from text\n *\n * @param text - Text to convert to slug\n * @param maxLength - Optional maximum length (default: no limit)\n * @returns Lowercase slug with hyphens\n *\n * @example\n * generateSlug('My Awesome App') // 'my-awesome-app'\n * generateSlug('Hello World!') // 'hello-world'\n * generateSlug('My Org Name', 48) // 'my-org-name' (max 48 chars)\n */\nexport function generateSlug(text: string, maxLength?: number): string {\n\tconst slug = text\n\t\t.toLowerCase()\n\t\t.trim()\n\t\t.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens\n\t\t.replace(/^-+|-+$/g, '') // Trim leading/trailing hyphens\n\t\t.replace(/-{2,}/g, '-') // Collapse multiple hyphens\n\n\tif (maxLength && slug.length > maxLength) {\n\t\t// Trim to maxLength, avoiding mid-word cut\n\t\treturn slug.slice(0, maxLength).replace(/-+$/, '')\n\t}\n\n\treturn slug\n}\n","/**\n * User Agent Parsing Utilities\n *\n * Extracts browser, OS, and device type from user agent strings.\n * Simple implementation - no external dependencies.\n */\n\nexport interface ParsedUserAgent {\n\tbrowser: string | null\n\tos: string | null\n\tdeviceType: 'desktop' | 'mobile' | 'tablet' | null\n}\n\n/**\n * Parse a user agent string to extract browser, OS, and device type.\n * Returns null values if unable to determine.\n */\nexport function parseUserAgent(ua: string): ParsedUserAgent {\n\tif (!ua) {\n\t\treturn { browser: null, os: null, deviceType: null }\n\t}\n\n\treturn {\n\t\tbrowser: detectBrowser(ua),\n\t\tos: detectOS(ua),\n\t\tdeviceType: detectDeviceType(ua),\n\t}\n}\n\n/**\n * Detect browser from user agent\n */\nfunction detectBrowser(ua: string): string | null {\n\t// Order matters - check more specific browsers first\n\tif (ua.includes('Edg/')) {\n\t\tconst match = ua.match(/Edg\\/(\\d+)/)\n\t\treturn match ? `Edge ${match[1]}` : 'Edge'\n\t}\n\tif (ua.includes('OPR/') || ua.includes('Opera')) {\n\t\tconst match = ua.match(/OPR\\/(\\d+)/) || ua.match(/Opera\\/(\\d+)/)\n\t\treturn match ? `Opera ${match[1]}` : 'Opera'\n\t}\n\tif (ua.includes('Brave')) {\n\t\treturn 'Brave'\n\t}\n\tif (ua.includes('Chrome/')) {\n\t\tconst match = ua.match(/Chrome\\/(\\d+)/)\n\t\treturn match ? `Chrome ${match[1]}` : 'Chrome'\n\t}\n\tif (ua.includes('Safari/') && !ua.includes('Chrome')) {\n\t\tconst match = ua.match(/Version\\/(\\d+)/)\n\t\treturn match ? `Safari ${match[1]}` : 'Safari'\n\t}\n\tif (ua.includes('Firefox/')) {\n\t\tconst match = ua.match(/Firefox\\/(\\d+)/)\n\t\treturn match ? `Firefox ${match[1]}` : 'Firefox'\n\t}\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\n\t\tconst match = ua.match(/(?:MSIE |rv:)(\\d+)/)\n\t\treturn match ? `IE ${match[1]}` : 'Internet Explorer'\n\t}\n\treturn null\n}\n\n/**\n * Detect operating system from user agent\n */\nfunction detectOS(ua: string): string | null {\n\t// Mobile OS - check first\n\tif (ua.includes('iPhone') || ua.includes('iPad')) {\n\t\tconst match = ua.match(/OS (\\d+[_\\d]*)/)\n\t\tif (match) {\n\t\t\tconst version = match[1].replace(/_/g, '.')\n\t\t\treturn `iOS ${version}`\n\t\t}\n\t\treturn 'iOS'\n\t}\n\tif (ua.includes('Android')) {\n\t\tconst match = ua.match(/Android (\\d+[.\\d]*)/)\n\t\treturn match ? `Android ${match[1]}` : 'Android'\n\t}\n\n\t// Desktop OS\n\tif (ua.includes('Mac OS X') || ua.includes('macOS')) {\n\t\tconst match = ua.match(/Mac OS X (\\d+[_\\d]*)/) || ua.match(/macOS (\\d+[_\\d]*)/)\n\t\tif (match) {\n\t\t\tconst version = match[1].replace(/_/g, '.')\n\t\t\treturn `macOS ${version}`\n\t\t}\n\t\treturn 'macOS'\n\t}\n\tif (ua.includes('Windows NT')) {\n\t\tconst match = ua.match(/Windows NT ([\\d.]+)/)\n\t\tif (match) {\n\t\t\tconst ntVersion = match[1]\n\t\t\t// Map NT versions to Windows versions\n\t\t\tconst windowsVersions: Record<string, string> = {\n\t\t\t\t'10.0': 'Windows 10/11',\n\t\t\t\t'6.3': 'Windows 8.1',\n\t\t\t\t'6.2': 'Windows 8',\n\t\t\t\t'6.1': 'Windows 7',\n\t\t\t\t'6.0': 'Windows Vista',\n\t\t\t\t'5.1': 'Windows XP',\n\t\t\t}\n\t\t\treturn windowsVersions[ntVersion] || `Windows NT ${ntVersion}`\n\t\t}\n\t\treturn 'Windows'\n\t}\n\tif (ua.includes('Linux')) {\n\t\tif (ua.includes('Ubuntu')) return 'Ubuntu'\n\t\tif (ua.includes('Fedora')) return 'Fedora'\n\t\tif (ua.includes('Debian')) return 'Debian'\n\t\treturn 'Linux'\n\t}\n\tif (ua.includes('CrOS')) {\n\t\treturn 'Chrome OS'\n\t}\n\treturn null\n}\n\n/**\n * Detect device type from user agent\n */\nfunction detectDeviceType(ua: string): 'desktop' | 'mobile' | 'tablet' | null {\n\t// Check for tablets first (before mobile, as some tablets have \"Mobile\" in UA)\n\tif (\n\t\tua.includes('iPad') ||\n\t\tua.includes('Tablet') ||\n\t\t(ua.includes('Android') && !ua.includes('Mobile'))\n\t) {\n\t\treturn 'tablet'\n\t}\n\n\t// Check for mobile\n\tif (\n\t\tua.includes('iPhone') ||\n\t\tua.includes('iPod') ||\n\t\tua.includes('Android') ||\n\t\tua.includes('Mobile') ||\n\t\tua.includes('webOS') ||\n\t\tua.includes('BlackBerry') ||\n\t\tua.includes('IEMobile') ||\n\t\tua.includes('Opera Mini')\n\t) {\n\t\treturn 'mobile'\n\t}\n\n\t// Desktop (Windows, Mac, Linux without mobile indicators)\n\tif (\n\t\tua.includes('Windows NT') ||\n\t\tua.includes('Mac OS X') ||\n\t\tua.includes('macOS') ||\n\t\tua.includes('Linux') ||\n\t\tua.includes('CrOS')\n\t) {\n\t\treturn 'desktop'\n\t}\n\n\treturn null\n}\n","import type {\n\tCreateWorkspaceInput,\n\tForkWorkspaceInput,\n\tListWorkspacesResult,\n\tManagedWorkspace,\n\tWorkspaceResult,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\nexport type WorkspaceRecord = ManagedWorkspace\nexport type { CreateWorkspaceInput, ForkWorkspaceInput }\n\nexport async function listWorkspaces(config: SylphxConfig): Promise<readonly WorkspaceRecord[]> {\n\tconst result = await callApi<ListWorkspacesResult>(config, '/workspaces')\n\treturn result.workspaces\n}\n\nexport async function getWorkspace(\n\tconfig: SylphxConfig,\n\tworkspaceId: string,\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(\n\t\tconfig,\n\t\t`/workspaces/${encodeURIComponent(workspaceId)}`,\n\t)\n\treturn result.workspace\n}\n\nexport async function createWorkspace(\n\tconfig: SylphxConfig,\n\tinput: CreateWorkspaceInput,\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(config, '/workspaces', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t\ttimeout: 300_000,\n\t})\n\treturn result.workspace\n}\n\nexport async function forkWorkspace(\n\tconfig: SylphxConfig,\n\tsourceWorkspaceId: string,\n\tinput: ForkWorkspaceInput = {},\n): Promise<WorkspaceRecord> {\n\tconst result = await callApi<WorkspaceResult>(\n\t\tconfig,\n\t\t`/workspaces/${encodeURIComponent(sourceWorkspaceId)}/forks`,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: input,\n\t\t\ttimeout: 5 * 60_000,\n\t\t},\n\t)\n\treturn result.workspace\n}\n\nexport async function deleteWorkspace(config: SylphxConfig, workspaceId: string): Promise<void> {\n\tawait callApi<Record<string, unknown>>(config, `/workspaces/${encodeURIComponent(workspaceId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n","/**\n * Authentication Configuration (SSOT)\n *\n * Single source of truth for authentication-related constants.\n * Used by: validation schemas, auth forms, password policies\n */\n\n// ==========================================\n// Password Policy\n// ==========================================\n\n/** Minimum password length */\nexport const MIN_PASSWORD_LENGTH = 8\n\n/** Maximum password length */\nexport const MAX_PASSWORD_LENGTH = 128\n\n/** Password requirements for display in UI */\nexport const PASSWORD_REQUIREMENTS = {\n\tminLength: MIN_PASSWORD_LENGTH,\n\tmaxLength: MAX_PASSWORD_LENGTH,\n\tdescription: `Must be at least ${MIN_PASSWORD_LENGTH} characters`,\n\tplaceholder: `Min. ${MIN_PASSWORD_LENGTH} characters`,\n} as const\n","/**\n * Billing Configuration (SSOT)\n *\n * Single source of truth for billing-related configuration.\n * Used by: billing pages, usage tracking, invoicing\n */\n\n// ==========================================\n// Billing Constants\n// ==========================================\n\n/** Bytes per gigabyte — use instead of hardcoding 1024*1024*1024 */\nexport const BYTES_PER_GB = 1024 * 1024 * 1024\n\n/** Microdollars per cent ($0.01 = 10,000 microdollars) */\nexport const MICRODOLLARS_PER_CENT = 10_000\n\n/** Invoice payment due after billing period ends (days) */\nexport const INVOICE_DUE_DAYS = 15\n\n// ==========================================\n// Service Billing Metrics (SSOT)\n// ==========================================\n// IMPORTANT: These strings must match the metrics used in:\n// - Platform pricing table (platform_pricing)\n// - Services marketing page (services/_data/services.ts)\n// - SDK routes that call recordServiceUsage()\n\n/**\n * Billing metrics per service\n * These are user-facing metric names (NOT technical terms like 'commands')\n */\nexport const SERVICE_METRICS = {\n\t// KV (Key-Value Store)\n\tkv: {\n\t\toperations: 'operations', // User-friendly: \"100K operations\"\n\t\tstorage: 'storage', // GB-month\n\t},\n\t// Realtime (Pub/Sub Messaging)\n\trealtime: {\n\t\tmessages: 'messages', // User-friendly: \"100K messages\"\n\t\tconnections: 'connections', // SSE subscribe connections\n\t},\n\t// Other services follow the same pattern\n\tai: {\n\t\ttokens: 'tokens',\n\t},\n\temail: {\n\t\temails: 'emails',\n\t\tmarketingEmails: 'marketing_emails',\n\t},\n\tnotifications: {\n\t\tsends: 'sends',\n\t},\n\tanalytics: {\n\t\tevents: 'events',\n\t\tforwarding: 'forwarding',\n\t},\n\tstorage: {\n\t\tcapacity: 'capacity',\n\t\tuploads: 'uploads',\n\t\tegress: 'egress',\n\t},\n\tauth: {\n\t\tmau: 'mau',\n\t},\n\tflags: {\n\t\tevaluations: 'evaluations',\n\t},\n\tconsent: {\n\t\trecords: 'records',\n\t},\n\treferrals: {\n\t\tconversions: 'conversions',\n\t},\n\tengagement: {\n\t\toperations: 'operations',\n\t},\n\tbilling: {\n\t\tsubscriptions: 'subscriptions',\n\t\tusageRecords: 'usage_records',\n\t},\n\tsearch: {\n\t\tdocuments: 'documents',\n\t\tsearches: 'searches',\n\t},\n\twebhooks: {\n\t\tdeliveries: 'deliveries',\n\t},\n\tmonitoring: {\n\t\terrors: 'errors',\n\t},\n\tjobs: {\n\t\tinvocations: 'invocations',\n\t\tcronSchedules: 'cron_schedules',\n\t},\n\tdatabase: {\n\t\tcomputeSeconds: 'compute_seconds',\n\t\tstorage: 'storage',\n\t\tdataTransferBytes: 'data_transfer_bytes',\n\t},\n\tdeploy: {\n\t\tbuildMinutes: 'build_minutes',\n\t},\n} as const\n\n// ==========================================\n// Compute Rates (ADR-034)\n// ==========================================\n\n/** Active vCPU rate: $0.024/hr = $0.0004/min = 400 microdollars/min (ADR-034) */\nexport const COMPUTE_VCPU_ACTIVE_RATE_MICRODOLLARS = 400\n\n/** Idle vCPU rate: $0.003/hr = $0.00005/min = 50 microdollars/min — 1/8 of active (ADR-034) */\nexport const COMPUTE_VCPU_IDLE_RATE_MICRODOLLARS = 50\n\n/** RAM rate: $0.010/GB-hr = $0.000167/GB-min = 167 microdollars/GB-min (ADR-034) */\nexport const COMPUTE_RAM_RATE_MICRODOLLARS = 167\n\n// ==========================================\n// Build Machine Rates (ADR-034)\n// ==========================================\n\n/** Build minute prices by machine type in microdollars per minute (ADR-034) */\nexport const BUILD_MINUTE_PRICES: Record<string, number> = {\n\tstandard: 14_000, // $0.014/min\n\tlarge: 30_000, // $0.030/min\n\txlarge: 126_000, // $0.126/min\n}\n\n/** Build minute size multipliers for quota tracking (ADR-034) */\nexport const BUILD_SIZE_MULTIPLIERS: Record<string, number> = {\n\tstandard: 1,\n\tlarge: 2,\n\txlarge: 9,\n}\n\n// ==========================================\n// Build Minutes Included Per Plan (ADR-034)\n// ==========================================\n\n/** Build minutes included per month by plan tier (ADR-034) */\nexport const BUILD_MINUTES_INCLUDED: Record<string, number> = {\n\tfree: 100,\n\tpro: 500,\n\tteam: 2_000,\n\tenterprise: 10_000,\n}\n\n// ==========================================\n// CI/CD Pricing (Legacy — backward compat)\n// ==========================================\n\n/**\n * @deprecated Use BUILD_MINUTE_PRICES.standard instead (ADR-034).\n * Kept for backward compatibility with existing billing pipelines.\n *\n * CI compute-minute price in microdollars.\n * Now references the standard build machine rate from ADR-034.\n */\nexport const CI_BUILD_MINUTE_PRICE_MICRODOLLARS = BUILD_MINUTE_PRICES.standard\n\n/**\n * @deprecated Use BUILD_MINUTES_INCLUDED[plan] instead (ADR-034).\n * Kept for backward compatibility. Maps to the `team` tier as the\n * previous default (2,000 free minutes).\n */\nexport const CI_FREE_MINUTES_PER_MONTH = BUILD_MINUTES_INCLUDED.team\n\n/**\n * Size multipliers for CI compute-minute accounting (legacy GitHub labels).\n *\n * Keys are **GitHub Actions runner labels** (not build machine type names).\n * These labels arrive on workflow_job webhooks and are stored as-is in\n * githubCiJobs.resourceClass. The billing pipeline maps them to multipliers\n * here; the build pipeline maps them to BuildMachineType via\n * normalizeBuildMachineType() in build-machine.ts.\n *\n * @see BUILD_SIZE_MULTIPLIERS for canonical build machine multipliers (ADR-034).\n */\nexport const CI_SIZE_MULTIPLIERS: Record<string, number> = {\n\tnano: 1,\n\tsmall: 1,\n\tstandard: 1,\n\tlarge: 2,\n\txlarge: 4,\n\t'2xlarge': 8,\n} as const\n\n/** macOS runner per-size multipliers (ADR-035: per-tier billing) */\nexport const CI_MACOS_SIZE_MULTIPLIERS: Record<string, number> = {\n\tstandard: 10,\n\tlarge: 20,\n\txlarge: 40,\n} as const\n\n/** @deprecated Use CI_MACOS_SIZE_MULTIPLIERS[size] instead. */\nexport const CI_MACOS_MULTIPLIER = CI_MACOS_SIZE_MULTIPLIERS.standard\n\n// Type-safe metric access\nexport type ServiceMetrics = typeof SERVICE_METRICS\nexport type KvMetric = keyof typeof SERVICE_METRICS.kv\nexport type RealtimeMetric = keyof typeof SERVICE_METRICS.realtime\n\n// ==========================================\n// Credit System\n// ==========================================\n\n/** Credit expiry period in months */\nexport const CREDIT_EXPIRY_MONTHS = 12\n\n// ==========================================\n// Payment Processing\n// ==========================================\n\n/** Maximum payment retry attempts before suspending account */\nexport const MAX_PAYMENT_ATTEMPTS = 3\n\n// ==========================================\n// Permission Roles\n// ==========================================\n\n/** Roles that can access billing pages */\nexport const BILLING_ALLOWED_ROLES = ['super_admin', 'admin', 'billing'] as const\nexport type BillingAllowedRole = (typeof BILLING_ALLOWED_ROLES)[number]\n\n/** Check if a role has billing access */\nexport function hasBillingAccess(role: string): boolean {\n\treturn BILLING_ALLOWED_ROLES.includes(role as BillingAllowedRole)\n}\n","/**\n * Console SDK Key Utilities\n *\n * The Platform Console is Customer Zero — it uses the exact same key format\n * as every other customer: pk_{env}_{ref}_{hex} / sk_{env}_{ref}_{hex}.\n *\n * No special key construction. No legacy app_* format. No special lookup paths.\n *\n * Keys are set via environment variables, just like any customer app:\n * NEXT_PUBLIC_SYLPHX_KEY = pk_prod_nlbaz63pd2gz_97ef4f90c48e7378b0f00a1e2cb8c15e\n * SYLPHX_SECRET_KEY = sk_prod_nlbaz63pd2gz_edea406b7988099f5826c143b0f6bd94...\n */\n\n/** Console project slug — must match bootstrap.ts PLATFORM_CONSOLE_APP.slug */\nexport const CONSOLE_APP_SLUG = 'sylphx-console'\n\n/**\n * Determine environment prefix from build/runtime environment.\n * Used by sdk-cookies.ts for cookie naming until it migrates to SDK-native getCookieNames().\n * NOTE: still actively consumed by sdk-cookies.ts and sdk-login.ts — remove only after\n * those modules parse the env prefix from NEXT_PUBLIC_SYLPHX_KEY directly.\n */\nexport function getEnvPrefix(): 'dev' | 'stg' | 'prod' {\n\tconst envType = process.env.NEXT_PUBLIC_ENV_TYPE\n\tif (envType === 'development') return 'dev'\n\tif (envType === 'staging') return 'stg'\n\tif (envType === 'production') return 'prod'\n\treturn process.env.NODE_ENV === 'production' ? 'prod' : 'dev'\n}\n","/**\n * Instance Type Catalog — SSOT\n *\n * Defines the compute instance types available for Sylphx platform workloads.\n * Each instance type maps to Kubernetes resource requests/limits for kata-clh\n * (Cloud Hypervisor) microVMs, along with billing rates and plan eligibility.\n *\n * Rates are in microdollars (1 USD = 1,000,000 µ$) per minute.\n *\n * Memory overcommit (ADR-028): CLH uses demand paging (mmap without MAP_POPULATE).\n * Host physical RAM is allocated on-demand as guest pages are touched, NOT pre-allocated\n * at VM boot. Verified 2026-03-30: a pod with limits=4Gi only consumed +8Mi host RAM\n * when idle. Memory requests are set to 50% of limits (2x overcommit) for efficient\n * scheduler bin-packing. CPU requests are 25% of limits (4x overcommit).\n *\n * ADR-034 T-shirt sizing: canonical names are xs/sm/md/lg/xl/2xl/4xl.\n * Legacy names (starter-1x, standard-1x, etc.) are kept as aliases for backward\n * compatibility with existing database values and API consumers.\n */\n\nimport type { PlatformPlanId } from './platform-plans'\n\n// ==========================================\n// Instance Type Types\n// ==========================================\n\nexport type InstanceTypeId =\n\t| 'xs'\n\t| 'sm'\n\t| 'md'\n\t| 'lg'\n\t| 'xl'\n\t| '2xl'\n\t| '4xl'\n\t// @deprecated — legacy names, use T-shirt sizes instead\n\t| 'starter-1x'\n\t| 'standard-1x'\n\t| 'standard-2x'\n\t| 'performance-m'\n\t| 'performance-l'\n\t| 'performance-xl'\n\nexport interface InstanceTypeDefinition {\n\tid: InstanceTypeId\n\tname: string\n\t/** Kubernetes CPU limit (e.g. '2000m') */\n\tcpuLimit: string\n\t/** Kubernetes memory limit (e.g. '8Gi') */\n\tmemoryLimit: string\n\t/** Kubernetes CPU request (e.g. '500m') */\n\tcpuRequest: string\n\t/** Kubernetes memory request (50% of limit — CLH demand paging, ADR-028) */\n\tmemoryRequest: string\n\t/** Billing vCPU count for metering denormalization */\n\tvcpus: number\n\t/** Billing memory in MiB for metering denormalization */\n\tmemoryMib: number\n\t/** Rate per vCPU per minute in microdollars */\n\tvcpuMinuteRateMicrodollars: number\n\t/** Rate per GiB per minute in microdollars */\n\tgbMinuteRateMicrodollars: number\n\t/** Platform plans that may provision this instance type */\n\tallowedPlans: PlatformPlanId[]\n\t/**\n\t * Maximum platform-managed instance count for this instance type tier.\n\t * Caps horizontal scale-out on smaller tiers. Users can set a lower\n\t * per-service maximum through ScalePolicy, but never exceed this ceiling.\n\t */\n\tmaxInstances: number\n\t/** Marketing bullet points for instance type cards */\n\thighlights: string[]\n\t/** Whether this instance type is deprecated (legacy name) */\n\tdeprecated?: boolean\n}\n\n// ==========================================\n// Billing Rates\n// ==========================================\n\n/** µ$/vCPU-minute — ~$0.028/vCPU-hour, competitive with Railway */\nconst VCPU_MINUTE_RATE = 463\n\n/** µ$/GiB-minute — ~$0.014/GiB-hour, competitive with Railway */\nconst GIB_MINUTE_RATE = 232\n\n// ==========================================\n// Alias Mapping (legacy → canonical)\n// ==========================================\n\n/**\n * Maps legacy instance type names to their canonical T-shirt size equivalents (ADR-034).\n *\n * Database values and API consumers may still use old names — this mapping lets\n * resolveInstanceType() transparently return the canonical definition without\n * requiring a data migration.\n */\nexport const INSTANCE_TYPE_ALIASES: Record<string, string> = {\n\t'starter-1x': 'xs',\n\t'standard-1x': 'sm',\n\t'standard-2x': 'md',\n\t'performance-m': 'lg',\n\t'performance-l': 'xl',\n\t'performance-xl': '2xl',\n}\n\n/**\n * Resolve a potentially-aliased instance type ID to its canonical T-shirt size.\n * Returns the input unchanged if it is already canonical or unknown.\n *\n * Use for display/UI and when accepting user input for NEW configurations.\n * Do NOT use in runtime paths (billing, K8s reconciler) — both old and new names\n * exist in INSTANCE_TYPES with their original specs, so direct lookup is correct\n * and avoids changing billing/resource behavior for existing services.\n */\nexport function resolveCanonicalInstanceType(id: string): string {\n\treturn INSTANCE_TYPE_ALIASES[id] ?? id\n}\n\n// ==========================================\n// Instance Type Definitions (ADR-034 T-shirt sizes)\n// ==========================================\n\nexport const INSTANCE_TYPES: Record<InstanceTypeId, InstanceTypeDefinition> = {\n\t// ---- Canonical T-shirt sizes (ADR-034) ----\n\n\txs: {\n\t\tid: 'xs',\n\t\tname: 'XS',\n\t\tcpuLimit: '250m',\n\t\tmemoryLimit: '512Mi',\n\t\tcpuRequest: '63m',\n\t\tmemoryRequest: '256Mi',\n\t\tvcpus: 0.25,\n\t\tmemoryMib: 512,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['free', 'starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 1,\n\t\thighlights: ['0.25 vCPU, 0.5 GiB RAM', 'Available on all plans', 'Ideal for dev/staging'],\n\t},\n\n\tsm: {\n\t\tid: 'sm',\n\t\tname: 'SM',\n\t\tcpuLimit: '500m',\n\t\tmemoryLimit: '1Gi',\n\t\tcpuRequest: '125m',\n\t\tmemoryRequest: '512Mi',\n\t\tvcpus: 0.5,\n\t\tmemoryMib: 1024,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 3,\n\t\thighlights: ['0.5 vCPU, 1 GiB RAM', 'Good for lightweight services', 'Starter plan default'],\n\t},\n\n\tmd: {\n\t\tid: 'md',\n\t\tname: 'MD',\n\t\tcpuLimit: '1000m',\n\t\tmemoryLimit: '2Gi',\n\t\tcpuRequest: '250m',\n\t\tmemoryRequest: '1Gi',\n\t\tvcpus: 1,\n\t\tmemoryMib: 2048,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: ['1 vCPU, 2 GiB RAM', 'Good for web apps and APIs', 'Pro plan default'],\n\t},\n\n\tlg: {\n\t\tid: 'lg',\n\t\tname: 'LG',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '4Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '2Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 4096,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['pro', 'team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['2 vCPUs, 4 GiB RAM', 'High-throughput APIs and workers', 'Team plan default'],\n\t},\n\n\txl: {\n\t\tid: 'xl',\n\t\tname: 'XL',\n\t\tcpuLimit: '4000m',\n\t\tmemoryLimit: '8Gi',\n\t\tcpuRequest: '1000m',\n\t\tmemoryRequest: '4Gi',\n\t\tvcpus: 4,\n\t\tmemoryMib: 8192,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['4 vCPUs, 8 GiB RAM', 'CI builds and ML inference', 'Enterprise plan default'],\n\t},\n\n\t'2xl': {\n\t\tid: '2xl',\n\t\tname: '2XL',\n\t\tcpuLimit: '8000m',\n\t\tmemoryLimit: '16Gi',\n\t\tcpuRequest: '2000m',\n\t\tmemoryRequest: '8Gi',\n\t\tvcpus: 8,\n\t\tmemoryMib: 16384,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['8 vCPUs, 16 GiB RAM', 'Heavy compute and large builds', 'Team/Enterprise'],\n\t},\n\n\t'4xl': {\n\t\tid: '4xl',\n\t\tname: '4XL',\n\t\tcpuLimit: '16000m',\n\t\tmemoryLimit: '32Gi',\n\t\tcpuRequest: '4000m',\n\t\tmemoryRequest: '16Gi',\n\t\tvcpus: 16,\n\t\tmemoryMib: 32768,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['16 vCPUs, 32 GiB RAM', 'Maximum compute power', 'Enterprise exclusive'],\n\t},\n\n\t// ---- Legacy names (deprecated — kept for backward compat with DB values) ----\n\n\t/** @deprecated Use 'xs' instead */\n\t'starter-1x': {\n\t\tid: 'starter-1x',\n\t\tname: 'Starter 1x',\n\t\tcpuLimit: '1000m',\n\t\tmemoryLimit: '2Gi',\n\t\tcpuRequest: '250m',\n\t\tmemoryRequest: '1Gi',\n\t\tvcpus: 1,\n\t\tmemoryMib: 2048,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['free', 'starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 3,\n\t\thighlights: ['1 vCPU, 2 GiB RAM', 'Available on all plans', 'Ideal for lightweight services'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'sm' instead */\n\t'standard-1x': {\n\t\tid: 'standard-1x',\n\t\tname: 'Standard 1x',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '4Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '2Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 4096,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: ['2 vCPUs, 4 GiB RAM', 'Good for web apps and APIs', 'Starter plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'md' instead */\n\t'standard-2x': {\n\t\tid: 'standard-2x',\n\t\tname: 'Standard 2x',\n\t\tcpuLimit: '2000m',\n\t\tmemoryLimit: '8Gi',\n\t\tcpuRequest: '500m',\n\t\tmemoryRequest: '4Gi',\n\t\tvcpus: 2,\n\t\tmemoryMib: 8192,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['starter', 'pro', 'team', 'enterprise'],\n\t\tmaxInstances: 5,\n\t\thighlights: [\n\t\t\t'2 vCPUs, 8 GiB RAM',\n\t\t\t'Double memory for data-heavy workloads',\n\t\t\t'Pro plan default',\n\t\t],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'lg' instead */\n\t'performance-m': {\n\t\tid: 'performance-m',\n\t\tname: 'Performance M',\n\t\tcpuLimit: '4000m',\n\t\tmemoryLimit: '16Gi',\n\t\tcpuRequest: '1000m',\n\t\tmemoryRequest: '8Gi',\n\t\tvcpus: 4,\n\t\tmemoryMib: 16384,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['pro', 'team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['4 vCPUs, 16 GiB RAM', 'High-throughput APIs and workers', 'Team plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use 'xl' instead */\n\t'performance-l': {\n\t\tid: 'performance-l',\n\t\tname: 'Performance L',\n\t\tcpuLimit: '8000m',\n\t\tmemoryLimit: '32Gi',\n\t\tcpuRequest: '2000m',\n\t\tmemoryRequest: '16Gi',\n\t\tvcpus: 8,\n\t\tmemoryMib: 32768,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['team', 'enterprise'],\n\t\tmaxInstances: 10,\n\t\thighlights: ['8 vCPUs, 32 GiB RAM', 'CI builds and ML inference', 'Enterprise plan default'],\n\t\tdeprecated: true,\n\t},\n\n\t/** @deprecated Use '2xl' instead */\n\t'performance-xl': {\n\t\tid: 'performance-xl',\n\t\tname: 'Performance XL',\n\t\tcpuLimit: '16000m',\n\t\tmemoryLimit: '64Gi',\n\t\tcpuRequest: '4000m',\n\t\tmemoryRequest: '32Gi',\n\t\tvcpus: 16,\n\t\tmemoryMib: 65536,\n\t\tvcpuMinuteRateMicrodollars: VCPU_MINUTE_RATE,\n\t\tgbMinuteRateMicrodollars: GIB_MINUTE_RATE,\n\t\tallowedPlans: ['enterprise'],\n\t\tmaxInstances: 20,\n\t\thighlights: ['16 vCPUs, 64 GiB RAM', 'Heavy compute and large builds', 'Enterprise exclusive'],\n\t\tdeprecated: true,\n\t},\n} as const\n\n// ==========================================\n// Display Ordering\n// ==========================================\n\n/** Ordered list of canonical instance type IDs for display (smallest to largest) */\nexport const INSTANCE_TYPE_ORDER: InstanceTypeId[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '4xl']\n\n/**\n * @deprecated Use INSTANCE_TYPE_ORDER instead.\n * Ordered list of legacy instance type IDs — kept for backward compat.\n */\nexport const LEGACY_INSTANCE_TYPE_ORDER: InstanceTypeId[] = [\n\t'starter-1x',\n\t'standard-1x',\n\t'standard-2x',\n\t'performance-m',\n\t'performance-l',\n\t'performance-xl',\n]\n\n// ==========================================\n// Helpers\n// ==========================================\n\nconst DEFAULT_INSTANCE_TYPE: Record<PlatformPlanId, InstanceTypeId> = {\n\tfree: 'xs',\n\tstarter: 'sm',\n\tpro: 'md',\n\tteam: 'lg',\n\tenterprise: 'xl',\n}\n\n/** Get the default instance type for a given platform plan */\nexport function getDefaultInstanceType(plan: PlatformPlanId): InstanceTypeId {\n\treturn DEFAULT_INSTANCE_TYPE[plan]\n}\n\n/** Get all canonical (non-deprecated) instance types available for a given platform plan, in display order */\nexport function getAvailableInstanceTypes(plan: PlatformPlanId): InstanceTypeDefinition[] {\n\treturn INSTANCE_TYPE_ORDER.map((id) => INSTANCE_TYPES[id]).filter(\n\t\t(t) => t.allowedPlans.includes(plan) && !t.deprecated,\n\t)\n}\n\n/** Resolve Kubernetes resource spec for a given instance type (accepts aliases) */\nexport function resolveResources(id: InstanceTypeId): {\n\trequests: { cpu: string; memory: string }\n\tlimits: { cpu: string; memory: string }\n} {\n\tconst t = INSTANCE_TYPES[id]\n\treturn {\n\t\trequests: { cpu: t.cpuRequest, memory: t.memoryRequest },\n\t\tlimits: { cpu: t.cpuLimit, memory: t.memoryLimit },\n\t}\n}\n\n/** Resolve the platform-managed instance ceiling for a given instance type. */\nexport function resolveMaxInstances(id: InstanceTypeId): number {\n\treturn INSTANCE_TYPES[id].maxInstances\n}\n\n/** Default instance ceiling when no instance type is resolved. */\nexport const DEFAULT_MAX_INSTANCES = 10\n\n/** Type guard: check if an arbitrary string is a valid InstanceTypeId (including legacy aliases) */\nexport function isValidInstanceType(id: string): id is InstanceTypeId {\n\treturn id in INSTANCE_TYPES\n}\n\n/** Validate that an instance type exists and is permitted for the given plan */\nexport function validateInstanceTypeForPlan(\n\tid: string,\n\tplan: PlatformPlanId,\n): { valid: boolean; error?: string } {\n\t// Resolve alias to canonical name for validation\n\tconst canonicalId = resolveCanonicalInstanceType(id)\n\n\tif (!isValidInstanceType(canonicalId)) {\n\t\treturn { valid: false, error: `Unknown instance type: ${id}` }\n\t}\n\n\tconst definition = INSTANCE_TYPES[canonicalId]\n\tif (!definition.allowedPlans.includes(plan)) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `Instance type \"${definition.name}\" is not available on the ${plan} plan`,\n\t\t}\n\t}\n\n\treturn { valid: true }\n}\n","/**\n * Platform Plan Tiers — SSOT\n *\n * Defines the Sylphx Platform plan tier system (ADR-034).\n * NOTE: This is for *platform* plans (what the organization pays Sylphx for).\n * It is separate from `plans` (which are in-app subscription products\n * that customers create for their own end-users).\n *\n * All prices in cents (USD). Credits in microdollars (1 USD = 1,000,000 µ$).\n *\n * ADR-034 tiers: Free → Pro ($20/mo) → Team ($20/user/mo) → Enterprise (custom)\n * The 'starter' tier is deprecated but kept in the type union for backward\n * compatibility with existing database rows and API consumers.\n */\n\n// ==========================================\n// Plan Tier Types\n// ==========================================\n\n/**\n * Platform plan identifiers.\n * 'starter' is deprecated (ADR-034) — retained for backward compat with existing records.\n */\nexport type PlatformPlanId = 'free' | 'starter' | 'pro' | 'team' | 'enterprise'\n\nexport type BuildMachineTier = 'standard' | 'large' | 'xlarge'\n\nexport interface PlatformPlanLimits {\n\t/** Max projects across all environments */\n\tmaxProjects: number | null\n\t/** Max organization members */\n\tmaxMembers: number | null\n\t/** Max custom domains */\n\tmaxCustomDomains: number | null\n\t/** Max concurrent CI runners */\n\tmaxConcurrentRunners: number | null\n\t/** Max concurrent macOS CI runners */\n\tmaxMacosRunners: number | null\n\t/** Max managed databases */\n\tmaxDatabases: number | null\n\t/** CI job max duration in seconds */\n\tciMaxJobDurationSeconds: number\n\t/** API rate limit (requests per minute) */\n\tapiRateLimitPerMin: number\n\t/** Audit log retention in days (0 = off) */\n\tauditLogDays: number\n\t/** Max instances per service (null = custom/negotiated) */\n\tmaxInstances: number | null\n\t/** Included build minutes per billing period */\n\tincludedBuildMinutes: number\n\t/** Included outbound bandwidth in GB per billing period */\n\tincludedBandwidthGb: number\n\t/** Log retention in days */\n\tlogRetentionDays: number\n\t/** Build machine tier determining build speed */\n\tbuildMachineTier: BuildMachineTier\n}\n\nexport interface PlatformPlanFeatures {\n\t/** Custom domain support */\n\tcustomDomains: boolean\n\t/** SSO / SAML support */\n\tsso: boolean\n\t/** Priority CI queue */\n\tpriorityCi: boolean\n\t/** macOS CI runners */\n\tmacosCi: boolean\n\t/** Shared / Priority / Dedicated */\n\tsupport: 'community' | 'email' | 'priority' | 'dedicated'\n\t/** SLA uptime guarantee string (e.g. '99.9%') */\n\tsla: string | null\n\t/** Role-based access control */\n\trbac: boolean\n\t/** Advanced analytics / insights */\n\tadvancedAnalytics: boolean\n\t/** White-label branding removal */\n\twhiteLabel: boolean\n}\n\nexport interface PlatformPlanDefinition {\n\tid: PlatformPlanId\n\tname: string\n\t/** Monthly price in cents (0 = free, null = custom) */\n\tpriceMonthly: number | null\n\t/** Annual price in cents, ~20% discount (null = custom or N/A) */\n\tpriceAnnual: number | null\n\t/** Included platform compute credits per billing period (microdollars) */\n\tincludedCreditsMicrodollars: number\n\t/** Whether price is per seat (per member) — Team plan */\n\tperSeat?: boolean\n\tfeatures: PlatformPlanFeatures\n\tlimits: PlatformPlanLimits\n\t/** Marketing bullet points for pricing cards */\n\thighlights: string[]\n\t/** Optional badge label (e.g. \"Most Popular\") */\n\tbadge?: string\n\t/** CTA button text */\n\tcta: string\n\t/** Whether this is a custom/enterprise plan with contact sales flow */\n\tisCustom?: boolean\n\t/**\n\t * Deprecated plan — no longer available for new subscriptions.\n\t * Existing subscribers are grandfathered until they change plans.\n\t */\n\tdeprecated?: boolean\n}\n\n// ==========================================\n// Plan Definitions\n// ==========================================\n\nexport const PLATFORM_PLANS: Record<PlatformPlanId, PlatformPlanDefinition> = {\n\tfree: {\n\t\tid: 'free',\n\t\tname: 'Free',\n\t\tpriceMonthly: 0,\n\t\tpriceAnnual: 0,\n\t\tincludedCreditsMicrodollars: 5_000_000, // $5\n\t\tfeatures: {\n\t\t\tcustomDomains: false,\n\t\t\tsso: false,\n\t\t\tpriorityCi: false,\n\t\t\tmacosCi: false,\n\t\t\tsupport: 'community',\n\t\t\tsla: null,\n\t\t\trbac: false,\n\t\t\tadvancedAnalytics: false,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: 1,\n\t\t\tmaxMembers: 1,\n\t\t\tmaxCustomDomains: 0,\n\t\t\tmaxConcurrentRunners: 1,\n\t\t\tmaxMacosRunners: 0,\n\t\t\tmaxDatabases: 1,\n\t\t\tciMaxJobDurationSeconds: 30 * 60, // 30 min\n\t\t\tapiRateLimitPerMin: 60,\n\t\t\tauditLogDays: 0,\n\t\t\tmaxInstances: 1,\n\t\t\tincludedBuildMinutes: 100,\n\t\t\tincludedBandwidthGb: 100,\n\t\t\tlogRetentionDays: 1,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: [\n\t\t\t'1 project',\n\t\t\t'$5 compute credits/mo',\n\t\t\t'100 build minutes',\n\t\t\t'100 GB bandwidth',\n\t\t\t'Community support',\n\t\t],\n\t\tcta: 'Start free',\n\t},\n\n\t/**\n\t * @deprecated ADR-034 removed the Starter tier. Retained for backward compatibility\n\t * with existing subscribers. Not shown in pricing UI for new sign-ups.\n\t */\n\tstarter: {\n\t\tid: 'starter',\n\t\tname: 'Starter',\n\t\tdeprecated: true,\n\t\tpriceMonthly: 1900, // $19\n\t\tpriceAnnual: 18240, // $19 x 12 x 0.80 = $182.40\n\t\tincludedCreditsMicrodollars: 19_000_000, // $19\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: false,\n\t\t\tpriorityCi: false,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'email',\n\t\t\tsla: null,\n\t\t\trbac: false,\n\t\t\tadvancedAnalytics: false,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: 10,\n\t\t\tmaxMembers: 10,\n\t\t\tmaxCustomDomains: 5,\n\t\t\tmaxConcurrentRunners: 5,\n\t\t\tmaxMacosRunners: 1,\n\t\t\tmaxDatabases: 10,\n\t\t\tciMaxJobDurationSeconds: 60 * 60, // 1 hr\n\t\t\tapiRateLimitPerMin: 300,\n\t\t\tauditLogDays: 30,\n\t\t\tmaxInstances: 3,\n\t\t\tincludedBuildMinutes: 250,\n\t\t\tincludedBandwidthGb: 500,\n\t\t\tlogRetentionDays: 7,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: ['$19 credits/mo', '10 projects', 'Custom domains', 'macOS CI', 'Email support'],\n\t\tcta: 'Get started',\n\t},\n\n\tpro: {\n\t\tid: 'pro',\n\t\tname: 'Pro',\n\t\tpriceMonthly: 2000, // $20\n\t\tpriceAnnual: 19200, // $20 x 12 x 0.80 = $192 ($16/mo)\n\t\tincludedCreditsMicrodollars: 20_000_000, // $20\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: false,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'priority',\n\t\t\tsla: '99.9%',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: false,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null, // unlimited\n\t\t\tmaxMembers: 25,\n\t\t\tmaxCustomDomains: null, // unlimited\n\t\t\tmaxConcurrentRunners: 20,\n\t\t\tmaxMacosRunners: 5,\n\t\t\tmaxDatabases: null, // unlimited\n\t\t\tciMaxJobDurationSeconds: 2 * 60 * 60, // 2 hrs\n\t\t\tapiRateLimitPerMin: 1000,\n\t\t\tauditLogDays: 90,\n\t\t\tmaxInstances: 10,\n\t\t\tincludedBuildMinutes: 500,\n\t\t\tincludedBandwidthGb: 1000,\n\t\t\tlogRetentionDays: 14,\n\t\t\tbuildMachineTier: 'standard',\n\t\t},\n\t\thighlights: [\n\t\t\t'Unlimited projects',\n\t\t\t'$20 credits/mo',\n\t\t\t'500 build minutes',\n\t\t\t'1 TB bandwidth',\n\t\t\t'Priority support',\n\t\t\t'SLA 99.9%',\n\t\t],\n\t\tbadge: 'Most Popular',\n\t\tcta: 'Start Pro',\n\t},\n\n\tteam: {\n\t\tid: 'team',\n\t\tname: 'Team',\n\t\tpriceMonthly: 2000, // $20/user\n\t\tpriceAnnual: 19200, // $192/user/yr ($16/user/mo)\n\t\tincludedCreditsMicrodollars: 20_000_000, // $20/user\n\t\tperSeat: true,\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: true,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'dedicated',\n\t\t\tsla: '99.95%',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: true,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null, // unlimited\n\t\t\tmaxMembers: null, // unlimited\n\t\t\tmaxCustomDomains: null, // unlimited\n\t\t\tmaxConcurrentRunners: null, // unlimited\n\t\t\tmaxMacosRunners: null, // unlimited\n\t\t\tmaxDatabases: null, // unlimited\n\t\t\tciMaxJobDurationSeconds: 6 * 60 * 60, // 6 hrs\n\t\t\tapiRateLimitPerMin: 5000,\n\t\t\tauditLogDays: 365,\n\t\t\tmaxInstances: 20,\n\t\t\tincludedBuildMinutes: 2000,\n\t\t\tincludedBandwidthGb: 5000,\n\t\t\tlogRetentionDays: 30,\n\t\t\tbuildMachineTier: 'large',\n\t\t},\n\t\thighlights: [\n\t\t\t'$20/user/mo',\n\t\t\t'SSO / SAML',\n\t\t\t'2,000 large build minutes',\n\t\t\t'5 TB bandwidth',\n\t\t\t'Dedicated support',\n\t\t\t'SLA 99.95%',\n\t\t],\n\t\tcta: 'Get Team',\n\t},\n\n\tenterprise: {\n\t\tid: 'enterprise',\n\t\tname: 'Enterprise',\n\t\tpriceMonthly: null, // custom\n\t\tpriceAnnual: null, // custom\n\t\tincludedCreditsMicrodollars: 0, // negotiated\n\t\tisCustom: true,\n\t\tfeatures: {\n\t\t\tcustomDomains: true,\n\t\t\tsso: true,\n\t\t\tpriorityCi: true,\n\t\t\tmacosCi: true,\n\t\t\tsupport: 'dedicated',\n\t\t\tsla: 'Custom',\n\t\t\trbac: true,\n\t\t\tadvancedAnalytics: true,\n\t\t\twhiteLabel: true,\n\t\t},\n\t\tlimits: {\n\t\t\tmaxProjects: null,\n\t\t\tmaxMembers: null,\n\t\t\tmaxCustomDomains: null,\n\t\t\tmaxConcurrentRunners: null,\n\t\t\tmaxMacosRunners: null,\n\t\t\tmaxDatabases: null,\n\t\t\tciMaxJobDurationSeconds: 12 * 60 * 60,\n\t\t\tapiRateLimitPerMin: 0, // 0 = unlimited / negotiated\n\t\t\tauditLogDays: 730,\n\t\t\tmaxInstances: null, // custom\n\t\t\tincludedBuildMinutes: 0, // negotiated\n\t\t\tincludedBandwidthGb: 0, // negotiated\n\t\t\tlogRetentionDays: 90,\n\t\t\tbuildMachineTier: 'xlarge',\n\t\t},\n\t\thighlights: [\n\t\t\t'Custom credit volume',\n\t\t\t'Volume discounts',\n\t\t\t'Dedicated infrastructure',\n\t\t\t'Custom SLA',\n\t\t\t'White-glove onboarding',\n\t\t\t'Custom contracts',\n\t\t],\n\t\tcta: 'Contact sales',\n\t},\n} as const\n\n// ==========================================\n// Helpers\n// ==========================================\n\n/** Active (non-deprecated) plan IDs for display in pricing UI */\nexport const PLATFORM_PLAN_ORDER: PlatformPlanId[] = ['free', 'pro', 'team', 'enterprise']\n\n/**\n * Full plan order including deprecated tiers.\n * Useful for admin screens and migration tooling that must handle legacy plans.\n */\nexport const PLATFORM_PLAN_ORDER_ALL: PlatformPlanId[] = [\n\t'free',\n\t'starter',\n\t'pro',\n\t'team',\n\t'enterprise',\n]\n\n/** Check whether a plan is deprecated and should not be offered to new subscribers */\nexport function isPlanDeprecated(planId: PlatformPlanId): boolean {\n\treturn PLATFORM_PLANS[planId].deprecated === true\n}\n\n/** Get only active (non-deprecated) plan definitions, in display order */\nexport function getActivePlans(): PlatformPlanDefinition[] {\n\treturn PLATFORM_PLAN_ORDER.map((id) => PLATFORM_PLANS[id])\n}\n\n/** Convert microdollars to human-readable dollar string (e.g. \"$5\") */\nexport function microsToDollars(microdollars: number): string {\n\treturn `$${(microdollars / 1_000_000).toFixed(0)}`\n}\n\n/** Convert cents to human-readable dollar string (e.g. \"$19\") */\nexport function centsToDollars(cents: number): string {\n\treturn `$${(cents / 100).toFixed(0)}`\n}\n\n/** Get monthly price display string */\nexport function getPlanMonthlyPrice(plan: PlatformPlanDefinition, annual = false): string {\n\tif (plan.isCustom) return 'Custom'\n\tconst cents =\n\t\tannual && plan.priceAnnual != null ? Math.round(plan.priceAnnual / 12) : plan.priceMonthly\n\tif (cents == null) return 'Custom'\n\tif (cents === 0) return '$0'\n\tconst base = centsToDollars(cents)\n\treturn plan.perSeat ? `${base}/user` : base\n}\n","/**\n * SDK Debug Mode\n *\n * Centralized debug logging for the SDK.\n *\n * Enable via:\n * - Browser: `localStorage.setItem('sylphx_debug', 'true')`\n * - Node.js: `SYLPHX_DEBUG=true`\n *\n * Debug messages are namespaced with [Sylphx] prefix for easy filtering.\n */\n\n// ============================================================================\n// Debug Configuration\n// ============================================================================\n\n/** Storage key for browser-side debug toggle */\nconst DEBUG_STORAGE_KEY = 'sylphx_debug'\n\n/**\n * Check if debug mode is enabled\n *\n * Checks multiple sources in order:\n * 1. localStorage (browser)\n * 2. SYLPHX_DEBUG environment variable\n * 3. NODE_ENV === 'development' with explicit opt-in\n */\nfunction isDebugEnabled(): boolean {\n\t// Browser environment\n\tif (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {\n\t\ttry {\n\t\t\treturn localStorage.getItem(DEBUG_STORAGE_KEY) === 'true'\n\t\t} catch {\n\t\t\t// localStorage may be blocked in some contexts\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Node.js environment\n\tif (typeof process !== 'undefined' && process.env) {\n\t\treturn process.env.SYLPHX_DEBUG === 'true'\n\t}\n\n\treturn false\n}\n\n// Cache the debug state to avoid repeated localStorage/env checks\nlet debugModeCache: boolean | null = null\n\n/**\n * Whether debug mode is currently enabled\n *\n * Cached after first access for performance.\n */\nexport function getDebugMode(): boolean {\n\tif (debugModeCache === null) {\n\t\tdebugModeCache = isDebugEnabled()\n\t}\n\treturn debugModeCache\n}\n\n/**\n * Reset debug mode cache (for testing)\n */\nexport function resetDebugModeCache(): void {\n\tdebugModeCache = null\n}\n\n// ============================================================================\n// Debug Logging\n// ============================================================================\n\n/** Debug log categories */\nexport type DebugCategory =\n\t| 'auth'\n\t| 'api'\n\t| 'analytics'\n\t| 'flags'\n\t| 'storage'\n\t| 'cache'\n\t| 'token'\n\t| 'webhook'\n\t| 'error'\n\n/**\n * Log a debug message with category prefix\n *\n * @example\n * ```ts\n * debugLog('auth', 'Token refreshed', { expiresIn: 300 })\n * // [Sylphx auth] Token refreshed { expiresIn: 300 }\n * ```\n */\nexport function debugLog(category: DebugCategory, message: string, data?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst _prefix = `[Sylphx ${category}]`\n\n\tif (data !== undefined) {\n\t} else {\n\t}\n}\n\n/**\n * Log a debug warning with category prefix\n */\nexport function debugWarn(category: DebugCategory, message: string, data?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst prefix = `[Sylphx ${category}]`\n\n\tif (data !== undefined) {\n\t\tconsole.warn(prefix, message, data)\n\t} else {\n\t\tconsole.warn(prefix, message)\n\t}\n}\n\n/**\n * Log a debug error with category prefix\n *\n * Note: This always logs when debug mode is enabled, regardless of error severity.\n * Production error tracking should use the error tracking service, not this.\n */\nexport function debugError(category: DebugCategory, message: string, error?: unknown): void {\n\tif (!getDebugMode()) return\n\n\tconst prefix = `[Sylphx ${category}]`\n\n\tif (error !== undefined) {\n\t\tconsole.error(prefix, message, error)\n\t} else {\n\t\tconsole.error(prefix, message)\n\t}\n}\n\n// ============================================================================\n// Performance Timing\n// ============================================================================\n\n/**\n * Create a debug timer for measuring operation duration\n *\n * @example\n * ```ts\n * const timer = debugTimer('api', 'Fetching user profile')\n * // ... operation ...\n * timer.end() // Logs duration if debug mode enabled\n * ```\n */\nexport function debugTimer(category: DebugCategory, operation: string): { end: () => void } {\n\tif (!getDebugMode()) {\n\t\treturn { end: () => {} }\n\t}\n\n\tconst start = performance.now()\n\n\treturn {\n\t\tend() {\n\t\t\tconst duration = performance.now() - start\n\t\t\tdebugLog(category, `${operation} completed`, {\n\t\t\t\tdurationMs: Math.round(duration),\n\t\t\t})\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Browser Console Helpers\n// ============================================================================\n\n/**\n * Enable debug mode from browser console\n *\n * Call this in the browser console to enable debug logging:\n * ```js\n * window.__sylphx?.enableDebug()\n * ```\n */\nexport function enableDebug(): void {\n\tif (typeof localStorage === 'undefined') {\n\t\tconsole.warn('[Sylphx] Debug mode can only be enabled in browser environments')\n\t\treturn\n\t}\n\n\ttry {\n\t\tlocalStorage.setItem(DEBUG_STORAGE_KEY, 'true')\n\t\tdebugModeCache = true\n\t} catch (e) {\n\t\tconsole.warn('[Sylphx] Failed to enable debug mode:', e)\n\t}\n}\n\n/**\n * Disable debug mode from browser console\n */\nexport function disableDebug(): void {\n\tif (typeof localStorage === 'undefined') {\n\t\tconsole.warn('[Sylphx] Debug mode can only be disabled in browser environments')\n\t\treturn\n\t}\n\n\ttry {\n\t\tlocalStorage.removeItem(DEBUG_STORAGE_KEY)\n\t\tdebugModeCache = false\n\t} catch (e) {\n\t\tconsole.warn('[Sylphx] Failed to disable debug mode:', e)\n\t}\n}\n\n// ============================================================================\n// Global Window Helper (Browser Only)\n// ============================================================================\n\n/**\n * Install debug helpers on window.__sylphx\n *\n * This is called automatically when the SDK is loaded in the browser,\n * providing developers easy console access to debug utilities.\n */\nexport function installGlobalDebugHelpers(): void {\n\tif (typeof window === 'undefined') return\n\n\t// Use type assertion to extend window\n\tconst w = window as typeof window & {\n\t\t__sylphx?: {\n\t\t\tenableDebug: typeof enableDebug\n\t\t\tdisableDebug: typeof disableDebug\n\t\t\tisDebugEnabled: typeof getDebugMode\n\t\t}\n\t}\n\n\tw.__sylphx = {\n\t\tenableDebug,\n\t\tdisableDebug,\n\t\tisDebugEnabled: getDebugMode,\n\t}\n}\n","/**\n * REST Client for Sylphx Platform\n *\n * Type-safe REST API client built on plain `fetch` (no runtime `openapi-fetch`\n * dependency — ADR-084 routes types through `@sylphx/contract` so the codegen\n * layer no longer needs a transport library of its own). Public surface is\n * preserved: consumers still call `client.GET('/path')`, `client.POST(...)`,\n * etc. The middleware chain (deduplication → circuit breaker → ETag →\n * retry) runs in the same order as the previous implementation.\n *\n * @example\n * ```typescript\n * import { createRestClient } from '@sylphx/sdk'\n *\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * const { data: user, error } = await client.GET('/auth/me')\n * const { data: result } = await client.POST('/auth/login', {\n * body: { email, password },\n * })\n * ```\n */\n\nimport {\n\tBASE_RETRY_DELAY_MS,\n\tCIRCUIT_BREAKER_FAILURE_THRESHOLD,\n\tCIRCUIT_BREAKER_OPEN_DURATION_MS,\n\tCIRCUIT_BREAKER_WINDOW_MS,\n\tDEFAULT_SDK_API_HOST,\n\tDEFAULT_TIMEOUT_MS,\n\tETAG_CACHE_MAX_ENTRIES,\n\tETAG_CACHE_TTL_MS,\n\tMAX_RETRY_DELAY_MS,\n\tSDK_API_PATH,\n\tSDK_PLATFORM,\n\tSDK_VERSION,\n} from './constants'\nimport { exponentialBackoff } from './errors'\nimport { validateAndSanitizeSecretKey } from './key-validation'\n\n/**\n * Retry configuration for automatic request retries\n */\nexport interface RetryConfig {\n\t/** Maximum number of retries (default: 3) */\n\tmaxRetries?: number\n\t/** Base delay in milliseconds (default: 1000) */\n\tbaseDelay?: number\n\t/** Maximum delay in milliseconds (default: 30000) */\n\tmaxDelay?: number\n\t/** Custom function to determine if error is retryable */\n\tshouldRetry?: (status: number, attempt: number) => boolean\n\t/** Request timeout in milliseconds (default: 30000) */\n\ttimeout?: number\n}\n\n/**\n * Request deduplication configuration\n */\nexport interface DeduplicationConfig {\n\t/** Enable request deduplication (default: true) */\n\tenabled?: boolean\n\t/** HTTP methods to deduplicate (default: ['GET']) */\n\tmethods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')[]\n}\n\ntype DeduplicatedMethod = NonNullable<DeduplicationConfig['methods']>[number]\n\n/**\n * Circuit breaker configuration (AWS/Resilience4j pattern)\n *\n * Prevents cascade failures by fast-failing when service is unhealthy.\n * States: CLOSED (normal) → OPEN (failing) → HALF_OPEN (testing)\n */\nexport interface CircuitBreakerConfig {\n\t/** Enable circuit breaker (default: true) */\n\tenabled?: boolean\n\t/** Number of failures before opening circuit (default: 5) */\n\tfailureThreshold?: number\n\t/** Time window for counting failures in ms (default: 10000) */\n\twindowMs?: number\n\t/** How long circuit stays open in ms (default: 30000) */\n\topenDurationMs?: number\n\t/** Custom function to determine if response is a failure */\n\tisFailure?: (status: number) => boolean\n}\n\n/**\n * ETag/Conditional request configuration (HTTP caching pattern)\n *\n * Enables HTTP conditional requests with If-None-Match header\n * to avoid re-downloading unchanged data (saves bandwidth).\n */\nexport interface ETagConfig {\n\t/** Enable ETag caching (default: true for GET requests) */\n\tenabled?: boolean\n\t/** Maximum cache entries (default: 100) */\n\tmaxEntries?: number\n\t/** Cache TTL in milliseconds (default: 5 minutes) */\n\tttlMs?: number\n}\n\n/**\n * Configuration for the REST client\n *\n * The app key identifies the app — no separate app ID needed.\n */\nexport interface RestClientConfig {\n\t/**\n\t * Your app key — identifies the app and environment.\n\t *\n\t * Accepts either:\n\t * - Secret key (sk_dev_, sk_stg_, sk_prod_) — full access, server-side only\n\t * - Publishable key (app_dev_, app_stg_, app_prod_) — limited access, safe for client\n\t */\n\tsecretKey: string\n\t/** Platform URL (default: https://sylphx.com) */\n\tplatformUrl?: string\n\t/** Retry configuration (default: 3 retries with exponential backoff) */\n\tretry?: RetryConfig | false\n\t/**\n\t * Request deduplication configuration (default: enabled for GET)\n\t *\n\t * Prevents duplicate concurrent requests for the same resource.\n\t * When multiple components request the same data simultaneously,\n\t * only one API call is made and the result is shared.\n\t */\n\tdeduplication?: DeduplicationConfig | false\n\t/**\n\t * Circuit breaker configuration (default: enabled)\n\t *\n\t * Prevents cascade failures by fast-failing when service is unhealthy.\n\t * Opens after 5 failures in 10s, stays open for 30s, then allows test request.\n\t */\n\tcircuitBreaker?: CircuitBreakerConfig | false\n\t/**\n\t * ETag caching configuration (default: enabled for GET)\n\t *\n\t * Uses HTTP conditional requests to avoid re-downloading unchanged data.\n\t * Saves bandwidth by returning 304 Not Modified when content hasn't changed.\n\t */\n\tetag?: ETagConfig | false\n}\n\n/**\n * Dynamic configuration that can change at runtime (e.g., access token)\n */\nexport interface RestDynamicConfig {\n\t/** Your secret key (sk_* or app_*) — identifies the app */\n\tsecretKey?: string\n\t/** Platform URL (default: https://sylphx.com) */\n\tplatformUrl?: string\n\t/** Get the current access token (called on each request) */\n\tgetAccessToken?: () => string | null | undefined\n\t/** Retry configuration (default: 3 retries with exponential backoff) */\n\tretry?: RetryConfig | false\n\t/** Request deduplication configuration (default: enabled for GET) */\n\tdeduplication?: DeduplicationConfig | false\n\t/** Circuit breaker configuration (default: enabled) */\n\tcircuitBreaker?: CircuitBreakerConfig | false\n\t/** ETag caching configuration (default: enabled for GET) */\n\tetag?: ETagConfig | false\n}\n\n// ============================================================================\n// Middleware Plumbing\n// ============================================================================\n\n/**\n * Middleware contract — kept structurally identical to the previous\n * `openapi-fetch` shape so existing implementations (dedup / circuit breaker\n * / ETag / retry) compose without change.\n */\nexport interface Middleware {\n\tonRequest?: (ctx: { request: Request }) => Promise<Request | undefined> | Request | undefined\n\tonFetch?: (ctx: {\n\t\trequest: Request\n\t\tnext: (request: Request) => Promise<Response>\n\t}) => Promise<Response | undefined> | Response | undefined\n\tonResponse?: (ctx: {\n\t\trequest: Request\n\t\tresponse: Response\n\t}) => Promise<Response | undefined> | Response | undefined\n}\n\n/**\n * Run middleware pipeline over a request → response round-trip.\n *\n * `onRequest` handlers may mutate/replace the outgoing `Request`. One of them\n * may throw to short-circuit the call (circuit breaker uses this). All\n * `onResponse` handlers run in the order middleware was registered, so later\n * middleware observes earlier middleware's transforms.\n */\nasync function runPipeline(\n\tmiddlewares: readonly Middleware[],\n\tinitial: Request,\n): Promise<Response> {\n\tlet request = initial\n\tfor (const mw of middlewares) {\n\t\tif (mw.onRequest) {\n\t\t\tconst next = await mw.onRequest({ request })\n\t\t\tif (next) request = next\n\t\t}\n\t}\n\n\tconst fetchWithMiddleware = middlewares.reduceRight<(request: Request) => Promise<Response>>(\n\t\t(next, mw) =>\n\t\t\tmw.onFetch\n\t\t\t\t? async (request) => {\n\t\t\t\t\t\tconst response = await mw.onFetch?.({ request, next })\n\t\t\t\t\t\treturn response ?? next(request)\n\t\t\t\t\t}\n\t\t\t\t: next,\n\t\t(request) => fetch(request),\n\t)\n\n\tlet response = await fetchWithMiddleware(request)\n\n\tfor (const mw of middlewares) {\n\t\tif (mw.onResponse) {\n\t\t\tconst next = await mw.onResponse({ request, response })\n\t\t\tif (next) response = next\n\t\t}\n\t}\n\n\treturn response\n}\n\n/**\n * Serialize a URL + query object. Undefined values are dropped. Values are\n * coerced to string via URLSearchParams (matches the previous openapi-fetch\n * behaviour — `{ foo: undefined }` does not emit `?foo=undefined`).\n */\nfunction buildUrl(baseUrl: string, path: string, params?: Record<string, unknown>): string {\n\tconst url = `${baseUrl}${path}`\n\tif (!params) return url\n\tconst entries = Object.entries(params).filter(([, v]) => v !== undefined)\n\tif (entries.length === 0) return url\n\tconst search = new URLSearchParams(\n\t\tentries.map(([k, v]) => [k, String(v)] as [string, string]),\n\t).toString()\n\treturn `${url}?${search}`\n}\n\nfunction cloneRequestWithHeaders(\n\trequest: Request,\n\tupdateHeaders: (headers: Headers) => void,\n): Request {\n\tconst headers = new Headers(request.headers)\n\tupdateHeaders(headers)\n\treturn new Request(request, { headers })\n}\n\n/**\n * Options for an HTTP request — structural subset of `openapi-fetch`'s\n * `FetchOptions` so existing callsites (`client.GET('/path', { params: {...} })`)\n * continue to compile.\n */\nexport interface RequestOptions {\n\tbody?: unknown\n\tparams?: {\n\t\tpath?: Record<string, string>\n\t\tquery?: Record<string, unknown>\n\t}\n\theaders?: Record<string, string>\n}\n\n/**\n * Response envelope — `{ data, error, response }` mirroring `openapi-fetch`.\n * `data` is present on 2xx, `error` on non-2xx; both carry the parsed JSON\n * body when the server returns one. `response` is always the raw `Response`\n * so consumers can read headers (Content-Type, Retry-After, etc.).\n */\nexport interface FetchResponse<TOk, TError> {\n\tdata?: TOk\n\terror?: TError\n\tresponse: Response\n}\n\n/**\n * Replace `{path}` style tokens with URL-encoded values. Mirrors\n * openapi-fetch's `params.path` substitution.\n */\nfunction interpolatePath(path: string, pathParams?: Record<string, string>): string {\n\tif (!pathParams) return path\n\treturn path.replace(/\\{(\\w+)\\}/g, (_match, key: string) => {\n\t\tconst value = pathParams[key]\n\t\tif (value === undefined) return `{${key}}`\n\t\treturn encodeURIComponent(value)\n\t})\n}\n\nasync function executeRequest<TOk = unknown, TError = unknown>(\n\tmethod: string,\n\tbaseUrl: string,\n\tpath: string,\n\toptions: RequestOptions | undefined,\n\tbaseHeaders: Record<string, string>,\n\tmiddlewares: readonly Middleware[],\n): Promise<FetchResponse<TOk, TError>> {\n\tconst finalPath = interpolatePath(path, options?.params?.path)\n\tconst url = buildUrl(baseUrl, finalPath, options?.params?.query)\n\n\tconst headers: Record<string, string> = { ...baseHeaders, ...options?.headers }\n\tconst init: RequestInit = { method, headers }\n\tif (options?.body !== undefined) {\n\t\tinit.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body)\n\t\tif (!headers['Content-Type'] && !headers['content-type']) {\n\t\t\theaders['Content-Type'] = 'application/json'\n\t\t}\n\t}\n\n\tconst request = new Request(url, init)\n\tconst response = await runPipeline(middlewares, request)\n\n\t// openapi-fetch's envelope:\n\t// - 2xx → { data: parsedBody, response }\n\t// - non-2xx → { error: parsedBody, response }\n\t// - parse failure → parsed becomes undefined on either branch (consumers\n\t// fall back to `response.text()` themselves).\n\tconst contentType = response.headers.get('content-type') ?? ''\n\tlet parsed: unknown\n\tif (contentType.includes('json')) {\n\t\ttry {\n\t\t\tparsed = await response.clone().json()\n\t\t} catch {\n\t\t\tparsed = undefined\n\t\t}\n\t} else if (response.status !== 204 && response.status !== 205) {\n\t\ttry {\n\t\t\tconst text = await response.clone().text()\n\t\t\tparsed = text === '' ? undefined : text\n\t\t} catch {\n\t\t\tparsed = undefined\n\t\t}\n\t}\n\n\tif (response.ok) {\n\t\treturn { data: parsed as TOk, response }\n\t}\n\treturn { error: parsed as TError, response }\n}\n\n/**\n * The typed method surface. Callers pass a path + options; return envelope\n * follows openapi-fetch's shape so migration is drop-in for the small number\n * of internal callers that relied on `.GET` / `.POST` etc.\n */\nexport interface RestClient {\n\tGET<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPOST<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPUT<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tPATCH<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tDELETE<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tHEAD<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tOPTIONS<TOk = unknown, TError = unknown>(\n\t\tpath: string,\n\t\toptions?: RequestOptions,\n\t): Promise<FetchResponse<TOk, TError>>\n\tuse(...middleware: readonly Middleware[]): void\n}\n\nexport type DynamicRestClient = RestClient\n\n/**\n * Build a method-dispatch REST client. Each verb thin-wraps `executeRequest`\n * so middleware composition and request shape stay identical across verbs.\n */\nfunction buildClient(\n\tbaseUrl: string,\n\tbaseHeaders: Record<string, string>,\n): { client: RestClient; middlewares: Middleware[] } {\n\tconst middlewares: Middleware[] = []\n\tconst dispatch =\n\t\t<TOk, TError>(method: string) =>\n\t\t(path: string, options?: RequestOptions) =>\n\t\t\texecuteRequest<TOk, TError>(method, baseUrl, path, options, baseHeaders, middlewares)\n\n\tconst client: RestClient = {\n\t\tGET: dispatch('GET'),\n\t\tPOST: dispatch('POST'),\n\t\tPUT: dispatch('PUT'),\n\t\tPATCH: dispatch('PATCH'),\n\t\tDELETE: dispatch('DELETE'),\n\t\tHEAD: dispatch('HEAD'),\n\t\tOPTIONS: dispatch('OPTIONS'),\n\t\tuse(...mws: readonly Middleware[]) {\n\t\t\tmiddlewares.push(...mws)\n\t\t},\n\t}\n\n\treturn { client, middlewares }\n}\n\n/**\n * Create auth middleware that adds app credentials, access token, and SDK headers\n */\nfunction createAuthMiddleware(config: RestDynamicConfig): Middleware {\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\treturn cloneRequestWithHeaders(request, (headers) => {\n\t\t\t\t// Add SDK identification headers for debugging and analytics\n\t\t\t\theaders.set('X-SDK-Version', SDK_VERSION)\n\t\t\t\theaders.set('X-SDK-Platform', SDK_PLATFORM)\n\n\t\t\t\t// Add secret key if provided — identifies the app\n\t\t\t\tif (config.secretKey) {\n\t\t\t\t\theaders.set('x-app-secret', config.secretKey)\n\t\t\t\t}\n\n\t\t\t\t// Add access token if available\n\t\t\t\tconst token = config.getAccessToken?.()\n\t\t\t\tif (token) {\n\t\t\t\t\theaders.set('Authorization', `Bearer ${token}`)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t}\n}\n\n/**\n * Check if a status code is retryable\n */\nfunction isRetryableStatus(status: number): boolean {\n\treturn status >= 500 || status === 429\n}\n\n// ============================================================================\n// Request Deduplication (React Query/SWR pattern)\n// ============================================================================\n\n/**\n * Generate a unique key for a request (for deduplication)\n */\nasync function getRequestKey(request: Request): Promise<string> {\n\tconst body = request.body ? await request.clone().text() : ''\n\treturn `${request.method}:${request.url}:${body}`\n}\n\n/**\n * Create request deduplication middleware (React Query/SWR pattern)\n *\n * Features:\n * - Deduplicates concurrent identical requests\n * - Only applies to GET requests by default (safe to dedupe)\n * - POST/PUT/DELETE are always executed (mutations must run)\n * - Cleans up in-flight tracking after completion\n *\n * @param config - Whether to enable deduplication (default: GET only)\n */\nfunction createDeduplicationMiddleware(\n\tconfig: { enabled?: boolean; methods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')[] } = {},\n): Middleware {\n\tconst { enabled = true, methods = ['GET'] } = config\n\n\tif (!enabled) return {}\n\n\tconst inFlightRequests = new Map<string, Promise<Response>>()\n\n\treturn {\n\t\tasync onFetch({ request, next }) {\n\t\t\t// Only dedupe specified methods (default: GET only)\n\t\t\tconst method = request.method.toUpperCase() as DeduplicatedMethod\n\t\t\tif (!methods.includes(method)) {\n\t\t\t\treturn next(request)\n\t\t\t}\n\n\t\t\tconst key = await getRequestKey(request)\n\t\t\tconst existing = inFlightRequests.get(key)\n\t\t\tif (existing) {\n\t\t\t\tconst cachedResponse = await existing\n\t\t\t\treturn cachedResponse.clone()\n\t\t\t}\n\n\t\t\tconst responsePromise = next(request).then((response) => response.clone())\n\t\t\tinFlightRequests.set(key, responsePromise)\n\n\t\t\ttry {\n\t\t\t\tconst response = await responsePromise\n\t\t\t\treturn response.clone()\n\t\t\t} finally {\n\t\t\t\tinFlightRequests.delete(key)\n\t\t\t}\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Circuit Breaker (AWS/Resilience4j pattern)\n// ============================================================================\n\n/**\n * Circuit breaker state machine\n *\n * CLOSED: Normal operation, requests pass through\n * OPEN: Service unhealthy, all requests fast-fail\n * HALF_OPEN: Testing recovery, allows one request\n */\nexport type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN'\n\n/**\n * Error thrown when circuit is open\n */\nexport class CircuitBreakerOpenError extends Error {\n\treadonly remainingMs: number\n\n\tconstructor(remainingMs: number) {\n\t\tsuper(`Circuit breaker is open. Retry after ${Math.ceil(remainingMs / 1000)}s`)\n\t\tthis.name = 'CircuitBreakerOpenError'\n\t\tthis.remainingMs = remainingMs\n\t}\n}\n\n/**\n * Circuit breaker instance with state management\n */\ninterface CircuitBreaker {\n\tstate: CircuitState\n\tfailures: number[]\n\topenedAt: number | null\n\tconfig: Required<CircuitBreakerConfig>\n}\n\n/**\n * Create a fresh circuit breaker instance.\n *\n * Each REST client gets its own instance — no shared module-level singleton.\n * This prevents cross-client state bleed and makes testing reliable.\n */\nfunction createCircuitBreakerInstance(config: CircuitBreakerConfig = {}): CircuitBreaker {\n\treturn {\n\t\tstate: 'CLOSED',\n\t\tfailures: [],\n\t\topenedAt: null,\n\t\tconfig: {\n\t\t\tenabled: config.enabled ?? true,\n\t\t\tfailureThreshold: config.failureThreshold ?? CIRCUIT_BREAKER_FAILURE_THRESHOLD,\n\t\t\twindowMs: config.windowMs ?? CIRCUIT_BREAKER_WINDOW_MS,\n\t\t\topenDurationMs: config.openDurationMs ?? CIRCUIT_BREAKER_OPEN_DURATION_MS,\n\t\t\tisFailure: config.isFailure ?? ((status) => status >= 500 || status === 429),\n\t\t},\n\t}\n}\n\n/**\n * Record a failure and potentially open the circuit\n */\nfunction recordFailure(cb: CircuitBreaker): void {\n\tconst now = Date.now()\n\n\t// Remove old failures outside the window\n\tcb.failures = cb.failures.filter((t) => now - t < cb.config.windowMs)\n\n\t// Add new failure\n\tcb.failures.push(now)\n\n\t// Check if threshold exceeded\n\tif (cb.failures.length >= cb.config.failureThreshold) {\n\t\tcb.state = 'OPEN'\n\t\tcb.openedAt = now\n\t}\n}\n\n/**\n * Record a success and potentially close the circuit\n */\nfunction recordSuccess(cb: CircuitBreaker): void {\n\tif (cb.state === 'HALF_OPEN') {\n\t\t// Test request succeeded, close the circuit\n\t\tcb.state = 'CLOSED'\n\t\tcb.failures = []\n\t\tcb.openedAt = null\n\t}\n}\n\n/**\n * Check if circuit should allow request\n */\nfunction shouldAllowRequest(cb: CircuitBreaker): {\n\tallowed: boolean\n\tremainingMs?: number\n} {\n\tconst now = Date.now()\n\n\tswitch (cb.state) {\n\t\tcase 'CLOSED':\n\t\t\treturn { allowed: true }\n\n\t\tcase 'OPEN': {\n\t\t\tconst elapsed = now - (cb.openedAt ?? now)\n\t\t\tif (elapsed >= cb.config.openDurationMs) {\n\t\t\t\t// Timeout expired, transition to half-open\n\t\t\t\tcb.state = 'HALF_OPEN'\n\t\t\t\treturn { allowed: true }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\tremainingMs: cb.config.openDurationMs - elapsed,\n\t\t\t}\n\t\t}\n\n\t\tcase 'HALF_OPEN':\n\t\t\t// Only allow one test request at a time\n\t\t\t// In production, you'd use a flag to track if test is in progress\n\t\t\treturn { allowed: true }\n\n\t\tdefault:\n\t\t\treturn { allowed: true }\n\t}\n}\n\n/**\n * Create circuit breaker middleware (AWS/Resilience4j pattern)\n *\n * Features:\n * - Fast-fails when service is unhealthy (prevents cascade failures)\n * - Auto-recovery with half-open state for testing\n * - Configurable failure threshold and timeout\n * - Only counts server errors (5xx) and rate limits (429)\n * - Per-client instance: no shared module-level state\n */\nfunction createCircuitBreakerMiddleware(\n\tconfig: CircuitBreakerConfig | false | undefined,\n): Middleware {\n\tif (config === false) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\t// Create a fresh circuit breaker per client (not a module-level singleton)\n\tconst cb = createCircuitBreakerInstance(config ?? {})\n\n\t// Track for deprecated getCircuitBreakerState() / resetCircuitBreaker() test helpers\n\t_lastCircuitBreaker = cb\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\tif (!cb.config.enabled) {\n\t\t\t\treturn request\n\t\t\t}\n\n\t\t\tconst check = shouldAllowRequest(cb)\n\t\t\tif (!check.allowed) {\n\t\t\t\tthrow new CircuitBreakerOpenError(check.remainingMs!)\n\t\t\t}\n\n\t\t\treturn request\n\t\t},\n\t\tasync onResponse({ response }) {\n\t\t\tif (!cb.config.enabled) {\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\tif (cb.config.isFailure(response.status)) {\n\t\t\t\trecordFailure(cb)\n\t\t\t} else {\n\t\t\t\trecordSuccess(cb)\n\t\t\t}\n\n\t\t\treturn response\n\t\t},\n\t}\n}\n\n/**\n * Module-level reference to the most recently created circuit breaker.\n *\n * Used ONLY by the deprecated `getCircuitBreakerState()` / `resetCircuitBreaker()`\n * helpers (kept for backward compatibility with test suites).\n *\n * Production code should use `client.circuitBreaker.getState()` / `.reset()`\n * from the client object returned by `createRestClient()`.\n */\nlet _lastCircuitBreaker: CircuitBreaker | null = null\n\n/**\n * @deprecated Prefer creating a new `createRestClient()` for isolated state in tests.\n * Resets the most recently created circuit breaker and clears the reference,\n * so `getCircuitBreakerState()` returns null until the next client is created.\n */\nexport function resetCircuitBreaker(): void {\n\tif (_lastCircuitBreaker) {\n\t\t_lastCircuitBreaker.state = 'CLOSED'\n\t\t_lastCircuitBreaker.failures = []\n\t\t_lastCircuitBreaker.openedAt = null\n\t}\n\t_lastCircuitBreaker = null\n}\n\n/**\n * @deprecated Prefer `client.circuitBreaker.getState()` for per-instance state.\n * Returns state of the most recently created circuit breaker (test helper only).\n */\nexport function getCircuitBreakerState(): {\n\tstate: CircuitState\n\tfailures: number\n\topenedAt: number | null\n} | null {\n\tif (!_lastCircuitBreaker) return null\n\treturn {\n\t\tstate: _lastCircuitBreaker.state,\n\t\tfailures: _lastCircuitBreaker.failures.length,\n\t\topenedAt: _lastCircuitBreaker.openedAt,\n\t}\n}\n\n// ============================================================================\n// ETag Cache (HTTP conditional requests)\n// ============================================================================\n\n/**\n * Cached response entry with ETag\n */\ninterface ETagCacheEntry {\n\tetag: string\n\tbody: string\n\ttimestamp: number\n}\n\n/**\n * ETag cache with LRU eviction\n */\nconst etagCache = new Map<string, ETagCacheEntry>()\n\n/**\n * Generate cache key for request\n */\nfunction getETagCacheKey(request: Request): string {\n\treturn `${request.method}:${request.url}`\n}\n\n/**\n * Evict oldest entries when cache is full\n */\nfunction evictOldEntries(maxEntries: number, ttlMs: number): void {\n\tconst now = Date.now()\n\n\t// First, remove expired entries\n\tfor (const [key, entry] of etagCache) {\n\t\tif (now - entry.timestamp > ttlMs) {\n\t\t\tetagCache.delete(key)\n\t\t}\n\t}\n\n\t// If still over limit, remove oldest entries (LRU)\n\tif (etagCache.size > maxEntries) {\n\t\tconst entries = Array.from(etagCache.entries())\n\t\tentries.sort((a, b) => a[1].timestamp - b[1].timestamp)\n\n\t\tconst toRemove = entries.slice(0, entries.length - maxEntries)\n\t\tfor (const [key] of toRemove) {\n\t\t\tetagCache.delete(key)\n\t\t}\n\t}\n}\n\n/**\n * Create ETag middleware for HTTP conditional requests\n *\n * Features:\n * - Caches responses with ETag headers\n * - Sends If-None-Match on subsequent requests\n * - Returns cached response on 304 Not Modified\n * - LRU eviction when cache is full\n * - TTL-based expiration\n */\nfunction createETagMiddleware(config: ETagConfig | false | undefined): Middleware {\n\tif (config === false) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\tconst {\n\t\tenabled = true,\n\t\tmaxEntries = ETAG_CACHE_MAX_ENTRIES,\n\t\tttlMs = ETAG_CACHE_TTL_MS,\n\t} = config ?? {}\n\n\tif (!enabled) {\n\t\treturn {\n\t\t\tasync onRequest({ request }) {\n\t\t\t\treturn request\n\t\t\t},\n\t\t}\n\t}\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\t// Only cache GET requests\n\t\t\tif (request.method !== 'GET') {\n\t\t\t\treturn request\n\t\t\t}\n\n\t\t\tconst cacheKey = getETagCacheKey(request)\n\t\t\tconst cached = etagCache.get(cacheKey)\n\n\t\t\tif (cached) {\n\t\t\t\t// Check TTL\n\t\t\t\tif (Date.now() - cached.timestamp > ttlMs) {\n\t\t\t\t\tetagCache.delete(cacheKey)\n\t\t\t\t} else {\n\t\t\t\t\treturn cloneRequestWithHeaders(request, (headers) => {\n\t\t\t\t\t\theaders.set('If-None-Match', cached.etag)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn request\n\t\t},\n\t\tasync onResponse({ request, response }) {\n\t\t\t// Only cache GET requests\n\t\t\tif (request.method !== 'GET') {\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\tconst cacheKey = getETagCacheKey(request)\n\n\t\t\t// Handle 304 Not Modified\n\t\t\tif (response.status === 304) {\n\t\t\t\tconst cached = etagCache.get(cacheKey)\n\t\t\t\tif (cached) {\n\t\t\t\t\t// Update timestamp (LRU)\n\t\t\t\t\tcached.timestamp = Date.now()\n\n\t\t\t\t\t// Return cached response with original body\n\t\t\t\t\treturn new Response(cached.body, {\n\t\t\t\t\t\tstatus: 200,\n\t\t\t\t\t\theaders: response.headers,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// No cache, return original response\n\t\t\t\treturn response\n\t\t\t}\n\n\t\t\t// Cache successful responses with ETag\n\t\t\tif (response.ok) {\n\t\t\t\tconst etag = response.headers.get('ETag')\n\t\t\t\tif (etag) {\n\t\t\t\t\t// Clone response to read body (can only read once)\n\t\t\t\t\tconst cloned = response.clone()\n\t\t\t\t\tconst body = await cloned.text()\n\n\t\t\t\t\t// Evict old entries if needed\n\t\t\t\t\tevictOldEntries(maxEntries, ttlMs)\n\n\t\t\t\t\t// Cache the response\n\t\t\t\t\tetagCache.set(cacheKey, {\n\t\t\t\t\t\tetag,\n\t\t\t\t\t\tbody,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn response\n\t\t},\n\t}\n}\n\n/**\n * Clear ETag cache (for testing)\n */\nexport function clearETagCache(): void {\n\tetagCache.clear()\n}\n\n/**\n * Get ETag cache stats (for monitoring)\n */\nexport function getETagCacheStats(): { size: number; entries: string[] } {\n\treturn {\n\t\tsize: etagCache.size,\n\t\tentries: Array.from(etagCache.keys()),\n\t}\n}\n\n// ============================================================================\n// Retry Middleware\n// ============================================================================\n\n/**\n * Per-request body storage for retry middleware.\n *\n * Using a WeakMap<Request, string | null> ensures each request has its own\n * stored body, preventing race conditions when multiple concurrent requests\n * use the same client instance.\n *\n * WeakMap keys are garbage-collected when the Request object is GC'd,\n * so no manual cleanup is needed.\n */\nconst retryBodyMap = new WeakMap<Request, string | null>()\n\n/**\n * Create retry middleware with exponential backoff and timeout\n *\n * Features:\n * - Request timeout (default 30s) prevents infinite hangs\n * - Exponential backoff with jitter for retries\n * - Respects Retry-After header for rate limiting\n * - Per-request body storage (WeakMap) — safe for concurrent requests\n */\nfunction createRetryMiddleware(retryConfig: RetryConfig | false | undefined): Middleware {\n\tif (retryConfig === false) {\n\t\t// No-op middleware - just passes through\n\t\treturn {\n\t\t\tasync onResponse({ response }) {\n\t\t\t\treturn response\n\t\t\t},\n\t\t}\n\t}\n\n\tconst {\n\t\tmaxRetries = 3,\n\t\tbaseDelay = BASE_RETRY_DELAY_MS,\n\t\tmaxDelay = MAX_RETRY_DELAY_MS,\n\t\tshouldRetry = isRetryableStatus,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t} = retryConfig ?? {}\n\n\treturn {\n\t\tasync onRequest({ request }) {\n\t\t\t// Read body before it's consumed, store per-request in WeakMap\n\t\t\tconst body = request.body ? await request.clone().text() : null\n\n\t\t\t// Add timeout signal\n\t\t\tconst controller = new AbortController()\n\t\t\tsetTimeout(() => controller.abort(), timeout)\n\n\t\t\tconst newRequest = new Request(request.url, {\n\t\t\t\tmethod: request.method,\n\t\t\t\theaders: request.headers,\n\t\t\t\tbody,\n\t\t\t\tsignal: controller.signal,\n\t\t\t})\n\n\t\t\t// Associate body with this specific request object (concurrent-safe)\n\t\t\tretryBodyMap.set(newRequest, body)\n\n\t\t\treturn newRequest\n\t\t},\n\t\tasync onResponse({ response, request }) {\n\t\t\t// Retrieve body for this specific request (not shared across requests)\n\t\t\tconst originalBody = retryBodyMap.get(request) ?? null\n\n\t\t\tlet attempt = 0\n\t\t\tlet currentResponse = response\n\n\t\t\t// Check if we need to retry using the shouldRetry callback\n\t\t\twhile (attempt < maxRetries && shouldRetry(currentResponse.status, attempt)) {\n\t\t\t\tconst retryAfter = currentResponse.headers.get('Retry-After')\n\t\t\t\tconst delay = retryAfter\n\t\t\t\t\t? Number.parseInt(retryAfter, 10) * 1000\n\t\t\t\t\t: exponentialBackoff(attempt, baseDelay, maxDelay)\n\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\tattempt++\n\n\t\t\t\t// Create timeout for retry\n\t\t\t\tconst controller = new AbortController()\n\t\t\t\tconst timeoutId = setTimeout(() => controller.abort(), timeout)\n\n\t\t\t\ttry {\n\t\t\t\t\t// Reconstruct request with per-request stored body and new signal\n\t\t\t\t\tconst retryRequest = new Request(request.url, {\n\t\t\t\t\t\tmethod: request.method,\n\t\t\t\t\t\theaders: request.headers,\n\t\t\t\t\t\tbody: originalBody,\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t})\n\n\t\t\t\t\tconst newResponse = await fetch(retryRequest)\n\t\t\t\t\tclearTimeout(timeoutId)\n\n\t\t\t\t\t// If successful or non-retryable client error, return\n\t\t\t\t\tif (newResponse.ok || !shouldRetry(newResponse.status, attempt)) {\n\t\t\t\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\t\t\t\treturn newResponse\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentResponse = newResponse\n\t\t\t\t} catch (error) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t// On network/timeout error during retry, continue to next attempt\n\t\t\t\t\tif (attempt >= maxRetries) {\n\t\t\t\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tretryBodyMap.delete(request) // cleanup\n\t\t\treturn currentResponse\n\t\t},\n\t}\n}\n\n/**\n * Validate and sanitize REST client configuration (SSOT helper)\n */\nfunction validateClientConfig(config: { secretKey?: string; platformUrl?: string }) {\n\treturn {\n\t\tsecretKey: validateAndSanitizeSecretKey(config.secretKey),\n\t\tbaseUrl: (config.platformUrl || `https://${DEFAULT_SDK_API_HOST}`).trim(),\n\t}\n}\n\n/**\n * Create a type-safe REST API client.\n *\n * Uses plain `fetch` with a configurable middleware chain (deduplication →\n * circuit breaker → ETag → retry). All endpoints accept a string path; the\n * return type is openapi-fetch-compatible (`{ data, error, response }`).\n *\n * @example\n * ```typescript\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * const { data: user } = await client.GET('/auth/me')\n * const { data: plans } = await client.GET('/billing/plans')\n * const { data: result } = await client.POST('/auth/login', {\n * body: { email: 'test@example.com', password: 'secret' },\n * })\n * ```\n */\nexport function createRestClient(config: RestClientConfig): RestClient {\n\tconst { secretKey, baseUrl } = validateClientConfig(config)\n\n\tconst { client, middlewares } = buildClient(`${baseUrl}${SDK_API_PATH}`, {\n\t\t'Content-Type': 'application/json',\n\t\t'x-app-secret': secretKey,\n\t})\n\n\t// Add deduplication middleware first (before other middleware)\n\tif (config.deduplication !== false) {\n\t\tmiddlewares.push(createDeduplicationMiddleware(config.deduplication))\n\t}\n\n\t// Add circuit breaker middleware (before retry)\n\tif (config.circuitBreaker !== false) {\n\t\tmiddlewares.push(createCircuitBreakerMiddleware(config.circuitBreaker))\n\t}\n\n\t// Add ETag caching middleware (before retry, for HTTP conditional requests)\n\tif (config.etag !== false) {\n\t\tmiddlewares.push(createETagMiddleware(config.etag))\n\t}\n\n\t// Add retry middleware (last, so it can retry after circuit allows)\n\tmiddlewares.push(createRetryMiddleware(config.retry))\n\n\treturn client\n}\n\n/**\n * Create a dynamic REST client with runtime token injection.\n *\n * Use this when you need to inject an access token that may change. Tokens\n * should be read from HttpOnly cookies via a server endpoint, never from\n * localStorage (XSS vulnerability).\n *\n * @example\n * ```typescript\n * const client = createDynamicRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * getAccessToken: async () => (await cookies()).get('session')?.value,\n * })\n * ```\n */\nexport function createDynamicRestClient(config: RestDynamicConfig): DynamicRestClient {\n\tconst { secretKey, baseUrl } = validateClientConfig(config)\n\n\tconst validatedConfig: RestDynamicConfig = {\n\t\t...config,\n\t\tsecretKey,\n\t\tplatformUrl: baseUrl,\n\t}\n\n\tconst { client, middlewares } = buildClient(`${baseUrl}${SDK_API_PATH}`, {\n\t\t'Content-Type': 'application/json',\n\t})\n\n\t// Add deduplication middleware first (before other middleware)\n\tif (config.deduplication !== false) {\n\t\tmiddlewares.push(createDeduplicationMiddleware(config.deduplication))\n\t}\n\n\t// Add auth middleware (runs on each request)\n\tmiddlewares.push(createAuthMiddleware(validatedConfig))\n\n\t// Add circuit breaker middleware (before retry)\n\tif (config.circuitBreaker !== false) {\n\t\tmiddlewares.push(createCircuitBreakerMiddleware(config.circuitBreaker))\n\t}\n\n\t// Add ETag caching middleware (before retry, for HTTP conditional requests)\n\tif (config.etag !== false) {\n\t\tmiddlewares.push(createETagMiddleware(config.etag))\n\t}\n\n\t// Add retry middleware (last, so it can retry after circuit allows)\n\tmiddlewares.push(createRetryMiddleware(config.retry))\n\n\treturn client\n}\n\n/**\n * Check if a REST response has an error\n */\nexport function hasError<T, E>(response: {\n\tdata?: T\n\terror?: E\n}): response is {\n\tdata: undefined\n\terror: E\n} {\n\treturn response.error !== undefined\n}\n\n/**\n * Extract error message from REST error response\n */\nexport function getRestErrorMessage(error: unknown): string {\n\tif (error && typeof error === 'object' && 'error' in error) {\n\t\tconst err = error as { error?: { message?: string } }\n\t\treturn err.error?.message ?? 'An unknown error occurred'\n\t}\n\tif (error instanceof Error) {\n\t\treturn error.message\n\t}\n\treturn 'An unknown error occurred'\n}\n","/**\n * API Key Validation — Single Source of Truth\n *\n * OAuth 2.0 standard key validation for Sylphx Platform.\n * ALL key validation, sanitization, and environment detection logic lives here.\n *\n * Principles:\n * 1. Fail fast - Invalid input rejected immediately with clear errors\n * 2. Helpful errors - Tell users exactly what's wrong and how to fix it\n * 3. Development warnings - Warn about issues that would fail in production\n * 4. No silent fixes - Transparency over convenience (but warn + continue)\n * 5. Single Source of Truth - All key logic in one place\n *\n * Key Formats (ADR-021):\n * - Publishable key: pk_(dev|stg|prod|prev)_{ref}_{32hex} — client-safe (new)\n * - Secret Key: sk_(dev|stg|prod|prev)_{ref}_{64hex} — server-side only\n *\n * Legacy Key Formats (backward-compat):\n * - App ID (old): app_(dev|stg|prod|prev)_[identifier] — Public identifier\n *\n * Special Internal Formats (NOT rotated):\n * - Console bootstrap: app_prod_platform_{slug} / sk_prod_platform_{slug}\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Environment type derived from key prefix */\nexport type EnvironmentType = 'development' | 'staging' | 'production' | 'preview'\n\n/** Key type - publicKey (pk_*), appId (legacy app_*), or secret (sk_*) */\nexport type KeyType = 'publicKey' | 'appId' | 'secret'\n\n/** Validation result with clear error information */\nexport interface KeyValidationResult {\n\t/** Whether the key is valid (possibly after sanitization) */\n\tvalid: boolean\n\t/** The sanitized key to use (only if valid) */\n\tsanitizedKey: string\n\t/** Detected key type */\n\tkeyType?: KeyType\n\t/** Detected environment */\n\tenvironment?: EnvironmentType\n\t/** Error message if invalid */\n\terror?: string\n\t/** Warning message if key was auto-fixed */\n\twarning?: string\n\t/** Detected issues for debugging */\n\tissues?: string[]\n}\n\n// =============================================================================\n// Patterns — Strict Format Validation\n// =============================================================================\n\n/**\n * Publishable key pattern: pk_(dev|stg|prod|prev)_{32hex} (ADR-055) or\n * pk_(dev|stg|prod|prev)_{ref}_{32hex} (legacy ADR-021).\n * - Prefix: pk_ (publishable key, safe for client)\n * - Environment: dev, stg, prod, or prev\n * - Ref: optional 12-char base36 project ref\n * - Token: 32 hex chars (128-bit random)\n *\n * Example: pk_prod_2dubco39o9so_f19e5cdc3cc54f7ff81bdc26ec5bfbad\n */\nconst PUBLIC_KEY_PATTERN = /^pk_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32}$/\n\n/**\n * Legacy App ID pattern: app_(dev|stg|prod|prev)_[identifier]\n * - Prefix: app_ (application identifier, public) — legacy format, backward-compat\n * - Environment: dev, stg, prod, or prev\n * - Suffix: alphanumeric with underscores/hyphens (hex for apps, or internal identifiers)\n *\n * Accepts both legacy app_* format and ADR-021 pk_* publishable keys.\n * pk_{env}_{ref}_{hex} is the standard customer key — Customer Zero uses it as appId.\n */\nconst APP_ID_PATTERN = /^(app|pk)_(dev|stg|prod|prev)_[a-z0-9_-]+$/\n\n/**\n * Secret key pattern: sk_(dev|stg|prod|prev)_[identifier]\n * - Prefix: sk_ (secret key)\n * - Environment: dev, stg, prod, or prev\n * - Suffix: alphanumeric with underscores/hyphens\n *\n * Accepts both old format (sk_prod_{64hex}) and new format (sk_prod_{ref}_{64hex}).\n */\nconst SECRET_KEY_PATTERN = /^sk_(dev|stg|prod|prev)_[a-z0-9_-]+$/\n\n/** Environment prefix to type mapping */\nconst ENV_PREFIX_MAP: Record<string, EnvironmentType> = {\n\tdev: 'development',\n\tstg: 'staging',\n\tprod: 'production',\n\tprev: 'preview',\n}\n\n// =============================================================================\n// Core Validation Functions\n// =============================================================================\n\n/**\n * Detect common issues with a key (whitespace, newlines, etc.)\n */\nfunction detectKeyIssues(key: string): string[] {\n\tconst issues: string[] = []\n\tif (key !== key.trim()) issues.push('whitespace')\n\tif (key.includes('\\n')) issues.push('newline')\n\tif (key.includes('\\r')) issues.push('carriage-return')\n\tif (key.includes(' ')) issues.push('space')\n\tif (key !== key.toLowerCase()) issues.push('uppercase-chars')\n\treturn issues\n}\n\n/**\n * Create a helpful warning message for keys that needed sanitization\n */\nfunction createSanitizationWarning(keyType: KeyType, issues: string[], envVarName: string): string {\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\treturn (\n\t\t`[Sylphx] ${keyTypeName} contains ${issues.join(', ')}. ` +\n\t\t`This is commonly caused by Vercel CLI's 'env pull' command.\\n\\n` +\n\t\t`To fix permanently:\\n` +\n\t\t`1. Go to Vercel Dashboard → Your Project → Settings → Environment Variables\\n` +\n\t\t`2. Edit ${envVarName}\\n` +\n\t\t`3. Remove any trailing whitespace or newline characters\\n` +\n\t\t`4. Redeploy your application\\n\\n` +\n\t\t`The SDK will automatically sanitize the key, but fixing the source is recommended.`\n\t)\n}\n\n/**\n * Create a helpful error message for invalid keys\n */\nfunction createInvalidKeyError(keyType: KeyType, key: string, envVarName: string): string {\n\tconst maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key\n\tconst formatHint =\n\t\tkeyType === 'appId'\n\t\t\t? 'pk_(dev|stg|prod|prev)_{ref}_{hex} or app_(dev|stg|prod|prev)_[id]'\n\t\t\t: 'sk_(dev|stg|prod|prev)_{ref}_{hex}'\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\n\treturn (\n\t\t`[Sylphx] Invalid ${keyTypeName} format.\\n\\n` +\n\t\t`Expected format: ${formatHint}\\n` +\n\t\t`Received: \"${maskedKey}\"\\n\\n` +\n\t\t`Please check your ${envVarName} environment variable.\\n` +\n\t\t`You can find your keys in the Sylphx Console → API Keys.\\n\\n` +\n\t\t`Common issues:\\n` +\n\t\t`• Key has uppercase characters (must be lowercase)\\n` +\n\t\t`• Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)\\n` +\n\t\t`• Key has invalid environment (must be dev, stg, prod, or prev)\\n` +\n\t\t`• Key was copied with extra whitespace`\n\t)\n}\n\n/**\n * Extract environment from a validated key\n */\nfunction extractEnvironment(key: string): EnvironmentType | undefined {\n\t// Match pk_, app_, or sk_ prefix followed by environment\n\tconst match = key.match(/^(?:app|pk|sk)_(dev|stg|prod|prev)_/)\n\tif (!match) return undefined\n\treturn ENV_PREFIX_MAP[match[1]]\n}\n\n/**\n * Internal: Generic key validation logic for specific key types\n */\nfunction validateKeyForType(\n\tkey: string | undefined | null,\n\tkeyType: KeyType,\n\tpattern: RegExp,\n\tenvVarName: string,\n): KeyValidationResult {\n\tconst keyTypeName = keyType === 'appId' ? 'App ID' : 'Secret Key'\n\n\t// Check if key is provided\n\tif (!key) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tsanitizedKey: '',\n\t\t\terror:\n\t\t\t\t`[Sylphx] ${keyTypeName} is required. ` +\n\t\t\t\t`Set ${envVarName} in your environment variables.`,\n\t\t\tissues: ['missing'],\n\t\t}\n\t}\n\n\t// Detect issues before validation\n\tconst issues = detectKeyIssues(key)\n\n\t// Check if key matches expected format exactly\n\tif (pattern.test(key)) {\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsanitizedKey: key,\n\t\t\tkeyType,\n\t\t\tenvironment: extractEnvironment(key),\n\t\t\tissues: [],\n\t\t}\n\t}\n\n\t// Key doesn't match - try sanitization (trim + lowercase)\n\tconst sanitized = key.trim().toLowerCase()\n\n\tif (pattern.test(sanitized)) {\n\t\t// Sanitization fixes the issue\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsanitizedKey: sanitized,\n\t\t\tkeyType,\n\t\t\tenvironment: extractEnvironment(sanitized),\n\t\t\twarning: createSanitizationWarning(keyType, issues, envVarName),\n\t\t\tissues,\n\t\t}\n\t}\n\n\t// Sanitization doesn't fix it - key format is genuinely wrong\n\treturn {\n\t\tvalid: false,\n\t\tsanitizedKey: '',\n\t\terror: createInvalidKeyError(keyType, key, envVarName),\n\t\tissues: [...issues, 'invalid-format'],\n\t}\n}\n\n// =============================================================================\n// Public API — Publishable Key (ADR-021: pk_*) and legacy App ID (app_*)\n// =============================================================================\n\n/**\n * Validate an ADR-021 publishable key (pk_*).\n *\n * @example\n * ```typescript\n * const result = validatePublicKey('pk_prod_...')\n * if (!result.valid) throw new Error(result.error)\n * ```\n */\nexport function validatePublicKey(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'publicKey', PUBLIC_KEY_PATTERN, 'publishable credential')\n}\n\n/**\n * Validate and sanitize a publishable key, throwing on invalid input.\n */\nexport function validateAndSanitizePublicKey(key: string | undefined | null): string {\n\tconst result = validatePublicKey(key)\n\tif (!result.valid) throw new Error(result.error)\n\tif (result.warning) console.warn(result.warning)\n\treturn result.sanitizedKey\n}\n\n/**\n * Validate a legacy App ID (app_*) and return detailed results.\n *\n * @deprecated Use validatePublicKey() for new pk_* keys (ADR-021).\n *\n * @example\n * ```typescript\n * const result = validateAppId(process.env.NEXT_PUBLIC_SYLPHX_APP_ID)\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * if (result.warning) {\n * console.warn(result.warning)\n * }\n * ```\n */\nexport function validateAppId(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'appId', APP_ID_PATTERN, 'NEXT_PUBLIC_SYLPHX_APP_ID')\n}\n\n/**\n * Validate and sanitize App ID, logging warnings.\n *\n * @deprecated Use validateAndSanitizePublicKey() for new pk_* keys (ADR-021).\n * @throws Error if the key is invalid and cannot be sanitized\n * @returns The sanitized App ID\n */\nexport function validateAndSanitizeAppId(key: string | undefined | null): string {\n\tconst result = validateAppId(key)\n\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\n\tif (result.warning) {\n\t\tconsole.warn(result.warning)\n\t}\n\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Secret Keys\n// =============================================================================\n\n/**\n * Validate a secret key and return detailed results\n *\n * @example\n * ```typescript\n * const result = validateSecretKey('sk_prod_...')\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * ```\n */\nexport function validateSecretKey(key: string | undefined | null): KeyValidationResult {\n\treturn validateKeyForType(key, 'secret', SECRET_KEY_PATTERN, 'secret credential')\n}\n\n/**\n * Validate and sanitize secret key, logging warnings\n *\n * @throws Error if the key is invalid and cannot be sanitized\n * @returns The sanitized secret key\n */\nexport function validateAndSanitizeSecretKey(key: string | undefined | null): string {\n\tconst result = validateSecretKey(key)\n\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\n\tif (result.warning) {\n\t\tconsole.warn(result.warning)\n\t}\n\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Environment Detection (SSOT)\n// =============================================================================\n\n/**\n * Detect environment type from any key (App ID or Secret Key)\n *\n * @example\n * ```typescript\n * detectEnvironment('sk_dev_abc123') // 'development'\n * detectEnvironment('app_prod_xyz789') // 'production'\n * detectEnvironment('sk_stg_qwe456') // 'staging'\n * ```\n *\n * @throws Error if key format is invalid\n */\nexport function detectEnvironment(key: string): EnvironmentType {\n\t// Validate and sanitize first\n\tconst sanitized = key.trim().toLowerCase()\n\n\t// Check all key types (ADR-021 and legacy)\n\tif (sanitized.startsWith('pk_')) {\n\t\tconst result = validatePublicKey(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tif (sanitized.startsWith('sk_')) {\n\t\tconst result = validateSecretKey(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tif (sanitized.startsWith('app_')) {\n\t\tconst result = validateAppId(sanitized)\n\t\tif (!result.valid) throw new Error(result.error)\n\t\treturn result.environment!\n\t}\n\n\tthrow new Error(\n\t\t`[Sylphx] Invalid key format. Key must start with 'pk_' (publishable), 'sk_' (secret), or 'app_' (legacy App ID).`,\n\t)\n}\n\n/**\n * Check if running in development environment based on key\n */\nexport function isDevelopmentKey(key: string): boolean {\n\treturn detectEnvironment(key) === 'development'\n}\n\n/**\n * Check if running in production environment based on key\n */\nexport function isProductionKey(key: string): boolean {\n\treturn detectEnvironment(key) === 'production'\n}\n\n// =============================================================================\n// Public API — Cookie Namespace (SSOT)\n// =============================================================================\n\n/**\n * Get the cookie namespace for a given secret key\n *\n * Used by auth middleware to namespace cookies per environment.\n * This prevents dev/staging/prod cookies from conflicting.\n *\n * @example\n * ```typescript\n * getCookieNamespace('sk_dev_abc123') // 'sylphx_dev'\n * getCookieNamespace('sk_prod_xyz789') // 'sylphx_prod'\n * ```\n */\nexport function getCookieNamespace(secretKey: string): string {\n\tconst env = detectEnvironment(secretKey)\n\tconst shortEnv =\n\t\tenv === 'development' ? 'dev' : env === 'staging' ? 'stg' : env === 'preview' ? 'prev' : 'prod'\n\treturn `sylphx_${shortEnv}`\n}\n\n// =============================================================================\n// Public API — Key Type Detection\n// =============================================================================\n\n/**\n * Detect the type of key.\n *\n * @returns 'publicKey' (pk_*), 'appId' (legacy app_*), 'secret' (sk_*), or null if unknown\n */\nexport function detectKeyType(key: string): KeyType | null {\n\tconst sanitized = key.trim().toLowerCase()\n\tif (sanitized.startsWith('pk_')) return 'publicKey'\n\tif (sanitized.startsWith('app_')) return 'appId'\n\tif (sanitized.startsWith('sk_')) return 'secret'\n\treturn null\n}\n\n/**\n * Check if a key is a publishable/public key (pk_* or legacy app_*)\n */\nexport function isPublishableKey(key: string): boolean {\n\tconst t = detectKeyType(key)\n\treturn t === 'publicKey' || t === 'appId'\n}\n\n/**\n * Check if a key is an App ID (legacy app_* format)\n *\n * @deprecated Use isPublishableKey() to also accept new pk_* keys\n */\nexport function isAppId(key: string): boolean {\n\treturn detectKeyType(key) === 'appId'\n}\n\n/**\n * Check if a key is a secret key (sk_*)\n */\nexport function isSecretKey(key: string): boolean {\n\treturn detectKeyType(key) === 'secret'\n}\n\n/**\n * Validate any key (auto-detects type)\n *\n * Use this when you accept either App ID or Secret Key.\n * The function auto-detects the key type and validates accordingly.\n *\n * @example\n * ```typescript\n * const result = validateKey('sk_prod_...')\n * if (!result.valid) {\n * throw new Error(result.error)\n * }\n * const sanitizedKey = result.sanitizedKey\n * ```\n */\nexport function validateKey(key: string | undefined | null): KeyValidationResult {\n\tconst keyType = key ? detectKeyType(key) : null\n\n\tif (keyType === 'publicKey') {\n\t\treturn validatePublicKey(key)\n\t}\n\tif (keyType === 'appId') {\n\t\treturn validateAppId(key)\n\t}\n\tif (keyType === 'secret') {\n\t\treturn validateSecretKey(key)\n\t}\n\n\t// Unknown key type - return detailed error\n\treturn {\n\t\tvalid: false,\n\t\tsanitizedKey: '',\n\t\terror: key\n\t\t\t? `Invalid key format. Keys must start with 'pk_' (publishable), 'app_' (legacy), or 'sk_' (secret), followed by environment (dev/stg/prod/prev). Got: ${key.slice(0, 20)}...`\n\t\t\t: 'API key is required but was not provided.',\n\t\tissues: key ? ['invalid_format'] : ['missing'],\n\t}\n}\n\n/**\n * Validate any key and return sanitized version (throws on error)\n *\n * Use this when you need the key value and want to throw on invalid input.\n */\nexport function validateAndSanitizeKey(key: string | undefined | null): string {\n\tconst result = validateKey(key)\n\tif (!result.valid) {\n\t\tthrow new Error(result.error)\n\t}\n\tif (result.warning) {\n\t\tconsole.warn(`[Sylphx] ${result.warning}`)\n\t}\n\treturn result.sanitizedKey\n}\n\n// =============================================================================\n// Public API — Runtime Environment Detection\n// =============================================================================\n\n/**\n * Check if we're in development mode (based on NODE_ENV or hostname)\n */\nexport function isDevelopmentRuntime(): boolean {\n\tif (typeof process !== 'undefined' && process.env) {\n\t\treturn process.env.NODE_ENV === 'development'\n\t}\n\tif (typeof window !== 'undefined') {\n\t\treturn window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'\n\t}\n\treturn false\n}\n","/**\n * @sylphx/sdk\n *\n * State-of-the-art platform SDK with pure functions.\n *\n * ## Architecture (see ADR.md for full details)\n *\n * This SDK follows Firebase's architecture pattern:\n * - **Pure functions** - No hidden state, config passed explicitly\n * - **Tree-shakeable** - Import only what you use, bundler removes the rest\n * - **4 entry points only** - Separate only when peer dependencies differ\n *\n * ## Entry Points\n *\n * | Entry | Purpose | Peer Dependencies |\n * |-------|---------|-------------------|\n * | `@sylphx/sdk` | All pure functions | None |\n * | `@sylphx/sdk/react` | React hooks & components | react, react-dom |\n * | `@sylphx/sdk/server` | Server utilities (JWT, webhooks) | jose |\n * | `@sylphx/sdk/nextjs` | Next.js integration | next |\n *\n * ## Service Quick Reference\n *\n * | Service | Key Functions | Description |\n * |---------|---------------|-------------|\n * | Config | `createClient`, `createServerClient`, `withToken` | SDK configuration |\n * | Auth | `signIn`, `signUp`, `signOut` | Authentication |\n * | Analytics | `track`, `page`, `identify` | Event tracking |\n * | AI | `chat`, `embed`, `complete` | AI/LLM operations |\n * | Database | `getDatabaseConnectionString`, `getDatabaseStatus` | Provisioned PostgreSQL |\n * | KV | `kvSet`, `kvGet`, `kvRateLimit` | Managed key-value store |\n * | Realtime | `realtimeEmit`, `getRealtimeHistory` | Managed pub/sub with durable history |\n * | Billing | `getPlans`, `createCheckout` | Subscriptions |\n * | Storage | `storage.uploads.create`, `storage.files.list` | File storage (ADR-100) |\n * | Tasks | `scheduleTask`, `createCron` | Background tasks |\n * | Flags | `checkFlag`, `getFlags`, `getAllFlags` | Feature flags |\n * | Consent | `hasConsent`, `setConsents` | GDPR/CCPA |\n * | Referrals | `getReferralStats`, `redeemReferralCode` | Referral system |\n * | Webhooks | `getWebhookConfig`, `replayWebhookDelivery` | Webhook management |\n * | Email | `sendEmail`, `sendTemplatedEmail` | Email sending |\n * | Notifications | `registerPush`, `sendPush` | Push notifications |\n * | Engagement | `getStreak`, `unlockAchievement` | Gamification |\n * | Monitoring | `captureException`, `captureMessage` | Error tracking |\n * | Deploy | `triggerDeploy`, `getDeployStatus` | CI/CD deployments |\n * | Compute | `MACHINE_SIZES`, `resolveMachineTierResources` | Public machine sizing |\n * | Orgs | `createOrganization`, `inviteOrganizationMember` | Multi-tenancy |\n * | Permissions | `listPermissions`, `hasPermission` | RBAC permissions |\n * | Roles | `listRoles`, `assignMemberRole` | RBAC roles |\n *\n * ## Usage\n *\n * @example\n * ```typescript\n * // Pure functions - tree-shakeable, works anywhere\n * import { createServerClient, track, signIn, getPlans } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Analytics\n * await track(config, { event: 'purchase', properties: { amount: 99 } })\n *\n * // Auth\n * const result = await signIn(config, { email, password })\n *\n * // Billing\n * const plans = await getPlans(config)\n * ```\n *\n * @example\n * ```typescript\n * // REST client for direct API access\n * import { createRestClient, createServerClient } from '@sylphx/sdk'\n *\n * const client = createRestClient({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Type-safe REST calls\n * const { data: plans } = await client.GET('/billing/plans')\n * const { data: user } = await client.GET('/auth/me')\n * ```\n */\n\nexport {\n\tDEFAULT_MACHINE_SIZE,\n\tisMachineSize,\n\tMACHINE_CONFIGS,\n\tMACHINE_MAX_INSTANCES,\n\tMACHINE_RESOURCE_REQUIREMENTS,\n\tMACHINE_SIZES,\n\ttype MachineConfig,\n\ttype MachineResourceRequirements,\n\ttype MachineTierResources,\n\tparseMachineSize,\n\tresolveMachineConfig,\n\tresolveMachineMaxInstances,\n\tresolveMachineResources,\n\tresolveMachineTierResources,\n\ttoPublicMachineSize,\n} from './compute'\nexport * from './config/database-pricing'\nexport * from './config/referrals'\nexport {\n\ttype ErrorDetails as ExtractedErrorDetails,\n\tgetErrorDetails as extractErrorDetails,\n\tgetErrorMessage as extractErrorMessage,\n} from './error-extract'\n\n// =============================================================================\n// Configuration (Foundation)\n// =============================================================================\n\nexport {\n\ttype BuildConnectionUrlInput,\n\tbuildConnectionUrl,\n\ttype ConnectionCredentialType,\n\ttype ConnectionEnv,\n\tCREDENTIAL_REGEX,\n\tcreateClient,\n\tcreateConfig,\n\tcreateServerClient,\n\tInvalidConnectionUrlError,\n\ttype ParsedConnectionUrl,\n\tparseConnectionUrl,\n\ttype SylphxClientInput,\n\ttype SylphxConfig,\n\ttype SylphxConfigInput,\n\twithToken,\n} from './config'\n\n// =============================================================================\n// CSV Utilities\n// =============================================================================\n\nexport { escapeCsvField } from './csv'\n\n// =============================================================================\n// Browser-Safe Shared Utilities\n// =============================================================================\n\nexport {\n\tcalculatePercentage,\n\tformatBytes,\n\tformatCents,\n\tformatCurrency,\n\tformatDate,\n\tformatDateTime,\n\tformatDuration,\n\tformatMicrodollars,\n\tformatMonthYear,\n\tformatNumber,\n\tformatPercent,\n\tformatRelativeTime,\n\tformatRelativeTimeShort,\n\tformatTime,\n\tgetBillingStatusVariant,\n\tgetInvoiceStatusVariant,\n} from './formatting'\n\nexport { safeJsonParse } from './json'\nexport { escapeHtml, generateSlug, getBaseUrl } from './utils'\nexport { type ParsedUserAgent, parseUserAgent } from './utils/user-agent'\nexport {\n\ttype CreateWorkspaceInput,\n\tcreateWorkspace,\n\tdeleteWorkspace,\n\ttype ForkWorkspaceInput,\n\tforkWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\ttype WorkspaceRecord,\n} from './workspaces'\n\n// =============================================================================\n// Shared Product Configuration\n// =============================================================================\n\nexport {\n\tMAX_PASSWORD_LENGTH,\n\tMIN_PASSWORD_LENGTH,\n\tPASSWORD_REQUIREMENTS,\n} from './config/auth'\n\nexport {\n\tBILLING_ALLOWED_ROLES,\n\ttype BillingAllowedRole,\n\tBUILD_MINUTE_PRICES,\n\tBUILD_MINUTES_INCLUDED,\n\tBUILD_SIZE_MULTIPLIERS,\n\tBYTES_PER_GB,\n\tCI_BUILD_MINUTE_PRICE_MICRODOLLARS,\n\tCI_FREE_MINUTES_PER_MONTH,\n\tCI_MACOS_MULTIPLIER,\n\tCI_MACOS_SIZE_MULTIPLIERS,\n\tCI_SIZE_MULTIPLIERS,\n\tCOMPUTE_RAM_RATE_MICRODOLLARS,\n\tCOMPUTE_VCPU_ACTIVE_RATE_MICRODOLLARS,\n\tCOMPUTE_VCPU_IDLE_RATE_MICRODOLLARS,\n\tCREDIT_EXPIRY_MONTHS,\n\thasBillingAccess,\n\tINVOICE_DUE_DAYS,\n\ttype KvMetric,\n\tMAX_PAYMENT_ATTEMPTS,\n\tMICRODOLLARS_PER_CENT,\n\ttype RealtimeMetric,\n\tSERVICE_METRICS,\n\ttype ServiceMetrics,\n} from './config/billing'\n\nexport { CONSOLE_APP_SLUG, getEnvPrefix } from './config/console-keys'\n\nexport {\n\tDEFAULT_MAX_INSTANCES,\n\tgetAvailableInstanceTypes,\n\tgetDefaultInstanceType,\n\tINSTANCE_TYPE_ALIASES,\n\tINSTANCE_TYPE_ORDER,\n\tINSTANCE_TYPES,\n\ttype InstanceTypeDefinition,\n\ttype InstanceTypeId,\n\tisValidInstanceType,\n\tLEGACY_INSTANCE_TYPE_ORDER,\n\tresolveCanonicalInstanceType,\n\tresolveMaxInstances,\n\tresolveResources,\n\tvalidateInstanceTypeForPlan,\n} from './config/instance-types'\n\nexport {\n\ttype BuildMachineTier,\n\tcentsToDollars,\n\tgetActivePlans,\n\tgetPlanMonthlyPrice,\n\tisPlanDeprecated,\n\tmicrosToDollars,\n\tPLATFORM_PLAN_ORDER,\n\tPLATFORM_PLAN_ORDER_ALL,\n\tPLATFORM_PLANS,\n\ttype PlatformPlanDefinition,\n\ttype PlatformPlanFeatures,\n\ttype PlatformPlanId,\n\ttype PlatformPlanLimits,\n} from './config/platform-plans'\n\n// =============================================================================\n// Debug Utilities\n// =============================================================================\n\nexport {\n\ttype DebugCategory,\n\tdebugError,\n\tdebugLog,\n\tdebugTimer,\n\tdebugWarn,\n\tdisableDebug,\n\tenableDebug,\n\tgetDebugMode,\n\tinstallGlobalDebugHelpers,\n\tresetDebugModeCache,\n} from './debug'\n\n// =============================================================================\n// REST Client\n// =============================================================================\n\nexport {\n\ttype CircuitBreakerConfig,\n\tCircuitBreakerOpenError,\n\ttype CircuitState,\n\tcreateDynamicRestClient,\n\tcreateRestClient,\n\ttype DeduplicationConfig,\n\ttype DynamicRestClient,\n\tgetCircuitBreakerState,\n\tgetRestErrorMessage,\n\thasError,\n\ttype RestClient,\n\ttype RestClientConfig,\n\ttype RestDynamicConfig,\n\ttype RetryConfig,\n\tresetCircuitBreaker,\n} from './rest-client'\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nexport {\n\tAuthenticationError,\n\tAuthorizationError,\n\tERROR_CODE_STATUS,\n\ttype ErrorCode,\n\texponentialBackoff,\n\tgetErrorCode,\n\tgetErrorDetails,\n\tgetErrorMessage,\n\tgetSafeErrorMessage,\n\tisChallengeRequired,\n\tisRetryableError,\n\tisSylphxError,\n\tNetworkError,\n\tNotFoundError,\n\tRateLimitError,\n\tRETRYABLE_CODES,\n\tSylphxError,\n\ttype SylphxErrorCode,\n\ttype SylphxErrorOptions,\n\tTimeoutError,\n\ttoSylphxError,\n\tValidationError,\n} from './errors'\n\n// =============================================================================\n// Auth Functions\n// =============================================================================\n\nexport {\n\tcookies,\n\ttype DeviceApproveInput,\n\ttype DeviceApproveResult,\n\ttype DeviceDenyInput,\n\ttype DeviceDenyResult,\n\ttype DeviceGrant,\n\ttype DeviceInitInput,\n\ttype DevicePollResult,\n\tdevice,\n\t// DPoP — sender-constrained access tokens (ADR-089 Phase 5.1e)\n\tdpop,\n\textendedSignUp,\n\tforgotPassword,\n\tgetOrgScopedToken,\n\tgetSession,\n\ttype ImpersonationActive,\n\ttype ImpersonationEndResult,\n\ttype ImpersonationInfo,\n\ttype ImpersonationStartResult,\n\ttype InviteUserRequest,\n\ttype InviteUserResponse,\n\timpersonation,\n\tintrospectToken,\n\tinviteUser,\n\t// Generated API types (from OpenAPI spec)\n\ttype LoginRequest,\n\ttype LoginResponse,\n\ttype MeResponse,\n\ttype MintAccessTokenClaims,\n\ttype MintAccessTokenResult,\n\t// SDK-specific types\n\ttype OrgScopedTokenResponse,\n\ttype OrgTokenPayload,\n\toauth,\n\ttype PlatformAccessTokenClaims,\n\t// Platform auth — refresh-token rotation + logout (ADR-089 SoC Tier-1)\n\ttype PlatformLogoutInput,\n\t// Platform password (ADR-089 Phase 2c)\n\ttype PlatformPasswordChangeInput,\n\ttype PlatformPasswordChangeResult,\n\ttype PlatformPasswordSetInput,\n\ttype PlatformPasswordSetResult,\n\ttype PlatformPasswordStatusResult,\n\ttype PlatformRefreshInput,\n\ttype PlatformRefreshResult,\n\t// Platform sessions (ADR-089 Phase 2b)\n\ttype PlatformSessionRenameInput,\n\ttype PlatformSessionRenameResult,\n\ttype PlatformSessionRevokeAllResult,\n\ttype PlatformSessionRevokeInput,\n\ttype PlatformSessionRevokeOtherResult,\n\ttype PlatformSessionRevokeResult,\n\ttype PlatformSessionsListResult,\n\ttype PlatformUserDeleteInput,\n\ttype PlatformUserDeleteResult,\n\ttype PlatformUserExportResult,\n\t// Platform user resolver (ADR-089 Phase 3b)\n\ttype PlatformUserRecord,\n\ttype PlatformUserResolution,\n\tpassword,\n\tplatformAuth,\n\ttype RegisterInput,\n\ttype RegisterRequest,\n\ttype RegisterResponse,\n\ttype ResendEmailVerificationRequest,\n\ttype ResendEmailVerificationResponse,\n\ttype RevokeTokenOptions,\n\trefreshToken,\n\tresendVerificationEmail,\n\tresetPassword,\n\tresetPlatformCookieCache,\n\tresetPlatformJwksCache,\n\trevokeAllTokens,\n\trevokeToken,\n\ttype SessionResult,\n\tsessions,\n\tsignIn,\n\tsignOut,\n\tsignUp,\n\tswitchOrg,\n\ttype TokenIntrospectionResult,\n\ttype TokenResponse,\n\ttype TwoFactorVerifyRequest,\n\tuser,\n\tverifyAccessToken,\n\tverifyEmail,\n\tverifyTwoFactor,\n} from './auth'\n\n// =============================================================================\n// Audit (Σ1 SoC rename — was `auth.audit`, now top-level `audit`)\n// =============================================================================\n\nexport { type AuditQueryFilter, type AuditQueryResult, audit } from './audit'\n\n// =============================================================================\n// Rate-Limits (Σ1 SoC rename — was `auth.rateLimits`)\n// =============================================================================\n\nexport {\n\ttype RateLimitStatusFilter,\n\ttype RateLimitStatusResult,\n\ttype RateLimitStrategiesFilter,\n\ttype RateLimitStrategiesResult,\n\ttype RateLimitStrategyDeleteInput,\n\ttype RateLimitStrategyDeleteResult,\n\ttype RateLimitStrategyUpsertInput,\n\ttype RateLimitStrategyUpsertResult,\n\trateLimits,\n} from './rate-limits'\n\n// =============================================================================\n// Realtime Admin + Functions Admin (Σ1 SoC rename — were nested inside `auth`\n// and re-exported as `realtimeAdmin` / `functionsInternal`; now nested under\n// `realtime.admin.*` and `functions.admin.*` at the package root)\n// =============================================================================\n\nimport { functionsAdmin } from './functions'\nimport * as realtimeCustomer from './realtime'\nimport { realtimeAdmin } from './realtime-admin'\n\nexport type { PlatformFunctionsDownloadBundleResult } from './functions'\nexport type {\n\tPlatformRealtimeChannel,\n\tPlatformRealtimeCreateChannelResult,\n\tPlatformRealtimeDeleteChannelResult,\n\tPlatformRealtimeListChannelsResult,\n\tPlatformRealtimeStatusResult,\n} from './realtime-admin'\n\n/**\n * `realtime` top-level namespace — customer-app data plane exposes\n * `realtime.emit` / `realtime.history`, Platform admin surface nests\n * at `realtime.admin.channels.*` (ADR-089 Phase 3a / Σ1 SoC rename).\n */\nexport const realtime = {\n\tadmin: realtimeAdmin.channels,\n\temit: realtimeCustomer.realtimeEmit,\n\thistory: realtimeCustomer.getRealtimeHistory,\n} as const\n\n/**\n * `functions` top-level namespace — Platform admin surface nests at\n * `functions.admin.downloadBundle` (ADR-089 Phase 3a / Σ1 SoC rename).\n * Customer-app function invocation surface is TBD.\n */\nexport const functions = {\n\tadmin: functionsAdmin,\n} as const\n\n// =============================================================================\n// Admin Functions (Server-side only — requires secretKey)\n// =============================================================================\n\nexport {\n\ttype AdminUser,\n\tdeleteUser,\n\tgetUser,\n\tgetUserByEmail,\n\ttype ListUsersOptions,\n\ttype ListUsersResult,\n\tlistUsers,\n\tsuspendUser,\n\tupdateUser,\n\tupdateUserMetadata,\n} from './admin'\n\n// =============================================================================\n// Analytics Functions\n// =============================================================================\n\nexport {\n\ttype BatchEvent,\n\tcreateTracker,\n\tgenerateAnonymousId,\n\ttype IdentifyInput,\n\tidentify,\n\ttype PageInput,\n\tpage,\n\ttype TrackInput,\n\ttrack,\n\ttrackBatch,\n} from './analytics'\n\n// =============================================================================\n// AI Functions\n// =============================================================================\n\nexport {\n\ttype AIModel,\n\ttype AIModelsResponse,\n\ttype AIRateLimitResponse,\n\t// Generated API types (from OpenAPI spec)\n\ttype AIUsageResponse,\n\ttype ChatInput,\n\t// SDK-specific types (OpenAI-compatible chat format)\n\ttype ChatMessage,\n\ttype ChatResult,\n\ttype ChatStreamChunk,\n\ttype ContentPart,\n\tchat,\n\tchatStream,\n\tcomplete,\n\ttype EmbedInput,\n\ttype EmbedResult,\n\tembed,\n\tstreamToString,\n\ttype Tool,\n\ttype ToolCall,\n} from './ai'\n\n// =============================================================================\n// Billing Functions\n// =============================================================================\n\nexport {\n\ttype BalanceResponse,\n\ttype CheckoutRequest,\n\ttype CheckoutResponse,\n\tcreateCheckout,\n\tcreatePortalSession,\n\tgetBillingBalance,\n\tgetBillingUsage,\n\tgetPlans,\n\tgetSubscription,\n\t// Generated API types\n\ttype Plan,\n\ttype PortalRequest,\n\ttype PortalResponse,\n\ttype Subscription,\n\ttype UsageResponse,\n} from './billing'\n\n// =============================================================================\n// Storage (ADR-100 — `storage.*` is the ONLY public surface)\n// =============================================================================\n\nexport {\n\ttype CopyFileOptions,\n\ttype File as StorageFile,\n\ttype FileId,\n\ttype FileVersion,\n\ttype FileVersionId,\n\ttype FileVisibility,\n\ttype ListFilesOptions,\n\ttype SignedUrlDisposition,\n\ttype SignedUrlOptions,\n\tstorage,\n\ttype UploadCreateOptions,\n\ttype UploadId,\n\ttype UploadProgressEvent,\n} from './storage'\n\n// =============================================================================\n// Notifications Functions\n// =============================================================================\n\n// Service Worker utilities for push notifications (OneSignal/FCM pattern)\nexport {\n\tcreateServiceWorkerScript,\n\tinitPushServiceWorker,\n\ttype PushNotificationPayload,\n\ttype PushServiceWorkerConfig,\n\tregisterPushServiceWorker,\n} from './lib/notifications/service-worker'\nexport {\n\tcampaigns as pushCampaigns,\n\tgetPushPreferences,\n\ttype PushCampaign,\n\ttype PushCampaignStats,\n\ttype PushCampaignVariant,\n\ttype PushNotification,\n\ttype PushSegment,\n\ttype PushSegmentFilter,\n\ttype PushSubscription,\n\tregisterPush,\n\tsegments as pushSegments,\n\tsendPush,\n\tunregisterPush,\n\tupdatePushPreferences,\n} from './notifications'\n\n// =============================================================================\n// Tasks Functions\n// =============================================================================\n\nexport {\n\ttype CronInput,\n\ttype CronSchedule,\n\tcancelTask,\n\tcreateCron,\n\tdeleteCron,\n\tgetTask,\n\tlistTasks,\n\tpauseCron,\n\tresumeCron,\n\tscheduleTask,\n\ttype TaskInput,\n\ttype TaskResult,\n\ttype TaskStatus,\n} from './tasks'\n\n// TaskTriggerConfig exported via './tasks' → lib/tasks → task.ts\n\n// =============================================================================\n// Native Tasks Engine — Handler API\n// =============================================================================\n\nexport {\n\tcreateStepContext,\n\tcreateTasksHandler,\n\tStepCompleteSignal,\n\tStepSleepSignal,\n\tverifySignature as verifyTaskSignature,\n} from './lib/tasks/handler'\n\nexport type {\n\tNativeStepContext,\n\tNativeTaskDefinition,\n\tTaskRunStatus as NativeTaskRunStatus,\n} from './lib/tasks/types'\n\n// =============================================================================\n// Feature Flags Functions\n// =============================================================================\n\nexport {\n\tcheckFlag,\n\ttype FlagContext,\n\ttype FlagResult,\n\tgetAllFlags,\n\tgetFlagPayload,\n\tgetFlags,\n\tgetVariant,\n\tisEnabled,\n} from './flags'\n\n// =============================================================================\n// Webhooks Functions\n// =============================================================================\n\nexport {\n\tgetWebhookConfig,\n\tgetWebhookDeliveries,\n\tgetWebhookDelivery,\n\tgetWebhookStats,\n\treplayWebhookDelivery,\n\tupdateWebhookConfig,\n\ttype WebhookConfig,\n\ttype WebhookConfigUpdate,\n\ttype WebhookDeliveriesResult,\n\ttype WebhookDelivery,\n\ttype WebhookStats,\n} from './webhooks'\n\n// =============================================================================\n// Email Functions\n// =============================================================================\n\nexport {\n\tcancelScheduledEmail,\n\tgetScheduledEmail,\n\tgetScheduledEmailStats,\n\tisEmailConfigured,\n\ttype ListScheduledEmailsOptions,\n\tlistScheduledEmails,\n\trescheduleEmail,\n\ttype ScheduledEmail,\n\ttype ScheduledEmailStats,\n\ttype ScheduledEmailsResult,\n\ttype ScheduleEmailOptions,\n\ttype SendEmailOptions,\n\ttype SendResult,\n\ttype SendTemplatedEmailOptions,\n\ttype SendToUserOptions,\n\tscheduleEmail,\n\tsendEmail,\n\tsendEmailToUser,\n\tsendTemplatedEmail,\n} from './email'\n\n// =============================================================================\n// Consent Functions (GDPR/CCPA)\n// =============================================================================\n\nexport {\n\tacceptAllConsents,\n\ttype ConsentCategory,\n\ttype ConsentHistoryEntry,\n\ttype ConsentHistoryResult,\n\ttype ConsentPurposeDefaults,\n\t// Types\n\ttype ConsentType,\n\tdeclineOptionalConsents,\n\ttype GetConsentHistoryInput,\n\ttype GetConsentsInput,\n\tgetConsentHistory,\n\t// Runtime functions\n\tgetConsentTypes,\n\tgetUserConsents,\n\thasConsent,\n\ttype LinkAnonymousConsentsInput,\n\tlinkAnonymousConsents,\n\ttype SetConsentsInput,\n\tsetConsents,\n\ttype UserConsent,\n} from './consent'\n\n// =============================================================================\n// Referrals Functions\n// =============================================================================\n\nexport {\n\tgetMyReferralCode,\n\tgetReferralLeaderboard,\n\tgetReferralStats,\n\ttype LeaderboardEntry,\n\ttype LeaderboardOptions,\n\ttype LeaderboardResult,\n\ttype RedeemReferralInput,\n\ttype RedeemResult,\n\ttype ReferralCode,\n\ttype ReferralStats,\n\tredeemReferralCode,\n\tregenerateReferralCode,\n} from './referrals'\n\n// =============================================================================\n// Engagement Functions (Streaks, Leaderboards, Achievements)\n// =============================================================================\n\nexport {\n\t// Constants\n\tACHIEVEMENT_TIER_CONFIG,\n\ttype AchievementCategory,\n\ttype AchievementCriteria,\n\ttype AchievementCriterion,\n\ttype AchievementDefinition,\n\ttype AchievementTier,\n\ttype AchievementType,\n\ttype AchievementUnlockEvent,\n\ttype CriteriaOperator,\n\tgetAchievement,\n\tgetAchievementPoints,\n\t// Achievement functions\n\tgetAchievements,\n\tgetAllStreaks,\n\t// Leaderboard functions\n\tgetLeaderboard,\n\t// Streak functions\n\tgetStreak,\n\tgetUserLeaderboardRank,\n\tincrementAchievementProgress,\n\ttype LeaderboardAggregation,\n\ttype LeaderboardDefinition,\n\ttype LeaderboardEntry as EngagementLeaderboardEntry,\n\ttype LeaderboardQueryOptions,\n\ttype LeaderboardResetPeriod,\n\ttype LeaderboardResult as EngagementLeaderboardResult,\n\ttype LeaderboardSortDirection,\n\ttype RecordActivityInput,\n\ttype RecordActivityResult,\n\trecordStreakActivity,\n\trecoverStreak,\n\t// Types\n\ttype StreakDefinition,\n\ttype StreakFrequency,\n\ttype StreakState,\n\ttype SubmitScoreInput,\n\ttype SubmitScoreResult,\n\tsubmitScore,\n\ttype UserAchievement,\n\tunlockAchievement,\n} from './engagement'\n\n// =============================================================================\n// Organization Functions\n// =============================================================================\n\nexport {\n\tacceptOrganizationInvitation,\n\ttype CreateOrgInput,\n\tcanDeleteOrganization,\n\tcanManageMembers,\n\tcanManageSettings,\n\tcreateOrganization,\n\tdeleteOrganization,\n\tgetOrganization,\n\t// Invitations\n\tgetOrganizationInvitations,\n\t// Members\n\tgetOrganizationMembers,\n\t// CRUD\n\tgetOrganizations,\n\t// Permission helpers\n\thasRole,\n\ttype InviteMemberInput,\n\tinviteOrganizationMember,\n\tleaveOrganization,\n\tlistOrganizations,\n\t// Types\n\ttype Organization,\n\ttype OrganizationInvitation,\n\ttype OrganizationMember,\n\ttype OrganizationMembership,\n\ttype OrganizationsListResult,\n\ttype OrgRole,\n\tremoveOrganizationMember,\n\trevokeOrganizationInvitation,\n\ttype UpdateOrgInput,\n\ttype UserOrganization,\n\tupdateOrganization,\n\tupdateOrganizationMemberRole,\n} from './orgs'\n\n// =============================================================================\n// Permission Functions (RBAC)\n// =============================================================================\n\nexport {\n\ttype CreatePermissionInput,\n\tcreatePermission,\n\tdeletePermission,\n\tgetMemberPermissions,\n\thasAllPermissions,\n\thasAnyPermission,\n\thasPermission,\n\tlistPermissions,\n\ttype MemberPermissionsResult,\n\ttype Permission,\n} from './permissions'\n\n// =============================================================================\n// Role Functions (RBAC)\n// =============================================================================\n\nexport {\n\tassignMemberRole,\n\ttype CreateRoleInput,\n\tcreateRole,\n\tdeleteRole,\n\tgetRole,\n\tlistRoles,\n\ttype Role,\n\ttype UpdateRoleInput,\n\tupdateRole,\n} from './roles'\n\n// =============================================================================\n// Secrets Functions\n// =============================================================================\n\nexport {\n\ttype GetSecretInput,\n\ttype GetSecretResult,\n\ttype GetSecretsInput,\n\ttype GetSecretsResult,\n\tgetAllSecrets,\n\tgetSecret,\n\tgetSecrets,\n\thasSecret,\n\ttype ListSecretKeysInput,\n\tlistSecretKeys,\n\ttype SecretKeyInfo,\n} from './secrets'\n\n// =============================================================================\n// Search Functions\n// =============================================================================\n\nexport {\n\ttype BatchIndexInput,\n\ttype BatchIndexResult,\n\tbatchIndex,\n\ttype DeleteDocumentInput,\n\tdeleteDocument,\n\ttype FacetsResponse,\n\ttype GetFacetsInput,\n\tgetFacets,\n\tgetSearchStats,\n\ttype IndexDocumentInput,\n\ttype IndexDocumentResult,\n\tindexDocument,\n\ttype SearchInput,\n\ttype SearchResponse,\n\ttype SearchResultItem,\n\ttype SearchStatsResult,\n\ttype SearchType,\n\tsearch,\n\ttype TrackClickInput,\n\ttrackClick,\n\ttype UpsertDocumentInput,\n\ttype UpsertDocumentResult,\n\tupsertDocument,\n} from './search'\n\n// =============================================================================\n// Database Functions (Platform-provisioned PostgreSQL)\n// =============================================================================\n\nexport {\n\ttype DatabaseConnectionInfo,\n\ttype DatabaseStatus,\n\ttype DatabaseStatusInfo,\n\tgetDatabaseConnectionString,\n\tgetDatabaseStatus,\n} from './database'\n\n// =============================================================================\n// KV Functions (managed key-value store)\n// =============================================================================\n\nexport {\n\ttype KvExpireRequest,\n\ttype KvHgetallRequest,\n\ttype KvHgetRequest,\n\ttype KvHsetRequest,\n\ttype KvIncrRequest,\n\ttype KvLpushRequest,\n\ttype KvLrangeRequest,\n\ttype KvMgetRequest,\n\ttype KvMsetRequest,\n\ttype KvRateLimitRequest,\n\ttype KvRateLimitResult,\n\ttype KvScanOptions,\n\ttype KvScanResult,\n\ttype KvSetOptions,\n\ttype KvSetRequest,\n\ttype KvZaddRequest,\n\ttype KvZMember,\n\ttype KvZrangeRequest,\n\tkvDelete,\n\tkvExists,\n\tkvExpire,\n\tkvGet,\n\tkvGetJSON,\n\tkvHget,\n\tkvHgetall,\n\tkvHset,\n\tkvIncr,\n\tkvLpush,\n\tkvLrange,\n\tkvMget,\n\tkvMset,\n\tkvRateLimit,\n\tkvScan,\n\tkvSet,\n\tkvSetJSON,\n\tkvZadd,\n\tkvZrange,\n} from './kv'\n\n// =============================================================================\n// Realtime Functions (managed pub/sub)\n// =============================================================================\n\nexport {\n\tgetRealtimeHistory,\n\ttype RealtimeEmitRequest,\n\ttype RealtimeEmitResponse,\n\ttype RealtimeHistoryRequest,\n\ttype RealtimeHistoryResponse,\n\trealtimeEmit,\n\ttype StreamMessage,\n} from './realtime'\n\n// =============================================================================\n// Deploy Functions (CI/CD)\n// =============================================================================\n\nexport {\n\ttype BuildLog,\n\ttype BuildLogHistoryResponse,\n\ttype DeployHistoryResponse,\n\ttype DeployInfo,\n\ttype DeployStatus,\n\tdeleteEnvVar,\n\ttype EnvVar,\n\tgetBuildLogHistory,\n\tgetDeployHistory,\n\tgetDeployStatus,\n\tlistEnvVars,\n\ttype RollbackDeployRequest,\n\trollbackDeploy,\n\ttype SetEnvVarRequest,\n\tsetEnvVar,\n\ttype TriggerDeployRequest,\n\ttriggerDeploy,\n} from './deploy'\n\n// =============================================================================\n// Monitoring Functions (Error Tracking)\n// =============================================================================\n\nexport {\n\ttype Breadcrumb,\n\ttype CaptureExceptionRequest,\n\ttype CaptureMessageRequest,\n\tcaptureException,\n\tcaptureExceptionRaw,\n\tcaptureMessage,\n\ttype ExceptionFrame,\n\ttype ExceptionValue,\n\ttype MonitoringResponse,\n\ttype MonitoringSeverity,\n} from './monitoring'\n\n// =============================================================================\n// Sandbox Functions (Ephemeral Compute)\n// =============================================================================\n\nexport {\n\ttype BrowserCreateOptions,\n\ttype BrowserSessionInfo,\n\ttype BrowserSessionStatus,\n\ttype CommandResult,\n\ttype ExecEvent,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype FileEvent,\n\ttype ProcessEvent,\n\ttype ProcessInfo,\n\ttype ProcessStartOptions,\n\ttype ProcessSummary,\n\tSandboxBrowser,\n\tSandboxClient,\n\ttype SandboxFile,\n\tSandboxFiles,\n\ttype SandboxMachineSize,\n\ttype SandboxOptions,\n\tSandboxProcesses,\n\ttype SandboxRecord,\n\tSandboxWatch,\n\ttype WatchEntry,\n\ttype WatchOptions,\n} from './sandbox'\n\n// =============================================================================\n// Workers (Batch Run-to-Completion Compute)\n// =============================================================================\n\nexport type { RunWorkerOptions } from './workers'\n\n// =============================================================================\n// Triggers (ADR-040 — Unified scheduling + event dispatch)\n// =============================================================================\n\nexport {\n\ttype CreateTriggerOptions,\n\ttype CronSource,\n\ttype EventSource,\n\ttype HttpTarget,\n\ttype ListTriggersResult,\n\ttype PublishEventResult,\n\ttype RunTarget,\n\ttype TaskTarget,\n\ttype Trigger,\n\ttype TriggerRunMachineSize,\n\ttype TriggerSource,\n\ttype TriggerSourceType,\n\ttype TriggerStatus,\n\tTriggersClient,\n\ttype TriggerTarget,\n\ttype TriggerTargetType,\n\ttype UpdateTriggerOptions,\n} from './lib/triggers'\n\n// =============================================================================\n// Runs (ADR-040 — formerly Workers)\n// =============================================================================\n\nexport {\n\ttype CreateRunOptions,\n\ttype ListRunsOptions,\n\ttype ListRunsResult,\n\ttype Run,\n\t// Primary exports (ADR-040)\n\tRunHandle,\n\ttype RunLogsResult,\n\ttype RunMachineSize,\n\ttype RunResult,\n\ttype RunStatus,\n\tRunsClient,\n\ttype RunVolumeMount,\n\t// Backward-compat aliases\n\tWorkerHandle,\n\ttype WorkerLogsResult,\n\ttype WorkerResult,\n\ttype WorkerRun,\n\ttype WorkerStatus,\n\tWorkersClient,\n\ttype WorkerVolumeMount,\n} from './workers'\n\n// =============================================================================\n// Common Types (SDK-specific helpers — SSOT: ./types)\n// =============================================================================\n\nexport type {\n\tAccessTokenPayload,\n\tErrorResponse,\n\tPaginatedResponse,\n\tPaginationInput,\n\tSuccessResponse,\n\tUser,\n} from './types'\n\n// =============================================================================\n// Application Logs (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype IngestLogsResult,\n\tingestLogs,\n\ttype LogEntry,\n\ttype LogLevel,\n\ttype QueryLogsOptions,\n\ttype QueryLogsResult,\n\tqueryLogs,\n\ttype StoredLogEntry,\n} from './logs'\n\n// =============================================================================\n// Misc — project metadata + step-up challenge (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype ChallengeMethod,\n\ttype ChallengeType,\n\ttype ChallengeVerifyInput,\n\ttype ChallengeVerifyResult,\n\tgetProjectMetadata,\n\ttype ProjectMetadata,\n\tverifyChallenge,\n} from './misc'\n\n// =============================================================================\n// User (customer-app profile + sessions, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype DeleteAccountResult,\n\tdeleteUserAccount,\n\texportUserData,\n\tgetUserProfile,\n\tgetUserSecurity,\n\tlistUserSessions,\n\trenameUserSession,\n\trevokeUserSession,\n\ttype UserDataExport,\n\ttype UserFullProfile,\n\ttype UserSecuritySettings,\n\ttype UserSession,\n\ttype UserSessionsList,\n\ttype UserUpdateProfileInput,\n\tupdateUserProfile,\n} from './user'\n\n// =============================================================================\n// Security — passwords, 2FA, passkeys, alerts (ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype BackupCodesResult,\n\tconfirmEmailChange,\n\tdeletePasskey,\n\tdisableTwoFactor,\n\tdisconnectOAuthProvider,\n\ttype EmailChangeInput,\n\ttype EmailConfirmInput,\n\tgetBackupCodes,\n\tgetSecurityScore,\n\tlistPasskeys,\n\tlistSecurityAlerts,\n\tmarkAllSecurityAlertsRead,\n\tmarkSecurityAlertRead,\n\ttype PasskeyRegistrationInput,\n\ttype PasskeyRegistrationOptions,\n\ttype PasskeySummary,\n\ttype PasskeysList,\n\ttype PasswordSetInput,\n\tregenerateBackupCodes,\n\trenamePasskey,\n\trequestEmailChange,\n\ttype SecurityAlert,\n\ttype SecurityAlertsList,\n\ttype SecurityScoreResult,\n\tsetPassword,\n\tsetupTwoFactor,\n\tstartPasskeyRegistration,\n\ttype TwoFactorEnableResult,\n\ttype TwoFactorSetupResult,\n\tverifyPasskeyRegistration,\n\tverifyTwoFactorEnable,\n} from './security'\n\n// =============================================================================\n// Customer-app OAuth flow (social login, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\tauthorizeOAuth,\n\texchangeOAuthCode,\n\tgeneratePkce,\n\tgetOidcDiscoveryDocument,\n\tlistOAuthProviders,\n\ttype OAuthAuthorizeInput,\n\ttype OAuthAuthorizeResult,\n\ttype OAuthCodeExchangeInput,\n\ttype OAuthProvider,\n\ttype OAuthProvidersResult,\n\ttype OidcDiscoveryDocument,\n\ttype OidcUserInfoResponse,\n\ttype PkceMethod,\n\tparseOAuthCallback,\n\tuserInfo,\n} from './oauth'\n\n// =============================================================================\n// Promo codes (validate / redeem + admin CRUD, ADR-089 Phase 4a)\n// =============================================================================\n\nexport {\n\ttype CreatePromoInput,\n\tcreatePromo,\n\tdeletePromo,\n\tgetPromo,\n\ttype ListPromosOptions,\n\ttype ListPromosResult,\n\ttype ListRedemptionsOptions,\n\ttype ListRedemptionsResult,\n\tlistPromoRedemptions,\n\tlistPromos,\n\ttype PromoCode,\n\ttype PromoRedemption,\n\ttype PromoStatus,\n\ttype PromoType,\n\ttype PromoValidationPreview,\n\ttype RedeemPromoInput,\n\ttype RedeemPromoResult,\n\tredeemPromo,\n\ttype UpdatePromoInput,\n\tupdatePromo,\n\ttype ValidatePromoInput,\n\ttype ValidatePromoResult,\n\tvalidatePromo,\n} from './promo'\n\n// =============================================================================\n// React-layer types (tRPC-like wrappers — SSOT: ./react/types)\n// =============================================================================\n// Re-exported from the non-React entry for backcompat; these ride along\n// at type-only cost (sideEffects: false) and don't tug in react runtime.\n\nexport type {\n\tAIListModelsOptions,\n\tAIListModelsResponse,\n\tAIMessage,\n\tAIMessageRole,\n\tAIModelInfo,\n\tAIProvider,\n\tAIRateLimitInfo,\n\tAIRequestType,\n\tAIStreamChunk,\n\tAITool,\n\tAIToolCall,\n\tAIUsageStats,\n\tChatCompletionInput,\n\tChatCompletionResponse,\n\tEmbeddingInput,\n\tEmbeddingResponse,\n\tLoginHistoryEntry,\n\tSecuritySettings,\n\tTextCompletionInput,\n\tTextCompletionResponse,\n\tUserProfile,\n\tVisionInput,\n} from './react/types'\n","/**\n * Auth Functions\n *\n * Pure functions for authentication - no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /api/sdk/auth/* for all operations.\n *\n * Types are re-exported from `@sylphx/contract` (ADR-084). The contract is\n * the single source of truth for every wire shape — this module only adds\n * SDK-specific ergonomics (User brand swap, introspection result, invite\n * envelopes, org-token claims).\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tAuthTokensResponse,\n\tDeviceApproveRequest as ContractDeviceApproveRequest,\n\tDeviceApproveResponse as ContractDeviceApproveResponse,\n\tDeviceDenyRequest as ContractDeviceDenyRequest,\n\tDeviceDenyResponse as ContractDeviceDenyResponse,\n\tDeviceInitRequest as ContractDeviceInitRequest,\n\tDeviceInitResponse as ContractDeviceInitResponse,\n\tDevicePollResponse as ContractDevicePollResponse,\n\tLoginRequest as ContractLoginRequest,\n\tLoginResponse as ContractLoginResponse,\n\tRegisterRequest as ContractRegisterRequest,\n\tRegisterResponse as ContractRegisterResponse,\n\tResendEmailVerificationRequest as ContractResendEmailVerificationRequest,\n\tResendEmailVerificationResponse as ContractResendEmailVerificationResponse,\n\tTwoFactorVerifyRequest as ContractTwoFactorVerifyRequest,\n\tUserFullProfile,\n} from '@sylphx/contract'\nimport { authEndpoints } from '@sylphx/contract'\nimport { buildApiUrl, callApi, type SylphxConfig } from './config'\nimport type { User } from './types'\n\nexport { dpop } from './dpop'\nexport type {\n\tOAuthClientCredentialsResult,\n\tOAuthPollError,\n\tOAuthPollResult,\n\tOAuthTokenResult,\n} from './oauth-token'\nexport type {\n\tOrgAuthPolicy,\n\tOrgAuthPolicyUpdate,\n\tPasskeySignupChallengeInput,\n\tPasskeySignupChallengeResult,\n\tPasskeySignupResult,\n\tPasskeySignupVerifyInput,\n} from './passkey'\nexport {\n\torgPolicies,\n\tPasskeyPolicyViolationError,\n\tpasskey,\n} from './passkey'\nexport type {\n\tPlatformLogoutInput,\n\tPlatformRefreshInput,\n\tPlatformRefreshResult,\n} from './platform-auth'\nexport { platformAuth } from './platform-auth'\nexport type {\n\tImpersonationActive,\n\tImpersonationChallenge,\n\tImpersonationConsentDecision,\n\tImpersonationConsentResponse,\n\tImpersonationEndResult,\n\tImpersonationInfo,\n\tImpersonationRequestRow,\n\tImpersonationStartChallengeInput,\n\tImpersonationStartResult,\n\tImpersonationStartStepupInput,\n\tImpersonationStartStepupResult,\n} from './platform-impersonation'\nexport { impersonation } from './platform-impersonation'\nexport type {\n\tPlatformAccessTokenClaims,\n\tPlatformUserRecord,\n\tPlatformUserResolution,\n} from './platform-jwt'\nexport {\n\tcookies,\n\tresetPlatformCookieCache,\n\tresetPlatformJwksCache,\n\tverifyAccessToken,\n} from './platform-jwt'\nexport type {\n\tMintAccessTokenClaims,\n\tMintAccessTokenResult,\n\tOAuthIntrospectInput,\n\tOAuthIntrospectResult,\n\tOAuthRevokeInput,\n} from './platform-oauth'\nexport { oauth } from './platform-oauth'\nexport type {\n\tPlatformPasswordChangeInput,\n\tPlatformPasswordChangeResult,\n\tPlatformPasswordSetInput,\n\tPlatformPasswordSetResult,\n\tPlatformPasswordStatusResult,\n} from './platform-password'\nexport { password } from './platform-password'\nexport type {\n\tPlatformSessionRenameInput,\n\tPlatformSessionRenameResult,\n\tPlatformSessionRevokeAllResult,\n\tPlatformSessionRevokeInput,\n\tPlatformSessionRevokeOtherResult,\n\tPlatformSessionRevokeResult,\n\tPlatformSessionsListResult,\n} from './platform-sessions'\nexport { sessions } from './platform-sessions'\nexport type {\n\tDataExportJob,\n\tPlatformUserDeleteInput,\n\tPlatformUserDeleteResult,\n\tPlatformUserExportResult,\n} from './platform-user'\nexport { user } from './platform-user'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type LoginRequest = ContractLoginRequest\nexport type LoginResponse = ContractLoginResponse\nexport type RegisterRequest = ContractRegisterRequest\nexport type RegisterResponse = ContractRegisterResponse\nexport type ResendEmailVerificationRequest = ContractResendEmailVerificationRequest\nexport type ResendEmailVerificationResponse = ContractResendEmailVerificationResponse\n/**\n * Token response — contract's `AuthTokensResponse.user` (optional `AuthUser`)\n * is re-mapped to the SDK's broader `User` type so legacy callers keep the\n * familiar brand. `AuthUser` and `User` are structurally identical, but\n * the SDK surface has wider reach (cookies, middleware, React hooks) and\n * renaming is out of scope for ADR-084 cleanup.\n */\nexport type TokenResponse = Omit<AuthTokensResponse, 'user'> & {\n\tuser: User\n}\nexport type TwoFactorVerifyRequest = ContractTwoFactorVerifyRequest\n/**\n * `GET /auth/me` — contract's `UserFullProfile` already includes the\n * optional `emailVerified` flag the backend returns, so the SDK can just\n * alias the contract type directly.\n */\nexport type MeResponse = UserFullProfile\n\n// SDK-specific types (not directly from API schema)\n/**\n * Token introspection result (RFC 7662)\n */\nexport interface TokenIntrospectionResult {\n\t/** Whether the token is active/valid */\n\tactive: boolean\n\t/** Token type (access_token or refresh_token) */\n\ttoken_type?: 'access_token' | 'refresh_token'\n\t/** User ID */\n\tsub?: string\n\t/** User email */\n\temail?: string\n\t/** User name */\n\tname?: string\n\t/** App ID */\n\tclient_id?: string\n\t/** Audience */\n\taud?: string\n\t/** Issuer */\n\tiss?: string\n\t/** Expiration time (Unix timestamp) */\n\texp?: number\n\t/** Issued at time (Unix timestamp) */\n\tiat?: number\n\t/** User role */\n\trole?: string\n\t/** Email verification status */\n\temail_verified?: boolean\n}\n\n/**\n * Token revocation options\n */\nexport interface RevokeTokenOptions {\n\t/** Revoke all tokens for a user in this app */\n\trevokeAll?: boolean\n\t/** User ID (required when revoking all) */\n\tuserId?: string\n}\n\n// SDK-specific types (not in generated API)\nexport interface SessionResult {\n\tuser: {\n\t\tid: string\n\t\temail: string\n\t\tname: string | null\n\t\timage: string | null\n\t\temailVerified: boolean\n\t} | null\n}\n\n/**\n * Extended registration input with metadata and invitation token support.\n * Use extendedSignUp() when you need to pass metadata or an invitation token.\n */\nexport interface RegisterInput {\n\temail: string\n\tpassword: string\n\tname?: string\n\tmetadata?: Record<string, unknown>\n\tinvitationToken?: string\n}\n\n/**\n * Org context claims present in org-scoped tokens (after switch-org).\n *\n * The JWT carries the role key only. Permissions are resolved server-side\n * via cached role→permissions lookup (WorkOS pattern). This keeps\n * tokens small and ensures permission changes take effect without token refresh.\n */\nexport interface OrgTokenPayload {\n\torg_id: string\n\torg_slug: string\n\t/** RBAC role key (e.g. \"hr_manager\", \"admin\"). Permissions resolved server-side. */\n\torg_role: string\n}\n\nexport interface OrgScopedTokenResponse {\n\t/** Org-scoped access token. */\n\ttoken: string\n\t/** Org-scoped access token, matching the SDK's token naming convention. */\n\taccessToken: string\n\t/** Token lifetime in seconds, when provided by the runtime. */\n\texpiresIn?: number\n\t/** Bearer token type, when provided by the runtime. */\n\ttokenType?: string\n\t/** User envelope returned by the runtime for session hydration. */\n\tuser?: User\n}\n\ninterface OrgScopedTokenWireResponse {\n\taccessToken?: string\n\taccess_token?: string\n\texpiresIn?: number\n\texpires_in?: number\n\ttokenType?: string\n\ttoken_type?: string\n\tuser?: User\n}\n\n/**\n * Invite a user request payload.\n */\nexport interface InviteUserRequest {\n\temail: string\n\tmetadata?: Record<string, unknown>\n\tredirectUrl?: string\n}\n\n/**\n * Response from inviteUser.\n */\nexport interface InviteUserResponse {\n\tinvitationToken: string\n\texpiresAt: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Sign in with email and password\n *\n * @example\n * ```typescript\n * const result = await signIn(config, { email: 'user@example.com', password: 'secret' })\n * if (result.requiresTwoFactor) {\n * // Handle 2FA flow\n * } else {\n * // Save tokens\n * const authenticatedConfig = withToken(config, result.accessToken!)\n * }\n * ```\n */\nexport async function signIn(config: SylphxConfig, input: LoginRequest): Promise<LoginResponse> {\n\t// Contract-derived path/method and wire body (SSOT per ADR-084).\n\tconst body = input satisfies ContractLoginRequest\n\tconst endpoint = authEndpoints.signIn\n\treturn callApi<LoginResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Sign up with email and password\n *\n * @example\n * ```typescript\n * const result = await signUp(config, {\n * email: 'user@example.com',\n * password: 'secret',\n * name: 'John Doe',\n * })\n * // User needs to verify email\n * ```\n */\nexport async function signUp(\n\tconfig: SylphxConfig,\n\tinput: RegisterRequest,\n): Promise<RegisterResponse> {\n\t// Contract-derived path/method and richer registration body (SSOT per ADR-084).\n\tconst endpoint = authEndpoints.signUp\n\treturn callApi<RegisterResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: input,\n\t})\n}\n\n/**\n * Sign out (revoke tokens)\n *\n * @example\n * ```typescript\n * await signOut(config)\n * ```\n */\nexport async function signOut(config: SylphxConfig): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = authEndpoints.signOut\n\tawait callApi<void>(config, endpoint.path, { method: endpoint.method })\n}\n\n/**\n * Refresh access token\n *\n * @example\n * ```typescript\n * const tokens = await refreshToken(config, refreshTokenString)\n * const newConfig = withToken(config, tokens.accessToken)\n * ```\n */\nexport async function refreshToken(config: SylphxConfig, token: string): Promise<TokenResponse> {\n\treturn callApi<TokenResponse>(config, '/auth/token', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: token,\n\t\t\tclient_secret: config.secretKey,\n\t\t},\n\t})\n}\n\n/**\n * Verify email with token\n *\n * @example\n * ```typescript\n * await verifyEmail(config, token)\n * ```\n */\nexport async function verifyEmail(config: SylphxConfig, token: string): Promise<void> {\n\tawait callApi<void>(config, '/auth/verify-email', {\n\t\tmethod: 'POST',\n\t\tbody: { token },\n\t})\n}\n\n/**\n * Request password reset email\n *\n * @example\n * ```typescript\n * await forgotPassword(config, 'user@example.com', {\n * redirectUrl: 'https://app.example.com/reset-password'\n * })\n * ```\n */\nexport async function forgotPassword(\n\tconfig: SylphxConfig,\n\temail: string,\n\toptions: { redirectUrl?: string } = {},\n): Promise<void> {\n\tawait callApi<{ success: boolean }>(config, '/auth/forgot-password', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\temail,\n\t\t\t...(options.redirectUrl ? { redirectUrl: options.redirectUrl } : {}),\n\t\t},\n\t})\n}\n\n/**\n * Request a verification email resend.\n *\n * The Platform response is intentionally privacy-preserving: it never\n * indicates whether the email exists or is already verified.\n *\n * @example\n * ```typescript\n * await resendVerificationEmail(config, 'user@example.com')\n * ```\n */\nexport async function resendVerificationEmail(config: SylphxConfig, email: string): Promise<void> {\n\tconst endpoint = authEndpoints.resendEmailVerification\n\tconst body = { email } satisfies ResendEmailVerificationRequest\n\tawait callApi<ResendEmailVerificationResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Reset password with token\n *\n * @example\n * ```typescript\n * await resetPassword(config, { token, password: 'newpassword' })\n * ```\n */\nexport async function resetPassword(\n\tconfig: SylphxConfig,\n\tinput: { token: string; password: string },\n): Promise<void> {\n\tawait callApi<{ success: boolean }>(config, '/auth/reset-password', {\n\t\tmethod: 'POST',\n\t\tbody: { token: input.token, password: input.password },\n\t})\n}\n\n/**\n * Get current session (requires authenticated config)\n *\n * @example\n * ```typescript\n * const session = await getSession(authenticatedConfig)\n * if (session.user) {\n * console.log(`Logged in as ${session.user.email}`)\n * }\n * ```\n */\nexport async function getSession(config: SylphxConfig): Promise<SessionResult> {\n\tif (!config.accessToken) {\n\t\treturn { user: null }\n\t}\n\n\t// Contract-derived path/method (SSOT per ADR-084). The contract's\n\t// `SessionResult` uses branded `UserId` — the SDK currently surfaces\n\t// the raw string on `SessionResult['user'].id` because consumers pass it\n\t// to URL builders, cookies, etc. that don't understand the brand. The\n\t// structural shape is otherwise identical, so this remains a public SDK\n\t// compatibility boundary.\n\tconst endpoint = authEndpoints.getSession\n\ttry {\n\t\tconst user = await callApi<SessionResult['user']>(config, endpoint.path, {\n\t\t\tmethod: endpoint.method,\n\t\t})\n\t\treturn { user }\n\t} catch {\n\t\treturn { user: null }\n\t}\n}\n\n/**\n * Verify 2FA code (when signIn returns requiresTwoFactor: true)\n *\n * @example\n * ```typescript\n * const result = await signIn(config, credentials)\n * if (result.requiresTwoFactor) {\n * const tokens = await verifyTwoFactor(config, result.userId!, code)\n * }\n * ```\n */\nexport async function verifyTwoFactor(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tcode: string,\n): Promise<TokenResponse> {\n\treturn callApi<TokenResponse>(config, '/auth/verify-2fa', {\n\t\tmethod: 'POST',\n\t\tbody: { userId, code },\n\t})\n}\n\n/**\n * Introspect a token to check its validity (RFC 7662)\n *\n * Use this to verify token status without decoding. Essential for:\n * - Checking if a token has been revoked\n * - Validating tokens at the edge\n * - Security-critical operations\n *\n * @example\n * ```typescript\n * const result = await introspectToken(config, accessToken)\n * if (!result.active) {\n * // Token is invalid, revoked, or expired\n * await refreshTokens()\n * }\n * ```\n */\nexport async function introspectToken(\n\tconfig: SylphxConfig,\n\ttoken: string,\n\ttokenTypeHint?: 'access_token' | 'refresh_token',\n): Promise<TokenIntrospectionResult> {\n\tconst response = await fetch(buildApiUrl(config, '/auth/introspect'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t// RFC 7662 §2: server-to-server call — authenticate with secret key\n\t\t\t'x-app-secret': config.secretKey ?? '',\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\ttoken,\n\t\t\ttoken_type_hint: tokenTypeHint,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\t// Per RFC 7662, errors should return inactive\n\t\treturn { active: false }\n\t}\n\n\treturn response.json()\n}\n\n/**\n * Revoke a token (RFC 7009)\n *\n * Use cases:\n * - Sign out user from specific device\n * - Security response to compromised token\n * - User-initiated session termination\n *\n * @example\n * ```typescript\n * // Revoke single refresh token\n * await revokeToken(config, refreshToken)\n *\n * // Revoke all tokens for a user (logout everywhere)\n * await revokeToken(config, '', { revokeAll: true, userId: 'user-123' })\n * ```\n */\nexport async function revokeToken(\n\tconfig: SylphxConfig,\n\ttoken: string,\n\toptions?: RevokeTokenOptions,\n): Promise<void> {\n\tawait fetch(buildApiUrl(config, '/auth/revoke'), {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({\n\t\t\ttoken: options?.revokeAll ? undefined : token,\n\t\t\tclient_secret: config.secretKey,\n\t\t\tuser_id: options?.userId,\n\t\t\trevoke_all: options?.revokeAll,\n\t\t}),\n\t})\n\t// Per RFC 7009, always succeeds (200 OK)\n}\n\n/**\n * Revoke all tokens for a user (logout from all devices)\n *\n * Convenience wrapper around revokeToken with revokeAll option.\n *\n * @example\n * ```typescript\n * // After password change, revoke all sessions\n * await revokeAllTokens(config, userId)\n * ```\n */\nexport async function revokeAllTokens(config: SylphxConfig, userId: string): Promise<void> {\n\tawait revokeToken(config, '', { revokeAll: true, userId })\n}\n\n/**\n * Sign up with extended input (metadata + invitation token support).\n *\n * Use this instead of signUp() when you need to:\n * - Pass metadata on registration (e.g., org context, role, referral info)\n * - Register with an invitation token\n *\n * @example\n * ```typescript\n * const result = await extendedSignUp(config, {\n * email: 'user@example.com',\n * password: 'secret',\n * name: 'John Doe',\n * metadata: { orgId: 'org-123', role: 'employee' },\n * invitationToken: 'inv_...',\n * })\n * ```\n */\nexport async function extendedSignUp(\n\tconfig: SylphxConfig,\n\tinput: RegisterInput,\n): Promise<RegisterResponse> {\n\treturn callApi<RegisterResponse>(config, '/auth/register', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Invite a user to sign up for this project.\n * Server-side only (requires secretKey).\n * Sends an email invitation; user signs up via signUp() or extendedSignUp() with the invitation token.\n *\n * @example\n * ```typescript\n * const invite = await inviteUser(config, {\n * email: 'newemployee@company.com',\n * metadata: { role: 'employee', orgId: 'org-123' },\n * redirectUrl: 'https://app.example.com/signup',\n * })\n * console.log(invite.invitationToken, invite.expiresAt)\n * ```\n */\nexport async function inviteUser(\n\tconfig: SylphxConfig,\n\tinput: InviteUserRequest,\n): Promise<InviteUserResponse> {\n\treturn callApi<InviteUserResponse>(config, '/auth/invite', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nfunction normalizeOrgScopedTokenResponse(data: OrgScopedTokenWireResponse): OrgScopedTokenResponse {\n\tconst accessToken = data.accessToken ?? data.access_token\n\tif (!accessToken) {\n\t\tthrow new Error('Invalid org-scoped token response: missing access token')\n\t}\n\n\treturn {\n\t\ttoken: accessToken,\n\t\taccessToken,\n\t\texpiresIn: data.expiresIn ?? data.expires_in,\n\t\ttokenType: data.tokenType ?? data.token_type,\n\t\tuser: data.user,\n\t}\n}\n\n/**\n * Exchange current user token for an org-scoped token.\n * The returned access_token JWT includes org_id, org_slug, org_role claims.\n *\n * @example\n * const { token } = await getOrgScopedToken(withToken(config, currentToken), 'org_xxx')\n */\nexport async function getOrgScopedToken(\n\tconfig: SylphxConfig,\n\torgId: string,\n): Promise<OrgScopedTokenResponse> {\n\tconst data = await callApi<OrgScopedTokenWireResponse>(config, '/auth/switch-org', {\n\t\tmethod: 'POST',\n\t\tbody: { orgId },\n\t})\n\treturn normalizeOrgScopedTokenResponse(data)\n}\n\n/**\n * @deprecated Use getOrgScopedToken(config, orgId). Kept as the shorter\n * organization switch alias for existing SDK callers.\n */\nexport async function switchOrg(\n\tconfig: SylphxConfig,\n\torgId: string,\n): Promise<OrgScopedTokenResponse> {\n\treturn getOrgScopedToken(config, orgId)\n}\n\n// ============================================================================\n// OAuth 2.0 Device Authorization Grant (RFC 8628)\n// ============================================================================\n//\n// The device flow is the *bootstrap* leg for headless clients (CLI, TV apps,\n// IoT) that can't host a browser. It is deliberately separate from the\n// pk_/sk_-credentialled `SylphxConfig` above because the device flow runs\n// BEFORE the client has any credential — that's the whole point.\n//\n// Accordingly the device-flow methods accept a `baseUrl` argument directly\n// rather than a `SylphxConfig`. For the Sylphx platform itself this will be\n// `https://your-app.api.sylphx.com/v1` (configurable via SYLPHX_BAAS_URL for\n// dev/staging). Callers that already have a `SylphxConfig` for their own app\n// can pass `config.baseUrl` — both surfaces resolve to the same endpoints.\n//\n// See ADR-089 Phase 2a for the migration notes and the apps/runtime handler.\n\nexport type DeviceInitInput = ContractDeviceInitRequest\nexport type DeviceGrant = ContractDeviceInitResponse\nexport type DevicePollResult = ContractDevicePollResponse\nexport type DeviceApproveInput = ContractDeviceApproveRequest\nexport type DeviceApproveResult = ContractDeviceApproveResponse\nexport type DeviceDenyInput = ContractDeviceDenyRequest\nexport type DeviceDenyResult = ContractDeviceDenyResponse\n\n/**\n * `device` namespace — RFC 8628 device authorization grant.\n *\n * Used by headless clients (CLI, TV apps, IoT) to authorise via a\n * companion browser instead of reading credentials from env vars.\n */\nexport const device = {\n\t/**\n\t * Start a device authorization grant.\n\t *\n\t * Returns a `DeviceGrant` with `verification_uri_complete` (open this\n\t * in the user's browser) and `device_code` (use for polling).\n\t *\n\t * @example\n\t * ```typescript\n\t * const grant = await device.init({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * clientId: 'sylphx-cli',\n\t * scope: ['org:read', 'project:*'],\n\t * })\n\t * openBrowser(grant.verification_uri_complete)\n\t * ```\n\t */\n\tasync init(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly scope?: readonly string[]\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceGrant> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildDeviceHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tscope: opts.scope ?? [],\n\t\t\t}),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.init')\n\t\treturn (await res.json()) as DeviceGrant\n\t},\n\n\t/**\n\t * Poll a pending grant. Returns `status: 'pending' | 'approved' |\n\t * 'denied' | 'expired'`. On `approved`, the result carries the OAuth\n\t * pair (access_token + refresh_token).\n\t *\n\t * Callers MUST respect the `interval` returned by `init()` — polling\n\t * faster than that may return 429 slow_down (RFC 8628 §5.5).\n\t */\n\tasync poll(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly deviceCode: string\n\t\treadonly userAgent?: string\n\t}): Promise<DevicePollResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/poll`)\n\t\turl.searchParams.set('device_code', opts.deviceCode)\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildDeviceHeaders(opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.poll')\n\t\treturn (await res.json()) as DevicePollResult\n\t},\n\n\t/**\n\t * Browser leg — the approving user confirms the grant.\n\t *\n\t * Requires a valid platform-issued access token (`Authorization:\n\t * Bearer <accessToken>`) proving the user is logged in on the\n\t * Console. Typically called by the Console's `/device` verification\n\t * page server-side, forwarding the user's session JWT.\n\t */\n\tasync approve(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly userCode: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceApproveResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/approve`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t...buildDeviceHeaders(opts.userAgent),\n\t\t\t\tAuthorization: `Bearer ${opts.accessToken}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ user_code: opts.userCode }),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.approve')\n\t\treturn (await res.json()) as DeviceApproveResult\n\t},\n\n\t/**\n\t * Browser leg — the user declines the grant.\n\t *\n\t * Requires a valid platform-issued access token just like `approve`.\n\t */\n\tasync deny(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly userCode: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<DeviceDenyResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/device/deny`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t...buildDeviceHeaders(opts.userAgent),\n\t\t\t\tAuthorization: `Bearer ${opts.accessToken}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ user_code: opts.userCode }),\n\t\t})\n\t\tif (!res.ok) throw await deviceError(res, 'device.deny')\n\t\treturn (await res.json()) as DeviceDenyResult\n\t},\n} as const\n\nfunction buildDeviceHeaders(userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\n/**\n * Normalise device-flow HTTP errors into `SylphxError`s with meaningful\n * codes so CLI-side retry logic can discriminate between `transient`\n * (5xx / network) and `invalid_grant` (404 / 409) outcomes.\n */\nasync function deviceError(res: Response, operation: string): Promise<Error> {\n\t// Dynamic import avoids a hard dependency on the error module for the\n\t// happy path. The tree-shaker drops this when unused.\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'device_flow_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as { error?: string; error_description?: string }\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\treturn new SylphxError(message, {\n\t\tcode: res.status === 429 ? 'TOO_MANY_REQUESTS' : 'BAD_REQUEST',\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * DPoP — Demonstration of Proof-of-Possession (RFC 9449 / ADR-089 Phase 5.1e).\n *\n * Client-side helpers for sender-constrained access tokens. Built on\n * `crypto.subtle` with no runtime dependencies.\n */\n\nexport const dpop = {\n\t/**\n\t * Generate a fresh ES256 key pair. Private key is non-extractable\n\t * (`extractable: false`) so it can be stored but never serialised.\n\t */\n\tasync generateKeyPair(): Promise<{\n\t\treadonly privateKey: CryptoKey\n\t\treadonly publicKey: CryptoKey\n\t\treadonly thumbprint: string\n\t}> {\n\t\tconst { privateKey, publicKey } = (await crypto.subtle.generateKey(\n\t\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\t\tfalse,\n\t\t\t['sign', 'verify'],\n\t\t)) as CryptoKeyPair\n\t\tconst publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n\t\tconst thumbprint = await thumbprintFromJwk(sanitisePublicJwk(publicJwk))\n\t\treturn { privateKey, publicKey, thumbprint }\n\t},\n\n\t/**\n\t * Sign a DPoP proof JWT. When `accessToken` is provided, the proof\n\t * includes `ath = base64url(sha256(accessToken))` so the resource\n\t * server can bind the proof to the token being presented.\n\t */\n\tasync generateProof(opts: {\n\t\treadonly privateKey: CryptoKey\n\t\treadonly publicKey: CryptoKey\n\t\treadonly method: string\n\t\treadonly uri: string\n\t\treadonly accessToken?: string\n\t\treadonly nonce?: string\n\t}): Promise<string> {\n\t\tconst publicJwkRaw = await crypto.subtle.exportKey('jwk', opts.publicKey)\n\t\tconst publicJwk = sanitisePublicJwk(publicJwkRaw)\n\n\t\tconst header = { typ: 'dpop+jwt', alg: 'ES256', jwk: publicJwk }\n\t\tconst payload: Record<string, unknown> = {\n\t\t\tjti: randomJti(),\n\t\t\thtm: opts.method.toUpperCase(),\n\t\t\thtu: stripQueryAndFragment(opts.uri),\n\t\t\tiat: Math.floor(Date.now() / 1000),\n\t\t}\n\t\tif (opts.accessToken) {\n\t\t\tpayload.ath = await sha256Base64Url(new TextEncoder().encode(opts.accessToken))\n\t\t}\n\t\tif (opts.nonce) payload.nonce = opts.nonce\n\n\t\tconst headerB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)))\n\t\tconst payloadB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)))\n\t\tconst signingInput = `${headerB64}.${payloadB64}`\n\n\t\tconst signingBytes = new TextEncoder().encode(signingInput)\n\t\tconst signingBuf = new Uint8Array(signingBytes.byteLength)\n\t\tsigningBuf.set(signingBytes)\n\t\tconst sigBuf = await crypto.subtle.sign(\n\t\t\t{ name: 'ECDSA', hash: 'SHA-256' },\n\t\t\topts.privateKey,\n\t\t\tsigningBuf.buffer,\n\t\t)\n\t\tconst sigB64 = base64UrlEncode(new Uint8Array(sigBuf))\n\t\treturn `${signingInput}.${sigB64}`\n\t},\n} as const\n\nfunction sanitisePublicJwk(jwk: JsonWebKey): { kty: string; crv: string; x: string; y: string } {\n\tif (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.x || !jwk.y) {\n\t\tthrow new Error('DPoP expects ES256 (EC P-256) JWK')\n\t}\n\treturn { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }\n}\n\nasync function thumbprintFromJwk(jwk: {\n\tkty: string\n\tcrv: string\n\tx: string\n\ty: string\n}): Promise<string> {\n\tconst canonical = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y })\n\treturn sha256Base64Url(new TextEncoder().encode(canonical))\n}\n\nasync function sha256Base64Url(data: Uint8Array): Promise<string> {\n\tconst copy = new Uint8Array(data.byteLength)\n\tcopy.set(data)\n\tconst digest = await crypto.subtle.digest('SHA-256', copy.buffer)\n\treturn base64UrlEncode(new Uint8Array(digest))\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet s = ''\n\tfor (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]!)\n\treturn btoa(s).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction stripQueryAndFragment(uri: string): string {\n\ttry {\n\t\tconst u = new URL(uri)\n\t\treturn `${u.protocol}//${u.host}${u.pathname}`\n\t} catch {\n\t\tconst q = uri.indexOf('?')\n\t\tconst h = uri.indexOf('#')\n\t\tconst cut = [q, h].filter((i) => i >= 0).sort((a, b) => a - b)[0]\n\t\treturn cut === undefined ? uri : uri.slice(0, cut)\n\t}\n}\n\nfunction randomJti(): string {\n\tconst bytes = new Uint8Array(16)\n\tcrypto.getRandomValues(bytes)\n\treturn base64UrlEncode(bytes)\n}\n","/**\n * Platform refresh-token rotation and logout SDK namespace.\n */\n\nimport type { LogoutInput, RefreshTokenInput, RefreshTokenResult } from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformRefreshInput = RefreshTokenInput\nexport type PlatformRefreshResult = RefreshTokenResult\nexport type PlatformLogoutInput = LogoutInput\n\nexport const platformAuth = {\n\tasync refresh(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly refreshToken: string\n\t\treadonly userAgent?: string\n\t\t/**\n\t\t * Path prefix between `baseUrl` and the resource path. Defaults\n\t\t * to `/api/v1` for back-compat with the admin-override host\n\t\t * (`sylphx.com`). Pass `/v1` when targeting the canonical host\n\t\t * (`api.sylphx.com`) per Rule 17.\n\t\t */\n\t\treadonly urlPrefix?: string\n\t}): Promise<PlatformRefreshResult> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' }\n\t\tif (opts.userAgent) headers['User-Agent'] = opts.userAgent\n\t\tconst prefix = opts.urlPrefix ?? '/api/v1'\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}${prefix}/auth/refresh`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({ refresh_token: opts.refreshToken } satisfies PlatformRefreshInput),\n\t\t})\n\t\tif (!res.ok) throw await platformAuthError(res, 'platformAuth.refresh')\n\t\treturn (await res.json()) as PlatformRefreshResult\n\t},\n\n\tasync logout(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly refreshToken: string\n\t\treadonly userAgent?: string\n\t\t/** See `refresh.urlPrefix`. */\n\t\treadonly urlPrefix?: string\n\t}): Promise<void> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' }\n\t\tif (opts.userAgent) headers['User-Agent'] = opts.userAgent\n\t\tconst prefix = opts.urlPrefix ?? '/api/v1'\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}${prefix}/auth/logout`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({ refresh_token: opts.refreshToken } satisfies PlatformLogoutInput),\n\t\t})\n\t\tif (!res.ok) throw await platformAuthError(res, 'platformAuth.logout')\n\t},\n} as const\n\nasync function platformAuthError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_auth_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string | { code?: string; message?: string }\n\t\t\tmessage?: string\n\t\t}\n\t\tif (typeof parsed.error === 'string') code = parsed.error\n\t\telse if (parsed.error && typeof parsed.error === 'object') {\n\t\t\tif (parsed.error.code) code = parsed.error.code\n\t\t\tif (parsed.error.message) message = parsed.error.message\n\t\t} else if (parsed.message) {\n\t\t\tmessage = parsed.message\n\t\t}\n\t} catch {\n\t\tif (body) message = body\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform impersonation SDK namespace.\n *\n * Covers the ADR-089 Phase 3b legacy helpers and Phase 5.9 WebAuthn\n * step-up + target-consent workflow.\n */\n\nimport { SylphxError } from './errors'\n\nexport interface ImpersonationStartResult {\n\treadonly success: true\n\treadonly token: string\n\treadonly sessionId: string\n\treadonly expiresAt: string\n}\n\nexport interface ImpersonationEndResult {\n\treadonly success: boolean\n\treadonly sessionsEnded: number\n}\n\nexport interface ImpersonationInfo {\n\treadonly isImpersonation: true\n\treadonly adminUserId: string\n\treadonly adminEmail: string\n\treadonly adminName: string | null\n\treadonly impersonatedAt: string\n}\n\nexport interface ImpersonationActive {\n\treadonly sessionId: string\n\treadonly adminUserId: string\n\treadonly adminEmail: string\n\treadonly adminName: string | null\n\treadonly targetUserId: string\n\treadonly targetEmail: string\n\treadonly targetName: string | null\n\treadonly impersonatedAt: string\n\treadonly lastActiveAt: string\n}\n\nexport interface ImpersonationStartChallengeInput {\n\treadonly baseUrl: string\n\treadonly accessToken: string\n\treadonly targetUserId: string\n\treadonly reason: string\n\treadonly userAgent?: string\n}\n\nexport interface ImpersonationChallenge {\n\treadonly requestId: string\n\treadonly challengeKey: string\n\treadonly webauthnOptions: {\n\t\treadonly challenge: string\n\t\treadonly rpId?: string\n\t\treadonly allowCredentials: ReadonlyArray<{\n\t\t\treadonly id: string\n\t\t\treadonly type: 'public-key'\n\t\t\treadonly transports?: readonly string[]\n\t\t}>\n\t\treadonly userVerification: 'required'\n\t\treadonly timeout: number\n\t}\n}\n\nexport interface ImpersonationStartStepupInput {\n\treadonly baseUrl: string\n\treadonly accessToken: string\n\treadonly requestId: string\n\treadonly challengeKey: string\n\treadonly assertion: unknown\n\treadonly emergencyBypass?: boolean\n\treadonly userAgent?: string\n}\n\nexport type ImpersonationStartStepupResult =\n\t| {\n\t\t\treadonly branch: 'emergency'\n\t\t\treadonly requestId: string\n\t\t\treadonly token: string\n\t\t\treadonly sessionId: string\n\t\t\treadonly expiresAt: string\n\t }\n\t| {\n\t\t\treadonly branch: 'awaiting-consent'\n\t\t\treadonly requestId: string\n\t\t\treadonly consentDeadline: string\n\t }\n\nexport type ImpersonationConsentDecision = 'approve' | 'deny'\n\nexport type ImpersonationConsentResponse =\n\t| {\n\t\t\treadonly branch: 'approved'\n\t\t\treadonly requestId: string\n\t\t\treadonly token: string\n\t\t\treadonly sessionId: string\n\t\t\treadonly expiresAt: string\n\t }\n\t| {\n\t\t\treadonly branch: 'denied'\n\t\t\treadonly requestId: string\n\t }\n\nexport interface ImpersonationRequestRow {\n\treadonly id: string\n\treadonly operatorId: string\n\treadonly targetUserId: string\n\treadonly reason: string\n\treadonly status:\n\t\t| 'awaiting-stepup'\n\t\t| 'awaiting-consent'\n\t\t| 'active'\n\t\t| 'denied'\n\t\t| 'expired'\n\t\t| 'ended'\n\t\t| 'revoked'\n\treadonly emergencyBypass: boolean\n\treadonly sessionId: string | null\n\treadonly consentDeadline: string | null\n\treadonly startedAt: string | null\n\treadonly endedAt: string | null\n\treadonly createdAt: string\n}\n\nexport const impersonation = {\n\tasync start(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly targetUserId: string\n\t\treadonly ipAddress?: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationStartResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\ttargetUserId: opts.targetUserId,\n\t\t\t\t\t...(opts.ipAddress !== undefined && { ipAddress: opts.ipAddress }),\n\t\t\t\t\t...(opts.userAgent !== undefined && { userAgent: opts.userAgent }),\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.start')\n\t\treturn (await res.json()) as ImpersonationStartResult\n\t},\n\n\tasync end(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId?: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationEndResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/end`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}),\n\t\t})\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.end')\n\t\treturn (await res.json()) as ImpersonationEndResult\n\t},\n\n\tasync info(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationInfo | null> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/info/${encodeURIComponent(opts.sessionId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.info')\n\t\treturn (await res.json()) as ImpersonationInfo | null\n\t},\n\n\tasync active(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<readonly ImpersonationActive[]> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/active`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.active')\n\t\treturn (await res.json()) as readonly ImpersonationActive[]\n\t},\n\n\tasync startChallenge(opts: ImpersonationStartChallengeInput): Promise<ImpersonationChallenge> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start-challenge`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\ttargetUserId: opts.targetUserId,\n\t\t\t\t\treason: opts.reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.startChallenge')\n\t\treturn (await res.json()) as ImpersonationChallenge\n\t},\n\n\tasync startStepup(opts: ImpersonationStartStepupInput): Promise<ImpersonationStartStepupResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/start`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\trequestId: opts.requestId,\n\t\t\t\t\tchallengeKey: opts.challengeKey,\n\t\t\t\t\tassertion: opts.assertion,\n\t\t\t\t\t...(opts.emergencyBypass ? { emergencyBypass: true } : {}),\n\t\t\t\t}),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.startStepup')\n\t\treturn (await res.json()) as ImpersonationStartStepupResult\n\t},\n\n\tasync respondConsent(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly requestId: string\n\t\treadonly decision: ImpersonationConsentDecision\n\t\treadonly userAgent?: string\n\t}): Promise<ImpersonationConsentResponse> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/impersonation-consent/${encodeURIComponent(opts.requestId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({ decision: opts.decision }),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.respondConsent')\n\t\treturn (await res.json()) as ImpersonationConsentResponse\n\t},\n\n\tasync listRequests(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: {\n\t\t\treadonly operatorId?: string\n\t\t\treadonly targetUserId?: string\n\t\t\treadonly status?: ImpersonationRequestRow['status']\n\t\t\treadonly limit?: number\n\t\t}\n\t\treadonly userAgent?: string\n\t}): Promise<readonly ImpersonationRequestRow[]> {\n\t\tconst params = new URLSearchParams()\n\t\tif (opts.filter?.operatorId) params.set('operatorId', opts.filter.operatorId)\n\t\tif (opts.filter?.targetUserId) params.set('targetUserId', opts.filter.targetUserId)\n\t\tif (opts.filter?.status) params.set('status', opts.filter.status)\n\t\tif (opts.filter?.limit != null) params.set('limit', String(opts.filter.limit))\n\t\tconst qs = params.toString()\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/requests${qs ? `?${qs}` : ''}`,\n\t\t\t{\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.listRequests')\n\t\treturn (await res.json()) as readonly ImpersonationRequestRow[]\n\t},\n\n\tasync endSession(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly requestId: string\n\t\treadonly userAgent?: string\n\t}): Promise<{ success: true; requestId: string; sessionId: string | null }> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-impersonation/end/${encodeURIComponent(opts.requestId)}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildImpersonationHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await impersonationError(res, 'impersonation.endSession')\n\t\treturn (await res.json()) as { success: true; requestId: string; sessionId: string | null }\n\t},\n} as const\n\nfunction buildImpersonationHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function impersonationError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_impersonation_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\t\t| 'CONFLICT'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 409) errorCode = 'CONFLICT'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform JWT verification and cookie-session resolution.\n *\n * This module owns the SDK's hot-path Platform auth helpers: cached JWKS\n * verification for bearer tokens and cached cookie-based user resolution.\n */\n\n// ---------------------------------------------------------------------------\n// JWKS cache — module scope\n// ---------------------------------------------------------------------------\n\ninterface JwksCache {\n\tkeys: unknown[]\n\texpiresAt: number\n}\n\nlet jwtJwksCache: JwksCache | null = null\n\n/**\n * Reset the platform-JWKS cache. Tests should call this between cases\n * to avoid state bleed. Production code relies on the TTL-based\n * expiry.\n */\nexport function resetPlatformJwksCache(): void {\n\tjwtJwksCache = null\n}\n\nconst PLATFORM_JWKS_TTL_MS = 60 * 60 * 1000 // 1 hour\n\nasync function fetchPlatformJwks(baseUrl: string): Promise<unknown[]> {\n\tconst now = Date.now()\n\tif (jwtJwksCache && jwtJwksCache.expiresAt > now) return jwtJwksCache.keys\n\n\tconst root = baseUrl.replace(/\\/$/, '').replace(/\\/v1$/, '')\n\tconst res = await fetch(`${root}/.well-known/jwks.json`)\n\tif (!res.ok) throw new Error(`JWKS fetch failed: HTTP ${res.status}`)\n\tconst data = (await res.json()) as { keys?: unknown[] }\n\tif (!Array.isArray(data.keys)) throw new Error('JWKS response missing keys[]')\n\n\tjwtJwksCache = { keys: data.keys, expiresAt: now + PLATFORM_JWKS_TTL_MS }\n\treturn data.keys\n}\n\nexport interface PlatformAccessTokenClaims {\n\treadonly sub: string\n\treadonly pid?: string\n\treadonly email: string\n\treadonly name?: string\n\treadonly picture?: string\n\treadonly email_verified: boolean\n\treadonly app_id: string\n\treadonly role: string\n\treadonly org_id?: string\n\treadonly org_slug?: string\n\treadonly org_role?: string\n\treadonly iat?: number\n\treadonly exp?: number\n\t/**\n\t * RFC 7800 confirmation claim — present when the token is sender-\n\t * constrained. Today we emit this for DPoP-bound tokens (RFC 9449)\n\t * where `cnf.jkt` is the SHA-256 thumbprint of the client's DPoP\n\t * public key.\n\t *\n\t * Resource servers (e.g. apps/api Management plane) that want to\n\t * enforce DPoP MUST:\n\t * 1. Look up `oauth_clients.dpop_bound_access_tokens` on the\n\t * issuing client to know whether DPoP is required.\n\t * 2. If required AND `cnf.jkt` is absent, reject 401.\n\t * 3. If `cnf.jkt` is present, verify the inbound `DPoP` header's\n\t * proof JWT and assert its public-key thumbprint matches `jkt`.\n\t *\n\t * Pre-Wave-5.3 this field was stripped from `verifyAccessToken`'s\n\t * return value, making resource-side enforcement impossible without\n\t * decoding the JWT a second time. Exposing it preserves the wire\n\t * format and unlocks the resource-server DPoP middleware.\n\t */\n\treadonly cnf?: {\n\t\treadonly jkt?: string\n\t}\n}\n\n/**\n * `verifyAccessToken` — local JWT verification against cached JWKS.\n *\n * Designed for the Platform API's hot-path auth middleware: JWKS is\n * fetched once per process (1h TTL), signature/iss/aud/exp\n * verification is local `jose` — no per-request HTTPS hop.\n *\n * @example\n * ```typescript\n * const claims = await auth.verifyAccessToken(bearer, {\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * audience: 'platform',\n * })\n * ```\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: {\n\t\treadonly baseUrl: string\n\t\treadonly audience: string\n\t},\n): Promise<PlatformAccessTokenClaims> {\n\tconst { importJWK, jwtVerify, decodeProtectedHeader } = await import('jose')\n\n\tlet keys = await fetchPlatformJwks(opts.baseUrl)\n\n\t// Kid-aware selection — if the token's kid isn't in cache, invalidate\n\t// and refetch once (handles key-rotation windows).\n\tlet kid: string | undefined\n\ttry {\n\t\tkid = decodeProtectedHeader(token).kid\n\t} catch {\n\t\t// Fall through — jwtVerify below will surface the malformed error.\n\t}\n\n\tconst hasKid = (keyList: unknown[]) =>\n\t\tkeyList.some((k) => (k as { kid?: string } | null)?.kid === kid)\n\n\tif (kid && !hasKid(keys)) {\n\t\tresetPlatformJwksCache()\n\t\tkeys = await fetchPlatformJwks(opts.baseUrl)\n\t}\n\n\tlet lastError: unknown = null\n\tfor (const key of keys) {\n\t\tconst candidate = key as { kid?: string }\n\t\tif (kid && candidate.kid && candidate.kid !== kid) continue\n\t\ttry {\n\t\t\tconst jwk = await importJWK(key as Parameters<typeof importJWK>[0], 'RS256')\n\t\t\tconst { payload } = await jwtVerify(token, jwk, {\n\t\t\t\taudience: opts.audience,\n\t\t\t})\n\t\t\t// Wave 5.3 — extract the optional RFC 7800 `cnf` confirmation\n\t\t\t// claim. Today we only support the DPoP `jkt` thumbprint\n\t\t\t// (RFC 9449); future sender-constrained schemes (mTLS\n\t\t\t// certificate thumbprint per RFC 8705) plug into the same\n\t\t\t// claim with a different sub-key. We intentionally narrow\n\t\t\t// to `{ jkt? }` rather than passing the raw object through\n\t\t\t// — the resource-server middleware should NEVER trust an\n\t\t\t// unparsed claim shape.\n\t\t\tconst cnfRaw = payload.cnf\n\t\t\tconst cnf =\n\t\t\t\tcnfRaw && typeof cnfRaw === 'object' && 'jkt' in cnfRaw\n\t\t\t\t\t? { jkt: typeof cnfRaw.jkt === 'string' ? cnfRaw.jkt : undefined }\n\t\t\t\t\t: undefined\n\n\t\t\treturn {\n\t\t\t\tsub: payload.sub as string,\n\t\t\t\tpid: payload.pid as string | undefined,\n\t\t\t\temail: payload.email as string,\n\t\t\t\tname: payload.name as string | undefined,\n\t\t\t\tpicture: payload.picture as string | undefined,\n\t\t\t\temail_verified: Boolean(payload.email_verified),\n\t\t\t\tapp_id: payload.app_id as string,\n\t\t\t\trole: (payload.role as string) ?? 'user',\n\t\t\t\torg_id: payload.org_id as string | undefined,\n\t\t\t\torg_slug: payload.org_slug as string | undefined,\n\t\t\t\torg_role: payload.org_role as string | undefined,\n\t\t\t\tiat: payload.iat,\n\t\t\t\texp: payload.exp,\n\t\t\t\t...(cnf ? { cnf } : {}),\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tlastError = err\n\t\t}\n\t}\n\n\tthrow lastError instanceof Error ? lastError : new Error('Access token verification failed')\n}\n\n// ---------------------------------------------------------------------------\n// cookies.resolvePlatformUser\n// ---------------------------------------------------------------------------\n\nexport interface PlatformUserRecord {\n\treadonly id: string\n\treadonly email: string\n\treadonly name: string | null\n\treadonly image: string | null\n\treadonly emailVerified: boolean\n\treadonly role: string\n\treadonly twoFactorEnabled: boolean\n}\n\nexport interface PlatformUserResolution {\n\treadonly user: PlatformUserRecord\n\treadonly sessionId: string\n}\n\n// 30s per-cookie cache for `resolvePlatformUser`. Hot-path middleware\n// calls this on every request; the upstream BaaS hop shouldn't run\n// more than once per user-session per 30s window. Cache is keyed on\n// the full cookie header string — any cookie change (login,\n// rotation, logout) invalidates automatically.\nconst COOKIE_RESOLVER_TTL_MS = 30 * 1000\nconst cookieResolverCache = new Map<\n\tstring,\n\t{ expiresAt: number; result: PlatformUserResolution | null }\n>()\n\nexport function resetPlatformCookieCache(): void {\n\tcookieResolverCache.clear()\n}\n\n/**\n * `cookies` namespace — Platform cookie / session resolution for the\n * Platform API's hot-path auth middleware (ADR-089 Phase 3b).\n */\nexport const cookies = {\n\t/**\n\t * Resolve a platform user from a forwarded `Cookie:` header.\n\t *\n\t * Delegates to BaaS `/auth/platform-sessions/whoami`. Caches each\n\t * unique cookie string for 30s to avoid hammering BaaS on every\n\t * SSR request.\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await auth.cookies.resolvePlatformUser({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * cookieHeader: req.headers.get('cookie') ?? '',\n\t * })\n\t * if (!result) // unauthenticated\n\t * ```\n\t */\n\tasync resolvePlatformUser(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly cookieHeader: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserResolution | null> {\n\t\tif (!opts.cookieHeader) return null\n\n\t\tconst now = Date.now()\n\t\tconst cached = cookieResolverCache.get(opts.cookieHeader)\n\t\tif (cached && cached.expiresAt > now) return cached.result\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/whoami`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tCookie: opts.cookieHeader,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t})\n\n\t\tif (!res.ok) {\n\t\t\t// Cache negative result briefly to avoid hammering on persistent\n\t\t\t// 5xx; 5s window balances responsiveness against amplification.\n\t\t\tcookieResolverCache.set(opts.cookieHeader, {\n\t\t\t\tresult: null,\n\t\t\t\texpiresAt: now + 5_000,\n\t\t\t})\n\t\t\treturn null\n\t\t}\n\n\t\tconst body = (await res.json()) as PlatformUserResolution | null\n\t\tcookieResolverCache.set(opts.cookieHeader, {\n\t\t\tresult: body,\n\t\t\texpiresAt: now + COOKIE_RESOLVER_TTL_MS,\n\t\t})\n\t\treturn body\n\t},\n} as const\n","/**\n * Platform OAuth namespace.\n *\n * Backs `auth.oauth.*` while keeping OAuth AS protocol handling out of the\n * monolithic auth module. Public exports are re-exported from `auth.ts`.\n */\n\nimport type {\n\tOAuthIntrospectRequest as ContractOAuthIntrospectRequest,\n\tOAuthIntrospectResponse as ContractOAuthIntrospectResponse,\n\tOAuthRevokeRequest as ContractOAuthRevokeRequest,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\nimport {\n\tdecodeOAuthClientCredentialsResult,\n\tdecodeOAuthTokenError,\n\tdecodeOAuthTokenResult,\n\ttype OAuthClientCredentialsResult,\n\ttype OAuthPollError,\n\ttype OAuthPollResult,\n\ttype OAuthTokenResult,\n\toauthTokenError,\n\toauthTokenFormBody,\n} from './oauth-token'\n\nexport type OAuthRevokeInput = ContractOAuthRevokeRequest\nexport type OAuthIntrospectInput = ContractOAuthIntrospectRequest\nexport type OAuthIntrospectResult = ContractOAuthIntrospectResponse\n\nexport interface MintAccessTokenClaims {\n\treadonly sub: string\n\treadonly email: string\n\treadonly name?: string\n\treadonly email_verified: boolean\n\treadonly app_id: string\n\treadonly role: string\n\treadonly org_id?: string\n\treadonly org_slug?: string\n\treadonly org_role?: string\n\treadonly picture?: string\n\treadonly pid?: string\n}\n\nexport interface MintAccessTokenResult {\n\treadonly accessToken: string\n\treadonly expiresIn: number\n}\n\ninterface OAuthClientCallOpts {\n\treadonly baseUrl: string\n\treadonly clientId: string\n\treadonly clientSecret?: string\n\treadonly token: string\n\treadonly tokenTypeHint?: 'access_token' | 'refresh_token'\n\treadonly userAgent?: string\n}\n\n/**\n * `oauth` namespace — Platform OAuth operations backed by BaaS.\n *\n * Phase 3b adds `mintAccessToken` for the refresh handler migration;\n * Phase 5.1 layered in full authorization-server verbs\n * (`/oauth/token`, `/oauth/revoke`, `/oauth/introspect`).\n */\nexport const oauth = {\n\t/**\n\t * Mint a platform-audience access token from supplied claims.\n\t *\n\t * Service-to-service call — authenticated via\n\t * `SYLPHX_INTERNAL_TOKEN` shared secret until ADR-068's\n\t * SPIFFE SVID mTLS platform-auth flip makes workload identity the\n\t * only accepted internal caller credential.\n\t */\n\tasync mintAccessToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly internalToken: string\n\t\treadonly claims: MintAccessTokenClaims\n\t\treadonly userAgent?: string\n\t}): Promise<MintAccessTokenResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-jwt/mint`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${opts.internalToken}`,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t\tbody: JSON.stringify(opts.claims),\n\t\t})\n\t\tif (!res.ok) throw await platformJwtError(res, 'oauth.mintAccessToken')\n\t\treturn (await res.json()) as MintAccessTokenResult\n\t},\n\n\tasync exchangeAuthorizationCode(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret?: string\n\t\treadonly code: string\n\t\treadonly redirectUri: string\n\t\treadonly codeVerifier: string\n\t}): Promise<OAuthTokenResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode: opts.code,\n\t\t\tredirect_uri: opts.redirectUri,\n\t\t\tclient_id: opts.clientId,\n\t\t\tcode_verifier: opts.codeVerifier,\n\t\t\t...(opts.clientSecret ? { client_secret: opts.clientSecret } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.exchangeAuthorizationCode')\n\t\treturn decodeOAuthTokenResult(await res.json())\n\t},\n\n\tasync refreshAccessToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret?: string\n\t\treadonly refreshToken: string\n\t\treadonly scope?: string\n\t}): Promise<OAuthTokenResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: opts.refreshToken,\n\t\t\tclient_id: opts.clientId,\n\t\t\t...(opts.clientSecret ? { client_secret: opts.clientSecret } : {}),\n\t\t\t...(opts.scope ? { scope: opts.scope } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.refreshAccessToken')\n\t\treturn decodeOAuthTokenResult(await res.json())\n\t},\n\n\tasync pollDeviceToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly deviceCode: string\n\t}): Promise<OAuthPollResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\tdevice_code: opts.deviceCode,\n\t\t\tclient_id: opts.clientId,\n\t\t})\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (res.ok) return { ok: true as const, tokens: decodeOAuthTokenResult(await res.json()) }\n\n\t\tconst text = await res.text().catch(() => '')\n\t\tlet code: OAuthPollError = 'oauth_error'\n\t\ttry {\n\t\t\tcode = decodeOAuthTokenError(JSON.parse(text)).error\n\t\t} catch {\n\t\t\t// Non-JSON or non-contract body — keep default.\n\t\t}\n\t\treturn { ok: false as const, error: code, status: res.status }\n\t},\n\n\tasync clientCredentialsToken(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly clientId: string\n\t\treadonly clientSecret: string\n\t\treadonly scope?: string\n\t}): Promise<OAuthClientCredentialsResult> {\n\t\tconst body = oauthTokenFormBody({\n\t\t\tgrant_type: 'client_credentials',\n\t\t\tclient_id: opts.clientId,\n\t\t\tclient_secret: opts.clientSecret,\n\t\t\t...(opts.scope ? { scope: opts.scope } : {}),\n\t\t})\n\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/x-www-form-urlencoded' },\n\t\t\tbody,\n\t\t})\n\t\tif (!res.ok) throw await oauthTokenError(res, 'oauth.clientCredentialsToken')\n\t\treturn decodeOAuthClientCredentialsResult(await res.json())\n\t},\n\n\tasync revokeToken(opts: OAuthClientCallOpts): Promise<void> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildJsonHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttoken: opts.token,\n\t\t\t\ttoken_type_hint: opts.tokenTypeHint,\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tclient_secret: opts.clientSecret,\n\t\t\t} satisfies OAuthRevokeInput),\n\t\t})\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.text().catch(() => '')\n\t\t\tthrow new Error(`oauth.revokeToken failed (${res.status}): ${body}`)\n\t\t}\n\t},\n\n\tasync introspectToken(opts: OAuthClientCallOpts): Promise<OAuthIntrospectResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/oauth/introspect`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildJsonHeaders(opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttoken: opts.token,\n\t\t\t\ttoken_type_hint: opts.tokenTypeHint,\n\t\t\t\tclient_id: opts.clientId,\n\t\t\t\tclient_secret: opts.clientSecret,\n\t\t\t} satisfies OAuthIntrospectInput),\n\t\t})\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.text().catch(() => '')\n\t\t\tthrow new Error(`oauth.introspectToken failed (${res.status}): ${body}`)\n\t\t}\n\t\treturn (await res.json()) as OAuthIntrospectResult\n\t},\n} as const\n\nfunction buildJsonHeaders(userAgent: string | undefined): Record<string, string> {\n\treturn {\n\t\t'Content-Type': 'application/json',\n\t\t...(userAgent ? { 'User-Agent': userAgent } : {}),\n\t}\n}\n\nasync function platformJwtError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_jwt_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode: 'UNAUTHORIZED' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR' | 'TOO_MANY_REQUESTS'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * OAuth token endpoint contract helpers.\n *\n * Keeps RFC 6749/8628 request encoding, success decoding, and error decoding\n * type-bound to `@sylphx/contract` while using SDK-local runtime guards so the\n * published Promise SDK does not import Effect internals.\n */\n\nimport type {\n\tOAuthClientCredentialsResponse as ContractOAuthClientCredentialsResponse,\n\tOAuthTokenErrorResponse as ContractOAuthTokenErrorResponse,\n\tOAuthTokenRequest as ContractOAuthTokenRequest,\n\tOAuthTokenResponse as ContractOAuthTokenResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type OAuthTokenResult = ContractOAuthTokenResponse\nexport type OAuthClientCredentialsResult = ContractOAuthClientCredentialsResponse\nexport type OAuthTokenEndpointError = ContractOAuthTokenErrorResponse['error']\nexport type OAuthPollError = OAuthTokenEndpointError | 'oauth_error'\nexport type OAuthPollResult =\n\t| { readonly ok: true; readonly tokens: OAuthTokenResult }\n\t| { readonly ok: false; readonly error: OAuthPollError; readonly status: number }\n\nconst OAUTH_TOKEN_ERROR_CODES = new Set<OAuthTokenEndpointError>([\n\t'invalid_request',\n\t'invalid_client',\n\t'invalid_grant',\n\t'unauthorized_client',\n\t'unsupported_grant_type',\n\t'invalid_scope',\n\t'authorization_pending',\n\t'slow_down',\n\t'access_denied',\n\t'expired_token',\n])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null\n}\n\nfunction requireString(record: Record<string, unknown>, key: string): string {\n\tconst value = record[key]\n\tif (typeof value !== 'string') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction optionalString(record: Record<string, unknown>, key: string): string | undefined {\n\tconst value = record[key]\n\tif (value === undefined) return undefined\n\tif (typeof value !== 'string') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction optionalField(key: string, value: string | undefined): Record<string, string> {\n\treturn value === undefined ? {} : { [key]: value }\n}\n\nfunction requireNumber(record: Record<string, unknown>, key: string): number {\n\tconst value = record[key]\n\tif (typeof value !== 'number') throw new Error(`Invalid OAuth token field: ${key}`)\n\treturn value\n}\n\nfunction assertGrantType<T extends ContractOAuthTokenRequest['grant_type']>(\n\trecord: Record<string, unknown>,\n\tgrantType: T,\n): asserts record is Record<string, unknown> & { grant_type: T } {\n\tif (record.grant_type !== grantType) {\n\t\tthrow new Error(`Invalid OAuth grant_type: expected ${grantType}`)\n\t}\n}\n\nfunction decodeOAuthTokenRequest(input: ContractOAuthTokenRequest): ContractOAuthTokenRequest {\n\tif (!isRecord(input)) throw new Error('Invalid OAuth token request')\n\tswitch (input.grant_type) {\n\t\tcase 'authorization_code':\n\t\t\tassertGrantType(input, 'authorization_code')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'authorization_code',\n\t\t\t\tcode: requireString(input, 'code'),\n\t\t\t\tredirect_uri: requireString(input, 'redirect_uri'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\tcode_verifier: requireString(input, 'code_verifier'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t}\n\t\tcase 'refresh_token':\n\t\t\tassertGrantType(input, 'refresh_token')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'refresh_token',\n\t\t\t\trefresh_token: requireString(input, 'refresh_token'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t\t...optionalField('scope', optionalString(input, 'scope')),\n\t\t\t}\n\t\tcase 'urn:ietf:params:oauth:grant-type:device_code':\n\t\t\tassertGrantType(input, 'urn:ietf:params:oauth:grant-type:device_code')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\t\tdevice_code: requireString(input, 'device_code'),\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\t...optionalField('client_secret', optionalString(input, 'client_secret')),\n\t\t\t}\n\t\tcase 'client_credentials':\n\t\t\tassertGrantType(input, 'client_credentials')\n\t\t\treturn {\n\t\t\t\tgrant_type: 'client_credentials',\n\t\t\t\tclient_id: requireString(input, 'client_id'),\n\t\t\t\tclient_secret: requireString(input, 'client_secret'),\n\t\t\t\t...optionalField('scope', optionalString(input, 'scope')),\n\t\t\t}\n\t\tdefault:\n\t\t\tthrow new Error('Unsupported OAuth grant_type')\n\t}\n}\n\nexport function oauthTokenFormBody(input: ContractOAuthTokenRequest): string {\n\tconst request = decodeOAuthTokenRequest(input)\n\treturn new URLSearchParams(\n\t\tObject.entries(request).filter(\n\t\t\t(entry): entry is [string, string] => typeof entry[1] === 'string',\n\t\t),\n\t).toString()\n}\n\nexport function decodeOAuthTokenResult(value: unknown): OAuthTokenResult {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth token response')\n\tconst tokenType = requireString(value, 'token_type')\n\tif (tokenType !== 'Bearer') throw new Error('Invalid OAuth token_type')\n\treturn {\n\t\taccess_token: requireString(value, 'access_token'),\n\t\ttoken_type: 'Bearer',\n\t\texpires_in: requireNumber(value, 'expires_in'),\n\t\trefresh_token: requireString(value, 'refresh_token'),\n\t\trefresh_expires_at: requireString(value, 'refresh_expires_at'),\n\t\tscope: requireString(value, 'scope'),\n\t}\n}\n\nexport function decodeOAuthClientCredentialsResult(value: unknown): OAuthClientCredentialsResult {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth client credentials response')\n\tconst tokenType = requireString(value, 'token_type')\n\tif (tokenType !== 'Bearer') throw new Error('Invalid OAuth token_type')\n\treturn {\n\t\taccess_token: requireString(value, 'access_token'),\n\t\ttoken_type: 'Bearer',\n\t\texpires_in: requireNumber(value, 'expires_in'),\n\t\tscope: requireString(value, 'scope'),\n\t}\n}\n\nexport function decodeOAuthTokenError(value: unknown): ContractOAuthTokenErrorResponse {\n\tif (!isRecord(value)) throw new Error('Invalid OAuth token error response')\n\tconst error = requireString(value, 'error')\n\tif (!OAUTH_TOKEN_ERROR_CODES.has(error as OAuthTokenEndpointError)) {\n\t\tthrow new Error('Invalid OAuth token error code')\n\t}\n\treturn {\n\t\terror: error as OAuthTokenEndpointError,\n\t\t...optionalField('error_description', optionalString(value, 'error_description')),\n\t\t...optionalField('error_uri', optionalString(value, 'error_uri')),\n\t}\n}\n\nexport async function oauthTokenError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code: OAuthPollError = 'oauth_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = decodeOAuthTokenError(JSON.parse(body))\n\t\tcode = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t} catch {\n\t\t// Non-JSON or non-contract body — keep default message.\n\t}\n\n\tlet errorCode: 'UNAUTHORIZED' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { oauthError: code },\n\t})\n}\n","/**\n * Platform password management SDK namespace.\n *\n * Backed by `/auth/platform-password/*` on the BaaS runtime. Crypto\n * primitives and breach checks stay server-side; callers only pass tokens\n * and plaintext password inputs over the established HTTPS boundary.\n */\n\nimport type {\n\tPlatformPasswordChangeRequest,\n\tPlatformPasswordChangeResponse,\n\tPlatformPasswordSetRequest,\n\tPlatformPasswordSetResponse,\n\tPlatformPasswordStatusResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformPasswordStatusResult = PlatformPasswordStatusResponse\nexport type PlatformPasswordSetInput = PlatformPasswordSetRequest\nexport type PlatformPasswordSetResult = PlatformPasswordSetResponse\nexport type PlatformPasswordChangeInput = PlatformPasswordChangeRequest\nexport type PlatformPasswordChangeResult = PlatformPasswordChangeResponse\n\nexport const password = {\n\tasync status(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordStatusResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/status`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.status')\n\t\treturn (await res.json()) as PlatformPasswordStatusResult\n\t},\n\n\tasync set(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly password: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordSetResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/set`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tpassword: opts.password,\n\t\t\t} satisfies PlatformPasswordSetInput),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.set')\n\t\treturn (await res.json()) as PlatformPasswordSetResult\n\t},\n\n\tasync change(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly currentPassword: string\n\t\treadonly newPassword: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformPasswordChangeResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-password/change`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformPasswordHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tcurrentPassword: opts.currentPassword,\n\t\t\t\tnewPassword: opts.newPassword,\n\t\t\t} satisfies PlatformPasswordChangeInput),\n\t\t})\n\t\tif (!res.ok) throw await platformPasswordError(res, 'password.change')\n\t\treturn (await res.json()) as PlatformPasswordChangeResult\n\t},\n} as const\n\nfunction buildPlatformPasswordHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformPasswordError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_password_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform session management SDK namespace.\n *\n * Backed by `/auth/platform-sessions/*` on the BaaS runtime. These helpers\n * accept platform-audience access tokens, not project `pk_`/`sk_` credentials.\n */\n\nimport type {\n\tPlatformSessionRenameRequest,\n\tPlatformSessionRenameResponse,\n\tPlatformSessionRevokeAllResponse,\n\tPlatformSessionRevokeOtherResponse,\n\tPlatformSessionRevokeRequest,\n\tPlatformSessionRevokeResponse,\n\tPlatformSessionsListResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformSessionsListResult = PlatformSessionsListResponse\nexport type PlatformSessionRevokeInput = PlatformSessionRevokeRequest\nexport type PlatformSessionRevokeResult = PlatformSessionRevokeResponse\nexport type PlatformSessionRevokeOtherResult = PlatformSessionRevokeOtherResponse\nexport type PlatformSessionRevokeAllResult = PlatformSessionRevokeAllResponse\nexport type PlatformSessionRenameInput = PlatformSessionRenameRequest\nexport type PlatformSessionRenameResult = PlatformSessionRenameResponse\n\nexport const sessions = {\n\tasync list(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionsListResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.list')\n\t\treturn (await res.json()) as PlatformSessionsListResult\n\t},\n\n\tasync revoke(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({ sessionId: opts.sessionId } satisfies PlatformSessionRevokeInput),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revoke')\n\t\treturn (await res.json()) as PlatformSessionRevokeResult\n\t},\n\n\tasync revokeOther(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeOtherResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke-other`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revokeOther')\n\t\treturn (await res.json()) as PlatformSessionRevokeOtherResult\n\t},\n\n\tasync revokeAll(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRevokeAllResult> {\n\t\tconst res = await fetch(\n\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/revoke-all`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\t},\n\t\t)\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.revokeAll')\n\t\treturn (await res.json()) as PlatformSessionRevokeAllResult\n\t},\n\n\tasync rename(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly sessionId: string\n\t\treadonly name: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformSessionRenameResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-sessions/rename`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: buildPlatformSessionsHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\tsessionId: opts.sessionId,\n\t\t\t\tname: opts.name,\n\t\t\t} satisfies PlatformSessionRenameInput),\n\t\t})\n\t\tif (!res.ok) throw await platformSessionError(res, 'sessions.rename')\n\t\treturn (await res.json()) as PlatformSessionRenameResult\n\t},\n} as const\n\nfunction buildPlatformSessionsHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformSessionError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_sessions_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Platform user GDPR export and erasure SDK namespace.\n *\n * These helpers are backed by `/auth/platform-user/*` on the BaaS runtime\n * and keep account data operations separate from generic auth/session helpers.\n */\n\nimport type {\n\tAuthUserDeleteRequest,\n\tAuthUserDeleteResponse,\n\tAuthUserExportResponse,\n} from '@sylphx/contract'\nimport { SylphxError } from './errors'\n\nexport type PlatformUserExportResult = AuthUserExportResponse\nexport type PlatformUserDeleteInput = AuthUserDeleteRequest\nexport type PlatformUserDeleteResult = AuthUserDeleteResponse\n\n/**\n * `user` namespace — Platform-plane (Console / CLI) GDPR operations.\n * Backed by `/auth/platform-user/*` on the BaaS runtime (ADR-089 Phase\n * 2d). See module header for the full rationale.\n */\nexport const user = {\n\t/**\n\t * Export every piece of personal data the platform holds about the\n\t * authenticated user (GDPR Article 20 — right to data portability).\n\t *\n\t * The returned record is deliberately loose — it contains the user\n\t * row, sessions, OAuth accounts, login history, security alerts,\n\t * organization memberships, subscriptions, per-project memberships,\n\t * and storage file metadata. Shape varies with customer provisioning.\n\t */\n\tasync exportData(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserExportResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await platformUserError(res, 'user.exportData')\n\t\treturn (await res.json()) as PlatformUserExportResult\n\t},\n\n\t/**\n\t * Permanently delete the authenticated user's account (GDPR Article\n\t * 17 — right to erasure). Cascades through every provisioned project\n\t * DB, cancels Stripe subscriptions, deletes S3 blobs, and anonymises\n\t * billing transactions.\n\t */\n\tasync deleteAccount(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly reason?: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformUserDeleteResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/account`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\tbody: JSON.stringify({\n\t\t\t\t...(opts.reason !== undefined && { reason: opts.reason }),\n\t\t\t} satisfies PlatformUserDeleteInput),\n\t\t})\n\t\tif (!res.ok) throw await platformUserError(res, 'user.deleteAccount')\n\t\treturn (await res.json()) as PlatformUserDeleteResult\n\t},\n\n\t/**\n\t * Async GDPR Article 20 export job API (ADR-089 Phase 5.5).\n\t *\n\t * `user.exportData` is the Phase 2d synchronous shortcut; production\n\t * callers should prefer the async flow for large accounts.\n\t */\n\texports: {\n\t\t/**\n\t\t * Kick off an export job. Poll `status({ id })` until\n\t\t * `status === 'complete'`.\n\t\t */\n\t\tasync initiate(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly format?: 'json' | 'json-ld'\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<DataExportJob> {\n\t\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify(opts.format !== undefined ? { format: opts.format } : {}),\n\t\t\t})\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.initiate')\n\t\t\treturn (await res.json()) as DataExportJob\n\t\t},\n\n\t\t/**\n\t\t * Read the current state of an in-flight or completed export job.\n\t\t */\n\t\tasync status(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly id: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<DataExportJob> {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export/${encodeURIComponent(\n\t\t\t\t\topts.id,\n\t\t\t\t)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t},\n\t\t\t)\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.status')\n\t\t\treturn (await res.json()) as DataExportJob\n\t\t},\n\n\t\t/**\n\t\t * Download the completed export payload. The BaaS route returns a\n\t\t * 302 to a freshly-signed object-storage URL; `fetch` follows it\n\t\t * and resolves to the raw `Blob`.\n\t\t */\n\t\tasync download(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly id: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<{ blob: Blob; sha256: string | null; sizeBytes: number | null }> {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/auth/platform-user/export/${encodeURIComponent(\n\t\t\t\t\topts.id,\n\t\t\t\t)}/download`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildPlatformUserHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t},\n\t\t\t)\n\t\t\tif (!res.ok) throw await platformUserError(res, 'user.exports.download')\n\t\t\tconst sha256 = res.headers.get('X-Sylphx-Export-Sha256')\n\t\t\tconst sizeHeader = res.headers.get('X-Sylphx-Export-Size')\n\t\t\tconst sizeBytes = sizeHeader ? Number.parseInt(sizeHeader, 10) : null\n\t\t\tconst blob = await res.blob()\n\t\t\treturn { blob, sha256, sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : null }\n\t\t},\n\t},\n} as const\n\n/**\n * Wire shape of a data-export job. `status` progresses through pending,\n * running, complete, or failed.\n */\nexport interface DataExportJob {\n\treadonly id: string\n\treadonly status: 'pending' | 'running' | 'complete' | 'failed'\n\treadonly format: 'json' | 'json-ld'\n\treadonly requestedAt: string\n\treadonly completedAt: string | null\n\treadonly downloadUrl: string | null\n\treadonly sizeBytes: number | null\n\treadonly sha256: string | null\n\treadonly errorMessage: string | null\n}\n\nfunction buildPlatformUserHeaders(\n\taccessToken: string,\n\tuserAgent: string | undefined,\n): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function platformUserError(res: Response, operation: string): Promise<Error> {\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'platform_user_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n","/**\n * Audit namespace — BaaS audit-log reader (ADR-089 Phase 5.3b,\n * Σ1 SoC rename).\n *\n * Scope-filtered reader for the tamper-evident audit-log chain. The\n * runtime enforces role-scoped visibility server-side so the caller\n * only needs to present their platform JWT. All filter fields are\n * optional; `limit` caps at 500 (default 100).\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `audit` and spoke to `/auth/platform-audit/*`. The server-side\n * surface moved to `/v1/audit/*` (audit is a cross-cutting BaaS\n * primitive — compliance / observability — not an auth verb); this\n * SDK module mirrors the move.\n *\n * @example\n * ```typescript\n * import { audit } from '@sylphx/sdk'\n * const { events, nextCursor } = await audit.query({\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * accessToken: platformJwt,\n * filter: { scope: 'platform-ops', limit: 200 },\n * })\n * ```\n */\n\nimport type {\n\tPlatformAuditQueryRequest as ContractPlatformAuditQueryRequest,\n\tPlatformAuditQueryResponse as ContractPlatformAuditQueryResponse,\n} from '@sylphx/contract'\n\nexport type AuditQueryFilter = ContractPlatformAuditQueryRequest\nexport type AuditQueryResult = ContractPlatformAuditQueryResponse\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\tAccept: 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function auditError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'audit_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'FORBIDDEN'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'FORBIDDEN'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\nexport const audit = {\n\tasync query(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: AuditQueryFilter\n\t\treadonly userAgent?: string\n\t}): Promise<AuditQueryResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/audit/query`)\n\t\tconst f = opts.filter ?? {}\n\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\tif (f.actor) url.searchParams.set('actor', f.actor)\n\t\tif (f.resourceType) url.searchParams.set('resource_type', f.resourceType)\n\t\tif (f.resourceId) url.searchParams.set('resource_id', f.resourceId)\n\t\tif (f.action) url.searchParams.set('action', f.action)\n\t\tif (f.from) url.searchParams.set('from', f.from)\n\t\tif (f.to) url.searchParams.set('to', f.to)\n\t\tif (f.cursor) url.searchParams.set('cursor', f.cursor)\n\t\tif (f.limit !== undefined) url.searchParams.set('limit', String(f.limit))\n\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await auditError(res, 'audit.query')\n\t\treturn (await res.json()) as AuditQueryResult\n\t},\n} as const\n","/**\n * Rate-Limits namespace — BaaS operator surface (ADR-089 Phase 5.2,\n * Σ1 SoC rename).\n *\n * Platform operators inspect + tune rate-limit enforcement without a\n * code deploy. All write paths are role-gated server-side\n * (`rate-limits.ts` on the runtime): super_admin/admin touch any\n * scope, project admins are narrowed to their project, regular users\n * to their own user row. Scope escalation returns 403.\n *\n * Phase Σ1 SoC rename: this was previously exported out of\n * `./auth` as `rateLimits` and spoke to `/auth/platform-rate-limits/*`.\n * The server-side surface moved to `/v1/rate-limits/*` (rate-limiting\n * is a cross-cutting BaaS primitive, not an auth verb); this SDK\n * module mirrors the move.\n *\n * @example\n * ```typescript\n * import { rateLimits } from '@sylphx/sdk'\n * await rateLimits.strategies.set({\n * baseUrl: 'https://your-app.api.sylphx.com/v1',\n * accessToken: platformJwt,\n * namespace: 'login',\n * body: {\n * scope: 'project',\n * scope_id: 'proj_abc',\n * strategy: 'fixed-window',\n * limit: 50,\n * windowSeconds: 300,\n * },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tPlatformRateLimitStatusRequest as ContractPlatformRateLimitStatusRequest,\n\tPlatformRateLimitStatusResponse as ContractPlatformRateLimitStatusResponse,\n\tPlatformRateLimitStrategiesListRequest as ContractPlatformRateLimitStrategiesListRequest,\n\tPlatformRateLimitStrategiesListResponse as ContractPlatformRateLimitStrategiesListResponse,\n\tPlatformRateLimitStrategyDeleteRequest as ContractPlatformRateLimitStrategyDeleteRequest,\n\tPlatformRateLimitStrategyDeleteResponse as ContractPlatformRateLimitStrategyDeleteResponse,\n\tPlatformRateLimitStrategyUpsertRequest as ContractPlatformRateLimitStrategyUpsertRequest,\n\tPlatformRateLimitStrategyUpsertResponse as ContractPlatformRateLimitStrategyUpsertResponse,\n} from '@sylphx/contract'\n\nexport type RateLimitStatusFilter = ContractPlatformRateLimitStatusRequest\nexport type RateLimitStatusResult = ContractPlatformRateLimitStatusResponse\nexport type RateLimitStrategiesFilter = ContractPlatformRateLimitStrategiesListRequest\nexport type RateLimitStrategiesResult = ContractPlatformRateLimitStrategiesListResponse\nexport type RateLimitStrategyUpsertInput = ContractPlatformRateLimitStrategyUpsertRequest\nexport type RateLimitStrategyUpsertResult = ContractPlatformRateLimitStrategyUpsertResponse\nexport type RateLimitStrategyDeleteInput = ContractPlatformRateLimitStrategyDeleteRequest\nexport type RateLimitStrategyDeleteResult = ContractPlatformRateLimitStrategyDeleteResponse\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\tAccept: 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function rateLimitError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'rate_limit_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'FORBIDDEN'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'FORBIDDEN'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\nexport const rateLimits = {\n\tasync status(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly accessToken: string\n\t\treadonly filter?: RateLimitStatusFilter\n\t\treadonly userAgent?: string\n\t}): Promise<RateLimitStatusResult> {\n\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/status`)\n\t\tconst f = opts.filter ?? {}\n\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\tif (f.scope_id) url.searchParams.set('scope_id', f.scope_id)\n\t\tif (f.namespace) url.searchParams.set('namespace', f.namespace)\n\t\tconst res = await fetch(url.toString(), {\n\t\t\tmethod: 'GET',\n\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t})\n\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.status')\n\t\treturn (await res.json()) as RateLimitStatusResult\n\t},\n\n\tstrategies: {\n\t\tasync list(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly filter?: RateLimitStrategiesFilter\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategiesResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies`)\n\t\t\tconst f = opts.filter ?? {}\n\t\t\tif (f.scope) url.searchParams.set('scope', f.scope)\n\t\t\tif (f.scope_id) url.searchParams.set('scope_id', f.scope_id)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.list')\n\t\t\treturn (await res.json()) as RateLimitStrategiesResult\n\t\t},\n\n\t\tasync set(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly namespace: string\n\t\t\treadonly body: RateLimitStrategyUpsertInput\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategyUpsertResult> {\n\t\t\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies/${encodeURIComponent(opts.namespace)}`\n\t\t\tconst res = await fetch(url, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t\t'content-type': 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(opts.body),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.set')\n\t\t\treturn (await res.json()) as RateLimitStrategyUpsertResult\n\t\t},\n\n\t\tasync delete(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly namespace: string\n\t\t\treadonly body: RateLimitStrategyDeleteInput\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<RateLimitStrategyDeleteResult> {\n\t\t\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/rate-limits/strategies/${encodeURIComponent(opts.namespace)}`\n\t\t\tconst res = await fetch(url, {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\t\t'content-type': 'application/json',\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(opts.body),\n\t\t\t})\n\t\t\tif (!res.ok) throw await rateLimitError(res, 'rateLimits.strategies.delete')\n\t\t\treturn (await res.json()) as RateLimitStrategyDeleteResult\n\t\t},\n\t},\n} as const\n","/**\n * Functions Admin namespace — Platform-plane function-bundle admin\n * (ADR-089 Phase 3a, Σ1 SoC rename).\n *\n * Function-bundle download for the Sylphx-internal edge-runtime\n * orchestrator. Unlike the sibling Platform namespaces this one\n * authenticates with a shared `internalToken` (service-internal secret)\n * rather than a platform-audience JWT, because the caller is another\n * Sylphx service (the edge-runtime that spawns V8 isolates to invoke\n * user functions) not an end user. The BaaS runtime\n * (`apps/runtime/src/server/runtime/routes/functions/admin.ts`)\n * owns the object-storage implementation — Platform callers dogfood through\n * this SDK surface and never touch backend storage credentials.\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `functions` (re-exported at the package root as `functionsInternal`)\n * and spoke to `/auth/platform-functions/*`. The server-side surface\n * moved to `/v1/functions/admin/*` (function bundle admin is a\n * cross-cutting BaaS primitive — function bundle storage — not an auth\n * verb); this SDK module nests the admin verbs under\n * `functions.admin.*` at the package root.\n */\n\nexport interface PlatformFunctionsDownloadBundleResult {\n\treadonly code: string\n}\n\nasync function functionsAdminError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'functions_admin_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode: 'UNAUTHORIZED' | 'NOT_FOUND' | 'BAD_REQUEST' | 'INTERNAL_SERVER_ERROR'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\n/**\n * `functionsAdmin` namespace — Platform-plane (edge-runtime\n * orchestrator) function-bundle fetch. Re-exported at the package\n * root as part of `functions.admin.*`.\n */\nexport const functionsAdmin = {\n\t/**\n\t * Download a function bundle (UTF-8 source) by its storage key.\n\t *\n\t * The storage key is an opaque string chosen by `sylphx deploy`\n\t * when the bundle was uploaded — callers read it from the\n\t * `functions.storagePath` column after resolving a function row\n\t * by `(projectId, name)`.\n\t *\n\t * @example\n\t * ```typescript\n\t * import { functions } from '@sylphx/sdk'\n\t * const { code } = await functions.admin.downloadBundle({\n\t * baseUrl: 'https://your-app.api.sylphx.com/v1',\n\t * internalToken: process.env.SYLPHX_INTERNAL_TOKEN!,\n\t * storagePath: 'bundles/proj_abc/my-fn/v3.js',\n\t * })\n\t * ```\n\t */\n\tasync downloadBundle(opts: {\n\t\treadonly baseUrl: string\n\t\treadonly internalToken: string\n\t\treadonly storagePath: string\n\t\treadonly userAgent?: string\n\t}): Promise<PlatformFunctionsDownloadBundleResult> {\n\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/functions/admin/download-bundle`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${opts.internalToken}`,\n\t\t\t\t...(opts.userAgent ? { 'User-Agent': opts.userAgent } : {}),\n\t\t\t},\n\t\t\tbody: JSON.stringify({ storagePath: opts.storagePath }),\n\t\t})\n\t\tif (!res.ok) throw await functionsAdminError(res, 'functions.admin.downloadBundle')\n\t\treturn (await res.json()) as PlatformFunctionsDownloadBundleResult\n\t},\n} as const\n","/**\n * Realtime Functions\n *\n * Pure functions for real-time messaging via managed durable streams.\n * Supports channel-based pub/sub with SSE delivery to browsers.\n *\n * @example\n * ```ts\n * import { createConfig, realtimeEmit } from '@sylphx/sdk'\n *\n * // Server: emit events to connected clients\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n * await realtimeEmit(config, {\n * channel: 'orders',\n * event: 'order.created',\n * data: { orderId: '123', amount: 99 },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tRealtimeEmitInput as ContractRealtimeEmitInput,\n\tRealtimeEmitResult as ContractRealtimeEmitResult,\n} from '@sylphx/contract'\nimport { realtimeEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport type { StreamMessage } from './realtime-types'\n\n// Re-export shared types\nexport type { StreamMessage } from './realtime-types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RealtimeEmitRequest {\n\t/** Channel to emit the event to */\n\tchannel: string\n\t/** Event type/name */\n\tevent: string\n\t/** Event data (any JSON-serializable value) */\n\tdata: unknown\n}\n\nexport interface RealtimeEmitResponse {\n\t/** Stream entry ID */\n\tid: string\n\t/** Channel the event was emitted to */\n\tchannel: string\n}\n\nexport interface RealtimeHistoryRequest {\n\t/** Channel to get history for */\n\tchannel: string\n\t/** Maximum number of messages to return (default: 50) */\n\tlimit?: number\n\t/** Return messages after this stream entry ID */\n\tafter?: string\n}\n\nexport interface RealtimeHistoryResponse {\n\t/** List of historical messages */\n\tmessages: StreamMessage[]\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Emit an event to a realtime channel.\n *\n * All clients subscribed to the channel (via `useRealtime` hook or SSE)\n * will receive the event instantly.\n *\n * @example\n * ```ts\n * // Notify all clients watching a document\n * await realtimeEmit(config, {\n * channel: `doc:${documentId}`,\n * event: 'doc.updated',\n * data: { updatedBy: userId, timestamp: Date.now() },\n * })\n * ```\n */\nexport async function realtimeEmit(\n\tconfig: SylphxConfig,\n\trequest: RealtimeEmitRequest,\n): Promise<RealtimeEmitResponse> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractRealtimeEmitInput\n\tconst endpoint = realtimeEndpoints.emit\n\treturn callApi<ContractRealtimeEmitResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Get historical messages from a channel.\n *\n * Useful for initializing state when a client first connects,\n * or for resuming from a known stream position.\n *\n * @example\n * ```ts\n * // Get last 20 messages when a user joins a chat\n * const { messages } = await getRealtimeHistory(config, {\n * channel: 'chat:general',\n * limit: 20,\n * })\n * ```\n */\nexport async function getRealtimeHistory(\n\tconfig: SylphxConfig,\n\trequest: RealtimeHistoryRequest,\n): Promise<RealtimeHistoryResponse> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// Public SDK compatibility keeps `messages` mutable; runtime payload is\n\t// the same contract-owned array shape.\n\tconst endpoint = realtimeEndpoints.history\n\treturn callApi<RealtimeHistoryResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: {\n\t\t\tchannel: request.channel,\n\t\t\t...(request.limit !== undefined && { limit: String(request.limit) }),\n\t\t\t...(request.after !== undefined && { after: request.after }),\n\t\t},\n\t})\n}\n","/**\n * Realtime Admin namespace — Platform-plane channel-registration\n * admin (ADR-089 Phase 3a, Σ1 SoC rename).\n *\n * Channel-registration admin for the Platform plane (Console / CLI\n * operators). Like the sibling device / platform-sessions / platform-\n * password / platform-user namespaces, these endpoints accept a\n * `baseUrl + accessToken` rather than a `SylphxConfig` — the caller\n * authenticates with a platform-audience JWT, not a customer-app\n * `pk_`/`sk_` pair. The BaaS runtime\n * (`apps/runtime/src/server/runtime/routes/realtime/admin.ts`)\n * verifies the token against audience `'platform'`, confirms\n * `verifyProjectAccess(userId, projectId)`, and owns the channel\n * registration operations.\n *\n * Phase Σ1 SoC rename: this was previously exported out of `./auth`\n * as `realtime` (re-exported at the package root as `realtimeAdmin`)\n * and spoke to `/auth/platform-realtime/*`. The server-side surface\n * moved to `/v1/realtime/admin/*` (realtime admin is a cross-cutting\n * BaaS primitive, not an auth verb); this SDK module nests the admin\n * verbs under `realtime.admin.channels.*` at the package root so it\n * no longer collides with the customer-app `realtimeEmit` /\n * `getRealtimeHistory` data-plane surface.\n *\n * For the Sylphx platform itself, `baseUrl` is\n * `https://your-app.api.sylphx.com/v1` (configurable via SYLPHX_BAAS_URL\n * for dev/staging).\n */\n\nexport interface PlatformRealtimeChannel {\n\treadonly name: string\n\treadonly activeConnections: number\n\treadonly messagesPerHour: number\n\treadonly status: 'active' | 'empty'\n}\n\nexport interface PlatformRealtimeStatusResult {\n\treadonly available: boolean\n}\n\nexport interface PlatformRealtimeListChannelsResult {\n\treadonly channels: readonly PlatformRealtimeChannel[]\n\treadonly count: number\n}\n\nexport interface PlatformRealtimeCreateChannelResult {\n\treadonly name: string\n}\n\nexport interface PlatformRealtimeDeleteChannelResult {\n\treadonly success: boolean\n}\n\nfunction buildHeaders(accessToken: string, userAgent: string | undefined): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t}\n\tif (userAgent) headers['User-Agent'] = userAgent\n\treturn headers\n}\n\nasync function realtimeAdminError(res: Response, operation: string): Promise<Error> {\n\tconst { SylphxError } = await import('./errors')\n\tconst body = await res.text().catch(() => '')\n\tlet code = 'realtime_admin_error'\n\tlet message = `${operation} failed: HTTP ${res.status}`\n\ttry {\n\t\tconst parsed = JSON.parse(body) as {\n\t\t\terror?: string\n\t\t\tmessage?: string\n\t\t\terror_description?: string\n\t\t}\n\t\tif (parsed.error) code = parsed.error\n\t\tif (parsed.error_description) message = parsed.error_description\n\t\telse if (parsed.message) message = parsed.message\n\t} catch {\n\t\t// Non-JSON body — keep default message.\n\t}\n\tlet errorCode:\n\t\t| 'UNAUTHORIZED'\n\t\t| 'NOT_FOUND'\n\t\t| 'TOO_MANY_REQUESTS'\n\t\t| 'BAD_REQUEST'\n\t\t| 'INTERNAL_SERVER_ERROR'\n\t\t| 'CONFLICT'\n\tif (res.status === 401) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 403) errorCode = 'UNAUTHORIZED'\n\telse if (res.status === 404) errorCode = 'NOT_FOUND'\n\telse if (res.status === 409) errorCode = 'CONFLICT'\n\telse if (res.status === 429) errorCode = 'TOO_MANY_REQUESTS'\n\telse if (res.status >= 500) errorCode = 'INTERNAL_SERVER_ERROR'\n\telse errorCode = 'BAD_REQUEST'\n\treturn new SylphxError(message, {\n\t\tcode: errorCode,\n\t\tstatus: res.status,\n\t\tdata: { operation, code },\n\t})\n}\n\n/**\n * `realtimeAdmin` namespace — Platform-plane realtime channel-\n * registration admin. Re-exported at the package root as part of\n * `realtime.admin.*` (nested under the existing `realtime` surface in\n * `./index.ts`).\n */\nexport const realtimeAdmin = {\n\tchannels: {\n\t\t/**\n\t\t * Get realtime service status for a project.\n\t\t */\n\t\tasync status(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeStatusResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/status`)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.status')\n\t\t\treturn (await res.json()) as PlatformRealtimeStatusResult\n\t\t},\n\n\t\t/**\n\t\t * List registered channels for a project.\n\t\t *\n\t\t * Returns an empty list when the project's hidden realtime store has\n\t\t * not been provisioned yet — BaaS side soft-fails because absent\n\t\t * registrations are semantically equivalent to \"none registered\".\n\t\t */\n\t\tasync list(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeListChannelsResult> {\n\t\t\tconst url = new URL(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels`)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.list')\n\t\t\treturn (await res.json()) as PlatformRealtimeListChannelsResult\n\t\t},\n\n\t\t/**\n\t\t * Register a named realtime channel for a project.\n\t\t *\n\t\t * Returns 409 if already registered — registration is\n\t\t * idempotent per name.\n\t\t */\n\t\tasync create(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly name: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeCreateChannelResult> {\n\t\t\tconst res = await fetch(`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t\tbody: JSON.stringify({ projectId: opts.projectId, name: opts.name }),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.create')\n\t\t\treturn (await res.json()) as PlatformRealtimeCreateChannelResult\n\t\t},\n\n\t\t/**\n\t\t * Unregister a named realtime channel for a project. Idempotent.\n\t\t */\n\t\tasync delete(opts: {\n\t\t\treadonly baseUrl: string\n\t\t\treadonly accessToken: string\n\t\t\treadonly projectId: string\n\t\t\treadonly channelName: string\n\t\t\treadonly userAgent?: string\n\t\t}): Promise<PlatformRealtimeDeleteChannelResult> {\n\t\t\tconst url = new URL(\n\t\t\t\t`${opts.baseUrl.replace(/\\/$/, '')}/realtime/admin/channels/${encodeURIComponent(\n\t\t\t\t\topts.channelName,\n\t\t\t\t)}`,\n\t\t\t)\n\t\t\turl.searchParams.set('projectId', opts.projectId)\n\t\t\tconst res = await fetch(url.toString(), {\n\t\t\t\tmethod: 'DELETE',\n\t\t\t\theaders: buildHeaders(opts.accessToken, opts.userAgent),\n\t\t\t})\n\t\t\tif (!res.ok) throw await realtimeAdminError(res, 'realtime.admin.channels.delete')\n\t\t\treturn (await res.json()) as PlatformRealtimeDeleteChannelResult\n\t\t},\n\t},\n} as const\n","/**\n * Admin Functions — Server-side user management\n * Requires secretKey (PLATFORM_TOKEN). Never use on client-side.\n */\nimport { callApi, type SylphxConfig } from './config'\n\nexport interface AdminUser {\n\tid: string\n\temail: string\n\tname: string | null\n\timage: string | null\n\temailVerified: boolean\n\trole: string\n\tstatus: 'active' | 'suspended' | 'deleted'\n\tmetadata: Record<string, unknown> | null\n\tfirstSeenAt: string\n\tlastActiveAt: string\n\tcreatedAt: string\n}\n\nexport interface ListUsersOptions {\n\temail?: string\n\tstatus?: 'active' | 'suspended'\n\tlimit?: number\n\toffset?: number\n}\n\nexport interface ListUsersResult {\n\tusers: AdminUser[]\n\ttotal: number\n\tlimit: number\n\toffset: number\n}\n\n/**\n * List users in this project (paginated).\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const { users, total } = await listUsers(config, { status: 'active', limit: 20 })\n * ```\n */\nexport async function listUsers(\n\tconfig: SylphxConfig,\n\topts?: ListUsersOptions,\n): Promise<ListUsersResult> {\n\tconst params = new URLSearchParams()\n\tif (opts?.email) params.set('email', opts.email)\n\tif (opts?.status) params.set('status', opts.status)\n\tif (opts?.limit) params.set('limit', String(opts.limit))\n\tif (opts?.offset) params.set('offset', String(opts.offset))\n\tconst qs = params.toString()\n\treturn callApi<ListUsersResult>(config, `/admin/users${qs ? `?${qs}` : ''}`)\n}\n\n/**\n * Get a single user by ID.\n * Server-side only (requires secretKey).\n */\nexport async function getUser(config: SylphxConfig, userId: string): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}`)\n}\n\n/**\n * Look up a user by email address. Returns null if not found.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const user = await getUserByEmail(config, 'user@example.com')\n * if (!user) console.log('User not found')\n * ```\n */\nexport async function getUserByEmail(\n\tconfig: SylphxConfig,\n\temail: string,\n): Promise<AdminUser | null> {\n\tconst result = await listUsers(config, { email, limit: 1 })\n\treturn result.users[0] ?? null\n}\n\n/**\n * Update a user's profile fields.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * const updated = await updateUser(config, userId, { role: 'admin', name: 'Jane' })\n * ```\n */\nexport async function updateUser(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tinput: { name?: string; metadata?: Record<string, unknown>; role?: string },\n): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}`, {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Update only the metadata for a user (merge-style update).\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await updateUserMetadata(config, userId, { employeeId: 'EMP-001', department: 'Engineering' })\n * ```\n */\nexport async function updateUserMetadata(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tmetadata: Record<string, unknown>,\n): Promise<AdminUser> {\n\treturn callApi<AdminUser>(config, `/admin/users/${userId}/metadata`, {\n\t\tmethod: 'PUT',\n\t\tbody: metadata,\n\t})\n}\n\n/**\n * Suspend a user account.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await suspendUser(config, userId, 'Violation of terms of service')\n * ```\n */\nexport async function suspendUser(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\treason?: string,\n): Promise<void> {\n\tawait callApi<void>(config, `/admin/users/${userId}/suspend`, {\n\t\tmethod: 'POST',\n\t\tbody: { reason },\n\t})\n}\n\n/**\n * Delete a user account.\n * Server-side only (requires secretKey).\n *\n * @example\n * ```typescript\n * await deleteUser(config, userId)\n * ```\n */\nexport async function deleteUser(config: SylphxConfig, userId: string): Promise<void> {\n\tawait callApi<void>(config, `/admin/users/${userId}/delete`, {\n\t\tmethod: 'POST',\n\t})\n}\n","/**\n * Analytics Functions\n *\n * Pure functions for event tracking - no hidden state.\n * Events are sent directly to the platform.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/analytics/track`,\n * `/analytics/identify`, `/analytics/page`, and `/analytics/batch`. SDK-\n * specific convenience shapes (`BatchEvent`) stay local since they model\n * the multi-event tracker ergonomics, not the platform wire.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tBatchTrackInput as ContractBatchTrackInput,\n\tBatchTrackResult as ContractBatchTrackResult,\n\tConversionData as ContractConversionData,\n\tIdentifyInput as ContractIdentifyInput,\n\tPageInput as ContractPageInput,\n\tTrackEvent as ContractTrackEvent,\n\tTrackInput as ContractTrackInput,\n} from '@sylphx/contract'\nimport { analyticsEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type TrackEventItem = ContractTrackEvent\nexport type BatchTrackRequest = ContractBatchTrackInput\nexport type BatchTrackResponse = ContractBatchTrackResult\nexport type ConversionData = ContractConversionData\n\n// SDK-specific types for convenience\nexport interface TrackInput {\n\t/** Event name */\n\tevent: string\n\t/** Event properties */\n\tproperties?: Record<string, unknown>\n\t/** User ID (optional, for server-side tracking) */\n\tuserId?: string\n\t/** Anonymous ID (for tracking before user signs in) */\n\tanonymousId?: string\n\t/** Timestamp (defaults to now) */\n\ttimestamp?: string\n}\n\nexport interface PageInput {\n\t/** Page name or title */\n\tname: string\n\t/** Page properties */\n\tproperties?: Record<string, unknown>\n\t/** User ID (optional) */\n\tuserId?: string\n\t/** Anonymous ID */\n\tanonymousId?: string\n}\n\nexport interface IdentifyInput {\n\t/** User ID */\n\tuserId: string\n\t/** User traits */\n\ttraits?: Record<string, unknown>\n\t/** Anonymous ID to link */\n\tanonymousId?: string\n}\n\nexport interface BatchEvent {\n\ttype: 'track' | 'page' | 'identify'\n\tevent?: string\n\tname?: string\n\tuserId?: string\n\tanonymousId?: string\n\tproperties?: Record<string, unknown>\n\ttraits?: Record<string, unknown>\n\ttimestamp?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Track a custom event\n *\n * @example\n * ```typescript\n * await track(config, {\n * event: 'purchase_completed',\n * properties: { amount: 99.99, currency: 'USD' },\n * userId: 'user-123',\n * })\n * ```\n */\nexport async function track(config: SylphxConfig, input: TrackInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.track\n\tconst body = {\n\t\tevent: input.event,\n\t\tproperties: input.properties ?? {},\n\t\tuserId: input.userId,\n\t\tanonymousId: input.anonymousId,\n\t\ttimestamp: input.timestamp ?? new Date().toISOString(),\n\t} satisfies ContractTrackInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Track a page view\n *\n * @example\n * ```typescript\n * await page(config, {\n * name: 'Home',\n * properties: { path: '/', referrer: document.referrer },\n * })\n * ```\n */\nexport async function page(config: SylphxConfig, input: PageInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084). Contract `PageInput`\n\t// now carries the optional `timestamp` field (ADR-084 Wave 2d), so the\n\t// page + track envelopes are symmetric.\n\tconst endpoint = analyticsEndpoints.page\n\tconst body = {\n\t\tname: input.name,\n\t\tproperties: input.properties ?? {},\n\t\tuserId: input.userId,\n\t\tanonymousId: input.anonymousId,\n\t} satisfies ContractPageInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: { ...body, timestamp: new Date().toISOString() },\n\t})\n}\n\n/**\n * Identify a user with traits\n *\n * @example\n * ```typescript\n * await identify(config, {\n * userId: 'user-123',\n * traits: { email: 'user@example.com', plan: 'pro' },\n * anonymousId: 'anon-456', // Links anonymous activity to user\n * })\n * ```\n */\nexport async function identify(config: SylphxConfig, input: IdentifyInput): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.identify\n\tconst body = {\n\t\tuserId: input.userId,\n\t\ttraits: input.traits ?? {},\n\t\tanonymousId: input.anonymousId,\n\t} satisfies ContractIdentifyInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n/**\n * Send multiple events in a single request (batch)\n *\n * @example\n * ```typescript\n * await trackBatch(config, [\n * { type: 'track', event: 'item_viewed', properties: { id: '1' } },\n * { type: 'track', event: 'item_added', properties: { id: '1' } },\n * { type: 'track', event: 'checkout_started' },\n * ])\n * ```\n */\nexport async function trackBatch(config: SylphxConfig, events: BatchEvent[]): Promise<void> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = analyticsEndpoints.batch\n\tconst body = {\n\t\tevents: events.map((e) => ({\n\t\t\tevent: e.type === 'track' ? (e.event ?? '') : e.type === 'page' ? `$pageview` : '$identify',\n\t\t\tproperties: {\n\t\t\t\t...e.properties,\n\t\t\t\t...(e.type === 'page' && e.name ? { name: e.name } : {}),\n\t\t\t\t...(e.type === 'identify' && e.traits ? { traits: e.traits } : {}),\n\t\t\t},\n\t\t\tuserId: e.userId,\n\t\t\tanonymousId: e.anonymousId,\n\t\t\ttimestamp: e.timestamp ?? new Date().toISOString(),\n\t\t})),\n\t} satisfies ContractBatchTrackInput\n\tawait callApi(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Generate a random anonymous ID (Segment pattern: pure UUID)\n *\n * Uses UUID v4 format without timestamp component to prevent collision risk\n * in high-traffic applications where multiple users might generate IDs at\n * the same millisecond.\n *\n * @example\n * ```typescript\n * const anonId = generateAnonymousId()\n * await track(config, { event: 'page_view', anonymousId: anonId })\n * ```\n */\nexport function generateAnonymousId(): string {\n\t// Use crypto.randomUUID if available (standard UUID v4)\n\tif (typeof crypto !== 'undefined' && crypto.randomUUID) {\n\t\treturn crypto.randomUUID()\n\t}\n\t// Fallback for older browsers: generate UUID v4 manually\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n\t\tconst r = (Math.random() * 16) | 0\n\t\tconst v = c === 'x' ? r : (r & 0x3) | 0x8\n\t\treturn v.toString(16)\n\t})\n}\n\n/**\n * Create a tracker bound to a specific config\n *\n * For convenience when making many calls with the same config.\n * This is optional - you can always use the individual functions.\n *\n * @example\n * ```typescript\n * const analytics = createTracker(config)\n *\n * // No need to pass config each time\n * analytics.track('event', { prop: 'value' })\n * analytics.page('Home')\n * analytics.identify('user-123', { email: 'user@example.com' })\n * ```\n */\nexport function createTracker(config: SylphxConfig, defaultAnonymousId?: string) {\n\tconst anonymousId = defaultAnonymousId ?? generateAnonymousId()\n\n\treturn {\n\t\ttrack: (event: string, properties?: Record<string, unknown>, userId?: string) =>\n\t\t\ttrack(config, { event, properties, userId, anonymousId }),\n\n\t\tpage: (name: string, properties?: Record<string, unknown>, userId?: string) =>\n\t\t\tpage(config, { name, properties, userId, anonymousId }),\n\n\t\tidentify: (userId: string, traits?: Record<string, unknown>) =>\n\t\t\tidentify(config, { userId, traits, anonymousId }),\n\n\t\tbatch: (events: BatchEvent[]) =>\n\t\t\ttrackBatch(\n\t\t\t\tconfig,\n\t\t\t\tevents.map((e) => ({\n\t\t\t\t\t...e,\n\t\t\t\t\tanonymousId: e.anonymousId ?? anonymousId,\n\t\t\t\t})),\n\t\t\t),\n\n\t\t/** Get the anonymous ID for this tracker */\n\t\tgetAnonymousId: () => anonymousId,\n\t}\n}\n","/**\n * AI Functions\n *\n * Pure functions for AI completions - Vercel AI SDK style.\n * Direct API calls with natural tree-shaking.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /ai/models`,\n * `/ai/usage`, `/ai/rate-limit`. Chat-completion / embedding envelopes\n * remain local to this module — they pass through the Vercel AI SDK\n * bridge and evolve independently of the platform contract.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tAIModel as ContractAIModel,\n\tGetModelsResponse,\n\tGetRateLimitResponse,\n\tGetUsageResponse,\n} from '@sylphx/contract'\nimport { buildApiUrl, buildHeaders, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type AIUsageResponse = GetUsageResponse\nexport type AIRateLimitResponse = GetRateLimitResponse\nexport type AIModelsResponse = GetModelsResponse\nexport type AIModel = ContractAIModel\n\n// ============================================================================\n// SDK-specific Types (OpenAI-compatible chat format)\n// ============================================================================\n\nexport interface ChatMessage {\n\trole: 'system' | 'user' | 'assistant' | 'tool'\n\tcontent: string | ContentPart[]\n\tname?: string\n\ttool_call_id?: string\n\ttool_calls?: ToolCall[]\n\t/** Timestamp for UI display */\n\ttimestamp?: Date\n}\n\nexport interface ContentPart {\n\ttype: 'text' | 'image_url'\n\ttext?: string\n\timage_url?: { url: string; detail?: 'auto' | 'low' | 'high' }\n}\n\nexport interface ToolCall {\n\tid: string\n\ttype: 'function'\n\tfunction: { name: string; arguments: string }\n}\n\nexport interface Tool {\n\ttype: 'function'\n\tfunction: {\n\t\tname: string\n\t\tdescription?: string\n\t\tparameters?: Record<string, unknown>\n\t}\n}\n\nexport interface ChatInput {\n\t/** Model ID (e.g., 'gpt-4o', 'claude-sonnet-4-20250514') */\n\tmodel: string\n\t/** Messages */\n\tmessages: ChatMessage[]\n\t/** Temperature (0-2) */\n\ttemperature?: number\n\t/** Max tokens to generate */\n\tmaxTokens?: number\n\t/** Top P sampling */\n\ttopP?: number\n\t/** Frequency penalty */\n\tfrequencyPenalty?: number\n\t/** Presence penalty */\n\tpresencePenalty?: number\n\t/** Stop sequences */\n\tstop?: string[]\n\t/** Tools for function calling */\n\ttools?: Tool[]\n\t/** Tool choice */\n\ttoolChoice?: 'auto' | 'none' | { type: 'function'; function: { name: string } }\n}\n\nexport interface ChatResult {\n\tid: string\n\tmodel: string\n\tchoices: Array<{\n\t\tindex: number\n\t\tmessage: {\n\t\t\trole: 'assistant'\n\t\t\tcontent: string | null\n\t\t\ttool_calls?: ToolCall[]\n\t\t}\n\t\tfinishReason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null\n\t}>\n\tusage: {\n\t\tpromptTokens: number\n\t\tcompletionTokens: number\n\t\ttotalTokens: number\n\t}\n}\n\nexport interface ChatStreamChunk {\n\tid: string\n\tmodel: string\n\tchoices: Array<{\n\t\tindex: number\n\t\tdelta: {\n\t\t\trole?: 'assistant'\n\t\t\tcontent?: string\n\t\t\ttool_calls?: ToolCall[]\n\t\t}\n\t\tfinishReason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null\n\t}>\n}\n\nexport interface EmbedInput {\n\t/** Model ID (e.g., 'text-embedding-3-small') */\n\tmodel: string\n\t/** Text(s) to embed */\n\tinput: string | string[]\n\t/** Dimensions (for models that support it) */\n\tdimensions?: number\n}\n\nexport interface EmbedResult {\n\tmodel: string\n\tdata: Array<{\n\t\tindex: number\n\t\tembedding: number[]\n\t}>\n\tusage: {\n\t\tpromptTokens: number\n\t\ttotalTokens: number\n\t}\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Create a chat completion\n *\n * @example\n * ```typescript\n * const response = await chat(config, {\n * model: 'gpt-4o',\n * messages: [\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Hello!' },\n * ],\n * })\n *\n * console.log(response.choices[0].message.content)\n * ```\n */\nexport async function chat(config: SylphxConfig, input: ChatInput): Promise<ChatResult> {\n\tconst response = await fetch(buildApiUrl(config, '/chat/completions'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t...buildHeaders(config),\n\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tmodel: input.model,\n\t\t\tmessages: input.messages,\n\t\t\ttemperature: input.temperature,\n\t\t\tmax_tokens: input.maxTokens,\n\t\t\ttop_p: input.topP,\n\t\t\tfrequency_penalty: input.frequencyPenalty,\n\t\t\tpresence_penalty: input.presencePenalty,\n\t\t\tstop: input.stop,\n\t\t\ttools: input.tools,\n\t\t\ttool_choice: input.toolChoice,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tconst error = await response.json().catch(() => ({ error: { message: 'Chat request failed' } }))\n\t\tthrow new SylphxError(error?.error?.message ?? 'Chat request failed', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\tconst data = await response.json()\n\n\treturn {\n\t\tid: data.id,\n\t\tmodel: data.model,\n\t\tchoices: data.choices.map((c: Record<string, unknown>) => ({\n\t\t\tindex: c.index as number,\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant' as const,\n\t\t\t\tcontent: (c.message as Record<string, unknown>)?.content as string | null,\n\t\t\t\ttool_calls: (c.message as Record<string, unknown>)?.tool_calls as ToolCall[] | undefined,\n\t\t\t},\n\t\t\tfinishReason: c.finish_reason as ChatResult['choices'][0]['finishReason'],\n\t\t})),\n\t\tusage: {\n\t\t\tpromptTokens: data.usage.prompt_tokens,\n\t\t\tcompletionTokens: data.usage.completion_tokens,\n\t\t\ttotalTokens: data.usage.total_tokens,\n\t\t},\n\t}\n}\n\n/**\n * Create a streaming chat completion\n *\n * @example\n * ```typescript\n * const stream = chatStream(config, {\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Write a poem' }],\n * })\n *\n * for await (const chunk of stream) {\n * process.stdout.write(chunk.choices[0].delta.content ?? '')\n * }\n * ```\n */\nexport function chatStream(config: SylphxConfig, input: ChatInput): AsyncIterable<ChatStreamChunk> {\n\treturn {\n\t\t[Symbol.asyncIterator]: async function* () {\n\t\t\tconst response = await fetch(buildApiUrl(config, '/chat/completions'), {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t...buildHeaders(config),\n\t\t\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: input.model,\n\t\t\t\t\tmessages: input.messages,\n\t\t\t\t\ttemperature: input.temperature,\n\t\t\t\t\tmax_tokens: input.maxTokens,\n\t\t\t\t\ttop_p: input.topP,\n\t\t\t\t\tfrequency_penalty: input.frequencyPenalty,\n\t\t\t\t\tpresence_penalty: input.presencePenalty,\n\t\t\t\t\tstop: input.stop,\n\t\t\t\t\ttools: input.tools,\n\t\t\t\t\ttool_choice: input.toolChoice,\n\t\t\t\t\tstream: true,\n\t\t\t\t}),\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst error = await response\n\t\t\t\t\t.json()\n\t\t\t\t\t.catch(() => ({ error: { message: 'Stream request failed' } }))\n\t\t\t\tthrow new SylphxError(error?.error?.message ?? 'Stream request failed', {\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tthrow new SylphxError('No response body', {\n\t\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = ''\n\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\t\tif (done) break\n\n\t\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\t\tbuffer = lines.pop() ?? ''\n\n\t\t\t\t\tfor (const line of lines) {\n\t\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\t\tconst data = line.slice(6).trim()\n\t\t\t\t\t\t\tif (data === '[DONE]') return\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\t\t\tid: chunk.id ?? '',\n\t\t\t\t\t\t\t\t\tmodel: chunk.model ?? input.model,\n\t\t\t\t\t\t\t\t\tchoices: (chunk.choices ?? []).map((c: Record<string, unknown>) => ({\n\t\t\t\t\t\t\t\t\t\tindex: typeof c.index === 'number' ? c.index : 0,\n\t\t\t\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\t\t\t\trole: (c.delta as Record<string, unknown>)?.role as 'assistant' | undefined,\n\t\t\t\t\t\t\t\t\t\t\tcontent: (c.delta as Record<string, unknown>)?.content as string | undefined,\n\t\t\t\t\t\t\t\t\t\t\ttool_calls: (c.delta as Record<string, unknown>)?.tool_calls as\n\t\t\t\t\t\t\t\t\t\t\t\t| ToolCall[]\n\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfinishReason:\n\t\t\t\t\t\t\t\t\t\t\t(c.finish_reason as ChatStreamChunk['choices'][0]['finishReason']) ?? null,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Skip malformed JSON\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treader.releaseLock()\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Create embeddings\n *\n * @example\n * ```typescript\n * const result = await embed(config, {\n * model: 'text-embedding-3-small',\n * input: ['Hello world', 'Goodbye world'],\n * })\n *\n * console.log(result.data[0].embedding) // [0.123, -0.456, ...]\n * ```\n */\nexport async function embed(config: SylphxConfig, input: EmbedInput): Promise<EmbedResult> {\n\tconst response = await fetch(buildApiUrl(config, '/embeddings'), {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t...buildHeaders(config),\n\t\t\tAuthorization: `Bearer ${config.secretKey}`,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tmodel: input.model,\n\t\t\tinput: input.input,\n\t\t\tdimensions: input.dimensions,\n\t\t}),\n\t})\n\n\tif (!response.ok) {\n\t\tconst error = await response\n\t\t\t.json()\n\t\t\t.catch(() => ({ error: { message: 'Embedding request failed' } }))\n\t\tthrow new SylphxError(error?.error?.message ?? 'Embedding request failed', {\n\t\t\tcode: 'BAD_REQUEST',\n\t\t})\n\t}\n\n\tconst data = await response.json()\n\n\treturn {\n\t\tmodel: data.model,\n\t\tdata: data.data,\n\t\tusage: {\n\t\t\tpromptTokens: data.usage.prompt_tokens,\n\t\t\ttotalTokens: data.usage.total_tokens,\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Simple text completion (convenience wrapper)\n *\n * @example\n * ```typescript\n * const text = await complete(config, 'gpt-4o', 'Explain quantum computing in one sentence.')\n * ```\n */\nexport async function complete(\n\tconfig: SylphxConfig,\n\tmodel: string,\n\tprompt: string,\n\toptions?: Omit<ChatInput, 'model' | 'messages'>,\n): Promise<string> {\n\tconst response = await chat(config, {\n\t\tmodel,\n\t\tmessages: [{ role: 'user', content: prompt }],\n\t\t...options,\n\t})\n\treturn response.choices[0]?.message.content ?? ''\n}\n\n/**\n * Stream text to string (collects all chunks)\n *\n * @example\n * ```typescript\n * const text = await streamToString(config, {\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Write a haiku' }],\n * })\n * ```\n */\nexport async function streamToString(config: SylphxConfig, input: ChatInput): Promise<string> {\n\tlet result = ''\n\tfor await (const chunk of chatStream(config, input)) {\n\t\tresult += chunk.choices[0]?.delta.content ?? ''\n\t}\n\treturn result\n}\n","/**\n * Billing Functions\n *\n * Pure functions for billing and subscriptions.\n * Uses REST API at /api/sdk/billing/* for all operations.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /billing/plans`,\n * `/billing/subscription`, `POST /billing/checkout`, `/billing/portal`,\n * `GET /billing/balance`, `/billing/usage`.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tBillingBalanceResponse,\n\tBillingCheckoutRequest,\n\tBillingCheckoutResponse,\n\tBillingPortalRequest,\n\tBillingPortalResponse,\n\tBillingUsageResponse,\n\tSdkBillingPlan,\n\tSdkBillingSubscription,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type Plan = SdkBillingPlan\nexport type Subscription = SdkBillingSubscription\nexport type { BillingCheckoutRequest as CheckoutRequest }\nexport type { BillingCheckoutResponse as CheckoutResponse }\nexport type { BillingPortalRequest as PortalRequest }\nexport type { BillingPortalResponse as PortalResponse }\nexport type { BillingBalanceResponse as BalanceResponse }\nexport type { BillingUsageResponse as UsageResponse }\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get available plans\n *\n * @example\n * ```typescript\n * const plans = await getPlans(config)\n * plans.forEach(plan => console.log(plan.name, plan.monthlyPrice))\n * ```\n */\nexport async function getPlans(config: SylphxConfig): Promise<Plan[]> {\n\treturn callApi<Plan[]>(config, '/billing/plans')\n}\n\n/**\n * Get user's subscription\n *\n * @example\n * ```typescript\n * const sub = await getSubscription(config, 'user-123')\n * if (sub?.status === 'active') {\n * console.log(`Active plan: ${sub.planSlug}`)\n * }\n * ```\n */\nexport async function getSubscription(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<Subscription | null> {\n\treturn callApi<Subscription | null>(config, '/billing/subscription', {\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Create a checkout session\n *\n * @example\n * ```typescript\n * const { checkoutUrl } = await createCheckout(config, {\n * userId: 'user-123',\n * planSlug: 'pro',\n * interval: 'monthly',\n * successUrl: 'https://myapp.com/success',\n * cancelUrl: 'https://myapp.com/pricing',\n * })\n *\n * window.location.href = checkoutUrl\n * ```\n */\nexport async function createCheckout(\n\tconfig: SylphxConfig,\n\tinput: BillingCheckoutRequest,\n): Promise<BillingCheckoutResponse> {\n\treturn callApi<BillingCheckoutResponse>(config, '/billing/checkout', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Create a billing portal session\n *\n * @example\n * ```typescript\n * const { portalUrl } = await createPortalSession(config, {\n * userId: 'user-123',\n * returnUrl: window.location.href,\n * })\n *\n * window.location.href = portalUrl\n * ```\n */\nexport async function createPortalSession(\n\tconfig: SylphxConfig,\n\tinput: BillingPortalRequest,\n): Promise<BillingPortalResponse> {\n\treturn callApi<BillingPortalResponse>(config, '/billing/portal', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get billing balance (credits, etc.)\n *\n * @example\n * ```typescript\n * const balance = await getBillingBalance(config)\n * console.log(`Balance: ${balance.balance.currentFormatted}`)\n * ```\n */\nexport async function getBillingBalance(config: SylphxConfig): Promise<BillingBalanceResponse> {\n\treturn callApi<BillingBalanceResponse>(config, '/billing/balance')\n}\n\n/**\n * Get billing usage\n *\n * @example\n * ```typescript\n * const usage = await getBillingUsage(config, { month: '2024-01' })\n * ```\n */\nexport async function getBillingUsage(\n\tconfig: SylphxConfig,\n\toptions?: { month?: string },\n): Promise<BillingUsageResponse> {\n\treturn callApi<BillingUsageResponse>(config, '/billing/usage', {\n\t\tquery: options?.month ? { month: options.month } : undefined,\n\t})\n}\n","/**\n * Storage SDK — pure functional, namespaced. Per ADR-100.\n *\n * Wire is the contract in `@sylphx/contract` (`schemas/storage.ts` +\n * `endpoints/storage.ts`). This module is the only public surface for\n * uploads / files; consumers import the `storage` namespace.\n *\n * Features (built-in defaults, ADR-100 §2.8):\n * - Idempotency-Key auto-generated (UUIDv7) on every POST\n * - Single-part PUT or multipart, picked server-side from `size`\n * - Streaming SHA-256 via the Web Crypto API\n * - Resumable: persists `(uploadId, completedParts[])` to localStorage when available\n * - AbortSignal cancellation; auto-DELETE upload session on abort\n * - Exponential backoff with full jitter (5 retries, 1s base, 30s cap)\n * - Progress: byte-accurate via XHR (browser) or stream sampling (node)\n *\n * No vendor SDKs. Pure `fetch` + `XMLHttpRequest`.\n */\n\nimport type {\n\tFile as ContractFile,\n\tCopyFileRequest,\n\tFileId,\n\tFileVersion,\n\tFileVersionId,\n\tListFilesQuery,\n\tSignedUrlRequest,\n\tTakedownFileRequest,\n\tTakedownFileResult,\n\tUploadCompleteResult,\n\tUploadCreateRequest,\n\tUploadCreateResult,\n\tUploadId,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\nimport {\n\tcalculateBackoffDelay,\n\tisAbortError,\n\tisRetryableError,\n\tsleep,\n\ttoAbortError,\n\tuuidv7,\n\twithRetry,\n} from './lib/retry'\n\n// ============================================================================\n// Public types — sourced from contract (ADR-084 / ADR-100)\n// ============================================================================\n\nexport type {\n\tCopyFileRequest,\n\tFile,\n\tFileId,\n\tFileVersion,\n\tFileVersionId,\n\tFileVisibility,\n\tListFilesQuery,\n\tSignedUrlDisposition,\n\tSignedUrlRequest,\n\tSignedUrlResult,\n\tStorageTakedownReason,\n\tTakedownFileRequest,\n\tTakedownFileResult,\n\tUploadId,\n} from '@sylphx/contract'\n\n// SDK-local helper types (input shapes — not on the wire)\n\nexport interface UploadProgressEvent {\n\tloaded: number\n\ttotal: number\n\tpartsCompleted: number\n\tpartsTotal: number\n}\n\nexport interface UploadCreateOptions {\n\t/** Logical name; preserved as metadata. Defaults to `blob.name` if `File`. */\n\tfilename?: string\n\t/** MIME type override; defaults to `blob.type` or `application/octet-stream`. */\n\tcontentType?: string\n\t/** Logical folder path within the project namespace. */\n\tfolder?: string\n\t/** Defaults to `'private'`. */\n\tvisibility?: 'public' | 'private'\n\t/** Arbitrary user-attached metadata. */\n\tmetadata?: Record<string, unknown>\n\t/** Pre-computed SHA-256 (hex). If absent, the SDK computes it. */\n\tchecksumSha256?: string\n\t/** Fail when a file already exists at `(folder, filename)`. */\n\tifNoneMatch?: '*'\n\t/** Cancellation. Aborts in-flight PUTs and triggers `DELETE /uploads/{id}`. */\n\tsignal?: AbortSignal\n\t/** Override the auto-generated UUIDv7 idempotency key. */\n\tidempotencyKey?: string\n\t/** Progress callback. */\n\tonProgress?: (event: UploadProgressEvent) => void\n}\n\nexport interface ListFilesOptions {\n\tfolder?: string\n\tcursor?: string\n\tlimit?: number\n\tincludeDeleted?: boolean\n}\n\nexport interface SignedUrlOptions {\n\texpiresIn?: number\n\tdisposition?: 'attachment' | 'inline'\n\tuserId?: string\n}\n\nexport interface CopyFileOptions {\n\tfolder?: string\n\tfilename?: string\n\tvisibility?: 'public' | 'private'\n\tmetadata?: Record<string, unknown>\n}\n\nexport type TakedownFileOptions = TakedownFileRequest\n\n// ============================================================================\n// Endpoints (path builders)\n// ============================================================================\n\nconst PATHS = {\n\tuploads: '/storage/uploads',\n\tupload: (id: UploadId | string) => `/storage/uploads/${encodeURIComponent(String(id))}`,\n\tuploadComplete: (id: UploadId | string) =>\n\t\t`/storage/uploads/${encodeURIComponent(String(id))}:complete`,\n\tuploadPart: (id: UploadId | string, n: number) =>\n\t\t`/storage/uploads/${encodeURIComponent(String(id))}/parts/${n}`,\n\tfiles: '/storage/files',\n\tfile: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}`,\n\tfileTakedown: (id: FileId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}:takedown`,\n\tfileRestore: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}:restore`,\n\tfileSignedUrl: (id: FileId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}:signedUrl`,\n\tfileCopy: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}:copy`,\n\tversions: (id: FileId | string) => `/storage/files/${encodeURIComponent(String(id))}/versions`,\n\tversionRestore: (id: FileId | string, vid: FileVersionId | string) =>\n\t\t`/storage/files/${encodeURIComponent(String(id))}/versions/${encodeURIComponent(String(vid))}:restore`,\n} as const\n\n// ============================================================================\n// Runtime feature detection\n// ============================================================================\n\nconst hasXhr = (): boolean =>\n\ttypeof (globalThis as { XMLHttpRequest?: unknown }).XMLHttpRequest !== 'undefined'\n\nconst hasLocalStorage = (): boolean => {\n\ttry {\n\t\tconst ls = (globalThis as { localStorage?: Storage }).localStorage\n\t\treturn Boolean(ls && typeof ls.getItem === 'function')\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// ============================================================================\n// SHA-256 (streaming)\n// ============================================================================\n\nasync function computeSha256Hex(blob: Blob): Promise<string> {\n\tconst subtle = (globalThis as { crypto?: { subtle?: { digest?: SubtleCrypto['digest'] } } })\n\t\t.crypto?.subtle\n\tif (!subtle?.digest) {\n\t\tthrow new SylphxError('Web Crypto SHA-256 support is required for storage uploads', {\n\t\t\tcode: 'NOT_IMPLEMENTED',\n\t\t})\n\t}\n\n\tconst buf = await blob.arrayBuffer()\n\tconst digest = await subtle.digest('SHA-256', buf)\n\treturn bytesToHex(new Uint8Array(digest))\n}\n\nfunction bytesToHex(b: Uint8Array): string {\n\tlet s = ''\n\tfor (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, '0')\n\treturn s\n}\n\n// ============================================================================\n// Resume persistence\n// ============================================================================\n\ninterface ResumeRecord {\n\tuploadId: string\n\tcompletedParts: Array<{ partNumber: number; etag: string }>\n\tupdatedAt: number\n}\n\nconst RESUME_KEY_PREFIX = 'sylphx_upload_'\n\nfunction resumeKey(uploadId: string): string {\n\treturn `${RESUME_KEY_PREFIX}${uploadId}`\n}\n\nfunction persistResume(rec: ResumeRecord): void {\n\tif (hasLocalStorage()) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(resumeKey(rec.uploadId), JSON.stringify(rec))\n\t\t} catch {\n\t\t\t/* quota or sandbox; ignore */\n\t\t}\n\t}\n}\n\nfunction clearResume(uploadId: string): void {\n\tif (hasLocalStorage()) {\n\t\ttry {\n\t\t\tlocalStorage.removeItem(resumeKey(uploadId))\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Low-level HTTP helpers (PUT to presigned URLs)\n// ============================================================================\n\ninterface PutResult {\n\tetag: string\n\tstatus: number\n}\n\n/**\n * PUT a blob (or slice) to a presigned URL. Captures `ETag` from the\n * response. Uses XHR when available (browser progress) and falls back\n * to `fetch` (node).\n */\nfunction putBlob(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\tif (hasXhr()) return putBlobXhr(url, body, headers, signal, onProgress)\n\treturn putBlobFetch(url, body, headers, signal, onProgress)\n}\n\nfunction putBlobXhr(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\treturn new Promise<PutResult>((resolve, reject) => {\n\t\tconst xhr = new XMLHttpRequest()\n\n\t\tconst handleAbort = () => {\n\t\t\ttry {\n\t\t\t\txhr.abort()\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t}\n\n\t\tif (signal?.aborted) {\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t\treturn\n\t\t}\n\t\tsignal?.addEventListener('abort', handleAbort, { once: true })\n\n\t\tif (onProgress) {\n\t\t\txhr.upload.addEventListener('progress', (e) => {\n\t\t\t\tif (e.lengthComputable) onProgress(e.loaded)\n\t\t\t})\n\t\t}\n\n\t\txhr.addEventListener('load', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\tif (xhr.status >= 200 && xhr.status < 300) {\n\t\t\t\tconst etag = resolveXhrEtag(xhr)\n\t\t\t\tresolve({ etag: stripQuotes(etag), status: xhr.status })\n\t\t\t} else {\n\t\t\t\tconst err = new Error(`PUT failed with status ${xhr.status}`) as Error & {\n\t\t\t\t\tstatus: number\n\t\t\t\t}\n\t\t\t\terr.status = xhr.status\n\t\t\t\treject(err)\n\t\t\t}\n\t\t})\n\t\txhr.addEventListener('error', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\treject(new TypeError('Network error during upload'))\n\t\t})\n\t\txhr.addEventListener('abort', () => {\n\t\t\tsignal?.removeEventListener('abort', handleAbort)\n\t\t\treject(toAbortError('Upload aborted'))\n\t\t})\n\n\t\txhr.open('PUT', url)\n\t\tfor (const [k, v] of Object.entries(headers)) {\n\t\t\ttry {\n\t\t\t\txhr.setRequestHeader(k, v)\n\t\t\t} catch {\n\t\t\t\t/* unsafe header — ignore */\n\t\t\t}\n\t\t}\n\t\txhr.send(body)\n\t})\n}\n\nfunction resolveXhrEtag(xhr: XMLHttpRequest): string {\n\tconst canonical = xhr.getResponseHeader('ETag')\n\tif (canonical !== null) return canonical\n\treturn xhr.getResponseHeader('etag') ?? ''\n}\n\nasync function putBlobFetch(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\t// Sample progress via TransformStream byte counter where available.\n\tlet stream: BodyInit = body\n\tif (onProgress && typeof TransformStream !== 'undefined' && typeof body.stream === 'function') {\n\t\tlet loaded = 0\n\t\tconst counter = new TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tloaded += chunk.byteLength\n\t\t\t\tonProgress(loaded)\n\t\t\t\tcontroller.enqueue(chunk)\n\t\t\t},\n\t\t})\n\t\tstream = body.stream().pipeThrough(counter) as unknown as BodyInit\n\t}\n\n\tconst res = await fetch(url, {\n\t\tmethod: 'PUT',\n\t\tbody: stream,\n\t\theaders,\n\t\tsignal,\n\t\t// Required when streaming a request body in Chromium / undici.\n\t\t// Cast: TS lib lacks `duplex`.\n\t\t...({ duplex: 'half' } as Record<string, unknown>),\n\t})\n\tif (!res.ok) {\n\t\tconst err = new Error(`PUT failed with status ${res.status}`) as Error & { status: number }\n\t\terr.status = res.status\n\t\tthrow err\n\t}\n\tconst etag = resolveFetchEtag(res.headers)\n\tif (onProgress) onProgress(body.size)\n\treturn { etag: stripQuotes(etag), status: res.status }\n}\n\nfunction resolveFetchEtag(headers: Headers): string {\n\tconst lowerCase = headers.get('etag')\n\tif (lowerCase !== null) return lowerCase\n\treturn headers.get('ETag') ?? ''\n}\n\nfunction stripQuotes(s: string): string {\n\tif (s.length >= 2 && s.startsWith('\"') && s.endsWith('\"')) return s.slice(1, -1)\n\treturn s\n}\n\n// ============================================================================\n// Upload retry wrapper around PUT\n// ============================================================================\n\nasync function putWithRetry(\n\turl: string,\n\tbody: Blob,\n\theaders: Record<string, string>,\n\tsignal: AbortSignal | undefined,\n\tonProgress: ((bytes: number) => void) | undefined,\n): Promise<PutResult> {\n\tlet lastErr: unknown\n\tconst maxRetries = 5\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\tif (signal?.aborted) throw toAbortError()\n\t\ttry {\n\t\t\treturn await putBlob(url, body, headers, signal, onProgress)\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) throw err\n\t\t\tlastErr = err\n\t\t\tif (attempt === maxRetries || !isRetryableError(err)) throw err\n\t\t\tawait sleep(calculateBackoffDelay(attempt), signal)\n\t\t}\n\t}\n\tthrow lastErr\n}\n\n// ============================================================================\n// uploads.create — the main flow\n// ============================================================================\n\nasync function uploadsCreate(\n\tconfig: SylphxConfig,\n\tblob: Blob | File,\n\toptions: UploadCreateOptions,\n): Promise<ContractFile> {\n\tconst signal = options.signal\n\tif (signal?.aborted) throw toAbortError()\n\n\tconst filename = options.filename ?? (isFile(blob) ? blob.name : undefined) ?? 'upload.bin'\n\tconst contentType =\n\t\toptions.contentType ??\n\t\t(blob.type && blob.type.length > 0 ? blob.type : 'application/octet-stream')\n\tconst size = blob.size\n\n\tconst checksumSha256 = options.checksumSha256 ?? (await computeSha256Hex(blob))\n\n\tconst idempotencyKey = options.idempotencyKey ?? uuidv7()\n\n\tconst createBody: UploadCreateRequest = {\n\t\tfilename,\n\t\tcontentType,\n\t\tsize,\n\t\tfolder: options.folder,\n\t\tvisibility: options.visibility,\n\t\tmetadata: options.metadata as Record<string, unknown> | undefined,\n\t\tchecksumSha256,\n\t\tifNoneMatch: options.ifNoneMatch,\n\t}\n\n\t// 1. Mint upload session.\n\tconst session = await callApi<UploadCreateResult>(config, PATHS.uploads, {\n\t\tmethod: 'POST',\n\t\tbody: createBody,\n\t\tidempotencyKey,\n\t\tsignal,\n\t})\n\n\tconst uploadId = session.uploadId\n\n\t// Helper: abort the session on cancellation, then propagate.\n\tconst onAborted = async () => {\n\t\ttry {\n\t\t\tawait callApi(config, PATHS.upload(uploadId), { method: 'DELETE' })\n\t\t} catch {\n\t\t\t/* best-effort */\n\t\t}\n\t\tclearResume(String(uploadId))\n\t}\n\n\ttry {\n\t\tif (session.method === 'PUT') {\n\t\t\treturn await runSinglePart(config, blob, session, options, idempotencyKey)\n\t\t}\n\t\treturn await runMultipart(config, blob, session, options, idempotencyKey)\n\t} catch (err) {\n\t\tif (isAbortError(err)) {\n\t\t\tawait onAborted()\n\t\t}\n\t\tthrow err\n\t}\n}\n\nfunction isFile(b: Blob | File): b is File {\n\treturn typeof (b as File).name === 'string'\n}\n\nasync function runSinglePart(\n\tconfig: SylphxConfig,\n\tblob: Blob,\n\tsession: Extract<UploadCreateResult, { method: 'PUT' }>,\n\toptions: UploadCreateOptions,\n\tidempotencyKey: string,\n): Promise<ContractFile> {\n\tconst { onProgress, signal } = options\n\tconst total = blob.size\n\n\tconst trackProgress = onProgress\n\t\t? (loaded: number) => {\n\t\t\t\tonProgress({ loaded, total, partsCompleted: 0, partsTotal: 1 })\n\t\t\t}\n\t\t: undefined\n\n\tconst put = await putWithRetry(session.url, blob, session.headers, signal, trackProgress)\n\n\tif (onProgress) onProgress({ loaded: total, total, partsCompleted: 1, partsTotal: 1 })\n\n\tconst completion = await callApi<UploadCompleteResult>(\n\t\tconfig,\n\t\tPATHS.uploadComplete(session.uploadId),\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: { parts: [{ partNumber: 1, etag: put.etag }] },\n\t\t\tidempotencyKey: `${idempotencyKey}:complete`,\n\t\t\tsignal,\n\t\t},\n\t)\n\n\tclearResume(String(session.uploadId))\n\treturn await fetchFile(config, completion.fileId)\n}\n\nasync function runMultipart(\n\tconfig: SylphxConfig,\n\tblob: Blob,\n\tsession: Extract<UploadCreateResult, { method: 'MULTIPART' }>,\n\toptions: UploadCreateOptions,\n\tidempotencyKey: string,\n): Promise<ContractFile> {\n\tconst { onProgress, signal } = options\n\tconst partSize = session.partSize\n\tconst total = blob.size\n\tconst partsTotal = session.partCount\n\tconst completedParts: Array<{ partNumber: number; etag: string }> = []\n\tconst partLoaded = new Map<number, number>()\n\n\tconst reportProgress = () => {\n\t\tif (!onProgress) return\n\t\tlet loaded = 0\n\t\tfor (const v of partLoaded.values()) loaded += v\n\t\tonProgress({ loaded, total, partsCompleted: completedParts.length, partsTotal })\n\t}\n\n\tfor (const part of session.parts) {\n\t\tif (signal?.aborted) throw toAbortError()\n\t\tconst offset = (part.partNumber - 1) * partSize\n\t\tconst end = Math.min(offset + partSize, total)\n\t\tconst slice = blob.slice(offset, end)\n\n\t\tconst trackProgress = onProgress\n\t\t\t? (loaded: number) => {\n\t\t\t\t\tpartLoaded.set(part.partNumber, loaded)\n\t\t\t\t\treportProgress()\n\t\t\t\t}\n\t\t\t: undefined\n\n\t\tconst result = await putWithRetry(part.url, slice, {}, signal, trackProgress)\n\t\tcompletedParts.push({ partNumber: part.partNumber, etag: result.etag })\n\t\tpartLoaded.set(part.partNumber, slice.size)\n\t\treportProgress()\n\n\t\t// Persist after each part for resume.\n\t\tpersistResume({\n\t\t\tuploadId: String(session.uploadId),\n\t\t\tcompletedParts: [...completedParts],\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tconst completion = await callApi<UploadCompleteResult>(\n\t\tconfig,\n\t\tPATHS.uploadComplete(session.uploadId),\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: { parts: completedParts },\n\t\t\tidempotencyKey: `${idempotencyKey}:complete`,\n\t\t\tsignal,\n\t\t},\n\t)\n\n\tclearResume(String(session.uploadId))\n\treturn await fetchFile(config, completion.fileId)\n}\n\nasync function fetchFile(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.file(fileId), { method: 'GET' })\n}\n\n// ============================================================================\n// uploads.abort\n// ============================================================================\n\nasync function uploadsAbort(config: SylphxConfig, uploadId: UploadId | string): Promise<void> {\n\tawait withRetry(() => callApi(config, PATHS.upload(uploadId), { method: 'DELETE' }), {\n\t\tmaxRetries: 3,\n\t})\n\tclearResume(String(uploadId))\n}\n\n// ============================================================================\n// files.* — list / get / delete / restore / signedUrl / copy\n// ============================================================================\n\ninterface ListPage {\n\tfiles: ContractFile[]\n\tnextCursor: string | null\n}\n\nfunction filesListPage(\n\tconfig: SylphxConfig,\n\toptions: ListFilesOptions,\n\tcursor: string | undefined,\n): Promise<ListPage> {\n\tconst query: ListFilesQuery = {\n\t\tfolder: options.folder,\n\t\tcursor: cursor ?? options.cursor,\n\t\tlimit: options.limit,\n\t\tincludeDeleted: options.includeDeleted,\n\t}\n\treturn callApi<ListPage>(config, PATHS.files, {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tfolder: query.folder,\n\t\t\tcursor: query.cursor,\n\t\t\tlimit: query.limit,\n\t\t\tincludeDeleted: query.includeDeleted,\n\t\t},\n\t})\n}\n\nfunction filesList(\n\tconfig: SylphxConfig,\n\toptions: ListFilesOptions = {},\n): AsyncIterable<ContractFile> & {\n\tfetchPage: (cursor?: string) => Promise<ListPage>\n} {\n\tconst fetchPage = (cursor?: string) => filesListPage(config, options, cursor)\n\tconst iter: AsyncIterable<ContractFile> = {\n\t\t[Symbol.asyncIterator]: async function* () {\n\t\t\tlet cursor: string | null | undefined = options.cursor\n\t\t\tdo {\n\t\t\t\tconst page = await fetchPage(cursor ?? undefined)\n\t\t\t\tfor (const f of page.files) yield f\n\t\t\t\tcursor = page.nextCursor\n\t\t\t} while (cursor)\n\t\t},\n\t}\n\treturn Object.assign(iter, { fetchPage })\n}\n\nasync function filesGet(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.file(fileId), { method: 'GET' })\n}\n\nasync function filesDelete(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n): Promise<{ id: FileId; isDeleted: true }> {\n\treturn callApi(config, PATHS.file(fileId), { method: 'DELETE' })\n}\n\nasync function filesTakedown(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: TakedownFileOptions,\n): Promise<TakedownFileResult> {\n\treturn callApi<TakedownFileResult>(config, PATHS.fileTakedown(fileId), {\n\t\tmethod: 'POST',\n\t\tbody: options,\n\t})\n}\n\nasync function filesRestore(config: SylphxConfig, fileId: FileId | string): Promise<ContractFile> {\n\treturn callApi<ContractFile>(config, PATHS.fileRestore(fileId), { method: 'POST' })\n}\n\nasync function filesSignedUrl(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: SignedUrlOptions = {},\n): Promise<{ url: string; expiresAt: string; file: ContractFile }> {\n\tconst body: SignedUrlRequest = {\n\t\texpiresIn: options.expiresIn,\n\t\tdisposition: options.disposition,\n\t\tuserId: options.userId,\n\t}\n\treturn callApi(config, PATHS.fileSignedUrl(fileId), {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\nasync function filesCopy(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\toptions: CopyFileOptions,\n): Promise<ContractFile> {\n\tconst body: CopyFileRequest = {\n\t\tfolder: options.folder,\n\t\tfilename: options.filename,\n\t\tvisibility: options.visibility,\n\t\tmetadata: options.metadata as Record<string, unknown> | undefined,\n\t}\n\treturn callApi<ContractFile>(config, PATHS.fileCopy(fileId), {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\nasync function filesVersionsList(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n): Promise<FileVersion[]> {\n\tconst data = await callApi<{ versions: FileVersion[] }>(config, PATHS.versions(fileId), {\n\t\tmethod: 'GET',\n\t})\n\treturn data.versions\n}\n\nasync function filesVersionsRestore(\n\tconfig: SylphxConfig,\n\tfileId: FileId | string,\n\tversionId: FileVersionId | string,\n): Promise<{ file: ContractFile; version: FileVersion }> {\n\treturn callApi(config, PATHS.versionRestore(fileId, versionId), { method: 'POST' })\n}\n\n// ============================================================================\n// Public namespace\n// ============================================================================\n\n/**\n * `storage` namespace — the only public surface for storage in `@sylphx/sdk`.\n *\n * @example\n * ```ts\n * import { storage } from '@sylphx/sdk'\n *\n * const file = await storage.uploads.create(config, blob, {\n * filename: 'report.pdf',\n * folder: 'documents',\n * onProgress: (e) => console.log(`${e.loaded}/${e.total}`),\n * })\n *\n * for await (const f of storage.files.list(config, { folder: 'documents' })) {\n * console.log(f.id, f.filename)\n * }\n * ```\n */\nexport const storage = {\n\tuploads: {\n\t\tcreate: uploadsCreate,\n\t\tabort: uploadsAbort,\n\t},\n\tfiles: {\n\t\tlist: filesList,\n\t\tget: filesGet,\n\t\tdelete: filesDelete,\n\t\ttakedown: filesTakedown,\n\t\trestore: filesRestore,\n\t\tsignedUrl: filesSignedUrl,\n\t\tcopy: filesCopy,\n\t\tversions: {\n\t\t\tlist: filesVersionsList,\n\t\t\trestore: filesVersionsRestore,\n\t\t},\n\t},\n} as const\n\n// Re-export `SylphxError` for catch-handlers in user code.\nexport { SylphxError }\n","/**\n * Retry & idempotency primitives — shared across SDK modules.\n *\n * Per ADR-100 §2.8, every BaaS write is retried with exponential backoff\n * + full jitter (AWS S3 pattern), respects `Retry-After`, and carries an\n * auto-generated UUIDv7 `Idempotency-Key`. Pulled out of `storage.ts`\n * into a shared module so other features can opt in.\n */\n\nimport { BASE_RETRY_DELAY_MS, MAX_RETRY_DELAY_MS } from '../constants'\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface RetryConfig {\n\t/** Maximum retry attempts (initial attempt + retries). Default: 5 (AWS S3). */\n\tmaxRetries: number\n\t/** Base delay in milliseconds. Default: `BASE_RETRY_DELAY_MS` (1s). */\n\tbaseDelayMs: number\n\t/** Maximum delay cap in milliseconds. Default: `MAX_RETRY_DELAY_MS` (30s). */\n\tmaxDelayMs: number\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n\tmaxRetries: 5,\n\tbaseDelayMs: BASE_RETRY_DELAY_MS,\n\tmaxDelayMs: MAX_RETRY_DELAY_MS,\n}\n\n// ============================================================================\n// Backoff\n// ============================================================================\n\n/**\n * Full-jitter exponential backoff (AWS recommended).\n * Formula: random(0, min(cap, base * 2^attempt))\n */\nexport function calculateBackoffDelay(\n\tattempt: number,\n\tconfig: RetryConfig = DEFAULT_RETRY_CONFIG,\n): number {\n\tconst exp = config.baseDelayMs * 2 ** attempt\n\tconst capped = Math.min(exp, config.maxDelayMs)\n\treturn Math.random() * capped\n}\n\n/** Sleep for `ms`, respecting an optional `AbortSignal`. */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(toAbortError())\n\t\t\treturn\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms)\n\t\tsignal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\treject(toAbortError())\n\t\t\t},\n\t\t\t{ once: true },\n\t\t)\n\t})\n}\n\n// ============================================================================\n// Retry classification\n// ============================================================================\n\n/** HTTP status codes that should be retried (per ADR-100 §2.8). */\nexport const RETRYABLE_STATUSES: ReadonlySet<number> = new Set([408, 425, 429])\n\n/**\n * Should this error trigger a retry?\n * - `AbortError`: never (user cancelled)\n * - `TypeError` / fetch network error: yes\n * - HTTP status: 408, 425, 429, 500–599\n */\nexport function isRetryableError(error: unknown): boolean {\n\tif (isAbortError(error)) return false\n\tif (error instanceof TypeError) return true\n\tif (error instanceof Error && 'status' in error) {\n\t\tconst status = (error as { status: number }).status\n\t\treturn status >= 500 || RETRYABLE_STATUSES.has(status)\n\t}\n\treturn false\n}\n\n/** True if the error is an `AbortError` (DOMException or named Error). */\nexport function isAbortError(error: unknown): boolean {\n\tif (error instanceof DOMException && error.name === 'AbortError') return true\n\tif (error instanceof Error && error.name === 'AbortError') return true\n\treturn false\n}\n\n/** Build a portable AbortError that works in node and browser. */\nexport function toAbortError(message = 'Aborted'): Error {\n\tif (typeof DOMException !== 'undefined') {\n\t\treturn new DOMException(message, 'AbortError')\n\t}\n\tconst err = new Error(message)\n\terr.name = 'AbortError'\n\treturn err\n}\n\n// ============================================================================\n// Retry wrapper\n// ============================================================================\n\nexport interface RetryOptions extends Partial<RetryConfig> {\n\tsignal?: AbortSignal\n\t/** Hook fired before each retry; receives 0-based attempt count + delay (ms). */\n\tonRetry?: (attempt: number, delayMs: number, error: unknown) => void\n}\n\n/**\n * Run `fn` with full-jitter exponential backoff. Honours `Retry-After`\n * if the thrown error carries it (e.g. `SylphxError`).\n *\n * `fn` should throw on error; the retry loop inspects and re-runs.\n */\nexport async function withRetry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {\n\tconst cfg: RetryConfig = {\n\t\tmaxRetries: options.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,\n\t\tbaseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,\n\t\tmaxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,\n\t}\n\tlet lastError: unknown\n\n\tfor (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {\n\t\tif (options.signal?.aborted) throw toAbortError('Aborted')\n\t\ttry {\n\t\t\treturn await fn()\n\t\t} catch (err) {\n\t\t\tlastError = err\n\t\t\tif (isAbortError(err)) throw err\n\t\t\tif (attempt === cfg.maxRetries) break\n\t\t\tif (!isRetryableError(err)) throw err\n\n\t\t\tconst retryAfter = extractRetryAfter(err)\n\t\t\tconst delay = retryAfter ?? calculateBackoffDelay(attempt, cfg)\n\t\t\toptions.onRetry?.(attempt, delay, err)\n\t\t\tawait sleep(delay, options.signal)\n\t\t}\n\t}\n\tthrow lastError\n}\n\nfunction extractRetryAfter(err: unknown): number | undefined {\n\tif (err && typeof err === 'object' && 'retryAfter' in err) {\n\t\tconst v = (err as { retryAfter?: number }).retryAfter\n\t\tif (typeof v === 'number' && v > 0) return v * 1000\n\t}\n\treturn undefined\n}\n\n// ============================================================================\n// Idempotency keys (UUIDv7)\n// ============================================================================\n\n/**\n * Generate a UUIDv7 — 48-bit Unix-ms timestamp prefix + random tail.\n * Time-sortable, monotonic-ish; matches Stripe/RFC 9562. Falls back to\n * v4 randomness via `crypto.randomUUID` if available; final fallback is\n * a tiny PRNG-based generator for environments without WebCrypto.\n *\n * Spec: https://www.rfc-editor.org/rfc/rfc9562#section-5.7\n */\nexport function uuidv7(): string {\n\tconst ms = BigInt(Date.now())\n\t// 16 random bytes\n\tconst bytes = randomBytes(16)\n\t// Time hi/lo: 48 bits → bytes 0-5\n\tbytes[0] = Number((ms >> 40n) & 0xffn)\n\tbytes[1] = Number((ms >> 32n) & 0xffn)\n\tbytes[2] = Number((ms >> 24n) & 0xffn)\n\tbytes[3] = Number((ms >> 16n) & 0xffn)\n\tbytes[4] = Number((ms >> 8n) & 0xffn)\n\tbytes[5] = Number(ms & 0xffn)\n\t// Version 7 in upper nibble of byte 6\n\tbytes[6] = (bytes[6] & 0x0f) | 0x70\n\t// Variant (RFC 9562) in upper bits of byte 8\n\tbytes[8] = (bytes[8] & 0x3f) | 0x80\n\n\tconst hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n\treturn `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n\nfunction randomBytes(n: number): Uint8Array {\n\tconst out = new Uint8Array(n)\n\tconst c = (globalThis as { crypto?: { getRandomValues?: (a: Uint8Array) => Uint8Array } }).crypto\n\tif (c?.getRandomValues) {\n\t\tc.getRandomValues(out)\n\t\treturn out\n\t}\n\tfor (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256)\n\treturn out\n}\n","/**\n * Push Notification Service Worker Template\n *\n * This module provides a service worker implementation for handling\n * push notifications. Apps should copy or import this into their\n * service worker file.\n *\n * ## Industry Patterns Implemented (OneSignal/FCM)\n * - Push event handling with notification display\n * - Notification click with deep link navigation\n * - Notification close tracking\n * - Token refresh handling\n * - Background sync for offline actions\n *\n * ## Usage\n *\n * Create a service worker file in your app's public directory:\n *\n * ```typescript\n * // public/sw.ts or src/service-worker.ts\n * import { initPushServiceWorker } from '@sylphx/platform-sdk/notifications'\n *\n * initPushServiceWorker({\n * defaultIcon: '/icon-192.png',\n * defaultBadge: '/badge-72.png',\n * onNotificationClick: (data) => {\n * // Custom click handling\n * console.log('Notification clicked:', data)\n * },\n * })\n * ```\n */\n\n/**\n * Service Worker type definitions\n * These are minimal type definitions for Service Worker APIs.\n * Full types available with `lib: [\"WebWorker\"]` in tsconfig.\n */\ninterface PushEventData {\n\tjson(): unknown\n\ttext(): string\n}\n\ninterface PushEvent extends ExtendableEvent {\n\tdata: PushEventData | null\n}\n\ninterface NotificationEvent extends ExtendableEvent {\n\tnotification: Notification & {\n\t\tdata?: Record<string, unknown>\n\t\tclose(): void\n\t}\n\taction?: string\n}\n\ninterface WindowClient {\n\turl: string\n\tfocus(): Promise<WindowClient>\n}\n\ninterface Clients {\n\tmatchAll(options: { type: 'window'; includeUncontrolled: boolean }): Promise<WindowClient[]>\n\topenWindow(url: string): Promise<WindowClient | null>\n\tclaim(): Promise<void>\n}\n\ninterface ExtendableEvent extends Event {\n\twaitUntil(promise: Promise<unknown>): void\n}\n\ninterface ServiceWorkerRegistration {\n\tshowNotification(title: string, options?: NotificationOptions): Promise<void>\n}\n\ninterface ServiceWorkerGlobalScopeSubset {\n\treadonly registration: ServiceWorkerRegistration\n\treadonly clients: Clients\n\taddEventListener(type: 'push', listener: (event: PushEvent) => void): void\n\taddEventListener(\n\t\ttype: 'notificationclick' | 'notificationclose',\n\t\tlistener: (event: NotificationEvent) => void,\n\t): void\n\taddEventListener(type: 'activate', listener: (event: ExtendableEvent) => void): void\n}\n\ndeclare const self: ServiceWorkerGlobalScopeSubset\n\n/**\n * Notification payload from Sylphx platform\n */\nexport interface PushNotificationPayload {\n\t/** Notification title */\n\ttitle: string\n\t/** Notification body text */\n\tbody: string\n\t/** Icon URL (optional, falls back to default) */\n\ticon?: string\n\t/** Badge URL for Android (optional) */\n\tbadge?: string\n\t/** Image URL for expanded notification (optional) */\n\timage?: string\n\t/** Click action URL (optional) */\n\turl?: string\n\t/** Action buttons (optional) */\n\tactions?: Array<{\n\t\taction: string\n\t\ttitle: string\n\t\ticon?: string\n\t}>\n\t/** Custom data payload */\n\tdata?: Record<string, unknown>\n\t/** Notification tag for grouping (optional) */\n\ttag?: string\n\t/** Whether to require interaction (optional) */\n\trequireInteraction?: boolean\n\t/** Vibration pattern (optional) */\n\tvibrate?: number[]\n\t/** Silent notification (optional) */\n\tsilent?: boolean\n}\n\n/**\n * Service worker configuration options\n */\nexport interface PushServiceWorkerConfig {\n\t/** Default icon for notifications without an icon */\n\tdefaultIcon?: string\n\t/** Default badge for notifications without a badge */\n\tdefaultBadge?: string\n\t/** Called when notification is clicked */\n\tonNotificationClick?: (data: PushNotificationPayload) => void\n\t/** Called when notification is closed without clicking */\n\tonNotificationClose?: (data: PushNotificationPayload) => void\n\t/** Platform API URL for analytics/token refresh */\n\tplatformUrl?: string\n\t/** App ID for API calls */\n\tappId?: string\n}\n\n/**\n * Initialize push notification handling in service worker\n *\n * Call this in your service worker file to enable push notification handling.\n *\n * NOTE: This function should only be called from within a service worker context.\n * The types are loosely defined to work in both browser and service worker contexts.\n *\n * @example\n * ```typescript\n * // In your service worker (e.g., public/sw.ts)\n * initPushServiceWorker({\n * defaultIcon: '/icon-192.png',\n * defaultBadge: '/badge-72.png',\n * })\n * ```\n */\nexport function initPushServiceWorker(config: PushServiceWorkerConfig = {}): void {\n\tconst { defaultIcon, defaultBadge, onNotificationClick, onNotificationClose } = config\n\n\t// Handle push events (when notification arrives)\n\tself.addEventListener('push', (event) => {\n\t\tif (!event.data) {\n\t\t\tconsole.warn('[Sylphx SW] Push event received without data')\n\t\t\treturn\n\t\t}\n\n\t\tlet payload: PushNotificationPayload\n\t\ttry {\n\t\t\tpayload = event.data.json() as PushNotificationPayload\n\t\t} catch {\n\t\t\t// Fallback for plain text payloads\n\t\t\tpayload = {\n\t\t\t\ttitle: 'Notification',\n\t\t\t\tbody: event.data.text(),\n\t\t\t}\n\t\t}\n\n\t\t// Build notification options (compatible with both browser and SW contexts)\n\t\tconst notificationOptions = {\n\t\t\tbody: payload.body,\n\t\t\ticon: payload.icon || defaultIcon,\n\t\t\tbadge: payload.badge || defaultBadge,\n\t\t\timage: payload.image,\n\t\t\tdata: {\n\t\t\t\t...payload.data,\n\t\t\t\turl: payload.url,\n\t\t\t\t_sylphxPayload: payload,\n\t\t\t},\n\t\t\ttag: payload.tag,\n\t\t\trequireInteraction: payload.requireInteraction ?? false,\n\t\t\tvibrate: payload.vibrate,\n\t\t\tsilent: payload.silent ?? false,\n\t\t\tactions: payload.actions,\n\t\t}\n\n\t\tevent.waitUntil(self.registration.showNotification(payload.title, notificationOptions))\n\t})\n\n\t// Handle notification click events\n\tself.addEventListener('notificationclick', (event) => {\n\t\tevent.notification.close()\n\n\t\tconst data = event.notification.data\n\t\tconst payload = data?._sylphxPayload as PushNotificationPayload | undefined\n\t\tconst url = data?.url as string | undefined\n\n\t\t// Call custom handler if provided\n\t\tif (onNotificationClick && payload) {\n\t\t\tonNotificationClick(payload)\n\t\t}\n\n\t\t// Handle action button clicks\n\t\tif (event.action) {\n\t\t}\n\n\t\t// Navigate to URL if provided\n\t\tif (url) {\n\t\t\tevent.waitUntil(\n\t\t\t\tself.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {\n\t\t\t\t\t// Try to focus an existing window with this URL\n\t\t\t\t\tfor (const client of clientList) {\n\t\t\t\t\t\tif (client.url === url && 'focus' in client) {\n\t\t\t\t\t\t\treturn client.focus()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Open a new window if no existing window found\n\t\t\t\t\treturn self.clients.openWindow(url)\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t})\n\n\t// Handle notification close events (for analytics)\n\tself.addEventListener('notificationclose', (event) => {\n\t\tconst data = event.notification.data\n\t\tconst payload = data?._sylphxPayload as PushNotificationPayload | undefined\n\n\t\tif (onNotificationClose && payload) {\n\t\t\tonNotificationClose(payload)\n\t\t}\n\t})\n\n\t// Handle service worker activation\n\tself.addEventListener('activate', (event) => {\n\t\tevent.waitUntil(\n\t\t\t// Claim all clients immediately\n\t\t\tself.clients.claim(),\n\t\t)\n\t})\n}\n\n/**\n * Helper to create a simple service worker script content\n *\n * For apps that want to dynamically generate their service worker,\n * this returns the JavaScript content as a string.\n *\n * @example\n * ```typescript\n * // In a route handler\n * export function GET() {\n * const content = createServiceWorkerScript({\n * defaultIcon: '/icon-192.png',\n * })\n * return new Response(content, {\n * headers: { 'Content-Type': 'application/javascript' },\n * })\n * }\n * ```\n */\nexport function createServiceWorkerScript(config: PushServiceWorkerConfig = {}): string {\n\tconst { defaultIcon = '/icon-192.png', defaultBadge = '/badge-72.png' } = config\n\n\treturn `\n// Sylphx Push Notification Service Worker\n// Auto-generated - do not edit directly\n\nconst DEFAULT_ICON = '${defaultIcon}';\nconst DEFAULT_BADGE = '${defaultBadge}';\n\nself.addEventListener('push', (event) => {\n if (!event.data) return;\n\n let payload;\n try {\n payload = event.data.json();\n } catch {\n payload = { title: 'Notification', body: event.data.text() };\n }\n\n const options = {\n body: payload.body,\n icon: payload.icon || DEFAULT_ICON,\n badge: payload.badge || DEFAULT_BADGE,\n image: payload.image,\n data: { ...payload.data, url: payload.url },\n tag: payload.tag,\n requireInteraction: payload.requireInteraction || false,\n vibrate: payload.vibrate,\n silent: payload.silent || false,\n actions: payload.actions,\n };\n\n event.waitUntil(\n self.registration.showNotification(payload.title, options)\n );\n});\n\nself.addEventListener('notificationclick', (event) => {\n event.notification.close();\n const url = event.notification.data?.url;\n\n if (url) {\n event.waitUntil(\n clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {\n for (const client of clientList) {\n if (client.url === url && 'focus' in client) {\n return client.focus();\n }\n }\n if (clients.openWindow) {\n return clients.openWindow(url);\n }\n })\n );\n }\n});\n\nself.addEventListener('activate', (event) => {\n event.waitUntil(clients.claim());\n});\n\nconsole.log('[Sylphx SW] Push notification service worker active');\n`.trim()\n}\n\n/**\n * Register the service worker from the client side\n *\n * Call this in your app's entry point to register the service worker.\n *\n * @example\n * ```typescript\n * // In your app's entry point (e.g., _app.tsx or layout.tsx)\n * import { registerPushServiceWorker } from '@sylphx/platform-sdk/notifications'\n *\n * useEffect(() => {\n * registerPushServiceWorker('/sw.js')\n * }, [])\n * ```\n */\nexport async function registerPushServiceWorker(\n\tswPath = '/sw.js',\n): Promise<ServiceWorkerRegistration | null> {\n\tif (typeof window === 'undefined') return null\n\tif (!('serviceWorker' in navigator)) {\n\t\tconsole.warn('[Sylphx] Service workers not supported')\n\t\treturn null\n\t}\n\n\ttry {\n\t\tconst registration = await navigator.serviceWorker.register(swPath)\n\t\treturn registration\n\t} catch (error) {\n\t\tconsole.error('[Sylphx] Service worker registration failed:', error)\n\t\treturn null\n\t}\n}\n","/**\n * Notifications Functions\n *\n * Pure functions for push notifications.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/notifications/register`,\n * `/unregister`, `/send`, `/preferences`, `/messages`, `/mobile/config`.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tInAppMessagesResponse as ContractInAppMessagesResponse,\n\tMobileConfigResponse as ContractMobileConfigResponse,\n\tMobileDevice as ContractMobileDevice,\n\tPushPreferencesResponse as ContractPushPreferencesResponse,\n\tRegisterPushResponse as ContractRegisterPushResponse,\n\tSendPushInput as ContractSendPushInput,\n\tInAppMessageFull,\n\tRegisterPushInput,\n\tUnregisterPushInput,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type RegisterPushRequest = RegisterPushInput\nexport type RegisterPushResponse = ContractRegisterPushResponse\nexport type UnregisterPushRequest = UnregisterPushInput\nexport type PushPreferencesResponse = ContractPushPreferencesResponse\n/**\n * Full in-app message envelope the REST API emits under\n * `GET /notifications/messages`. Aliased to the contract's rich shape\n * (`InAppMessageFull`) — the contract also exports a leaner `InAppMessage`\n * primitive keyed off `userId`, which endpoint modules use; the SDK needs\n * the full presentation envelope.\n */\nexport type InAppMessage = InAppMessageFull\nexport type InAppMessagesResponse = ContractInAppMessagesResponse\nexport type MobileConfigResponse = ContractMobileConfigResponse\nexport type MobileDevice = ContractMobileDevice\n\n// SDK-specific types for convenience\nexport interface PushSubscription {\n\tendpoint: string\n\tkeys: {\n\t\tp256dh: string\n\t\tauth: string\n\t}\n}\n\nexport interface PushNotification {\n\ttitle: string\n\tbody: string\n\ticon?: string\n\turl?: string\n}\n\nexport interface PushDeliveryPlatformCounts {\n\treadonly web?: number\n\treadonly ios?: number\n\treadonly android?: number\n}\n\nexport interface SendPushWireResult {\n\treadonly status?: 'delivered' | 'queued' | 'failed'\n\treadonly messageId?: string\n\treadonly reason?: string\n\treadonly sent?: number\n\treadonly failed?: number\n\treadonly platforms?: PushDeliveryPlatformCounts\n\treadonly sentTo?: number\n\treadonly expired?: number\n}\n\nexport type SendPushResult = SendPushWireResult & {\n\treadonly sent: number\n\treadonly failed: number\n\treadonly sentTo: number\n\treadonly expired: number\n}\n\nfunction resolveSentCount(result: SendPushWireResult): number {\n\tif (result.sent !== undefined) return result.sent\n\tif (result.sentTo !== undefined) return result.sentTo\n\treturn result.status === 'delivered' ? 1 : 0\n}\n\nfunction resolveFailedCount(result: SendPushWireResult): number {\n\tif (result.failed !== undefined) return result.failed\n\tif (result.expired !== undefined) return result.expired\n\treturn result.status === 'failed' ? 1 : 0\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Register a push subscription\n *\n * @example\n * ```typescript\n * // Get subscription from browser\n * const registration = await navigator.serviceWorker.ready\n * const sub = await registration.pushManager.subscribe({\n * userVisibleOnly: true,\n * applicationServerKey: vapidPublicKey,\n * })\n *\n * // Register with platform\n * await registerPush(config, {\n * endpoint: sub.endpoint,\n * keys: {\n * p256dh: sub.toJSON().keys!.p256dh,\n * auth: sub.toJSON().keys!.auth,\n * },\n * })\n * ```\n */\nexport async function registerPush(\n\tconfig: SylphxConfig,\n\tsubscription: PushSubscription,\n): Promise<void> {\n\t// Contract path (`/notifications/register`) is now the SSOT after\n\t// ADR-084 Wave 2d reconciliation — matches production SDK.\n\tawait callApi(config, '/notifications/register', {\n\t\tmethod: 'POST',\n\t\tbody: { subscription },\n\t})\n}\n\n/**\n * Unregister a push subscription\n *\n * @example\n * ```typescript\n * await unregisterPush(config, subscription.endpoint)\n * ```\n */\nexport async function unregisterPush(config: SylphxConfig, endpoint: string): Promise<void> {\n\tawait callApi(config, '/notifications/unregister', {\n\t\tmethod: 'POST',\n\t\tbody: { endpoint },\n\t})\n}\n\n/**\n * Send a push notification to a user (admin only)\n *\n * @example\n * ```typescript\n * await sendPush(config, 'user-123', {\n * title: 'New message',\n * body: 'You have a new message',\n * url: '/messages',\n * })\n * ```\n */\nexport async function sendPush(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\tnotification: PushNotification,\n): Promise<SendPushResult> {\n\t// Contract path + `PushDeliveryResult` shape both widened to match the\n\t// SDK production surface (`/notifications/send` + counter fields) in\n\t// ADR-084 Wave 2d. The contract type is now a superset that accepts\n\t// the runtime aggregate shape (`sent`/`failed`/`platforms`), the legacy\n\t// counter aliases (`sentTo`/`expired`), or the status union\n\t// (`status`/`messageId`/`reason`). Normalize aliases so old consumers\n\t// and new analytics both get stable fields.\n\tconst body: ContractSendPushInput = {\n\t\tuserId,\n\t\ttitle: notification.title,\n\t\tbody: notification.body,\n\t\t...(notification.icon !== undefined && { icon: notification.icon }),\n\t\t...(notification.url !== undefined && { url: notification.url }),\n\t}\n\tconst result = await callApi<SendPushWireResult>(config, '/notifications/send', {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n\tconst sent = resolveSentCount(result)\n\tconst failed = resolveFailedCount(result)\n\treturn {\n\t\t...result,\n\t\tsent,\n\t\tfailed,\n\t\tsentTo: result.sentTo ?? sent,\n\t\texpired: result.expired ?? failed,\n\t}\n}\n\n/**\n * Get push notification preferences\n *\n * @example\n * ```typescript\n * const prefs = await getPushPreferences(config)\n * ```\n */\nexport async function getPushPreferences(\n\tconfig: SylphxConfig,\n): Promise<{ enabled: boolean; categories: Record<string, boolean> }> {\n\treturn callApi(config, '/notifications/preferences', { method: 'GET' })\n}\n\n/**\n * Update push notification preferences\n *\n * @example\n * ```typescript\n * await updatePushPreferences(config, {\n * enabled: true,\n * categories: { marketing: false, updates: true },\n * })\n * ```\n */\nexport async function updatePushPreferences(\n\tconfig: SylphxConfig,\n\tpreferences: { enabled?: boolean; categories?: Record<string, boolean> },\n): Promise<void> {\n\tawait callApi(config, '/notifications/preferences', {\n\t\tmethod: 'PUT',\n\t\tbody: preferences,\n\t})\n}\n\n// ============================================================================\n// Phase 5.7 — Advanced push: segments, campaigns, scheduled + A/B sends\n//\n// Namespaced under `notifications.segments.*` + `notifications.campaigns.*`\n// to match the Firebase-style SDK pattern elsewhere in this file. Types\n// are intentionally structural (not re-exported from @sylphx/contract\n// yet — contract additions land separately as part of Wave 3 migration).\n// ============================================================================\n\n/**\n * Structured filter DSL for segments. Matches the server-side shape\n * defined in `@sylphx/core/lib/push/filter-dsl.ts`.\n */\nexport type PushSegmentFilter =\n\t| { field: string; op: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte'; value: unknown }\n\t| { field: string; op: 'in'; value: readonly unknown[] }\n\t| { field: string; op: 'contains'; value: unknown }\n\t| { all: readonly PushSegmentFilter[] }\n\t| { any: readonly PushSegmentFilter[] }\n\t| { not: PushSegmentFilter }\n\nexport type PushSegment = {\n\tid: string\n\tname: string\n\tdescription: string | null\n\tfilter: PushSegmentFilter\n\tmemberCount: number | null\n\tcomputedAt: string | null\n\tcreatedAt: string\n\tcreatedBy: string\n}\n\nexport type PushCampaignVariant = {\n\tname: string\n\tweight: number\n\ttitle: string\n\tbody: string\n\turl?: string\n\ticon?: string\n\tdata?: Record<string, unknown>\n}\n\nexport type PushCampaign = {\n\tid: string\n\tname: string\n\tsegmentId: string | null\n\tstatus: 'draft' | 'scheduled' | 'sending' | 'sent' | 'cancelled' | 'failed'\n\tvariants: readonly PushCampaignVariant[]\n\tscheduledAt: string | null\n\tsentAt: string | null\n\tcreatedAt: string\n}\n\nexport type PushCampaignStats = {\n\tcampaignId: string\n\ttotalDeliveries: number\n\tbyVariant: Record<\n\t\tstring,\n\t\t{ pending: number; sent: number; delivered: number; failed: number; clicked: number }\n\t>\n}\n\n/**\n * Segment-management namespace. Requires secret-key auth — call from\n * trusted server contexts only.\n */\nexport const segments = {\n\tasync create(\n\t\tconfig: SylphxConfig,\n\t\tinput: { name: string; description?: string; filter: PushSegmentFilter },\n\t): Promise<PushSegment> {\n\t\treturn callApi(config, '/notifications/segments', { method: 'POST', body: input })\n\t},\n\tasync list(config: SylphxConfig): Promise<{ segments: PushSegment[] }> {\n\t\treturn callApi(config, '/notifications/segments', { method: 'GET' })\n\t},\n\tasync get(config: SylphxConfig, id: string): Promise<PushSegment> {\n\t\treturn callApi(config, `/notifications/segments/${id}`, { method: 'GET' })\n\t},\n\tasync delete(config: SylphxConfig, id: string): Promise<void> {\n\t\tawait callApi(config, `/notifications/segments/${id}`, { method: 'DELETE' })\n\t},\n}\n\n/**\n * Campaign-management namespace. A/B variants assigned deterministically\n * per-user on the server; scheduled campaigns are picked up by the\n * scheduled-send worker.\n */\nexport const campaigns = {\n\tasync create(\n\t\tconfig: SylphxConfig,\n\t\tinput: {\n\t\t\tname: string\n\t\t\tsegmentId?: string\n\t\t\tvariants: readonly PushCampaignVariant[]\n\t\t\tscheduledAt?: string\n\t\t},\n\t): Promise<PushCampaign> {\n\t\treturn callApi(config, '/notifications/campaigns', { method: 'POST', body: input })\n\t},\n\tasync get(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}`, { method: 'GET' })\n\t},\n\tasync schedule(config: SylphxConfig, id: string, scheduledAt: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/schedule`, {\n\t\t\tmethod: 'POST',\n\t\t\tbody: { scheduledAt },\n\t\t})\n\t},\n\tasync send(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/send`, { method: 'POST' })\n\t},\n\tasync cancel(config: SylphxConfig, id: string): Promise<PushCampaign> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/cancel`, { method: 'POST' })\n\t},\n\tasync stats(config: SylphxConfig, id: string): Promise<PushCampaignStats> {\n\t\treturn callApi(config, `/notifications/campaigns/${id}/stats`, { method: 'GET' })\n\t},\n}\n","/**\n * Triggers Client (ADR-040)\n *\n * Unified scheduling + event dispatch API.\n * Create cron schedules and event triggers that dispatch to Tasks, Runs, or HTTP URLs.\n *\n * ## Usage\n *\n * ### Cron → Task\n * ```typescript\n * import { createServerClient, TriggersClient } from '@sylphx/sdk'\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * const trigger = await TriggersClient.create(config, {\n * name: 'daily-cleanup',\n * source: { type: 'cron', expression: '0 2 * * *' },\n * target: { type: 'task', taskName: 'daily-cleanup' },\n * })\n * ```\n *\n * ### Event → Task (fires when event is published via publishEvent)\n * ```typescript\n * const trigger = await TriggersClient.create(config, {\n * name: 'welcome-email-on-signup',\n * source: { type: 'event', eventName: 'user.signup' },\n * target: { type: 'task', taskName: 'send-welcome-email' },\n * })\n * // Publish from your app:\n * await TriggersClient.publishEvent(config, 'user.signup', { userId: '123' })\n * ```\n *\n * ### Cron → HTTP URL (any language, no code required)\n * ```typescript\n * const trigger = await TriggersClient.create(config, {\n * name: 'nightly-backup',\n * source: { type: 'cron', expression: '0 3 * * *' },\n * target: { type: 'http', url: 'https://myapp.com/api/backup', payload: { type: 'full' } },\n * })\n * ```\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport type { SylphxConfig } from '../../config'\nimport { callApi } from '../../config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TriggerTargetType = 'task' | 'run' | 'http'\nexport type TriggerSourceType = 'cron' | 'event'\nexport type TriggerStatus = 'active' | 'paused' | 'deleted'\nexport type TriggerRunMachineSize = MachineSize\n\nexport interface TaskTarget {\n\ttype: 'task'\n\ttaskName: string\n\thandlerPath?: string\n\tpayload?: Record<string, unknown>\n}\n\nexport interface HttpTarget {\n\ttype: 'http'\n\turl: string\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\theaders?: Record<string, string>\n\tpayload?: Record<string, unknown>\n}\n\nexport interface RunTarget {\n\ttype: 'run'\n\timage: string\n\tcommand: string[]\n\tmachine?: TriggerRunMachineSize\n}\n\nexport type TriggerTarget = TaskTarget | HttpTarget | RunTarget\n\nexport interface CronSource {\n\ttype: 'cron'\n\texpression: string\n}\n\nexport interface EventSource {\n\ttype: 'event'\n\t/** The event name to listen for. e.g. 'user.signup', 'order.paid' */\n\teventName: string\n}\n\nexport type TriggerSource = CronSource | EventSource\n\nexport interface CreateTriggerOptions {\n\tname?: string\n\tsource: TriggerSource\n\ttarget: TriggerTarget\n\tpaused?: boolean\n\t/** Idempotency key — prevents duplicate trigger creation per project+environment */\n\tidempotencyKey?: string\n}\n\nexport interface UpdateTriggerOptions {\n\tname?: string\n\tsource?: TriggerSource\n\tpaused?: boolean\n}\n\nexport interface Trigger {\n\tid: string\n\tname: string\n\ttargetType: TriggerTargetType\n\tsourceType: TriggerSourceType\n\tcronExpression: string | null\n\teventName: string | null\n\thandlerPath: string | null\n\tcallbackUrl: string | null\n\tpayload: unknown\n\tstatus: TriggerStatus\n\tnextRunAt: string | null\n\tlastRunAt: string | null\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\nexport interface ListTriggersResult {\n\ttriggers: Trigger[]\n}\n\nexport interface PublishEventResult {\n\tdispatched: number\n\twaitResolved: number\n\teventName: string\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\n/** Create a new trigger (cron or event source, task/run/http target) */\nasync function createTrigger(\n\tconfig: SylphxConfig,\n\toptions: CreateTriggerOptions,\n): Promise<Trigger> {\n\treturn callApi<Trigger>(config, '/triggers', {\n\t\tmethod: 'POST',\n\t\tbody: options,\n\t})\n}\n\n/** List all triggers for the project */\nasync function listTriggers(config: SylphxConfig): Promise<ListTriggersResult> {\n\treturn callApi<ListTriggersResult>(config, '/triggers')\n}\n\n/** Get a trigger by ID */\nasync function getTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}`)\n}\n\n/** Update a trigger */\nasync function updateTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n\toptions: UpdateTriggerOptions,\n): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}`, {\n\t\tmethod: 'PATCH',\n\t\tbody: options,\n\t})\n}\n\n/** Delete a trigger */\nasync function deleteTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/triggers/${triggerId}`, { method: 'DELETE' })\n}\n\n/** Pause a trigger */\nasync function pauseTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}/pause`, { method: 'POST' })\n}\n\n/** Resume a paused trigger */\nasync function resumeTrigger(config: SylphxConfig, triggerId: string): Promise<Trigger> {\n\treturn callApi<Trigger>(config, `/triggers/${triggerId}/resume`, { method: 'POST' })\n}\n\n/** Fire a trigger immediately (one-shot, regardless of schedule) */\nasync function fireTrigger(\n\tconfig: SylphxConfig,\n\ttriggerId: string,\n): Promise<{ success: boolean; message: string }> {\n\treturn callApi<{ success: boolean; message: string }>(config, `/triggers/${triggerId}/fire`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Publish an event — dispatches all active event triggers matching the event name.\n * Endpoint: POST /triggers/events\n *\n * @example\n * ```typescript\n * await TriggersClient.publishEvent(config, 'user.signup', { userId: '123', plan: 'pro' })\n * ```\n */\nasync function publishEvent(\n\tconfig: SylphxConfig,\n\teventName: string,\n\tpayload?: Record<string, unknown>,\n): Promise<PublishEventResult> {\n\treturn callApi<PublishEventResult>(config, '/triggers/events', {\n\t\tmethod: 'POST',\n\t\tbody: { eventName, payload: payload ?? {} },\n\t})\n}\n\n/**\n * Triggers client — namespace object exposing all trigger operations.\n * Use via `TriggersClient.create(...)`, `TriggersClient.publishEvent(...)`, etc.\n */\nexport const TriggersClient = {\n\tcreate: createTrigger,\n\tlist: listTriggers,\n\tget: getTrigger,\n\tupdate: updateTrigger,\n\tdelete: deleteTrigger,\n\tpause: pauseTrigger,\n\tresume: resumeTrigger,\n\tfire: fireTrigger,\n\tpublishEvent,\n} as const\n","/**\n * Tasks Handler\n *\n * Server-side endpoint factory for user task definitions.\n * Implements the Sylphx stateless-replay model:\n *\n * 1. Platform dispatches POST to the app's /api/tasks endpoint.\n * 2. The handler replays from the beginning.\n * 3. Completed steps hit a cache and return immediately.\n * 4. The first uncached step executes, then throws StepCompleteSignal.\n * 5. The platform saves the result and re-dispatches.\n * 6. After all steps complete the handler returns normally.\n *\n * @example\n * ```typescript\n * // app/api/tasks/route.ts\n * import { sylphx } from '@/lib/sylphx'\n *\n * const sendEmail = sylphx.tasks.define('send-email', async (payload, { step }) => {\n * const validated = await step.run('validate', () => validateEmail(payload.to))\n * await step.sleep('cool-down', '5 minutes')\n * return step.run('send', () => sendEmail(validated))\n * })\n *\n * export const { GET, POST } = sylphx.tasks.handler([sendEmail])\n * ```\n */\n\nimport { createHmac, timingSafeEqual } from 'node:crypto'\nimport type { NativeStepContext, NativeTaskDefinition } from './types'\n\n// ============================================================================\n// Signals (control flow via throw)\n// ============================================================================\n\n/**\n * Thrown by step.run() when a step's result is not yet cached.\n * The handler aborts; the platform saves the result and re-dispatches.\n */\nexport class StepCompleteSignal {\n\treadonly _isStepCompleteSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly result: unknown,\n\t) {}\n}\n\n/**\n * Thrown by step.sleep() when the sleep is not yet resolved.\n * The handler aborts; the platform waits and re-dispatches.\n */\nexport class StepSleepSignal {\n\treadonly _isStepSleepSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly duration: string,\n\t) {}\n}\n\n/**\n * Thrown by step.waitForEvent() when the event has not yet been received.\n * The handler aborts; the platform persists the wait record.\n * When the event arrives (via TriggersClient.publishEvent()), the platform\n * resolves the wait and re-dispatches with the event payload in context.waits.\n */\nexport class StepWaitEventSignal {\n\treadonly _isStepWaitEventSignal = true\n\n\tconstructor(\n\t\tpublic readonly stepName: string,\n\t\tpublic readonly eventName: string,\n\t\tpublic readonly options: {\n\t\t\t/** Timeout duration, e.g. '24h', '7d'. Task fails if event not received in time. */\n\t\t\ttimeout?: string\n\t\t\t/** Optional JSON filter — only resolve if event payload matches */\n\t\t\tfilter?: Record<string, unknown>\n\t\t} = {},\n\t) {}\n}\n\n// ============================================================================\n// Step context factory\n// ============================================================================\n\n/**\n * Build the step proxy for a single handler invocation.\n *\n * @param completedSteps Map of stepName → cached result\n * @param resolvedWaits Map of stepName → wait result (sleep = undefined, event = event payload)\n */\nexport function createStepContext(\n\tcompletedSteps: Map<string, unknown>,\n\tresolvedWaits: Map<string, unknown>,\n): NativeStepContext {\n\treturn {\n\t\t/**\n\t\t * Execute a named step.\n\t\t *\n\t\t * - If cached: return the cached value immediately (replay).\n\t\t * - If not cached: execute fn, then throw StepCompleteSignal with the result.\n\t\t */\n\t\tasync run<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n\t\t\tif (completedSteps.has(name)) {\n\t\t\t\treturn completedSteps.get(name) as T\n\t\t\t}\n\n\t\t\tconst result = await fn()\n\t\t\tthrow new StepCompleteSignal(name, result)\n\t\t},\n\n\t\t/**\n\t\t * Sleep for the given duration.\n\t\t *\n\t\t * - If resolved (platform woke us up): return immediately.\n\t\t * - If not resolved: throw StepSleepSignal to pause execution.\n\t\t */\n\t\tasync sleep(name: string, duration: string): Promise<void> {\n\t\t\tif (resolvedWaits.has(name)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthrow new StepSleepSignal(name, duration)\n\t\t},\n\n\t\t/**\n\t\t * Pause execution until a named event is published via TriggersClient.publishEvent().\n\t\t *\n\t\t * - If event already arrived (platform re-dispatched with result): return event payload.\n\t\t * - If not yet arrived: throw StepWaitEventSignal to pause execution.\n\t\t *\n\t\t * @param name Step identifier (unique within handler).\n\t\t * @param eventName The event name to listen for (e.g. 'user.approved').\n\t\t * @param options Optional timeout ('24h', '7d') and payload filter.\n\t\t *\n\t\t * @example Human-in-the-loop approval\n\t\t * ```typescript\n\t\t * const approval = await step.waitForEvent('wait-approval', 'order.approved', {\n\t\t * timeout: '48h',\n\t\t * filter: { orderId: payload.orderId },\n\t\t * })\n\t\t * if (!approval) throw new Error('Approval timed out')\n\t\t * await sendConfirmation(approval.approvedBy)\n\t\t * ```\n\t\t */\n\t\tasync waitForEvent<T = unknown>(\n\t\t\tname: string,\n\t\t\teventName: string,\n\t\t\toptions: { timeout?: string; filter?: Record<string, unknown> } = {},\n\t\t): Promise<T | null> {\n\t\t\tif (resolvedWaits.has(name)) {\n\t\t\t\t// Platform resolved the wait — return the event payload (null if timed out)\n\t\t\t\treturn (resolvedWaits.get(name) as T | null) ?? null\n\t\t\t}\n\n\t\t\tthrow new StepWaitEventSignal(name, eventName, options)\n\t\t},\n\t}\n}\n\n// ============================================================================\n// Signature verification\n// ============================================================================\n\n/**\n * Constant-time HMAC-SHA256 signature verification.\n * Accepts both raw hex and 'sha256={hex}' formats.\n */\nexport function verifySignature(body: string, signature: string, secret: string): boolean {\n\ttry {\n\t\tconst hmac = createHmac('sha256', secret).update(body, 'utf8').digest('hex')\n\t\tconst normalised = signature.replace(/^sha256=/, '')\n\n\t\tconst expected = Buffer.from(hmac, 'hex')\n\t\tconst provided = Buffer.from(normalised, 'hex')\n\n\t\tif (expected.length !== provided.length) return false\n\t\treturn timingSafeEqual(expected, provided)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// ============================================================================\n// Request / response types\n// ============================================================================\n\ninterface DispatchStepEntry {\n\tname: string\n\tresult: unknown\n\tcompletedAt: string\n}\n\ninterface DispatchWaitEntry {\n\tname: string\n\tresolvedAt: string\n\tresult?: unknown\n}\n\ninterface DispatchContext {\n\tattempt: number\n\tsteps: DispatchStepEntry[]\n\twaits: DispatchWaitEntry[]\n}\n\ninterface DispatchBody {\n\ttaskRunId: string\n\ttaskName: string\n\tpayload: unknown\n\tcontext: DispatchContext\n}\n\n// ============================================================================\n// Handler factory\n// ============================================================================\n\nexport interface TasksHandlerOptions {\n\t/** Platform signing secret. If not set, signature verification is skipped. */\n\tsigningSecret?: string\n}\n\nexport interface TasksHandlerResult {\n\tGET: (req: Request) => Promise<Response>\n\tPOST: (req: Request) => Promise<Response>\n}\n\n/**\n * Create a Next.js-compatible route handler for a list of task definitions.\n *\n * @example\n * ```typescript\n * export const { GET, POST } = createTasksHandler([sendEmail, processOrder])\n * ```\n */\nexport function createTasksHandler(\n\ttaskDefs: NativeTaskDefinition[],\n\toptions: TasksHandlerOptions = {},\n): TasksHandlerResult {\n\tconst taskMap = new Map<string, NativeTaskDefinition>()\n\tfor (const def of taskDefs) {\n\t\ttaskMap.set(def.name, def)\n\t}\n\n\t// GET: health/discovery — returns registered task names\n\tconst GET = async (_req: Request): Promise<Response> => {\n\t\treturn Response.json({\n\t\t\tok: true,\n\t\t\ttasks: Array.from(taskMap.keys()),\n\t\t})\n\t}\n\n\t// POST: dispatch handler\n\tconst POST = async (req: Request): Promise<Response> => {\n\t\t// ------------------------------------------------------------------\n\t\t// 1. Read raw body as text\n\t\t// ------------------------------------------------------------------\n\t\tlet rawBody: string\n\t\ttry {\n\t\t\trawBody = await req.text()\n\t\t} catch {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Failed to read request body' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 2. Verify signature if configured\n\t\t// ------------------------------------------------------------------\n\t\tconst signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ?? ''\n\n\t\tif (signingSecret) {\n\t\t\tconst signature = req.headers.get('x-sylphx-signature') ?? ''\n\t\t\tif (!signature) {\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{ status: 'error', message: 'Missing X-Sylphx-Signature header' },\n\t\t\t\t\t{ status: 401 },\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!verifySignature(rawBody, signature, signingSecret)) {\n\t\t\t\treturn Response.json({ status: 'error', message: 'Invalid signature' }, { status: 401 })\n\t\t\t}\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 3. Parse body\n\t\t// ------------------------------------------------------------------\n\t\tlet dispatch: DispatchBody\n\t\ttry {\n\t\t\tdispatch = JSON.parse(rawBody) as DispatchBody\n\t\t} catch {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Request body is not valid JSON' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\tconst { taskName, payload, context } = dispatch\n\n\t\tif (!taskName || typeof taskName !== 'string') {\n\t\t\treturn Response.json(\n\t\t\t\t{ status: 'error', message: 'Missing or invalid \"taskName\" field' },\n\t\t\t\t{ status: 400 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 4. Find task definition\n\t\t// ------------------------------------------------------------------\n\t\tconst taskDef = taskMap.get(taskName)\n\t\tif (!taskDef) {\n\t\t\treturn Response.json(\n\t\t\t\t{\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage: `Task \"${taskName}\" not found. Registered tasks: ${Array.from(taskMap.keys()).join(', ')}`,\n\t\t\t\t},\n\t\t\t\t{ status: 404 },\n\t\t\t)\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// 5. Build step context from dispatch context\n\t\t// ------------------------------------------------------------------\n\t\tconst completedSteps = new Map<string, unknown>()\n\t\tfor (const step of context?.steps ?? []) {\n\t\t\tcompletedSteps.set(step.name, step.result)\n\t\t}\n\n\t\t// resolvedWaits: stepName → result (sleep=undefined, event=payload, timeout=null)\n\t\tconst resolvedWaits = new Map<string, unknown>()\n\t\tfor (const wait of context?.waits ?? []) {\n\t\t\tresolvedWaits.set(wait.name, wait.result ?? undefined)\n\t\t}\n\n\t\tconst stepCtx = createStepContext(completedSteps, resolvedWaits)\n\n\t\t// ------------------------------------------------------------------\n\t\t// 6. Invoke the handler\n\t\t// ------------------------------------------------------------------\n\t\ttry {\n\t\t\tconst result = await taskDef.handler(payload, { step: stepCtx })\n\n\t\t\t// Handler returned normally → task is complete\n\t\t\treturn Response.json({ status: 'complete', result })\n\t\t} catch (err: unknown) {\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7a. Step completed — save result and re-dispatch\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (err instanceof StepCompleteSignal || (err as StepCompleteSignal)?._isStepCompleteSignal) {\n\t\t\t\tconst signal = err as StepCompleteSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_complete',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\tresult: signal.result,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7b. Step sleep — create wait record\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (err instanceof StepSleepSignal || (err as StepSleepSignal)?._isStepSleepSignal) {\n\t\t\t\tconst signal = err as StepSleepSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_sleep',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\tduration: signal.duration,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7c. Step waitForEvent — suspend until named event arrives\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (\n\t\t\t\terr instanceof StepWaitEventSignal ||\n\t\t\t\t(err as StepWaitEventSignal)?._isStepWaitEventSignal\n\t\t\t) {\n\t\t\t\tconst signal = err as StepWaitEventSignal\n\t\t\t\treturn Response.json({\n\t\t\t\t\tstatus: 'step_wait_event',\n\t\t\t\t\tstepName: signal.stepName,\n\t\t\t\t\teventName: signal.eventName,\n\t\t\t\t\ttimeout: signal.options.timeout ?? null,\n\t\t\t\t\tfilter: signal.options.filter ?? null,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// 7d. Unexpected error\n\t\t\t// ------------------------------------------------------------------\n\t\t\tconst message = err instanceof Error ? err.message : String(err)\n\t\t\tconsole.error(`[sylphx/tasks] Task \"${taskName}\" threw an error:`, err)\n\t\t\treturn Response.json(\n\t\t\t\t{\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage,\n\t\t\t\t\tretriable: true,\n\t\t\t\t},\n\t\t\t\t{ status: 500 },\n\t\t\t)\n\t\t}\n\t}\n\n\treturn { GET, POST }\n}\n","/**\n * Task API Functions\n *\n * Standalone functions for scheduling tasks and managing crons via the Sylphx API.\n * These are backward-compat exports preserved from the pre-refactor tasks.ts.\n */\n\nimport { callApi, type SylphxConfig } from '../../config'\nimport type { CronSchedule } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TaskInput {\n\t/** Callback URL to call when task executes */\n\tcallbackUrl: string\n\t/** Task name/type */\n\tname?: string\n\t/** Task type for categorization */\n\ttype?: string\n\t/** Task payload sent to callback */\n\tpayload?: Record<string, unknown>\n\t/** HTTP method for callback (default: POST) */\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'DELETE'\n\t/** Additional headers for callback */\n\theaders?: Record<string, string>\n\t/** Delay before executing (in seconds, max 604800 = 7 days) */\n\tdelay?: number\n\t/** Schedule for later (ISO timestamp) */\n\tscheduledFor?: string\n\t/** Number of retries on failure (0-5, default: 3) */\n\tretries?: number\n\t/** Request timeout in seconds (1-300, default: 30) */\n\ttimeout?: number\n\t/**\n\t * Idempotency key for safe retries (Stripe/Inngest pattern).\n\t *\n\t * When provided, prevents duplicate task execution if the same\n\t * key is used within a 24-hour window.\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface TaskResult {\n\t/** Task ID */\n\ttaskId: string\n\t/** Scheduled execution time */\n\tscheduledFor?: string\n}\n\nexport interface TaskStatus {\n\tid: string\n\tname?: string\n\tstatus: 'pending' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'\n\tpayload?: Record<string, unknown>\n\tresult?: unknown\n\terror?: string\n\tcreatedAt: string\n\tqueuedAt?: string\n\tstartedAt?: string\n\tcompletedAt?: string\n}\n\nexport interface CronInput {\n\t/** Callback URL to call on each cron trigger */\n\tcallbackUrl: string\n\t/** Cron expression (e.g., '0 0 * * *' for daily at midnight) */\n\tcron: string\n\t/** Task name (required, max 200 chars) */\n\tname: string\n\t/** Task type for categorization */\n\ttype?: string\n\t/** Task payload sent to callback */\n\tpayload?: Record<string, unknown>\n\t/** HTTP method for callback (default: POST) */\n\tmethod?: 'GET' | 'POST' | 'PUT' | 'DELETE'\n\t/** Additional headers for callback */\n\theaders?: Record<string, string>\n\t/** Number of retries on failure (0-5, default: 3) */\n\tretries?: number\n\t/** Start in paused state */\n\tpaused?: boolean\n\t/**\n\t * Idempotency key for safe cron creation.\n\t *\n\t * When provided, prevents duplicate cron schedule creation if\n\t * the same key is used.\n\t */\n\tidempotencyKey?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Schedule a one-time task for execution\n */\nexport async function scheduleTask(config: SylphxConfig, input: TaskInput): Promise<TaskResult> {\n\treturn callApi<TaskResult>(config, '/tasks/schedule', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get a task's status by ID\n */\nexport async function getTask(config: SylphxConfig, taskId: string): Promise<TaskStatus> {\n\treturn callApi<TaskStatus>(config, `/tasks/${taskId}`, { method: 'GET' })\n}\n\n/**\n * Cancel a pending task\n */\nexport async function cancelTask(config: SylphxConfig, taskId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/${taskId}/cancel`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * List tasks with optional filters\n */\nexport async function listTasks(\n\tconfig: SylphxConfig,\n\toptions?: { status?: TaskStatus['status']; limit?: number; offset?: number },\n): Promise<{ tasks: TaskStatus[]; total: number }> {\n\treturn callApi(config, '/tasks', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Create a recurring cron task\n */\nexport async function createCron(config: SylphxConfig, input: CronInput): Promise<CronSchedule> {\n\treturn callApi<CronSchedule>(config, '/tasks/cron', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Pause a cron schedule\n */\nexport async function pauseCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}/pause`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * Resume a cron schedule\n */\nexport async function resumeCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}/resume`, {\n\t\tmethod: 'POST',\n\t})\n\treturn result.success\n}\n\n/**\n * Delete a cron schedule\n */\nexport async function deleteCron(config: SylphxConfig, scheduleId: string): Promise<boolean> {\n\tconst result = await callApi<{ success: boolean }>(config, `/tasks/cron/${scheduleId}`, {\n\t\tmethod: 'DELETE',\n\t})\n\treturn result.success\n}\n","/**\n * Feature Flags Functions\n *\n * Pure functions for feature flag evaluation.\n *\n * Pattern: LaunchDarkly/Statsig server-side evaluation\n * - Server-side: POST /flags/evaluate with context\n * - Returns evaluated results (enabled/disabled for this context)\n *\n * Types are derived from the OpenAPI spec (generated/api.d.ts).\n * Run `bun run generate:types:local` to regenerate after API changes.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type { EvaluateFlagsInput as ContractEvaluateFlagsInput } from '@sylphx/contract'\nimport { flagsEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (SDK-specific - no direct API schema for flag results)\n// ============================================================================\n\nexport interface FlagResult {\n\t/** Flag key */\n\tkey: string\n\t/** Whether the flag is enabled for this context */\n\tenabled: boolean\n\t/** Variant value (for multivariate flags) */\n\tvariant?: string\n\t/** Reason for the evaluation result */\n\treason?: string\n\t/** Additional payload data */\n\tpayload?: Record<string, unknown>\n}\n\nexport interface FlagContext {\n\t/** User ID for consistent targeting */\n\tuserId?: string\n\t/** Anonymous ID for pre-auth targeting */\n\tanonymousId?: string\n\t/** User properties for targeting rules (plan, isAdmin, etc.) */\n\tproperties?: Record<string, unknown>\n}\n\n/** Response from the evaluate endpoint */\ninterface EvaluateFlagsResponse {\n\tdata: Record<string, FlagResult>\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Check a single feature flag (server-side evaluation)\n *\n * Uses POST /flags/evaluate for consistent, server-side targeting.\n * The server evaluates rollout percentage, premium targeting, etc.\n *\n * @example\n * ```typescript\n * const flag = await checkFlag(config, 'new-checkout', {\n * userId: 'user-123',\n * properties: { plan: 'pro' },\n * })\n *\n * if (flag.enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function checkFlag(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<FlagResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\tkeys: [flagKey],\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\t// Return the evaluated flag, or a disabled default if not found\n\treturn (\n\t\tresponse.data[flagKey] ?? {\n\t\t\tkey: flagKey,\n\t\t\tenabled: false,\n\t\t\treason: 'flag_not_found',\n\t\t}\n\t)\n}\n\n/**\n * Get multiple feature flags at once (batch evaluation)\n *\n * Evaluates all requested flags in a single API call.\n * More efficient than calling checkFlag() multiple times.\n *\n * @example\n * ```typescript\n * const flags = await getFlags(config, ['new-checkout', 'dark-mode', 'ai-features'], {\n * userId: 'user-123',\n * })\n *\n * if (flags['new-checkout'].enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function getFlags(\n\tconfig: SylphxConfig,\n\tflagKeys: string[],\n\tcontext?: FlagContext,\n): Promise<Record<string, FlagResult>> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\tkeys: flagKeys,\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\treturn response.data\n}\n\n/**\n * Get all feature flags for a context (bootstrap)\n *\n * Evaluates ALL flags for the app in a single API call.\n * Useful for bootstrapping the flag state on app load.\n *\n * @example\n * ```typescript\n * // Bootstrap all flags on app load\n * const allFlags = await getAllFlags(config, { userId: 'user-123' })\n *\n * // Use throughout the app\n * if (allFlags['new-checkout']?.enabled) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function getAllFlags(\n\tconfig: SylphxConfig,\n\tcontext?: FlagContext,\n): Promise<Record<string, FlagResult>> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = flagsEndpoints.evaluate\n\tconst body = {\n\t\tcontext: {\n\t\t\tuserId: context?.userId,\n\t\t\tanonymousId: context?.anonymousId,\n\t\t\tproperties: context?.properties,\n\t\t},\n\t\t// Omit keys to get all flags\n\t} satisfies ContractEvaluateFlagsInput\n\tconst response = await callApi<EvaluateFlagsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\n\treturn response.data\n}\n\n/**\n * Check if a flag is enabled (boolean helper)\n *\n * @example\n * ```typescript\n * if (await isEnabled(config, 'new-checkout', { userId: 'user-123' })) {\n * // Show new checkout\n * }\n * ```\n */\nexport async function isEnabled(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<boolean> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.enabled\n}\n\n/**\n * Get flag variant (for A/B tests)\n *\n * @example\n * ```typescript\n * const variant = await getVariant(config, 'checkout-experiment', {\n * userId: 'user-123',\n * })\n *\n * switch (variant) {\n * case 'control':\n * // Show original checkout\n * break\n * case 'variant-a':\n * // Show variant A\n * break\n * case 'variant-b':\n * // Show variant B\n * break\n * }\n * ```\n */\nexport async function getVariant(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<string | undefined> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.variant\n}\n\n/**\n * Get flag payload (for remote config)\n *\n * @example\n * ```typescript\n * const payload = await getFlagPayload<{ maxItems: number }>(config, 'cart-config', {\n * userId: 'user-123',\n * })\n *\n * console.log(payload?.maxItems) // 10\n * ```\n */\nexport async function getFlagPayload<T extends Record<string, unknown>>(\n\tconfig: SylphxConfig,\n\tflagKey: string,\n\tcontext?: FlagContext,\n): Promise<T | undefined> {\n\tconst flag = await checkFlag(config, flagKey, context)\n\treturn flag.payload as T | undefined\n}\n","/**\n * Webhooks Functions\n *\n * Pure functions for webhook configuration and delivery management.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for every `/webhooks/*` endpoint\n * under the Console / Management plane. This module keeps a handful of\n * SDK-specific ergonomic envelopes (`WebhookConfig`, `WebhookEnvironment`,\n * `WebhookStats`, `WebhookConfigUpdate`, `ListDeliveriesOptions`,\n * `WebhookDeliveriesResult`) that are looser or richer than the strict\n * contract shapes — they predate the contract and stay put to avoid\n * breaking downstream consumers (SDK tests, apps/web hooks layer).\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tGetWebhookConfigResult,\n\tListWebhookDeliveriesResult,\n\tReplayDeliveryResult,\n\tUpdateWebhookConfigInput,\n\tUpdateWebhookConfigResult,\n\tWebhookDelivery as ContractWebhookDelivery,\n\tWebhookEnvironment as ContractWebhookEnvironment,\n\tWebhookStatsResult,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type WebhookConfigResponse = GetWebhookConfigResult\nexport type WebhookEnvironmentConfig = ContractWebhookEnvironment\nexport type UpdateWebhookConfigRequest = UpdateWebhookConfigInput\nexport type UpdateWebhookConfigResponse = UpdateWebhookConfigResult\nexport type ListWebhookDeliveriesResponse = ListWebhookDeliveriesResult\nexport type WebhookDelivery = ContractWebhookDelivery\nexport type ReplayDeliveryResponse = ReplayDeliveryResult\nexport type WebhookStatsResponse = WebhookStatsResult\n\n// SDK-specific types for convenience\nexport interface WebhookEnvironment {\n\tid: string\n\tname: string\n\twebhookUrl: string | null\n\twebhookSecret?: string | null\n\thasSecret?: boolean\n\tevents?: string[]\n\tcreatedAt: string\n\tupdatedAt: string | null\n}\n\nexport interface WebhookConfig {\n\tenvironments: WebhookEnvironment[]\n\tsupportedEvents?: string[]\n\tenabled?: boolean\n\turl?: string | null\n\tsecret?: string | null\n\tevents?: string[]\n}\n\nexport interface WebhookConfigUpdate {\n\tenvironmentId: string\n\twebhookUrl: string | null\n}\n\nexport interface WebhookDeliveriesResult {\n\tdeliveries: WebhookDelivery[]\n\ttotal: number\n\thasMore: boolean\n}\n\nexport interface WebhookStats {\n\t// Summary totals\n\ttotal: number\n\tdelivered: number\n\tfailed: number\n\tpending: number\n\tdeliveryRate: number\n\tavgLatencyMs: number | null\n\t// Extended stats (for UI)\n\tperiod?: string\n\ttotals?: {\n\t\ttotal: number\n\t\tdelivered: number\n\t\tfailed: number\n\t\tpending: number\n\t\tdeliveryRate: number | string\n\t}\n\tbyEvent?: Array<{ event: string; count: number }>\n\tbyStatus?: Array<{ status: string; count: number }>\n}\n\nexport interface ListDeliveriesOptions {\n\tenvironmentId?: string\n\tstatus?: 'pending' | 'queued' | 'delivered' | 'failed'\n\tlimit?: number\n\toffset?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get webhook configuration for the app\n *\n * @example\n * ```typescript\n * const config = await getWebhookConfig(sylphxConfig)\n * console.log(config.environments)\n * ```\n */\nexport async function getWebhookConfig(config: SylphxConfig): Promise<WebhookConfig> {\n\treturn callApi(config, '/webhooks/config', { method: 'GET' })\n}\n\n/**\n * Update webhook URL for an environment\n *\n * @example\n * ```typescript\n * await updateWebhookConfig(config, {\n * environmentId: 'env-123',\n * webhookUrl: 'https://myapp.com/webhooks',\n * })\n * ```\n */\nexport async function updateWebhookConfig(\n\tconfig: SylphxConfig,\n\tdata: WebhookConfigUpdate,\n): Promise<void> {\n\treturn callApi(config, '/webhooks/config', { method: 'PUT', body: data })\n}\n\n/**\n * Get webhook delivery history\n *\n * @example\n * ```typescript\n * const { deliveries, total } = await getWebhookDeliveries(config, {\n * status: 'failed',\n * limit: 20,\n * })\n * ```\n */\nexport async function getWebhookDeliveries(\n\tconfig: SylphxConfig,\n\toptions?: ListDeliveriesOptions,\n): Promise<WebhookDeliveriesResult> {\n\treturn callApi(config, '/webhooks/deliveries', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Get a single webhook delivery by ID\n *\n * @example\n * ```typescript\n * const delivery = await getWebhookDelivery(config, 'del-123')\n * console.log(delivery.payload)\n * ```\n */\nexport async function getWebhookDelivery(\n\tconfig: SylphxConfig,\n\tdeliveryId: string,\n): Promise<WebhookDelivery> {\n\treturn callApi(config, `/webhooks/deliveries/${deliveryId}`, {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Replay a failed webhook delivery\n *\n * @example\n * ```typescript\n * await replayWebhookDelivery(config, 'del-123')\n * ```\n */\nexport async function replayWebhookDelivery(\n\tconfig: SylphxConfig,\n\tdeliveryId: string,\n): Promise<void> {\n\treturn callApi(config, `/webhooks/deliveries/${deliveryId}/replay`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Get webhook statistics\n *\n * @example\n * ```typescript\n * const stats = await getWebhookStats(config)\n * console.log(`Delivery rate: ${stats.deliveryRate}%`)\n * ```\n */\nexport async function getWebhookStats(\n\tconfig: SylphxConfig,\n\tenvironmentId?: string,\n): Promise<WebhookStats> {\n\treturn callApi(config, '/webhooks/stats', {\n\t\tmethod: 'GET',\n\t\tquery: environmentId ? { environmentId } : undefined,\n\t})\n}\n","/**\n * Email Functions\n *\n * Pure functions for transactional email operations.\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `/email/configured`,\n * `/email/send`, `/email/send-templated`, and `/email/send-to-user`. SDK-\n * specific convenience shapes (scheduling options, domain management) stay\n * local — their routes are served by the Console plane and live alongside\n * the BaaS `emailEndpoints` in the contract's `emailAdmin` namespace.\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tSendEmailInput as ContractSendEmailInput,\n\tSendEmailResult as ContractSendEmailResult,\n\tSendEmailToUserInput as ContractSendEmailToUserInput,\n\tSendTemplatedEmailInput as ContractSendTemplatedEmailInput,\n} from '@sylphx/contract'\nimport { emailEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type { ContractSendEmailInput as SendEmailRequest }\nexport type { ContractSendEmailResult as SendEmailResponse }\nexport type { ContractSendTemplatedEmailInput as SendTemplatedEmailRequest }\nexport type { ContractSendEmailResult as SendTemplatedEmailResponse }\nexport type { ContractSendEmailToUserInput as SendToUserRequest }\nexport type { ContractSendEmailResult as SendToUserResponse }\n\n// SDK-specific types for convenience\nexport interface SendEmailOptions {\n\t/** Recipient email address */\n\tto: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml: string\n\t/** Plain text content (optional fallback) */\n\ttext?: string\n\t/** Reply-to address */\n\treplyTo?: string\n\t/**\n\t * Sender email address (must be from a verified domain).\n\t * Falls back to the app environment default, then the platform FROM_EMAIL.\n\t *\n\t * @example `support@yourdomain.com`\n\t */\n\tfromEmail?: string\n\t/**\n\t * Sender display name (used together with fromEmail).\n\t *\n\t * @example `Acme Support`\n\t */\n\tfromName?: string\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * When provided, prevents duplicate email sends if the same request\n\t * is retried within 24 hours. Use a unique key per logical operation.\n\t *\n\t * @example `welcome-email-${userId}`\n\t */\n\tidempotencyKey?: string\n\t/**\n\t * Custom email headers passed directly to the provider.\n\t * Use for email threading (In-Reply-To, References) or other RFC 5322 headers.\n\t * Do not override From, To, or Subject here.\n\t *\n\t * @example `{ 'In-Reply-To': '<abc@cubeage.com>', References: '<abc@cubeage.com>' }`\n\t */\n\theaders?: Record<string, string>\n}\n\nexport interface SendTemplatedEmailOptions {\n\t/** Template name: 'welcome', 'verification', 'password_reset', 'security_alert' */\n\ttemplate: 'welcome' | 'verification' | 'password_reset' | 'security_alert'\n\t/** Recipient email address */\n\tto: string\n\t/** Template variables */\n\tdata?: Record<string, unknown>\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * @example `verification-email-${userId}`\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface SendToUserOptions {\n\t/** User ID to send to */\n\tuserId: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml: string\n\t/** Plain text content (optional fallback) */\n\ttext?: string\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * @example `notification-${userId}-${Date.now()}`\n\t */\n\tidempotencyKey?: string\n}\n\nexport interface ScheduleEmailOptions {\n\t/** Recipient email address */\n\tto: string\n\t/** Recipient name (optional) */\n\ttoName?: string\n\t/** Email subject line */\n\tsubject: string\n\t/** HTML content */\n\thtml?: string\n\t/** Plain text content */\n\ttext?: string\n\t/** Reply-to address */\n\treplyTo?: string\n\t/** From email (defaults to app's configured sender) */\n\tfromEmail?: string\n\t/** From name */\n\tfromName?: string\n\t/** ISO timestamp for when to send */\n\tscheduledFor: string\n\t/** Template key for templated emails */\n\ttemplateKey?: string\n\t/** Template variables */\n\ttemplateData?: Record<string, unknown>\n\t/** Idempotency key to prevent duplicates */\n\tidempotencyKey?: string\n\t/** Custom metadata */\n\tmetadata?: Record<string, unknown>\n}\n\nexport interface ScheduledEmail {\n\tid: string\n\tto: string\n\ttoName: string | null\n\tsubject: string\n\tstatus: 'pending' | 'queued' | 'sent' | 'cancelled' | 'failed'\n\tscheduledFor: string\n\tsentAt: string | null\n\tcreatedAt: string\n}\n\nexport interface ScheduledEmailsResult {\n\temails: ScheduledEmail[]\n\ttotal: number\n\thasMore: boolean\n}\n\nexport interface ScheduledEmailStats {\n\ttotal: number\n\tpending: number\n\tqueued: number\n\tsent: number\n\tcancelled: number\n\tfailed: number\n}\n\nexport interface ListScheduledEmailsOptions {\n\tstatus?: 'pending' | 'queued' | 'sent' | 'cancelled' | 'failed' | 'all'\n\tlimit?: number\n\toffset?: number\n}\n\nexport interface SendResult {\n\tid: string\n\tsuccess: boolean\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Check if email service is configured for the app\n *\n * @example\n * ```typescript\n * const configured = await isEmailConfigured(config)\n * if (!configured) console.log('Please configure email settings')\n * ```\n */\nexport async function isEmailConfigured(config: SylphxConfig): Promise<boolean> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = emailEndpoints.isConfigured\n\treturn callApi(config, endpoint.path, { method: endpoint.method })\n}\n\n/**\n * Send a custom email\n *\n * @example\n * ```typescript\n * const result = await sendEmail(config, {\n * to: 'user@example.com',\n * subject: 'Hello!',\n * html: '<p>Welcome to our app!</p>',\n * idempotencyKey: `welcome-${userId}`, // Safe retry\n * })\n * ```\n */\nexport async function sendEmail(\n\tconfig: SylphxConfig,\n\toptions: SendEmailOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// SDK's `SendEmailOptions` adds transport-level `idempotencyKey`; strip\n\t// it before sending the contract-owned request body.\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.send\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body as ContractSendEmailInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Send a templated email\n *\n * @example\n * ```typescript\n * await sendTemplatedEmail(config, {\n * template: 'welcome',\n * to: 'user@example.com',\n * data: { name: 'John' },\n * idempotencyKey: `welcome-${userId}`, // Safe retry\n * })\n * ```\n */\nexport async function sendTemplatedEmail(\n\tconfig: SylphxConfig,\n\toptions: SendTemplatedEmailOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.sendTemplated\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body satisfies ContractSendTemplatedEmailInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Send email to a user by their ID\n *\n * @example\n * ```typescript\n * await sendEmailToUser(config, {\n * userId: 'user-123',\n * subject: 'Account Update',\n * html: '<p>Your account has been updated.</p>',\n * idempotencyKey: `update-${userId}-${timestamp}`, // Safe retry\n * })\n * ```\n */\nexport async function sendEmailToUser(\n\tconfig: SylphxConfig,\n\toptions: SendToUserOptions,\n): Promise<SendResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst { idempotencyKey, ...body } = options\n\tconst endpoint = emailEndpoints.sendToUser\n\treturn callApi<ContractSendEmailResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: body satisfies ContractSendEmailToUserInput,\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Schedule an email for future delivery\n *\n * @example\n * ```typescript\n * const scheduled = await scheduleEmail(config, {\n * to: 'user@example.com',\n * subject: 'Reminder',\n * html: '<p>Don\\'t forget!</p>',\n * scheduledFor: new Date(Date.now() + 86400000).toISOString(), // 24 hours\n * })\n * ```\n */\nexport async function scheduleEmail(\n\tconfig: SylphxConfig,\n\toptions: ScheduleEmailOptions,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, '/email/schedule', { method: 'POST', body: options })\n}\n\n/**\n * List scheduled emails\n *\n * @example\n * ```typescript\n * const { emails, total } = await listScheduledEmails(config, {\n * status: 'pending',\n * limit: 20,\n * })\n * ```\n */\nexport async function listScheduledEmails(\n\tconfig: SylphxConfig,\n\toptions?: ListScheduledEmailsOptions,\n): Promise<ScheduledEmailsResult> {\n\treturn callApi(config, '/email/scheduled', {\n\t\tmethod: 'GET',\n\t\tquery: options as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Get a scheduled email by ID\n *\n * @example\n * ```typescript\n * const email = await getScheduledEmail(config, 'email-123')\n * console.log(email.status)\n * ```\n */\nexport async function getScheduledEmail(\n\tconfig: SylphxConfig,\n\temailId: string,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, `/email/scheduled/${emailId}`, { method: 'GET' })\n}\n\n/**\n * Cancel a scheduled email\n *\n * @example\n * ```typescript\n * await cancelScheduledEmail(config, 'email-123')\n * ```\n */\nexport async function cancelScheduledEmail(config: SylphxConfig, emailId: string): Promise<void> {\n\treturn callApi(config, `/email/scheduled/${emailId}/cancel`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Reschedule an email\n *\n * @example\n * ```typescript\n * await rescheduleEmail(config, 'email-123', new Date(Date.now() + 3600000).toISOString())\n * ```\n */\nexport async function rescheduleEmail(\n\tconfig: SylphxConfig,\n\temailId: string,\n\tscheduledFor: string,\n): Promise<ScheduledEmail> {\n\treturn callApi(config, `/email/scheduled/${emailId}/reschedule`, {\n\t\tmethod: 'POST',\n\t\tbody: { scheduledFor },\n\t})\n}\n\n/**\n * Get scheduled email statistics\n *\n * @example\n * ```typescript\n * const stats = await getScheduledEmailStats(config)\n * console.log(`${stats.pending} emails pending`)\n * ```\n */\nexport async function getScheduledEmailStats(config: SylphxConfig): Promise<ScheduledEmailStats> {\n\treturn callApi(config, '/email/scheduled/stats', { method: 'GET' })\n}\n\n// ============================================================================\n// Email Domain Management\n// ============================================================================\n\n/**\n * DNS record required for domain verification\n */\nexport interface DnsRecord {\n\t/** DNS record type */\n\ttype: 'MX' | 'TXT' | 'CNAME'\n\t/** DNS record name (hostname) */\n\tname: string\n\t/** DNS record value */\n\tvalue: string\n\t/** MX priority (only for MX records) */\n\tpriority?: number\n\t/** TTL in seconds */\n\tttl: number\n}\n\n/**\n * A registered custom sending domain\n */\nexport interface EmailDomain {\n\tid: string\n\tdomain: string\n\tstatus: 'pending' | 'verifying' | 'verified' | 'failed'\n\tdefaultFromEmail: string | null\n\tdefaultFromName: string | null\n\tdnsRecords: DnsRecord[]\n\tcreatedAt: string\n\tverifiedAt: string | null\n}\n\nexport interface RegisterEmailDomainOptions {\n\t/** Default from address for this domain (e.g. support@cubeage.com) */\n\tdefaultFromEmail?: string\n\t/** Default sender display name (e.g. Cubeage Support) */\n\tdefaultFromName?: string\n}\n\nexport interface SetDefaultEmailDomainOptions {\n\t/** Override the default from address */\n\tdefaultFromEmail?: string\n\t/** Override the default sender name */\n\tdefaultFromName?: string\n}\n\n/**\n * Register a custom sending domain\n *\n * Enables email sending for this domain. Returns DNS records to add to your DNS provider.\n * After adding DNS records, call verifyEmailDomain to confirm ownership.\n *\n * @example\n * ```typescript\n * const domain = await registerEmailDomain(config, 'cubeage.com', {\n * defaultFromEmail: 'support@cubeage.com',\n * defaultFromName: 'Cubeage Support',\n * })\n * console.log('Add these DNS records:', domain.dnsRecords)\n * ```\n */\nexport async function registerEmailDomain(\n\tconfig: SylphxConfig,\n\tdomain: string,\n\topts?: RegisterEmailDomainOptions,\n): Promise<EmailDomain> {\n\treturn callApi(config, '/email/domains', {\n\t\tmethod: 'POST',\n\t\tbody: { domain, ...opts },\n\t})\n}\n\n/**\n * List all custom sending domains for this app\n *\n * @example\n * ```typescript\n * const { domains } = await listEmailDomains(config)\n * for (const d of domains) {\n * console.log(d.domain, d.status)\n * }\n * ```\n */\nexport async function listEmailDomains(config: SylphxConfig): Promise<{ domains: EmailDomain[] }> {\n\treturn callApi(config, '/email/domains', { method: 'GET' })\n}\n\n/**\n * Get a specific domain by ID\n *\n * @example\n * ```typescript\n * const domain = await getEmailDomain(config, 'domain-uuid')\n * console.log(domain.dnsRecords)\n * ```\n */\nexport async function getEmailDomain(config: SylphxConfig, domainId: string): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}`, { method: 'GET' })\n}\n\n/**\n * Delete a custom sending domain\n *\n * Disables email sending for this domain and removes DKIM signing configuration.\n *\n * @example\n * ```typescript\n * await deleteEmailDomain(config, 'domain-uuid')\n * ```\n */\nexport async function deleteEmailDomain(config: SylphxConfig, domainId: string): Promise<void> {\n\treturn callApi(config, `/email/domains/${domainId}`, { method: 'DELETE' })\n}\n\n/**\n * Trigger DNS verification for a domain\n *\n * Checks if your DNS records have propagated. Status changes to 'verified' when DKIM is confirmed.\n * Status changes to 'verified' on success or 'failed' on error.\n *\n * @example\n * ```typescript\n * const domain = await verifyEmailDomain(config, 'domain-uuid')\n * if (domain.status === 'verified') {\n * console.log('Domain verified!')\n * }\n * ```\n */\nexport async function verifyEmailDomain(\n\tconfig: SylphxConfig,\n\tdomainId: string,\n): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}/verify`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n/**\n * Set a domain as the default sender for this app\n *\n * @example\n * ```typescript\n * const domain = await setDefaultEmailDomain(config, 'domain-uuid', {\n * defaultFromEmail: 'support@cubeage.com',\n * defaultFromName: 'Cubeage Support',\n * })\n * ```\n */\nexport async function setDefaultEmailDomain(\n\tconfig: SylphxConfig,\n\tdomainId: string,\n\topts?: SetDefaultEmailDomainOptions,\n): Promise<EmailDomain> {\n\treturn callApi(config, `/email/domains/${domainId}/set-default`, {\n\t\tmethod: 'POST',\n\t\tbody: opts ?? {},\n\t})\n}\n","/**\n * Consent Functions\n *\n * Pure functions for GDPR/CCPA consent management.\n *\n * ## Architecture (ADR-004)\n *\n * Consent uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when checking consent\n * - Platform auto-discovers/creates consent types when first referenced\n * - Console can override names, descriptions, requirements without deployment\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /consent/types`,\n * `GET /consent`, `POST /consent`, and friends. SDK-specific input types\n * (history pagination, anonymous linking) remain local — they describe\n * ergonomic helpers on top of the wire shapes rather than the wire\n * shapes themselves.\n *\n * @example\n * ```typescript\n * import { hasConsent, getUserConsents, setConsents } from '@sylphx/sdk'\n *\n * // Check consent with inline defaults (auto-discovered if doesn't exist)\n * if (await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })) {\n * track('pageview')\n * }\n *\n * // Get user's current consents\n * const consents = await getUserConsents(config, { userId: 'user-123' })\n *\n * // Set specific consents\n * await setConsents(config, {\n * userId: 'user-123',\n * consents: { analytics: true, marketing: false }\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tSdkConsentType as ContractConsentType,\n\tSetConsentsRequest as ContractSetConsentsRequest,\n\tSetConsentsResponse as ContractSetConsentsResponse,\n\tUserConsent as ContractUserConsent,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type ConsentType = ContractConsentType\nexport type UserConsent = ContractUserConsent\nexport type SetConsentRequest = ContractSetConsentsRequest\nexport type SetConsentResponse = ContractSetConsentsResponse\n\n// SDK-specific types (not directly from API schema)\n/** Consent category for grouping */\nexport type ConsentCategory = 'necessary' | 'analytics' | 'marketing' | 'functional' | 'preferences'\n\nexport interface SetConsentsInput {\n\t/** User ID (optional for anonymous users) */\n\tuserId?: string\n\t/** Anonymous ID (for guest users) */\n\tanonymousId?: string\n\t/** Consent settings by type slug */\n\tconsents: Record<string, boolean>\n}\n\nexport interface LinkAnonymousConsentsInput {\n\t/** The authenticated user ID to link to */\n\tuserId: string\n\t/** The anonymous ID whose consents should be linked */\n\tanonymousId: string\n}\n\nexport interface GetConsentHistoryInput {\n\t/** User ID (for authenticated users) */\n\tuserId?: string\n\t/** Anonymous ID (for anonymous users) */\n\tanonymousId?: string\n\t/** Maximum records to return (default: 50) */\n\tlimit?: number\n\t/**\n\t * Opaque pagination cursor returned by a previous response.\n\t * Omit (or pass undefined) to fetch the first page.\n\t */\n\tcursor?: string\n}\n\n/** A single consent change history entry */\nexport interface ConsentHistoryEntry {\n\t/** Unique entry ID */\n\tid: string\n\t/** Consent type slug (e.g., 'analytics') */\n\tconsentType: string\n\t/** Display name of the consent type */\n\tconsentTypeName: string\n\t/** Previous consent state (null = initial consent) */\n\tpreviousGranted: boolean | null\n\t/** New consent state */\n\tnewGranted: boolean\n\t/** Source of the change (banner, settings, api) */\n\tsource: string\n\t/** Reason for change (user_action, policy_update, etc.) */\n\treason: string | null\n\t/** ISO timestamp when change occurred */\n\tcreatedAt: string\n}\n\nexport interface ConsentHistoryResult {\n\t/** List of consent change entries */\n\tentries: ConsentHistoryEntry[]\n\t/**\n\t * Opaque cursor for the next page. Pass as `cursor` to the next call.\n\t * Null when this is the last page.\n\t */\n\tnextCursor: string | null\n\t/** Whether there are more entries */\n\thasMore: boolean\n}\n\nexport interface GetConsentsInput {\n\t/** User ID (optional for anonymous users) */\n\tuserId?: string\n\t/** Anonymous ID (for guest users) */\n\tanonymousId?: string\n}\n\n/**\n * Inline defaults for consent purpose auto-discovery\n *\n * @example\n * ```typescript\n * await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })\n * ```\n */\nexport interface ConsentPurposeDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Category */\n\tcategory?: ConsentCategory\n\t/** Whether consent is required (always granted) */\n\trequired?: boolean\n\t/** Whether enabled by default */\n\tdefaultEnabled?: boolean\n\t/** Sort order in UI */\n\tsortOrder?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get all consent types configured for the app\n *\n * Returns GDPR-standard defaults if none configured.\n *\n * @example\n * ```typescript\n * const types = await getConsentTypes(config)\n * types.forEach(t => console.log(`${t.name}: ${t.required ? 'Required' : 'Optional'}`))\n * ```\n */\nexport async function getConsentTypes(config: SylphxConfig): Promise<ConsentType[]> {\n\treturn callApi(config, '/consent/types', { method: 'GET' })\n}\n\n/**\n * Check if user has granted consent for a specific purpose\n *\n * If the consent type doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param purposeSlug - Consent purpose slug (e.g., 'analytics', 'marketing')\n * @param input - User identification (userId or anonymousId)\n * @param defaults - Optional inline defaults for auto-discovery\n * @returns Whether consent is granted\n *\n * @example\n * ```typescript\n * // Check analytics consent with inline defaults\n * if (await hasConsent(config, 'analytics', { userId: 'user-123' }, {\n * name: 'Analytics Cookies',\n * description: 'Help us understand how visitors use our site',\n * category: 'analytics',\n * required: false,\n * })) {\n * track('pageview')\n * }\n *\n * // Required consent always returns true\n * const hasNecessary = await hasConsent(config, 'necessary', { userId }, {\n * name: 'Essential Cookies',\n * description: 'Required for the website to function',\n * category: 'necessary',\n * required: true,\n * })\n * ```\n */\nexport async function hasConsent(\n\tconfig: SylphxConfig,\n\tpurposeSlug: string,\n\tinput: GetConsentsInput,\n\tdefaults?: ConsentPurposeDefaults,\n): Promise<boolean> {\n\treturn callApi(config, '/consent/check', {\n\t\tmethod: 'POST',\n\t\tbody: { purposeSlug, ...input, defaults },\n\t})\n}\n\n/**\n * Get user's current consent settings\n *\n * @example\n * ```typescript\n * // For authenticated user\n * const consents = await getUserConsents(config, { userId: 'user-123' })\n *\n * // For anonymous user\n * const consents = await getUserConsents(config, { anonymousId: 'anon-456' })\n * ```\n */\nexport async function getUserConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<UserConsent[]> {\n\treturn callApi(config, '/consent/user', {\n\t\tmethod: 'GET',\n\t\tquery: input as Record<string, string | undefined>,\n\t})\n}\n\n/**\n * Set user's consent preferences\n *\n * @example\n * ```typescript\n * await setConsents(config, {\n * userId: 'user-123',\n * consents: {\n * analytics: true,\n * marketing: false,\n * },\n * })\n * ```\n */\nexport async function setConsents(config: SylphxConfig, input: SetConsentsInput): Promise<void> {\n\treturn callApi(config, '/consent/set', { method: 'POST', body: input })\n}\n\n/**\n * Accept all consent types\n *\n * @example\n * ```typescript\n * await acceptAllConsents(config, { userId: 'user-123' })\n * ```\n */\nexport async function acceptAllConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/accept-all', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Decline all optional consent types (keeps required ones)\n *\n * @example\n * ```typescript\n * await declineOptionalConsents(config, { anonymousId: 'anon-456' })\n * ```\n */\nexport async function declineOptionalConsents(\n\tconfig: SylphxConfig,\n\tinput: GetConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/decline-optional', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Link anonymous user's consents to authenticated user\n *\n * Call this after user signs up/logs in to merge their anonymous consent history.\n *\n * @example\n * ```typescript\n * await linkAnonymousConsents(config, {\n * userId: 'user-123',\n * anonymousId: 'anon-456',\n * })\n * ```\n */\nexport async function linkAnonymousConsents(\n\tconfig: SylphxConfig,\n\tinput: LinkAnonymousConsentsInput,\n): Promise<void> {\n\treturn callApi(config, '/consent/link-anonymous', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Get consent change history for GDPR audit trail\n *\n * Returns a paginated list of all consent state changes for a user.\n * Required for GDPR compliance - provides complete audit trail of consent decisions.\n *\n * @example\n * ```typescript\n * // Get consent history for authenticated user (first page)\n * const history = await getConsentHistory(config, { userId: 'user-123', limit: 20 })\n * history.entries.forEach(entry => {\n * console.log(`${entry.consentType}: ${entry.previousGranted} → ${entry.newGranted}`)\n * })\n *\n * // Fetch next page using the cursor\n * if (history.nextCursor) {\n * const page2 = await getConsentHistory(config, {\n * userId: 'user-123',\n * limit: 20,\n * cursor: history.nextCursor,\n * })\n * }\n * ```\n */\nexport async function getConsentHistory(\n\tconfig: SylphxConfig,\n\tinput: GetConsentHistoryInput,\n): Promise<ConsentHistoryResult> {\n\treturn callApi(config, '/consent/history', {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tuserId: input.userId,\n\t\t\tanonymousId: input.anonymousId,\n\t\t\tlimit: input.limit?.toString(),\n\t\t\tcursor: input.cursor,\n\t\t},\n\t})\n}\n","/**\n * Referrals Functions\n *\n * Pure functions for referral code management and tracking.\n *\n * ## Architecture (ADR-004)\n *\n * Referrals uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when redeeming referral codes\n * - Platform uses defaults if no Console override exists\n * - Console can override reward values without deployment\n *\n * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The\n * contract is the single source of truth for `GET /referrals/code`,\n * `/referrals/code/regenerate`, `/referrals/stats`, `/referrals/redeem`,\n * `/referrals/leaderboard`.\n *\n * @example\n * ```typescript\n * import { redeemReferralCode } from '@sylphx/sdk'\n *\n * // Redeem with inline defaults (overridable in Console)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * }, {\n * referrerReward: { type: 'premium_trial', days: 7 },\n * refereeReward: { type: 'premium_trial', days: 7 },\n * })\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tReferralRewardConfig as ContractReferralRewardConfig,\n\tReferralRewardDefaults as ContractReferralRewardDefaults,\n\tRegenerateCodeResponse as ContractRegenerateCodeResponse,\n\tGetCodeResponse,\n\tGetStatsResponse,\n\tRedeemRequest,\n\tRedeemResponse,\n\tReferralLeaderboardEntry,\n\tReferralLeaderboardResponse,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type ReferralCodeResponse = GetCodeResponse\nexport type RegenerateCodeResponse = ContractRegenerateCodeResponse\nexport type ReferralStatsResponse = GetStatsResponse\nexport type RedeemReferralRequest = RedeemRequest\nexport type RedeemReferralResponse = RedeemResponse\nexport type ReferralRewardDefaults = ContractReferralRewardDefaults\nexport type ReferralRewardConfig = ContractReferralRewardConfig\nexport type LeaderboardResponse = ReferralLeaderboardResponse\nexport type LeaderboardEntry = ReferralLeaderboardEntry\n\n// SDK-specific types for convenience\nexport interface ReferralCode {\n\tcode: string\n\tcreatedAt: string\n}\n\nexport interface ReferralStats {\n\t/** User's referral code */\n\tcode: string\n\t/** Total referrals made */\n\ttotalReferrals: number\n\t/** Successful (redeemed) referrals */\n\tsuccessfulReferrals: number\n\t/** Pending referrals */\n\tpendingReferrals: number\n\t/** Total rewards earned */\n\ttotalRewards: number\n}\n\ntype LeaderboardPeriod = 'all' | 'month' | 'week'\n\nexport interface LeaderboardResult {\n\t/** Time period for the leaderboard */\n\tperiod?: LeaderboardPeriod\n\tentries: LeaderboardEntry[]\n\t/** Current user's position (may not be in top entries) */\n\tcurrentUserRank: number | null\n\t/** Total participants */\n\ttotalParticipants: number\n}\n\nexport interface RedeemReferralInput {\n\t/** Referral code to redeem */\n\tcode: string\n\t/** User ID of the person redeeming (optional for anonymous) */\n\tuserId?: string\n}\n\nexport interface RedeemResult {\n\tsuccess: boolean\n\t/** Reward type - platform types or app-specific types */\n\trewardType: 'points' | 'premium_trial' | 'discount' | 'credit' | (string & {})\n\treferredReward?: Record<string, unknown>\n\treferrerReward?: Record<string, unknown>\n}\n\nexport interface LeaderboardOptions {\n\t/** Number of entries to return (default: 10) */\n\tlimit?: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get current user's referral code\n *\n * Creates one if it doesn't exist.\n *\n * @example\n * ```typescript\n * const { code } = await getMyReferralCode(config, 'user-123')\n * console.log(`Share your code: ${code}`)\n * ```\n */\nexport async function getMyReferralCode(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralCode> {\n\treturn callApi(config, '/referrals/code', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Get referral statistics for a user\n *\n * @example\n * ```typescript\n * const stats = await getReferralStats(config, 'user-123')\n * console.log(`${stats.successfulReferrals} successful referrals`)\n * ```\n */\nexport async function getReferralStats(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralStats> {\n\treturn callApi(config, '/referrals/stats', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Redeem a referral code\n *\n * If the referral program rewards aren't configured in Console, the provided\n * defaults will be used. Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Referral redemption input (code, userId)\n * @param defaults - Optional inline defaults for reward configuration\n *\n * @example\n * ```typescript\n * // Basic redemption (uses Console-configured rewards)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * })\n *\n * // With inline defaults (auto-discovered if not in Console)\n * const result = await redeemReferralCode(config, {\n * code: 'ABC123',\n * userId: 'new-user-456',\n * }, {\n * referrerReward: { type: 'premium_trial', days: 7 },\n * refereeReward: { type: 'premium_trial', days: 7 },\n * })\n *\n * if (result.success) {\n * console.log(`Reward: ${result.reward?.type}`)\n * }\n * ```\n */\nexport async function redeemReferralCode(\n\tconfig: SylphxConfig,\n\tinput: RedeemReferralInput,\n\tdefaults?: ReferralRewardDefaults,\n): Promise<RedeemResult> {\n\treturn callApi(config, '/referrals/redeem', {\n\t\tmethod: 'POST',\n\t\tbody: { ...input, defaults },\n\t})\n}\n\n/**\n * Get referral leaderboard\n *\n * @example\n * ```typescript\n * const { entries, currentUserRank } = await getReferralLeaderboard(config, 'user-123')\n *\n * entries.forEach(e => {\n * console.log(`#${e.rank} ${e.displayName}: ${e.completedReferrals} referrals`)\n * })\n * ```\n */\nexport async function getReferralLeaderboard(\n\tconfig: SylphxConfig,\n\tuserId: string,\n\toptions?: LeaderboardOptions,\n): Promise<LeaderboardResult> {\n\treturn callApi(config, '/referrals/leaderboard', {\n\t\tmethod: 'GET',\n\t\tquery: { userId, ...options } as Record<string, string | number | undefined>,\n\t})\n}\n\n/**\n * Regenerate user's referral code\n *\n * Use this if the current code has been compromised or user wants a fresh start.\n *\n * @example\n * ```typescript\n * const { code } = await regenerateReferralCode(config, 'user-123')\n * console.log(`New code: ${code}`)\n * ```\n */\nexport async function regenerateReferralCode(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<ReferralCode> {\n\treturn callApi(config, '/referrals/code/regenerate', {\n\t\tmethod: 'POST',\n\t\tbody: { userId },\n\t})\n}\n","/**\n * Engagement Service Types\n *\n * Core types for streaks, leaderboards, and achievements.\n *\n * ## Architecture (ADR-004)\n *\n * Engagement uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when calling APIs\n * - Platform auto-discovers/creates entities when first referenced\n * - Console can override names, descriptions, values without deployment\n */\n\n// ============================================================================\n// Streaks\n// ============================================================================\n\n/** Streak activity frequency */\nexport type StreakFrequency = 'daily' | 'weekly' | 'custom'\n\n/** Streak definition (auto-discovered or from Console) */\nexport interface StreakDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description */\n\tdescription?: string\n\t/** Activity frequency */\n\tfrequency: StreakFrequency\n\t/** Grace period in hours (default: 0) */\n\tgracePeriodHours?: number\n\t/** Whether streak resets on miss (default: true) */\n\tresetOnMiss?: boolean\n\t/** Maximum streak value (optional cap) */\n\tmaxValue?: number\n\t/** Custom interval in hours (only for 'custom' frequency) */\n\tcustomIntervalHours?: number\n}\n\n/** User's streak state (from platform) */\nexport interface StreakState {\n\t/** Streak definition ID */\n\tstreakId: string\n\t/** Current streak count */\n\tcurrent: number\n\t/** Longest streak ever */\n\tlongest: number\n\t/** Last activity timestamp */\n\tlastActivityAt: string | null\n\t/** When current streak will expire */\n\texpiresAt: string | null\n\t/** Whether streak can be recovered (within grace period) */\n\tcanRecover: boolean\n\t/** Time remaining until expiry in ms */\n\ttimeRemainingMs: number | null\n\t/** User's timezone preference for streak expiry (IANA timezone, e.g., 'America/New_York') */\n\tuserTimezone: string | null\n}\n\n/** Activity recording input */\nexport interface RecordActivityInput {\n\t/** Streak ID */\n\tstreakId: string\n\t/** User's IANA timezone (e.g., 'America/New_York') for calculating streak expiry at user's local midnight */\n\tuserTimezone?: string\n\t/** Optional metadata */\n\tmetadata?: Record<string, unknown>\n\t/**\n\t * Idempotency key for safe retries (Stripe pattern)\n\t *\n\t * Prevents duplicate streak recordings if the same request is retried.\n\t * Use a unique key per logical activity (e.g., `streak-${userId}-${date}`).\n\t */\n\tidempotencyKey?: string\n}\n\n/** Activity recording result */\nexport interface RecordActivityResult {\n\t/** Updated streak state */\n\tstreak: StreakState\n\t/** Whether this activity extended the streak */\n\textended: boolean\n\t/** Whether a new personal best was achieved */\n\tnewPersonalBest: boolean\n\t/** Previous streak value (for animation) */\n\tpreviousValue: number\n}\n\n// ============================================================================\n// Leaderboards\n// ============================================================================\n\n/** Leaderboard sort direction */\nexport type LeaderboardSortDirection = 'asc' | 'desc'\n\n/** Leaderboard reset period */\nexport type LeaderboardResetPeriod = 'hourly' | 'daily' | 'weekly' | 'monthly' | 'never'\n\n/** Score aggregation method */\nexport type LeaderboardAggregation = 'max' | 'sum' | 'latest' | 'count' | 'min' | 'avg'\n\n/** Leaderboard definition (auto-discovered or from Console) */\nexport interface LeaderboardDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description */\n\tdescription?: string\n\t/** Sort direction (desc = higher is better) */\n\tsortDirection: LeaderboardSortDirection\n\t/** Reset period */\n\tresetPeriod: LeaderboardResetPeriod\n\t/** How to aggregate multiple scores from same user */\n\taggregation: LeaderboardAggregation\n\t/** Default privacy for entries */\n\tdefaultPrivacy?: 'public' | 'friends' | 'anonymous'\n\t/** Maximum entries to keep per period */\n\tmaxEntries?: number\n}\n\n/** Leaderboard entry */\nexport interface LeaderboardEntry {\n\t/** Rank (1-indexed) */\n\trank: number\n\t/** User ID (may be null for anonymous) */\n\tuserId: string | null\n\t/** Display name */\n\tdisplayName: string\n\t/** Avatar URL */\n\tavatarUrl: string | null\n\t/** Score/value */\n\tvalue: number\n\t/** Whether this is the current user */\n\tisCurrentUser: boolean\n\t/** Entry metadata */\n\tmetadata?: Record<string, unknown>\n\t/** When the score was submitted */\n\tsubmittedAt: string\n}\n\n/** Leaderboard query options */\nexport interface LeaderboardQueryOptions {\n\t/** Number of entries to return (default: 10) */\n\tlimit?: number\n\t/** Offset for pagination */\n\toffset?: number\n\t/** Include surrounding entries for current user */\n\tincludeSurrounding?: boolean\n\t/** Number of surrounding entries (default: 2) */\n\tsurroundingCount?: number\n}\n\n/** Leaderboard query result */\nexport interface LeaderboardResult {\n\t/** Leaderboard definition ID */\n\tleaderboardId: string\n\t/** Period (for periodic leaderboards) */\n\tperiod?: string\n\t/** Entries (top N or paginated) */\n\tentries: LeaderboardEntry[]\n\t/** Current user's entry (may not be in top entries) */\n\tcurrentUserEntry: LeaderboardEntry | null\n\t/** Entries surrounding the current user (when includeSurrounding=true and user is outside main entries) */\n\tsurroundingEntries?: LeaderboardEntry[]\n\t/** Total participants */\n\ttotalParticipants: number\n\t/** Next reset time (for periodic leaderboards) */\n\tnextResetAt: string | null\n}\n\n/** Score submission input */\nexport interface SubmitScoreInput {\n\t/** Leaderboard ID */\n\tleaderboardId: string\n\t/** Score value */\n\tvalue: number\n\t/** Optional metadata */\n\tmetadata?: Record<string, unknown>\n}\n\n/** Score submission result */\nexport interface SubmitScoreResult {\n\t/** Whether submission was accepted */\n\taccepted: boolean\n\t/** New rank (if in leaderboard) */\n\trank: number | null\n\t/** Previous best (if any) */\n\tpreviousBest: number | null\n\t/** Whether this is a new personal best */\n\tnewPersonalBest: boolean\n\t/** Rank change (positive = improved) */\n\trankChange: number | null\n}\n\n// ============================================================================\n// Achievements\n// ============================================================================\n\n/** Achievement type */\nexport type AchievementType = 'standard' | 'hidden' | 'incremental'\n\n/** Achievement tier */\nexport type AchievementTier = 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond'\n\n/** Achievement category */\nexport type AchievementCategory = string // App-defined\n\n/** Achievement criteria operator */\nexport type CriteriaOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains'\n\n/** Single criterion */\nexport interface AchievementCriterion {\n\t/** Property to check (e.g., 'event', 'count', 'streak.daily') */\n\tproperty: string\n\t/** Comparison operator */\n\toperator: CriteriaOperator\n\t/** Value to compare against */\n\tvalue: string | number | boolean | string[] | number[]\n}\n\n/** Achievement criteria (AND logic within, OR between arrays) */\nexport interface AchievementCriteria {\n\t/** Event name to track (for event-based achievements) */\n\tevent?: string\n\t/** Required count of events */\n\tcount?: number\n\t/** Additional conditions */\n\tconditions?: AchievementCriterion[]\n}\n\n/** Achievement definition (auto-discovered or from Console) */\nexport interface AchievementDefinition {\n\t/** Unique identifier */\n\tid: string\n\t/** Display name */\n\tname: string\n\t/** Description (shown before unlock) */\n\tdescription: string\n\t/** Description shown after unlock (optional) */\n\tunlockedDescription?: string\n\t/** Achievement type */\n\ttype: AchievementType\n\t/** Tier/rarity */\n\ttier: AchievementTier\n\t/** Category (app-defined) */\n\tcategory: AchievementCategory\n\t/** Icon (Iconify name or URL) */\n\ticon: string\n\t/** Points awarded */\n\tpoints?: number\n\t/** Unlock criteria */\n\tcriteria: AchievementCriteria\n\t/** Target value for incremental achievements */\n\ttarget?: number\n\t/** Whether to show in list before unlock */\n\tsecret?: boolean\n\t/** Order in list */\n\torder?: number\n}\n\n/** User's achievement state */\nexport interface UserAchievement {\n\t/** Achievement definition ID */\n\tachievementId: string\n\t/** Whether unlocked */\n\tunlocked: boolean\n\t/** Unlock timestamp */\n\tunlockedAt: string | null\n\t/** Progress (for incremental) */\n\tprogress: number\n\t/** Target (for incremental) */\n\ttarget: number | null\n\t/** Progress percentage (0-100) */\n\tprogressPercent: number\n}\n\n/** Achievement unlock event */\nexport interface AchievementUnlockEvent {\n\t/** Achievement definition */\n\tachievement: AchievementDefinition\n\t/** User achievement state */\n\tuserAchievement: UserAchievement\n\t/** Whether this is a new unlock (vs already unlocked) */\n\tisNew: boolean\n}\n\n// ============================================================================\n// Engagement Config (from Platform)\n// ============================================================================\n\n/** Complete engagement configuration (fetched from platform) */\nexport interface EngagementConfig {\n\t/** Streak definitions */\n\tstreaks?: StreakDefinition[]\n\t/** Leaderboard definitions */\n\tleaderboards?: LeaderboardDefinition[]\n\t/** Achievement definitions */\n\tachievements?: AchievementDefinition[]\n\t/** Achievement categories (for UI grouping) */\n\tachievementCategories?: {\n\t\tid: string\n\t\tname: string\n\t\ticon?: string\n\t\torder?: number\n\t}[]\n}\n\n// ============================================================================\n// Tier Metadata (for UI)\n// ============================================================================\n\nexport const ACHIEVEMENT_TIER_CONFIG = {\n\tbronze: { color: '#CD7F32', points: 10 },\n\tsilver: { color: '#C0C0C0', points: 25 },\n\tgold: { color: '#FFD700', points: 50 },\n\tplatinum: { color: '#00CED1', points: 100 },\n\tdiamond: { color: '#B9F2FF', points: 200 },\n} as const\n\n// ============================================================================\n// Inline Defaults (Auto-Discovery)\n// ============================================================================\n// These types define the optional inline defaults that can be passed when\n// calling engagement functions. If the entity doesn't exist, the platform\n// will auto-create it with these defaults. Console can override any values.\n\n/**\n * Inline defaults for streak auto-discovery\n *\n * @example\n * ```typescript\n * await recordStreakActivity(config, { streakId: 'daily-login' }, userId, {\n * name: 'Daily Login',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n * ```\n */\nexport interface StreakDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Activity frequency */\n\tfrequency?: StreakFrequency\n\t/** Grace period in hours (default: 0) */\n\tgracePeriodHours?: number\n\t/** Whether streak resets on miss (default: true) */\n\tresetOnMiss?: boolean\n\t/** Maximum streak value (optional cap) */\n\tmaxValue?: number\n\t/** Custom interval in hours (only for 'custom' frequency) */\n\tcustomIntervalHours?: number\n}\n\n/**\n * Inline defaults for leaderboard auto-discovery\n *\n * @example\n * ```typescript\n * await submitScore(config, { leaderboardId: 'high-scores', value: 1500 }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * })\n * ```\n */\nexport interface LeaderboardDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description */\n\tdescription?: string\n\t/** Sort direction (desc = higher is better) */\n\tsortDirection?: LeaderboardSortDirection\n\t/** Reset period */\n\tresetPeriod?: LeaderboardResetPeriod\n\t/** How to aggregate multiple scores from same user */\n\taggregation?: LeaderboardAggregation\n\t/** Maximum entries to keep per period */\n\tmaxEntries?: number\n}\n\n/**\n * Inline defaults for achievement auto-discovery\n *\n * @example\n * ```typescript\n * await unlockAchievement(config, 'first-purchase', userId, {\n * name: 'First Purchase',\n * description: 'Made your first purchase',\n * points: 100,\n * tier: 'bronze',\n * })\n * ```\n */\nexport interface AchievementDefaults {\n\t/** Display name */\n\tname?: string\n\t/** Description (shown before unlock) */\n\tdescription?: string\n\t/** Description shown after unlock */\n\tunlockedDescription?: string\n\t/** Achievement type */\n\ttype?: AchievementType\n\t/** Tier/rarity */\n\ttier?: AchievementTier\n\t/** Category (app-defined) */\n\tcategory?: AchievementCategory\n\t/** Icon (Iconify name or URL) */\n\ticon?: string\n\t/** Points awarded */\n\tpoints?: number\n\t/** Target value for incremental achievements */\n\ttarget?: number\n\t/** Whether to show in list before unlock */\n\tsecret?: boolean\n}\n","/**\n * Engagement Functions\n *\n * Pure functions for streaks, leaderboards, and achievements.\n *\n * ## Architecture (ADR-004)\n *\n * Engagement uses **Inline Defaults + Auto-Discovery + Console Override**:\n * - Code provides optional inline defaults when calling functions\n * - Platform auto-discovers/creates entities when first referenced\n * - Console can override names, descriptions, values without deployment\n *\n * @example\n * ```typescript\n * import { unlockAchievement, recordStreakActivity, submitScore } from '@sylphx/sdk'\n *\n * // Unlock achievement with inline defaults (auto-discovered if doesn't exist)\n * await unlockAchievement(config, 'first-win', userId, {\n * name: 'First Win',\n * description: 'Won your first game',\n * points: 100,\n * tier: 'bronze',\n * })\n *\n * // Record streak activity with inline defaults\n * await recordStreakActivity(config, { streakId: 'daily-login' }, userId, {\n * name: 'Daily Login',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n *\n * // Submit leaderboard score with inline defaults\n * await submitScore(config, { leaderboardId: 'high-scores', value: 1500 }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * })\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// Re-export types from types file\nexport type {\n\tAchievementCategory,\n\tAchievementCriteria,\n\tAchievementCriterion,\n\t// Achievements\n\tAchievementDefinition,\n\tAchievementTier,\n\tAchievementType,\n\tAchievementUnlockEvent,\n\tCriteriaOperator,\n\tLeaderboardAggregation,\n\t// Leaderboards\n\tLeaderboardDefinition,\n\tLeaderboardEntry,\n\tLeaderboardQueryOptions,\n\tLeaderboardResetPeriod,\n\tLeaderboardResult,\n\tLeaderboardSortDirection,\n\tRecordActivityInput,\n\tRecordActivityResult,\n\t// Streaks\n\tStreakDefinition,\n\tStreakFrequency,\n\tStreakState,\n\tSubmitScoreInput,\n\tSubmitScoreResult,\n\tUserAchievement,\n} from './lib/engagement/types'\n\nexport { ACHIEVEMENT_TIER_CONFIG } from './lib/engagement/types'\n\n// ============================================================================\n// Streak Functions\n// ============================================================================\n\nimport type {\n\tRecordActivityInput,\n\tRecordActivityResult,\n\tStreakDefaults,\n\tStreakState,\n} from './lib/engagement/types'\n\n/**\n * Get current streak state for a user\n *\n * @example\n * ```typescript\n * const streak = await getStreak(config, 'daily-challenge', userId)\n * console.log(`Current streak: ${streak.current}`)\n * console.log(`Expires in: ${streak.timeRemainingMs}ms`)\n * ```\n */\nexport async function getStreak(\n\tconfig: SylphxConfig,\n\tstreakId: string,\n\tuserId: string,\n): Promise<StreakState> {\n\treturn callApi(config, '/engagement/streaks/get', {\n\t\tmethod: 'GET',\n\t\tquery: { streakId, userId },\n\t})\n}\n\n/**\n * Get all streak states for a user\n *\n * @example\n * ```typescript\n * const streaks = await getAllStreaks(config, userId)\n * for (const streak of streaks) {\n * console.log(`${streak.streakId}: ${streak.current}`)\n * }\n * ```\n */\nexport async function getAllStreaks(config: SylphxConfig, userId: string): Promise<StreakState[]> {\n\treturn callApi(config, '/engagement/streaks', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Record an activity to extend/maintain a streak\n *\n * If the streak doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Activity input (streakId required)\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await recordStreakActivity(config, {\n * streakId: 'daily-challenge',\n * }, userId, {\n * name: 'Daily Challenge',\n * frequency: 'daily',\n * gracePeriodHours: 12,\n * })\n *\n * if (result.extended) {\n * console.log(`Streak extended to ${result.streak.current}!`)\n * }\n * if (result.newPersonalBest) {\n * console.log('New personal best!')\n * }\n * ```\n */\nexport async function recordStreakActivity(\n\tconfig: SylphxConfig,\n\tinput: RecordActivityInput,\n\tuserId: string,\n\tdefaults?: StreakDefaults,\n): Promise<RecordActivityResult> {\n\tconst { idempotencyKey, ...inputBody } = input\n\treturn callApi(config, '/engagement/streaks/record', {\n\t\tmethod: 'POST',\n\t\tbody: { ...inputBody, userId, defaults },\n\t\tidempotencyKey,\n\t})\n}\n\n/**\n * Recover a streak within grace period (may require payment/reward)\n *\n * @example\n * ```typescript\n * const result = await recoverStreak(config, 'daily-challenge', userId)\n * if (result.success) {\n * console.log(`Streak recovered at ${result.streak.current}`)\n * }\n * ```\n */\nexport async function recoverStreak(\n\tconfig: SylphxConfig,\n\tstreakId: string,\n\tuserId: string,\n): Promise<{ success: boolean; streak: StreakState }> {\n\treturn callApi(config, '/engagement/streaks/recover', {\n\t\tmethod: 'POST',\n\t\tbody: { streakId, userId },\n\t})\n}\n\n// ============================================================================\n// Leaderboard Functions\n// ============================================================================\n\nimport type {\n\tLeaderboardDefaults,\n\tLeaderboardQueryOptions,\n\tLeaderboardResult,\n\tSubmitScoreInput,\n\tSubmitScoreResult,\n} from './lib/engagement/types'\n\n/**\n * Get leaderboard entries\n *\n * @example\n * ```typescript\n * const result = await getLeaderboard(config, 'high-scores', userId, {\n * limit: 10,\n * includeSurrounding: true,\n * })\n *\n * for (const entry of result.entries) {\n * console.log(`#${entry.rank} ${entry.displayName}: ${entry.value}`)\n * }\n *\n * if (result.currentUserEntry) {\n * console.log(`Your rank: #${result.currentUserEntry.rank}`)\n * }\n * ```\n */\nexport async function getLeaderboard(\n\tconfig: SylphxConfig,\n\tleaderboardId: string,\n\tuserId: string | null,\n\toptions?: LeaderboardQueryOptions,\n): Promise<LeaderboardResult> {\n\treturn callApi(config, '/engagement/leaderboards/get', {\n\t\tmethod: 'GET',\n\t\tquery: { leaderboardId, userId: userId ?? undefined, ...options } as Record<\n\t\t\tstring,\n\t\t\tstring | number | boolean | undefined\n\t\t>,\n\t})\n}\n\n/**\n * Submit a score to a leaderboard\n *\n * If the leaderboard doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param input - Score submission input (leaderboardId, value required)\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await submitScore(config, {\n * leaderboardId: 'high-scores',\n * value: 1500,\n * metadata: { level: 'hard' },\n * }, userId, {\n * name: 'High Scores',\n * sortDirection: 'desc',\n * resetPeriod: 'weekly',\n * aggregation: 'max',\n * })\n *\n * if (result.newPersonalBest) {\n * console.log('New personal best!')\n * }\n * if (result.rank !== null) {\n * console.log(`Ranked #${result.rank}`)\n * }\n * ```\n */\nexport async function submitScore(\n\tconfig: SylphxConfig,\n\tinput: SubmitScoreInput,\n\tuserId: string,\n\tdefaults?: LeaderboardDefaults,\n): Promise<SubmitScoreResult> {\n\treturn callApi(config, '/engagement/leaderboards/submit', {\n\t\tmethod: 'POST',\n\t\tbody: { ...input, userId, defaults },\n\t})\n}\n\n/**\n * Get user's rank on a leaderboard (even if not in top entries)\n *\n * @example\n * ```typescript\n * const rank = await getUserRank(config, 'high-scores', userId)\n * if (rank) {\n * console.log(`You are ranked #${rank.rank} with score ${rank.value}`)\n * }\n * ```\n */\nexport async function getUserLeaderboardRank(\n\tconfig: SylphxConfig,\n\tleaderboardId: string,\n\tuserId: string,\n): Promise<{ rank: number; value: number } | null> {\n\treturn callApi(config, '/engagement/leaderboards/rank', {\n\t\tmethod: 'GET',\n\t\tquery: { leaderboardId, userId },\n\t})\n}\n\n// ============================================================================\n// Achievement Functions\n// ============================================================================\n\nimport type {\n\tAchievementDefaults,\n\tAchievementUnlockEvent,\n\tUserAchievement,\n} from './lib/engagement/types'\n\n/**\n * Get all achievements with user progress\n *\n * @example\n * ```typescript\n * const achievements = await getAchievements(config, userId)\n *\n * const unlocked = achievements.filter(a => a.unlocked)\n * console.log(`${unlocked.length} achievements unlocked`)\n *\n * const inProgress = achievements.filter(a => !a.unlocked && a.progress > 0)\n * for (const a of inProgress) {\n * console.log(`${a.achievementId}: ${a.progress}/${a.target}`)\n * }\n * ```\n */\nexport async function getAchievements(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<UserAchievement[]> {\n\treturn callApi(config, '/engagement/achievements', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n\n/**\n * Get a single achievement with user progress\n *\n * @example\n * ```typescript\n * const achievement = await getAchievement(config, 'first-win', userId)\n * if (achievement?.unlocked) {\n * console.log(`Unlocked at ${achievement.unlockedAt}`)\n * }\n * ```\n */\nexport async function getAchievement(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tuserId: string,\n): Promise<UserAchievement | null> {\n\treturn callApi(config, '/engagement/achievements/get', {\n\t\tmethod: 'GET',\n\t\tquery: { achievementId, userId },\n\t})\n}\n\n/**\n * Manually unlock an achievement\n *\n * If the achievement doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param achievementId - Achievement ID\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * const result = await unlockAchievement(config, 'first-purchase', userId, {\n * name: 'First Purchase',\n * description: 'Made your first purchase',\n * points: 100,\n * tier: 'bronze',\n * })\n * if (result.isNew) {\n * showAchievementToast(result.achievement)\n * }\n * ```\n */\nexport async function unlockAchievement(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tuserId: string,\n\tdefaults?: AchievementDefaults,\n): Promise<AchievementUnlockEvent> {\n\treturn callApi(config, '/engagement/achievements/unlock', {\n\t\tmethod: 'POST',\n\t\tbody: { achievementId, userId, defaults },\n\t})\n}\n\n/**\n * Increment progress on an incremental achievement\n *\n * If the achievement doesn't exist, it will be auto-discovered with the provided defaults.\n * Console can override any values without deployment.\n *\n * @param config - SDK configuration\n * @param achievementId - Achievement ID\n * @param amount - Amount to increment\n * @param userId - User ID\n * @param defaults - Optional inline defaults for auto-discovery\n *\n * @example\n * ```typescript\n * // User collected an item\n * const result = await incrementAchievementProgress(config, 'collector-100', 1, userId, {\n * name: 'Collector',\n * description: 'Collect 100 items',\n * type: 'incremental',\n * target: 100,\n * tier: 'silver',\n * })\n *\n * if (result.unlocked) {\n * console.log('Achievement unlocked!')\n * } else {\n * console.log(`Progress: ${result.progress}/${result.target}`)\n * }\n * ```\n */\nexport async function incrementAchievementProgress(\n\tconfig: SylphxConfig,\n\tachievementId: string,\n\tamount: number,\n\tuserId: string,\n\tdefaults?: AchievementDefaults,\n): Promise<UserAchievement> {\n\treturn callApi(config, '/engagement/achievements/progress', {\n\t\tmethod: 'POST',\n\t\tbody: { achievementId, amount, userId, defaults },\n\t})\n}\n\n/**\n * Get total achievement points for a user\n *\n * @example\n * ```typescript\n * const points = await getAchievementPoints(config, userId)\n * console.log(`Total points: ${points.total}`)\n * console.log(`This month: ${points.thisMonth}`)\n * ```\n */\nexport async function getAchievementPoints(\n\tconfig: SylphxConfig,\n\tuserId: string,\n): Promise<{ total: number; thisMonth: number; rank: number | null }> {\n\treturn callApi(config, '/engagement/achievements/points', {\n\t\tmethod: 'GET',\n\t\tquery: { userId },\n\t})\n}\n","/**\n * Organization Functions\n *\n * Pure functions for organization management - no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /api/sdk/orgs/* for all operations.\n *\n * Types are re-exported from `@sylphx/contract` (ADR-084). The contract is\n * the single source of truth for every wire shape — this module only adds\n * ergonomic aliases (`Organization*` wrappers) and inline envelopes for\n * request bodies + member role updates.\n */\n\n// Contract is the SSOT for both endpoint paths and wire types.\nimport {\n\ttype CreateOrgInput as ContractCreateOrgInput,\n\ttype InviteMemberInput as ContractInviteMemberInput,\n\ttype UpdateOrgInput as ContractUpdateOrgInput,\n\ttype MembershipInfo,\n\ttype Organization,\n\ttype OrgInvitation,\n\ttype OrgMember,\n\ttype OrgSdkRole,\n\torganizationsEndpoints,\n\ttype UserOrganizationMembership,\n\ttype UserOrganizationsResponse,\n} from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types (re-exported from @sylphx/contract)\n// ============================================================================\n\nexport type { Organization }\n/**\n * The contract types `OrgMember.role` as a permissive `string` so it can\n * absorb any role stored in the DB (including legacy `'member'`). The SDK\n * surface is narrower — components key off the six-value role union — so\n * re-narrow here to keep `member.role` valid in existing call sites.\n */\nexport type OrganizationMember = Omit<OrgMember, 'role'> & {\n\trole: OrgSdkRole\n}\nexport type OrganizationInvitation = OrgInvitation\n/**\n * Membership info returned inline with `GET /orgs/:idOrSlug`. The endpoint\n * may return `null` when the caller has no role on the org (platform-admin\n * path) — SDK callers get the union on the response envelope, not here.\n */\nexport type OrganizationMembership = MembershipInfo\nexport type UserOrganization = UserOrganizationMembership\nexport type OrganizationsListResult = {\n\torganizations: Organization[]\n\ttotal: number\n\tlimit: number\n\toffset: number\n}\nexport type OrgRole = OrgSdkRole\nexport type CreateOrgInput = ContractCreateOrgInput\nexport type UpdateOrgInput = ContractUpdateOrgInput & {\n\tmetadata?: Record<string, unknown> | null\n}\nexport type InviteMemberInput = ContractInviteMemberInput\n\n// ============================================================================\n// Organization CRUD\n// ============================================================================\n\n/**\n * Get all organizations the current user belongs to\n *\n * @example\n * ```typescript\n * const { organizations } = await getOrganizations(config)\n * ```\n */\nexport async function getOrganizations(config: SylphxConfig): Promise<UserOrganizationsResponse> {\n\tconst endpoint = organizationsEndpoints.memberships\n\treturn callApi<UserOrganizationsResponse>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Admin-only organization list.\n *\n * `getOrganizations()` is intentionally user-scoped. Use this function only\n * from Platform operator contexts that are allowed to enumerate every org.\n *\n * @example\n * ```typescript\n * const { organizations, total } = await listOrganizations(adminConfig)\n * ```\n */\nexport async function listOrganizations(\n\tconfig: SylphxConfig,\n\toptions: { limit?: number; offset?: number } = {},\n): Promise<OrganizationsListResult> {\n\tconst endpoint = organizationsEndpoints.list\n\treturn callApi<OrganizationsListResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: options,\n\t})\n}\n\n/**\n * Get organization by ID or slug\n *\n * @example\n * ```typescript\n * const { organization, membership } = await getOrganization(config, 'my-org')\n * ```\n */\nexport async function getOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{\n\torganization: Organization\n\tmembership: OrganizationMembership | null\n}> {\n\treturn callApi<{\n\t\torganization: Organization\n\t\tmembership: OrganizationMembership | null\n\t}>(config, `/orgs/${orgIdOrSlug}`)\n}\n\n/**\n * Create a new organization\n *\n * @example\n * ```typescript\n * const { organization } = await createOrganization(config, {\n * name: 'My Company',\n * slug: 'my-company',\n * })\n * ```\n */\nexport async function createOrganization(\n\tconfig: SylphxConfig,\n\tinput: CreateOrgInput,\n): Promise<{ organization: Organization }> {\n\tconst endpoint = organizationsEndpoints.create\n\tconst organization = await callApi<Organization>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody: input,\n\t})\n\treturn { organization }\n}\n\n/**\n * Update an organization\n *\n * @example\n * ```typescript\n * const { organization } = await updateOrganization(config, 'my-org', {\n * name: 'New Name',\n * })\n * ```\n */\nexport async function updateOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinput: UpdateOrgInput,\n): Promise<{ organization: Organization }> {\n\treturn callApi<{ organization: Organization }>(config, `/orgs/${orgIdOrSlug}`, {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Delete an organization\n *\n * Requires super_admin role.\n *\n * @example\n * ```typescript\n * await deleteOrganization(config, 'my-org')\n * ```\n */\nexport async function deleteOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Members\n// ============================================================================\n\n/**\n * Get organization members\n *\n * @example\n * ```typescript\n * const { members } = await getOrganizationMembers(config, 'my-org')\n * ```\n */\nexport async function getOrganizationMembers(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ members: OrganizationMember[] }> {\n\treturn callApi<{ members: OrganizationMember[] }>(config, `/orgs/${orgIdOrSlug}/members`)\n}\n\n/**\n * Invite a member to an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { invitation } = await inviteOrganizationMember(config, 'my-org', {\n * email: 'user@example.com',\n * role: 'developer',\n * })\n * ```\n */\nexport async function inviteOrganizationMember(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinput: InviteMemberInput,\n): Promise<{ invitation: OrganizationInvitation }> {\n\treturn callApi<{ invitation: OrganizationInvitation }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/invite`,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: input,\n\t\t},\n\t)\n}\n\n/**\n * Update a member's role\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { member } = await updateOrganizationMemberRole(config, 'my-org', userId, 'admin')\n * ```\n */\nexport async function updateOrganizationMemberRole(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n\trole: OrgRole,\n): Promise<{ member: OrganizationMember }> {\n\treturn callApi<{ member: OrganizationMember }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/role`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { role },\n\t\t},\n\t)\n}\n\n/**\n * Remove a member from an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * await removeOrganizationMember(config, 'my-org', userId)\n * ```\n */\nexport async function removeOrganizationMember(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/members/${memberId}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n/**\n * Leave an organization\n *\n * @example\n * ```typescript\n * await leaveOrganization(config, 'my-org')\n * ```\n */\nexport async function leaveOrganization(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/leave`, {\n\t\tmethod: 'POST',\n\t})\n}\n\n// ============================================================================\n// Invitations\n// ============================================================================\n\n/**\n * Get pending invitations for an organization\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * const { invitations } = await getOrganizationInvitations(config, 'my-org')\n * ```\n */\nexport async function getOrganizationInvitations(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n): Promise<{ invitations: OrganizationInvitation[] }> {\n\treturn callApi<{ invitations: OrganizationInvitation[] }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/invitations`,\n\t)\n}\n\n/**\n * Accept an organization invitation\n *\n * @example\n * ```typescript\n * const { organization } = await acceptOrganizationInvitation(config, invitationToken)\n * ```\n */\nexport async function acceptOrganizationInvitation(\n\tconfig: SylphxConfig,\n\ttoken: string,\n): Promise<{ organization: Organization }> {\n\treturn callApi<{ organization: Organization }>(config, '/orgs/invitations/accept', {\n\t\tmethod: 'POST',\n\t\tbody: { token },\n\t})\n}\n\n/**\n * Revoke a pending invitation\n *\n * Requires admin or super_admin role.\n *\n * @example\n * ```typescript\n * await revokeOrganizationInvitation(config, 'my-org', invitationId)\n * ```\n */\nexport async function revokeOrganizationInvitation(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tinvitationId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/orgs/${orgIdOrSlug}/invitations/${invitationId}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Check if user has a specific role or higher in the organization\n */\nexport function hasRole(membership: OrganizationMembership | null, minimumRole: OrgRole): boolean {\n\tif (!membership) return false\n\n\tconst roleHierarchy: OrgRole[] = [\n\t\t'viewer',\n\t\t'analytics',\n\t\t'developer',\n\t\t'billing',\n\t\t'admin',\n\t\t'super_admin',\n\t]\n\n\tconst userRoleIndex = roleHierarchy.indexOf(membership.role)\n\tconst requiredRoleIndex = roleHierarchy.indexOf(minimumRole)\n\n\treturn userRoleIndex >= requiredRoleIndex\n}\n\n/**\n * Check if user can manage members (invite, remove, change roles)\n */\nexport function canManageMembers(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'admin')\n}\n\n/**\n * Check if user can manage organization settings\n */\nexport function canManageSettings(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'admin')\n}\n\n/**\n * Check if user can delete the organization\n */\nexport function canDeleteOrganization(membership: OrganizationMembership | null): boolean {\n\treturn hasRole(membership, 'super_admin')\n}\n","/**\n * Permission Functions\n *\n * Pure functions for permission management — no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /permissions/* for project-scoped operations.\n * Uses REST API at /orgs/{orgId}/members/{memberId}/permissions for member checks.\n *\n * Types are self-contained (not dependent on generated OpenAPI spec) because\n * the RBAC routes were added after the last spec generation.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A permission definition within a project.\n *\n * Permissions are the atomic building blocks of RBAC roles.\n * They use colon-separated keys (e.g. \"org:members:read\", \"payroll:approve\").\n */\nexport interface Permission {\n\t/** Prefixed permission ID (e.g. \"perm_xxx\") */\n\tid: string\n\t/** Unique key within the project (e.g. \"org:members:read\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription: string | null\n\t/** Whether this is a system-defined permission (immutable) */\n\tisSystem: boolean\n\t/** ISO 8601 creation timestamp */\n\tcreatedAt: string\n}\n\n/**\n * Input for creating a custom permission.\n */\nexport interface CreatePermissionInput {\n\t/** Unique key — colon-separated lowercase segments (e.g. \"org:leave:approve\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription?: string\n}\n\n/**\n * Resolved permissions for a member, including their assigned role.\n */\nexport interface MemberPermissionsResult {\n\t/** Prefixed user ID of the member */\n\tmemberId: string\n\t/** Assigned role (null if no role assigned) */\n\trole: { key: string; name: string } | null\n\t/** Flattened, deduplicated permission keys from all assigned roles */\n\tpermissions: string[]\n}\n\n// ============================================================================\n// Permission CRUD (project-scoped, requires secretKey)\n// ============================================================================\n\n/**\n * List all permissions for the current project.\n *\n * Returns both system-defined and custom permissions.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { permissions } = await listPermissions(config)\n * console.log(permissions.map(p => p.key))\n * // ['org:members:read', 'org:members:manage', 'payroll:view', ...]\n * ```\n */\nexport async function listPermissions(\n\tconfig: SylphxConfig,\n): Promise<{ permissions: Permission[] }> {\n\treturn callApi<{ permissions: Permission[] }>(config, '/permissions')\n}\n\n/**\n * Create a custom permission for the project.\n *\n * Permission keys must be colon-separated lowercase segments\n * (e.g. \"org:leave:approve\", \"payroll:run\").\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { permission } = await createPermission(config, {\n * key: 'payroll:approve',\n * name: 'Approve Payroll',\n * description: 'Can approve payroll runs for the organization',\n * })\n * ```\n */\nexport async function createPermission(\n\tconfig: SylphxConfig,\n\tinput: CreatePermissionInput,\n): Promise<{ permission: Permission }> {\n\treturn callApi<{ permission: Permission }>(config, '/permissions', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/**\n * Delete a custom permission by key.\n *\n * System permissions cannot be deleted.\n * Role-permission assignments are removed automatically via cascading delete.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { success } = await deletePermission(config, 'payroll:approve')\n * ```\n */\nexport async function deletePermission(\n\tconfig: SylphxConfig,\n\tpermissionKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/permissions/${permissionKey}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Member Permissions (org-scoped, requires accessToken)\n// ============================================================================\n\n/**\n * Get a member's resolved permissions within an organization.\n *\n * Returns the flattened, deduplicated set of permission keys from all\n * roles assigned to the member. Also returns their current role info.\n * Requires the caller to be a member of the same organization.\n *\n * @example\n * ```typescript\n * const result = await getMemberPermissions(config, 'my-org', 'usr_abc123')\n * console.log(result.permissions)\n * // ['org:members:read', 'payroll:view', 'payroll:approve']\n * console.log(result.role)\n * // { key: 'hr_manager', name: 'HR Manager' }\n * ```\n */\nexport async function getMemberPermissions(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n): Promise<MemberPermissionsResult> {\n\treturn callApi<MemberPermissionsResult>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/permissions`,\n\t)\n}\n\n// ============================================================================\n// Pure Helpers (no API calls — client-side permission checks)\n// ============================================================================\n\n/**\n * Check if a permission set includes a specific permission.\n *\n * Pure function — no API call. Use with permissions from JWT claims\n * (org_permissions) or from getMemberPermissions().\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view']\n * hasPermission(permissions, 'payroll:view') // true\n * hasPermission(permissions, 'payroll:approve') // false\n * ```\n */\nexport function hasPermission(permissions: string[], required: string): boolean {\n\treturn permissions.includes(required)\n}\n\n/**\n * Check if a permission set includes ANY of the required permissions.\n *\n * Pure function — no API call. Returns true if at least one of the\n * required permissions is present.\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view']\n * hasAnyPermission(permissions, ['payroll:view', 'payroll:approve']) // true\n * hasAnyPermission(permissions, ['admin:full', 'super:admin']) // false\n * ```\n */\nexport function hasAnyPermission(permissions: string[], required: string[]): boolean {\n\treturn required.some((perm) => permissions.includes(perm))\n}\n\n/**\n * Check if a permission set includes ALL of the required permissions.\n *\n * Pure function — no API call. Returns true only if every required\n * permission is present.\n *\n * @example\n * ```typescript\n * const permissions = ['org:members:read', 'payroll:view', 'payroll:approve']\n * hasAllPermissions(permissions, ['payroll:view', 'payroll:approve']) // true\n * hasAllPermissions(permissions, ['payroll:view', 'admin:full']) // false\n * ```\n */\nexport function hasAllPermissions(permissions: string[], required: string[]): boolean {\n\treturn required.every((perm) => permissions.includes(perm))\n}\n","/**\n * Role Functions\n *\n * Pure functions for role management — no hidden state.\n * Each function takes config as the first parameter.\n *\n * Uses REST API at /roles/* for project-scoped operations.\n * Uses REST API at /orgs/{orgId}/members/{memberId}/assign-role for assignment.\n *\n * Types are self-contained (not dependent on generated OpenAPI spec) because\n * the RBAC routes were added after the last spec generation.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A role definition within a project.\n *\n * Roles bundle permissions into named groups that can be assigned to\n * organization members (e.g. \"HR Manager\", \"Payroll Admin\").\n */\nexport interface Role {\n\t/** Prefixed role ID (e.g. \"role_xxx\") */\n\tid: string\n\t/** Unique key within the project (e.g. \"hr_manager\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription: string | null\n\t/** Whether this is a system-defined role (metadata immutable) */\n\tisSystem: boolean\n\t/** Whether this role is automatically assigned to new org members */\n\tisDefault: boolean\n\t/** Display order (lower = higher priority) */\n\tsortOrder: number\n\t/** Permission keys assigned to this role */\n\tpermissions: string[]\n\t/** ISO 8601 creation timestamp */\n\tcreatedAt: string\n\t/** ISO 8601 update timestamp (present on roles route response) */\n\tupdatedAt?: string\n}\n\n/**\n * Input for creating a custom role.\n */\nexport interface CreateRoleInput {\n\t/** Unique key — lowercase alphanumeric with underscores (e.g. \"hr_manager\") */\n\tkey: string\n\t/** Human-readable name */\n\tname: string\n\t/** Optional description */\n\tdescription?: string\n\t/** Permission keys to assign to this role */\n\tpermissions?: string[]\n\t/** Whether to auto-assign to new org members */\n\tisDefault?: boolean\n\t/** Display order (lower = higher priority) */\n\tsortOrder?: number\n}\n\n/**\n * Input for updating an existing role.\n *\n * System role metadata (name, description) is immutable, but their\n * permissions can be changed.\n */\nexport interface UpdateRoleInput {\n\t/** Human-readable name (ignored for system roles) */\n\tname?: string\n\t/** Description (ignored for system roles) */\n\tdescription?: string | null\n\t/** Permission keys to assign (replaces existing) */\n\tpermissions?: string[]\n\t/** Whether to auto-assign to new org members */\n\tisDefault?: boolean\n\t/** Display order */\n\tsortOrder?: number\n}\n\n// ============================================================================\n// Role CRUD (project-scoped, requires secretKey)\n// ============================================================================\n\n/**\n * List all roles for the current project.\n *\n * Returns both system-defined and custom roles, each with their\n * assigned permission keys. Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { roles } = await listRoles(config)\n * for (const role of roles) {\n * console.log(`${role.name}: ${role.permissions.join(', ')}`)\n * }\n * ```\n */\nexport async function listRoles(config: SylphxConfig): Promise<{ roles: Role[] }> {\n\treturn callApi<{ roles: Role[] }>(config, '/roles')\n}\n\n/**\n * Get a single role by key, including its assigned permission keys.\n *\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await getRole(config, 'hr_manager')\n * console.log(role.permissions)\n * // ['org:members:read', 'payroll:view', 'payroll:approve']\n * ```\n */\nexport async function getRole(config: SylphxConfig, roleKey: string): Promise<{ role: Role }> {\n\treturn callApi<{ role: Role }>(config, `/roles/${roleKey}`)\n}\n\n/**\n * Create a custom role with optional permission assignments.\n *\n * Role keys must be lowercase alphanumeric with underscores\n * (e.g. \"hr_manager\", \"payroll_admin\").\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await createRole(config, {\n * key: 'hr_manager',\n * name: 'HR Manager',\n * description: 'Can manage employees and approve leave',\n * permissions: ['org:members:read', 'leave:approve', 'payroll:view'],\n * })\n * ```\n */\nexport async function createRole(\n\tconfig: SylphxConfig,\n\tinput: CreateRoleInput,\n): Promise<{ role: Role }> {\n\t// Map SDK field names to API field names\n\tconst body: Record<string, unknown> = {\n\t\tkey: input.key,\n\t\tname: input.name,\n\t}\n\tif (input.description !== undefined) body.description = input.description\n\tif (input.permissions !== undefined) body.permissionKeys = input.permissions\n\tif (input.isDefault !== undefined) body.isDefault = input.isDefault\n\tif (input.sortOrder !== undefined) body.sortOrder = input.sortOrder\n\n\treturn callApi<{ role: Role }>(config, '/roles', {\n\t\tmethod: 'POST',\n\t\tbody,\n\t})\n}\n\n/**\n * Update a role's metadata and/or permission assignments.\n *\n * System role metadata (name, description) is immutable, but their\n * permissions can be changed. Passing `permissions` replaces the\n * entire permission set for the role.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { role } = await updateRole(config, 'hr_manager', {\n * permissions: ['org:members:read', 'org:members:manage', 'leave:approve'],\n * })\n * ```\n */\nexport async function updateRole(\n\tconfig: SylphxConfig,\n\troleKey: string,\n\tinput: UpdateRoleInput,\n): Promise<{ role: Role }> {\n\t// Map SDK field names to API field names\n\tconst body: Record<string, unknown> = {}\n\tif (input.name !== undefined) body.name = input.name\n\tif (input.description !== undefined) body.description = input.description\n\tif (input.permissions !== undefined) body.permissionKeys = input.permissions\n\tif (input.isDefault !== undefined) body.isDefault = input.isDefault\n\tif (input.sortOrder !== undefined) body.sortOrder = input.sortOrder\n\n\treturn callApi<{ role: Role }>(config, `/roles/${roleKey}`, {\n\t\tmethod: 'PUT',\n\t\tbody,\n\t})\n}\n\n/**\n * Delete a custom role by key.\n *\n * System roles cannot be deleted. Roles with active member assignments\n * cannot be deleted — reassign members first.\n * Requires a secret key (server-side only).\n *\n * @example\n * ```typescript\n * const { success } = await deleteRole(config, 'hr_manager')\n * ```\n */\nexport async function deleteRole(\n\tconfig: SylphxConfig,\n\troleKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/roles/${roleKey}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\n// ============================================================================\n// Member Role Assignment (org-scoped, requires accessToken)\n// ============================================================================\n\n/**\n * Assign an RBAC role to an organization member.\n *\n * Replaces any existing role assignment (single-role mode).\n * Requires admin access to the organization.\n *\n * @example\n * ```typescript\n * const { success } = await assignMemberRole(config, 'my-org', 'usr_abc123', 'hr_manager')\n * ```\n */\nexport async function assignMemberRole(\n\tconfig: SylphxConfig,\n\torgIdOrSlug: string,\n\tmemberId: string,\n\troleKey: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/orgs/${orgIdOrSlug}/members/${memberId}/assign-role`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { roleKey },\n\t\t},\n\t)\n}\n","/**\n * Secrets SDK\n *\n * Secure secrets management for applications.\n * Secrets are encrypted at rest with AES-256-GCM.\n *\n * @example\n * ```typescript\n * import { createConfig, getSecret, getSecrets, listSecretKeys } from '@sylphx/sdk'\n *\n * const config = createConfig({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Get a single secret\n * const dbUrl = await getSecret(config, { key: 'DATABASE_URL' })\n * console.log(dbUrl.value) // postgres://...\n *\n * // Get multiple secrets at once\n * const secrets = await getSecrets(config, {\n * keys: ['DATABASE_URL', 'API_KEY', 'JWT_SECRET']\n * })\n * console.log(secrets.DATABASE_URL) // postgres://...\n *\n * // List all secret keys (without values)\n * const keys = await listSecretKeys(config)\n * keys.forEach(k => console.log(k.key, k.description))\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GetSecretInput {\n\t/** Secret key (uppercase, underscores allowed) */\n\tkey: string\n\t/** Optional environment ID override */\n\tenvironmentId?: string\n}\n\nexport interface GetSecretResult {\n\t/** Secret key */\n\tkey: string\n\t/** Decrypted secret value */\n\tvalue: string\n\t/** Version number */\n\tversion: string\n}\n\nexport interface GetSecretsInput {\n\t/** Array of secret keys to retrieve */\n\tkeys: string[]\n\t/** Optional environment ID override */\n\tenvironmentId?: string\n}\n\n/** Map of key -> decrypted value */\nexport type GetSecretsResult = Record<string, string>\n\nexport interface ListSecretKeysInput {\n\t/** Optional environment ID filter */\n\tenvironmentId?: string\n}\n\nexport interface SecretKeyInfo {\n\t/** Secret key name */\n\tkey: string\n\t/** Human-readable description */\n\tdescription: string | null\n\t/** Current version */\n\tversion: string\n\t/** Whether this is environment-specific */\n\tisEnvironmentSpecific: boolean\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get a single secret value by key.\n *\n * @param config - SDK configuration\n * @param input - Secret key and optional environment ID\n * @returns Decrypted secret value\n * @throws Error if secret not found or access denied\n *\n * @example\n * ```typescript\n * const secret = await getSecret(config, { key: 'DATABASE_URL' })\n * const dbConnection = createPool(secret.value)\n * ```\n */\nexport async function getSecret(\n\tconfig: SylphxConfig,\n\tinput: GetSecretInput,\n): Promise<GetSecretResult> {\n\treturn callApi<GetSecretResult>(config, '/secrets/get', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tkey: input.key,\n\t\t\tenvironmentId: input.environmentId,\n\t\t},\n\t})\n}\n\n/**\n * Get multiple secrets at once.\n *\n * More efficient than multiple getSecret calls.\n *\n * @param config - SDK configuration\n * @param input - Array of secret keys\n * @returns Map of key -> decrypted value\n *\n * @example\n * ```typescript\n * const secrets = await getSecrets(config, {\n * keys: ['DATABASE_URL', 'CACHE_URL', 'JWT_SECRET']\n * })\n *\n * const db = createPool(secrets.DATABASE_URL)\n * const cache = createCacheClient(secrets.CACHE_URL)\n * ```\n */\nexport async function getSecrets(\n\tconfig: SylphxConfig,\n\tinput: GetSecretsInput,\n): Promise<GetSecretsResult> {\n\treturn callApi<GetSecretsResult>(config, '/secrets/getMany', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tkeys: input.keys,\n\t\t\tenvironmentId: input.environmentId,\n\t\t},\n\t})\n}\n\n/**\n * List all secret keys (without values).\n *\n * Useful for showing available secrets in UI or debugging.\n *\n * @param config - SDK configuration\n * @param input - Optional environment filter\n * @returns Array of secret key info\n *\n * @example\n * ```typescript\n * const keys = await listSecretKeys(config)\n * console.log('Available secrets:')\n * keys.forEach(k => console.log(` ${k.key}: ${k.description}`))\n * ```\n */\nexport async function listSecretKeys(\n\tconfig: SylphxConfig,\n\tinput: ListSecretKeysInput = {},\n): Promise<SecretKeyInfo[]> {\n\treturn callApi<SecretKeyInfo[]>(config, '/secrets/listKeys', {\n\t\tmethod: 'GET',\n\t\tquery: input.environmentId ? { environmentId: input.environmentId } : undefined,\n\t})\n}\n\n/**\n * Check if a secret exists without retrieving its value.\n *\n * @param config - SDK configuration\n * @param key - Secret key to check\n * @returns true if the secret exists\n *\n * @example\n * ```typescript\n * if (await hasSecret(config, 'STRIPE_SECRET_KEY')) {\n * // Stripe is configured, enable payment features\n * }\n * ```\n */\nexport async function hasSecret(config: SylphxConfig, key: string): Promise<boolean> {\n\ttry {\n\t\tconst keys = await listSecretKeys(config)\n\t\treturn keys.some((k) => k.key === key)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Get all secrets for an environment as an object.\n *\n * Useful for loading all secrets into process.env at startup.\n *\n * @param config - SDK configuration\n * @param environmentId - Optional environment ID\n * @returns Object with all secrets\n *\n * @example\n * ```typescript\n * // Load all secrets into process.env at app startup\n * const secrets = await getAllSecrets(config)\n * Object.assign(process.env, secrets)\n * ```\n */\nexport async function getAllSecrets(\n\tconfig: SylphxConfig,\n\tenvironmentId?: string,\n): Promise<GetSecretsResult> {\n\tconst keys = await listSecretKeys(config, { environmentId })\n\tif (keys.length === 0) {\n\t\treturn {}\n\t}\n\treturn getSecrets(config, { keys: keys.map((k) => k.key), environmentId })\n}\n","/**\n * Search SDK\n *\n * State-of-the-art search with full-text, semantic, and hybrid modes.\n * Powered by PostgreSQL tsvector + pgvector.\n *\n * @example\n * ```typescript\n * import { createConfig, indexDocument, search, batchIndex } from '@sylphx/sdk'\n *\n * const config = createConfig({\n * secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,\n * })\n *\n * // Index a document\n * await indexDocument(config, {\n * content: 'How to reset your password...',\n * title: 'Password Reset Guide',\n * namespace: 'help-articles',\n * category: 'account',\n * tags: ['password', 'security'],\n * })\n *\n * // Search (hybrid mode by default)\n * const results = await search(config, {\n * query: 'forgot my login credentials',\n * namespace: 'help-articles',\n * searchType: 'hybrid',\n * highlight: true,\n * })\n *\n * results.results.forEach(r => console.log(r.title, r.score, r.highlight))\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface IndexDocumentInput {\n\t/** Document title (weighted higher in search) */\n\ttitle?: string\n\t/** Document content to index */\n\tcontent: string\n\t/** Namespace for data isolation (e.g., 'products', 'articles') */\n\tnamespace?: string\n\t/** External document ID (your system's ID) */\n\texternalId?: string\n\t/** URL or path */\n\turl?: string\n\t/** Searchable metadata */\n\tmetadata?: Record<string, unknown>\n\t/** Category facet for filtering */\n\tcategory?: string\n\t/** Type facet for filtering */\n\ttype?: string\n\t/** Tags facet for filtering */\n\ttags?: string[]\n\t/** Language for full-text search (default: english) */\n\tlanguage?: string\n\t/** Skip embedding generation (for keyword-only search) */\n\tskipEmbedding?: boolean\n\t/** Embedding model to use */\n\tembeddingModel?: string\n}\n\nexport interface IndexDocumentResult {\n\t/** Generated document ID */\n\tid: string\n\t/** External ID if provided */\n\texternalId: string | null\n\t/** Namespace */\n\tnamespace: string\n}\n\nexport interface BatchIndexInput {\n\t/** Documents to index (max 100) */\n\tdocuments: Array<{\n\t\ttitle?: string\n\t\tcontent: string\n\t\texternalId?: string\n\t\turl?: string\n\t\tmetadata?: Record<string, unknown>\n\t\tcategory?: string\n\t\ttype?: string\n\t\ttags?: string[]\n\t}>\n\t/** Namespace for all documents */\n\tnamespace?: string\n\t/** Language for full-text search */\n\tlanguage?: string\n\t/** Skip embedding generation */\n\tskipEmbedding?: boolean\n\t/** Embedding model to use */\n\tembeddingModel?: string\n}\n\nexport interface BatchIndexResult {\n\t/** Number of documents indexed */\n\tindexed: number\n\t/** Generated document IDs */\n\tids: string[]\n}\n\nexport type SearchType = 'keyword' | 'semantic' | 'hybrid'\n\nexport interface SearchInput {\n\t/** Search query text */\n\tquery: string\n\t/** Namespace to search within */\n\tnamespace?: string\n\t/** Search type: keyword, semantic, or hybrid (default) */\n\tsearchType?: SearchType\n\t/** Maximum results to return (default: 10, max: 100) */\n\tlimit?: number\n\t/** Offset for pagination */\n\toffset?: number\n\t/** Minimum similarity threshold (0-1) for semantic search */\n\tminSimilarity?: number\n\t/** Enable typo tolerance (default: true) */\n\ttypoTolerance?: boolean\n\t/** Language for full-text search */\n\tlanguage?: string\n\t/** Facet filters */\n\tfilters?: {\n\t\tcategory?: string\n\t\ttype?: string\n\t\ttags?: string[]\n\t\tmetadata?: Record<string, unknown>\n\t}\n\t/** Include highlighted snippets (default: true) */\n\thighlight?: boolean\n\t/** Embedding model for semantic search */\n\tembeddingModel?: string\n\t/** Track this query for analytics (default: true) */\n\ttrackQuery?: boolean\n\t/** Session ID for analytics */\n\tsessionId?: string\n\t/** User ID for analytics */\n\tuserId?: string\n}\n\nexport interface SearchResultItem {\n\t/** Document ID */\n\tid: string\n\t/** External ID if set */\n\texternalId: string | null\n\t/** Document title */\n\ttitle: string | null\n\t/** Document content */\n\tcontent: string\n\t/** Document URL */\n\turl: string | null\n\t/** Document metadata */\n\tmetadata: Record<string, unknown> | null\n\t/** Category facet */\n\tcategory: string | null\n\t/** Type facet */\n\ttype: string | null\n\t/** Tags facet */\n\ttags: string[] | null\n\t/** Combined score */\n\tscore: number\n\t/** Keyword search score (if hybrid) */\n\tkeywordScore?: number\n\t/** Semantic search score (if hybrid) */\n\tsemanticScore?: number\n\t/** Highlighted snippet (if enabled) */\n\thighlight?: string\n}\n\nexport interface SearchResponse {\n\t/** Search results */\n\tresults: SearchResultItem[]\n\t/** Total results found */\n\ttotal: number\n\t/** Original query */\n\tquery: string\n\t/** Search type used */\n\tsearchType: SearchType\n\t/** Query processing time in ms */\n\tlatencyMs: number\n}\n\nexport interface GetFacetsInput {\n\t/** Namespace to get facets from */\n\tnamespace?: string\n\t/** Facets to retrieve */\n\tfacets?: Array<'category' | 'type' | 'tags'>\n\t/** Filter facets by category or type */\n\tfilters?: {\n\t\tcategory?: string\n\t\ttype?: string\n\t}\n}\n\nexport interface FacetsResponse {\n\tfacets: {\n\t\tcategory?: Array<{ value: string; count: number }>\n\t\ttype?: Array<{ value: string; count: number }>\n\t\ttags?: Array<{ value: string; count: number }>\n\t}\n}\n\nexport interface DeleteDocumentInput {\n\t/** Document ID to delete */\n\tid?: string\n\t/** Or delete by external ID */\n\texternalId?: string\n\t/** Namespace (required if using externalId) */\n\tnamespace?: string\n}\n\nexport interface UpsertDocumentInput extends IndexDocumentInput {\n\t/** External ID is required for upsert */\n\texternalId: string\n}\n\nexport interface UpsertDocumentResult extends IndexDocumentResult {\n\t/** Whether the document was created (true) or updated (false) */\n\tcreated: boolean\n}\n\nexport interface SearchStatsResult {\n\t/** Total documents indexed */\n\ttotalDocuments: number\n\t/** Documents with embeddings */\n\tdocumentsWithEmbedding: number\n\t/** Documents by namespace */\n\tbyNamespace: Array<{ namespace: string; count: number }>\n}\n\nexport interface TrackClickInput {\n\t/** Search query ID */\n\tqueryId: string\n\t/** Clicked document ID */\n\tdocumentId: string\n\t/** Position in results (1-indexed) */\n\tposition: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Index a document for search.\n *\n * Automatically generates tsvector for full-text search and\n * optional embedding for semantic search.\n *\n * @param config - SDK configuration\n * @param input - Document to index\n * @returns Indexed document info\n *\n * @example\n * ```typescript\n * const result = await indexDocument(config, {\n * title: 'Getting Started Guide',\n * content: 'Welcome to our platform...',\n * namespace: 'docs',\n * category: 'tutorials',\n * tags: ['beginner', 'setup'],\n * })\n * ```\n */\nexport async function indexDocument(\n\tconfig: SylphxConfig,\n\tinput: IndexDocumentInput,\n): Promise<IndexDocumentResult> {\n\treturn callApi<IndexDocumentResult>(config, '/search/index', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\ttitle: input.title,\n\t\t\tcontent: input.content,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\texternalId: input.externalId,\n\t\t\turl: input.url,\n\t\t\tmetadata: input.metadata,\n\t\t\tcategory: input.category,\n\t\t\ttype: input.type,\n\t\t\ttags: input.tags,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Index multiple documents in a single batch.\n *\n * More efficient than multiple indexDocument calls.\n * Max 100 documents per batch.\n *\n * @param config - SDK configuration\n * @param input - Documents to index\n * @returns Batch index result\n *\n * @example\n * ```typescript\n * const result = await batchIndex(config, {\n * namespace: 'products',\n * documents: products.map(p => ({\n * title: p.name,\n * content: p.description,\n * externalId: p.id,\n * category: p.category,\n * }))\n * })\n * console.log(`Indexed ${result.indexed} products`)\n * ```\n */\nexport async function batchIndex(\n\tconfig: SylphxConfig,\n\tinput: BatchIndexInput,\n): Promise<BatchIndexResult> {\n\treturn callApi<BatchIndexResult>(config, '/search/batchIndex', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tdocuments: input.documents,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Search documents.\n *\n * Supports three search modes:\n * - `keyword`: Full-text search with typo tolerance\n * - `semantic`: AI-powered vector search\n * - `hybrid`: Combined ranking (default, best results)\n *\n * @param config - SDK configuration\n * @param input - Search query and options\n * @returns Search results with scores\n *\n * @example\n * ```typescript\n * // Hybrid search (recommended)\n * const results = await search(config, {\n * query: 'how to change email address',\n * namespace: 'help',\n * searchType: 'hybrid',\n * highlight: true,\n * })\n *\n * results.results.forEach(r => {\n * console.log(`[${r.score.toFixed(3)}] ${r.title}`)\n * console.log(r.highlight)\n * })\n * ```\n */\nexport async function search(config: SylphxConfig, input: SearchInput): Promise<SearchResponse> {\n\treturn callApi<SearchResponse>(config, '/search/search', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tquery: input.query,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tsearchType: input.searchType ?? 'hybrid',\n\t\t\tlimit: input.limit ?? 10,\n\t\t\toffset: input.offset ?? 0,\n\t\t\tminSimilarity: input.minSimilarity,\n\t\t\ttypoTolerance: input.typoTolerance ?? true,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tfilters: input.filters,\n\t\t\thighlight: input.highlight ?? true,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t\ttrackQuery: input.trackQuery ?? true,\n\t\t\tsessionId: input.sessionId,\n\t\t\tuserId: input.userId,\n\t\t},\n\t})\n}\n\n/**\n * Get facet counts for filtering.\n *\n * Returns counts of documents by category, type, and tags.\n *\n * @param config - SDK configuration\n * @param input - Facet options\n * @returns Facet counts\n *\n * @example\n * ```typescript\n * const facets = await getFacets(config, {\n * namespace: 'products',\n * facets: ['category', 'type'],\n * })\n *\n * facets.facets.category?.forEach(f => {\n * console.log(`${f.value}: ${f.count} products`)\n * })\n * ```\n */\nexport async function getFacets(\n\tconfig: SylphxConfig,\n\tinput: GetFacetsInput = {},\n): Promise<FacetsResponse> {\n\treturn callApi<FacetsResponse>(config, '/search/getFacets', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\tfacets: input.facets ?? ['category', 'type'],\n\t\t\tfilters: input.filters,\n\t\t},\n\t})\n}\n\n/**\n * Delete a document from the search index.\n *\n * @param config - SDK configuration\n * @param input - Document ID or external ID\n * @returns Deletion result\n *\n * @example\n * ```typescript\n * // Delete by internal ID\n * await deleteDocument(config, { id: 'doc-uuid-123' })\n *\n * // Delete by external ID\n * await deleteDocument(config, {\n * externalId: 'product-456',\n * namespace: 'products'\n * })\n * ```\n */\nexport async function deleteDocument(\n\tconfig: SylphxConfig,\n\tinput: DeleteDocumentInput,\n): Promise<{ deleted: number }> {\n\treturn callApi<{ deleted: number }>(config, '/search/delete', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tid: input.id,\n\t\t\texternalId: input.externalId,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t},\n\t})\n}\n\n/**\n * Upsert a document (insert or update by externalId).\n *\n * If a document with the same externalId exists, it will be replaced.\n * Otherwise, a new document is created.\n *\n * @param config - SDK configuration\n * @param input - Document to upsert (externalId required)\n * @returns Upsert result\n *\n * @example\n * ```typescript\n * const result = await upsertDocument(config, {\n * externalId: 'product-123',\n * title: 'Updated Product Name',\n * content: 'New description...',\n * namespace: 'products',\n * })\n * console.log(result.created ? 'Created' : 'Updated')\n * ```\n */\nexport async function upsertDocument(\n\tconfig: SylphxConfig,\n\tinput: UpsertDocumentInput,\n): Promise<UpsertDocumentResult> {\n\treturn callApi<UpsertDocumentResult>(config, '/search/upsert', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\ttitle: input.title,\n\t\t\tcontent: input.content,\n\t\t\tnamespace: input.namespace ?? 'default',\n\t\t\texternalId: input.externalId,\n\t\t\turl: input.url,\n\t\t\tmetadata: input.metadata,\n\t\t\tcategory: input.category,\n\t\t\ttype: input.type,\n\t\t\ttags: input.tags,\n\t\t\tlanguage: input.language ?? 'english',\n\t\t\tskipEmbedding: input.skipEmbedding ?? false,\n\t\t\tembeddingModel: input.embeddingModel ?? 'openai/text-embedding-3-small',\n\t\t},\n\t})\n}\n\n/**\n * Get search index statistics.\n *\n * @param config - SDK configuration\n * @param namespace - Optional namespace filter\n * @returns Index statistics\n *\n * @example\n * ```typescript\n * const stats = await getSearchStats(config)\n * console.log(`Total docs: ${stats.totalDocuments}`)\n * console.log(`With embeddings: ${stats.documentsWithEmbedding}`)\n * ```\n */\nexport async function getSearchStats(\n\tconfig: SylphxConfig,\n\tnamespace?: string,\n): Promise<SearchStatsResult> {\n\treturn callApi<SearchStatsResult>(config, '/search/getStats', {\n\t\tmethod: 'POST',\n\t\tbody: { namespace },\n\t})\n}\n\n/**\n * Track a click on a search result.\n *\n * Use this to improve search quality over time.\n *\n * @param config - SDK configuration\n * @param input - Click information\n * @returns Success status\n *\n * @example\n * ```typescript\n * await trackClick(config, {\n * queryId: searchResponse.queryId,\n * documentId: clickedResult.id,\n * position: 3,\n * })\n * ```\n */\nexport async function trackClick(\n\tconfig: SylphxConfig,\n\tinput: TrackClickInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/search/trackClick', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n","/**\n * Database Functions\n *\n * Pure functions for retrieving Platform-provisioned database connection strings.\n * Server-side only (requires secret key `sk_*`).\n *\n * The Platform provisions a PostgreSQL database for each app and encrypts\n * the connection string. These functions retrieve and decrypt the connection\n * string at startup, so your app never needs to store it.\n *\n * @example\n * ```ts\n * import { createConfig, getDatabaseConnectionString } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n * const { connectionString } = await getDatabaseConnectionString(config)\n *\n * const pool = new Pool({ connectionString })\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DatabaseStatus =\n\t| 'provisioning'\n\t| 'ready'\n\t| 'suspended'\n\t| 'failed'\n\t| 'deleted'\n\t| 'not_provisioned'\n\nexport interface DatabaseConnectionInfo {\n\t/** Decrypted PostgreSQL connection string */\n\tconnectionString: string\n\t/** Database name */\n\tdatabaseName: string\n\t/** Database role/user name */\n\troleName: string\n\t/** Database provisioning status */\n\tstatus: Exclude<DatabaseStatus, 'not_provisioned'>\n}\n\nexport interface DatabaseStatusInfo {\n\t/** Current database status */\n\tstatus: DatabaseStatus\n\t/** Provisioned region */\n\tregion: string | null\n\t/** PostgreSQL major version */\n\tpgVersion: number | null\n\t/** Database name */\n\tdatabaseName: string | null\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get the provisioned PostgreSQL connection string for this app.\n *\n * Requires secret key authentication (server-side only).\n * The connection string is decrypted on the Platform and returned in plaintext.\n *\n * @throws `NOT_FOUND` if no database has been provisioned for this app\n * @throws `UNPROCESSABLE_ENTITY` if database is not yet ready\n *\n * @example\n * ```ts\n * const { connectionString } = await getDatabaseConnectionString(config)\n * const pool = new Pool({ connectionString })\n * ```\n */\nexport async function getDatabaseConnectionString(\n\tconfig: SylphxConfig,\n): Promise<DatabaseConnectionInfo> {\n\treturn callApi<DatabaseConnectionInfo>(config, '/sdk/database/connection-string', {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Get the current status of the provisioned database.\n *\n * Use this to check if the database is ready before attempting to connect.\n * Requires secret key authentication (server-side only).\n *\n * @example\n * ```ts\n * const { status } = await getDatabaseStatus(config)\n * if (status === 'ready') {\n * // Connect to database\n * }\n * ```\n */\nexport async function getDatabaseStatus(config: SylphxConfig): Promise<DatabaseStatusInfo> {\n\treturn callApi<DatabaseStatusInfo>(config, '/sdk/database/status', {\n\t\tmethod: 'GET',\n\t})\n}\n","/**\n * KV (Key-Value Store) Functions\n *\n * Pure functions for managed key-value storage.\n * Supports strings, hashes, lists, sorted sets, and built-in rate limiting.\n *\n * Keys are automatically namespaced per app, so no key collisions occur\n * between different apps on the same Platform.\n *\n * @example\n * ```ts\n * import { createConfig, kvSet, kvGet, kvDelete } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n *\n * // Basic key-value operations\n * await kvSet(config, { key: 'user:123', value: { name: 'Alice' }, ex: 3600 })\n * const user = await kvGet(config, 'user:123')\n * await kvDelete(config, 'user:123')\n * ```\n */\n\n// Type-only imports from @sylphx/contract per ADR-084 Rule 3 — prevents\n// Effect runtime types from leaking into the published .d.ts surface.\nimport type {\n\tKvDeleteResult as ContractKvDeleteResult,\n\tKvExistsResult as ContractKvExistsResult,\n\tKvExpireInput as ContractKvExpireInput,\n\tKvIncrInput as ContractKvIncrInput,\n\tKvIncrResult as ContractKvIncrResult,\n\tKvRateLimitInput as ContractKvRateLimitInput,\n\tKvScanResult as ContractKvScanResult,\n\tKvSetInput as ContractKvSetInput,\n\tKvSetResult as ContractKvSetResult,\n} from '@sylphx/contract'\nimport { kvEndpoints } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport type { KvRateLimitResult, KvSetOptions, KvZMember } from './kv-types'\n\n// Re-export shared types\nexport type { KvRateLimitResult, KvSetOptions, KvZMember } from './kv-types'\n\n/**\n * Interpolate `:param` placeholders in a path template with URL-encoded values.\n *\n * Example: `interpolatePath('/kv/:key', { key: 'a/b' })` -> `/kv/a%2Fb`.\n */\nfunction interpolatePath(template: string, params: Record<string, string>): string {\n\tlet path = template\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tpath = path.replace(`:${key}`, encodeURIComponent(value))\n\t}\n\treturn path\n}\n\nconst KV_PATHS = {\n\tmset: '/kv/mset',\n\tmget: '/kv/mget',\n\thset: '/kv/hset',\n\thget: '/kv/hget',\n\thgetall: '/kv/hgetall',\n\tlpush: '/kv/lpush',\n\tlrange: '/kv/lrange',\n\tzadd: '/kv/zadd',\n\tzrange: '/kv/zrange',\n} as const\n\ntype SuccessWireResult = { success?: boolean; ok?: boolean }\n\nfunction toLegacyOkResult(result: SuccessWireResult): { ok: boolean } {\n\tif (result.success !== undefined) return { ok: result.success }\n\tif (result.ok !== undefined) return { ok: result.ok }\n\treturn { ok: false }\n}\n\nfunction resolveCreatedCount(result: { created?: number; count?: number }): number {\n\tif (result.created !== undefined) return result.created\n\tif (result.count !== undefined) return result.count\n\treturn 0\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface KvSetRequest extends KvSetOptions {\n\t/** Key to store */\n\tkey: string\n\t/** Value to store (any JSON-serializable value) */\n\tvalue: unknown\n\t/** @deprecated Use `ex`. Kept for compatibility with early SDK adopters. */\n\tttl?: number\n}\n\nexport interface KvMsetRequest {\n\t/** Key-value pairs to set in a single atomic operation */\n\tentries: Array<{ key: string; value: unknown }>\n}\n\nexport interface KvMgetRequest {\n\t/** Keys to retrieve */\n\tkeys: string[]\n}\n\nexport interface KvHsetRequest {\n\t/** Hash key */\n\tkey: string\n\t/** Field-value pairs to set on the hash */\n\tfields: Record<string, unknown>\n}\n\nexport interface KvHgetRequest {\n\t/** Hash key */\n\tkey: string\n\t/** Field to get */\n\tfield: string\n}\n\nexport interface KvHgetallRequest {\n\t/** Hash key */\n\tkey: string\n}\n\nexport interface KvLpushRequest {\n\t/** List key */\n\tkey: string\n\t/** Values to prepend (left push) */\n\tvalues: unknown[]\n}\n\nexport interface KvLrangeRequest {\n\t/** List key */\n\tkey: string\n\t/** Start index (0-based, negative counts from end) */\n\tstart: number\n\t/** Stop index (inclusive, negative counts from end) */\n\tstop: number\n}\n\nexport interface KvZaddRequest {\n\t/** Sorted set key */\n\tkey: string\n\t/** Members with scores to add */\n\tmembers: KvZMember[]\n}\n\nexport interface KvZrangeRequest {\n\t/** Sorted set key */\n\tkey: string\n\t/** Start index or score */\n\tstart: number | string\n\t/** Stop index or score */\n\tstop: number | string\n\t/** Return scores alongside members */\n\twithScores?: boolean\n\t/** Reverse order */\n\trev?: boolean\n\t/** Treat start/stop as scores (BYSCORE) */\n\tbyScore?: boolean\n}\n\nexport interface KvIncrRequest {\n\t/** Key to increment */\n\tkey: string\n\t/** Amount to increment by (default: 1) */\n\tby?: number\n}\n\nexport interface KvExpireRequest {\n\t/** Key to set expiry on */\n\tkey: string\n\t/** TTL in seconds */\n\tseconds: number\n}\n\nexport interface KvRateLimitRequest {\n\t/** Rate limit key (e.g., userId, IP) */\n\tkey?: string\n\t/** @deprecated Use `key`. Kept for compatibility with early SDK adopters. */\n\tidentifier?: string\n\t/** Maximum requests allowed in the window */\n\tlimit: number\n\t/** Window duration in seconds */\n\twindow: number | string\n}\n\n// ============================================================================\n// Functions — Basic Operations\n// ============================================================================\n\n/**\n * Set a key-value pair, with optional TTL and conditional flags.\n *\n * @example\n * ```ts\n * // Simple set\n * await kvSet(config, { key: 'session:abc', value: { userId: '123' }, ex: 86400 })\n *\n * // Set only if not exists (NX)\n * await kvSet(config, { key: 'lock:task', value: '1', ex: 30, nx: true })\n * ```\n */\nexport async function kvSet(config: SylphxConfig, request: KvSetRequest): Promise<{ ok: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// `KvSetRequest` is structurally compatible with `ContractKvSetInput` (key/value/ex/nx/xx).\n\tconst { ttl, ...rest } = request\n\tconst body = {\n\t\t...rest,\n\t\t...(rest.ex === undefined && ttl !== undefined ? { ex: ttl } : {}),\n\t} satisfies ContractKvSetInput\n\tconst endpoint = kvEndpoints.set\n\tconst result = await callApi<ContractKvSetResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Get a value by key.\n *\n * Returns `null` if the key does not exist or has expired.\n *\n * @example\n * ```ts\n * const session = await kvGet<{ userId: string }>(config, 'session:abc')\n * if (session) {\n * console.log(session.userId)\n * }\n * ```\n */\nexport async function kvGet<T = unknown>(config: SylphxConfig, key: string): Promise<T | null> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.get\n\tconst result = await callApi<{ value: T | null }>(\n\t\tconfig,\n\t\tinterpolatePath(endpoint.path, { key }),\n\t\t{ method: endpoint.method },\n\t)\n\treturn result.value\n}\n\n/**\n * Delete one or more keys.\n *\n * @example\n * ```ts\n * const { deleted } = await kvDelete(config, 'session:abc')\n * console.log(`Deleted ${deleted} keys`)\n * ```\n */\nexport async function kvDelete(config: SylphxConfig, key: string): Promise<{ deleted: number }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.delete\n\treturn callApi<ContractKvDeleteResult>(config, interpolatePath(endpoint.path, { key }), {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Check if a key exists.\n *\n * @example\n * ```ts\n * const { exists } = await kvExists(config, 'session:abc')\n * ```\n */\nexport async function kvExists(config: SylphxConfig, key: string): Promise<{ exists: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst endpoint = kvEndpoints.exists\n\treturn callApi<ContractKvExistsResult>(config, interpolatePath(endpoint.path, { key }), {\n\t\tmethod: endpoint.method,\n\t})\n}\n\n/**\n * Set expiry on an existing key.\n *\n * @example\n * ```ts\n * await kvExpire(config, { key: 'session:abc', seconds: 3600 })\n * ```\n */\nexport async function kvExpire(\n\tconfig: SylphxConfig,\n\trequest: KvExpireRequest,\n): Promise<{ ok: boolean }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractKvExpireInput\n\tconst endpoint = kvEndpoints.expire\n\tconst result = await callApi<ContractKvSetResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Increment a numeric value.\n *\n * @example\n * ```ts\n * const { value } = await kvIncr(config, { key: 'page:views', by: 1 })\n * ```\n */\nexport async function kvIncr(\n\tconfig: SylphxConfig,\n\trequest: KvIncrRequest,\n): Promise<{ value: number }> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst body = request satisfies ContractKvIncrInput\n\tconst endpoint = kvEndpoints.incr\n\treturn callApi<ContractKvIncrResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Functions — Bulk Operations\n// ============================================================================\n\n/**\n * Set multiple key-value pairs atomically.\n */\nexport async function kvMset(\n\tconfig: SylphxConfig,\n\trequest: KvMsetRequest,\n): Promise<{ ok: boolean }> {\n\tconst result = await callApi<SuccessWireResult>(config, KV_PATHS.mset, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn toLegacyOkResult(result)\n}\n\n/**\n * Get multiple values by keys in a single request.\n *\n * Returns `null` for keys that don't exist.\n */\nexport async function kvMget<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvMgetRequest,\n): Promise<Array<T | null>> {\n\tconst result = await callApi<{ values: Array<T | null> | Record<string, T | null> }>(\n\t\tconfig,\n\t\tKV_PATHS.mget,\n\t\t{\n\t\t\tmethod: 'POST',\n\t\t\tbody: request,\n\t\t},\n\t)\n\tconst values = result.values\n\tif (Array.isArray(values)) {\n\t\treturn values\n\t}\n\treturn request.keys.map((key) => values[key] ?? null)\n}\n\n// ============================================================================\n// Functions — Hash Operations\n// ============================================================================\n\n/**\n * Set fields on a hash key.\n *\n * @example\n * ```ts\n * await kvHset(config, { key: 'user:123', fields: { name: 'Alice', age: 30 } })\n * ```\n */\nexport async function kvHset(\n\tconfig: SylphxConfig,\n\trequest: KvHsetRequest,\n): Promise<{ count: number }> {\n\tconst result = await callApi<{ created?: number; count?: number }>(config, KV_PATHS.hset, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn { count: resolveCreatedCount(result) }\n}\n\n/**\n * Get a single field from a hash key.\n */\nexport async function kvHget<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvHgetRequest,\n): Promise<T | null> {\n\tconst result = await callApi<{ value: T | null }>(config, KV_PATHS.hget, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn result.value\n}\n\n/**\n * Get all fields from a hash key.\n */\nexport async function kvHgetall<T extends Record<string, unknown> = Record<string, unknown>>(\n\tconfig: SylphxConfig,\n\trequest: KvHgetallRequest,\n): Promise<T | null> {\n\tconst result = await callApi<{ fields?: T | null; value?: T | null }>(config, KV_PATHS.hgetall, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\tif (result.fields !== undefined) return result.fields\n\tif (result.value !== undefined) return result.value\n\treturn null\n}\n\n// ============================================================================\n// Functions — List Operations\n// ============================================================================\n\n/**\n * Left-push values onto a list.\n *\n * @example\n * ```ts\n * const { length } = await kvLpush(config, { key: 'events', values: [event] })\n * ```\n */\nexport async function kvLpush(\n\tconfig: SylphxConfig,\n\trequest: KvLpushRequest,\n): Promise<{ length: number }> {\n\treturn callApi<{ length: number }>(config, KV_PATHS.lpush, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Get a range of elements from a list.\n *\n * @example\n * ```ts\n * // Get last 10 events\n * const items = await kvLrange(config, { key: 'events', start: 0, stop: 9 })\n * ```\n */\nexport async function kvLrange<T = unknown>(\n\tconfig: SylphxConfig,\n\trequest: KvLrangeRequest,\n): Promise<T[]> {\n\tconst result = await callApi<{ values?: T[]; items?: T[] }>(config, KV_PATHS.lrange, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\tif (result.values !== undefined) return result.values\n\tif (result.items !== undefined) return result.items\n\treturn []\n}\n\n// ============================================================================\n// Functions — Sorted Set Operations\n// ============================================================================\n\n/**\n * Add members to a sorted set.\n *\n * @example\n * ```ts\n * // Add to leaderboard\n * await kvZadd(config, {\n * key: 'leaderboard',\n * members: [{ member: 'user:123', score: 1500 }],\n * })\n * ```\n */\nexport async function kvZadd(\n\tconfig: SylphxConfig,\n\trequest: KvZaddRequest,\n): Promise<{ added: number }> {\n\treturn callApi<{ added: number }>(config, KV_PATHS.zadd, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Get a range of members from a sorted set.\n *\n * @example\n * ```ts\n * // Get top 10 leaderboard entries\n * const entries = await kvZrange(config, {\n * key: 'leaderboard',\n * start: 0,\n * stop: 9,\n * rev: true,\n * withScores: true,\n * })\n * ```\n */\nexport async function kvZrange(\n\tconfig: SylphxConfig,\n\trequest: KvZrangeRequest,\n): Promise<Array<{ member: string; score?: number }>> {\n\tconst result = await callApi<{\n\t\tmembers: Array<{ member: string; score?: number }>\n\t}>(config, KV_PATHS.zrange, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n\treturn result.members\n}\n\n// ============================================================================\n// Functions — Rate Limiting\n// ============================================================================\n\n/**\n * Check and consume a rate limit token using the platform sliding window.\n *\n * This is a built-in rate limiter — no external service needed.\n *\n * @example\n * ```ts\n * // 10 requests per 60 seconds per user\n * const result = await kvRateLimit(config, {\n * key: `user:${userId}`,\n * limit: 10,\n * window: '60s',\n * })\n *\n * if (!result.success) {\n * return Response.json({ error: 'Rate limit exceeded' }, { status: 429 })\n * }\n * ```\n */\nexport async function kvRateLimit(\n\tconfig: SylphxConfig,\n\trequest: KvRateLimitRequest,\n): Promise<KvRateLimitResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\tconst key = request.key ?? request.identifier\n\tif (!key) {\n\t\tthrow new Error('kvRateLimit requires `key`')\n\t}\n\tconst body = {\n\t\tkey,\n\t\tlimit: request.limit,\n\t\twindow: typeof request.window === 'number' ? `${request.window}s` : request.window,\n\t} satisfies ContractKvRateLimitInput\n\tconst endpoint = kvEndpoints.rateLimit\n\treturn callApi<KvRateLimitResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tbody,\n\t})\n}\n\n// ============================================================================\n// Functions — Scan / Iteration\n// ============================================================================\n\nexport interface KvScanOptions {\n\t/** Key pattern to match (e.g. 'user:*'). Defaults to '*' (all keys). */\n\tpattern?: string\n\t/** Cursor for pagination. Use '0' to start a new scan (default). */\n\tcursor?: string\n\t/** Hint for how many keys to return per iteration (1–1000). Default: 100. */\n\tcount?: number\n}\n\nexport interface KvScanResult {\n\t/** Keys matching the pattern (namespace prefix stripped). */\n\tkeys: string[]\n\t/** Cursor for the next page. Pass this as `cursor` in the next call. */\n\tnextCursor: string\n\t/** True when the full keyspace has been scanned (nextCursor is '0'). */\n\tdone: boolean\n}\n\n/**\n * Scan keys matching a pattern using cursor-based pagination.\n *\n * Unlike `KEYS`, SCAN is safe to use in production — it iterates incrementally.\n * Call repeatedly with the returned `nextCursor` until `done` is true.\n *\n * @example\n * ```ts\n * // Iterate all user keys\n * let cursor = '0'\n * do {\n * const result = await kvScan(config, { pattern: 'user:*', cursor })\n * for (const key of result.keys) console.log(key)\n * cursor = result.nextCursor\n * } while (!result.done)\n * ```\n */\nexport async function kvScan(config: SylphxConfig, options?: KvScanOptions): Promise<KvScanResult> {\n\t// Contract-derived path/method (SSOT per ADR-084).\n\t// Public SDK compatibility keeps `keys` mutable; runtime payload is the\n\t// same contract-owned array shape.\n\tconst endpoint = kvEndpoints.scan\n\treturn callApi<ContractKvScanResult>(config, endpoint.path, {\n\t\tmethod: endpoint.method,\n\t\tquery: {\n\t\t\t...(options?.pattern !== undefined && { pattern: options.pattern }),\n\t\t\t...(options?.cursor !== undefined && { cursor: options.cursor }),\n\t\t\t...(options?.count !== undefined && { count: String(options.count) }),\n\t\t},\n\t}) as Promise<KvScanResult>\n}\n\n// ============================================================================\n// Convenience — JSON helpers\n// ============================================================================\n\n/**\n * Get a JSON value by key. Automatically parses the stored JSON string.\n *\n * Returns `null` if the key does not exist or has expired.\n *\n * @example\n * ```ts\n * const profile = await kvGetJSON<UserProfile>(config, 'user:123:profile')\n * if (profile) console.log(profile.name)\n * ```\n */\nexport async function kvGetJSON<T = unknown>(config: SylphxConfig, key: string): Promise<T | null> {\n\tconst raw = await kvGet<string>(config, key)\n\tif (raw === null) return null\n\ttry {\n\t\treturn JSON.parse(raw) as T\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a JSON value by key. Automatically serializes the value to JSON.\n *\n * @example\n * ```ts\n * await kvSetJSON(config, 'user:123:profile', { name: 'Alice', plan: 'pro' }, { ex: 3600 })\n * ```\n */\nexport async function kvSetJSON<T>(\n\tconfig: SylphxConfig,\n\tkey: string,\n\tvalue: T,\n\toptions?: KvSetOptions,\n): Promise<{ ok: boolean }> {\n\treturn kvSet(config, { key, value: JSON.stringify(value), ...options })\n}\n","/**\n * Deploy Functions\n *\n * Pure functions for managing app deployments, environment variables,\n * and custom domains via the Platform Deploy API.\n *\n * Requires secret key authentication (`sk_*`).\n *\n * @example\n * ```ts\n * import { createConfig, triggerDeploy, getDeployStatus } from '@sylphx/sdk'\n *\n * const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })\n *\n * // Trigger a deployment\n * const deploy = await triggerDeploy(config, { envId: 'env_prod_xxx' })\n *\n * // Poll for completion\n * const status = await getDeployStatus(config, deploy.envId)\n * console.log(status.status) // 'building' | 'deploying' | 'success' | 'failed'\n * ```\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DeployStatus = 'queued' | 'building' | 'deploying' | 'success' | 'failed' | 'cancelled'\n\nexport interface DeployInfo {\n\t/** Deployment ID */\n\tdeploymentId: string\n\t/** Environment ID */\n\tenvId: string\n\t/** Current deployment status */\n\tstatus: DeployStatus\n\t/** Deployment URL */\n\turl?: string\n\t/** Git commit SHA */\n\tcommitSha?: string\n\t/** Git branch */\n\tbranch?: string\n\t/** Deployment started at (ISO timestamp) */\n\tstartedAt?: string\n\t/** Deployment completed at (ISO timestamp) */\n\tcompletedAt?: string\n\t/** Error message if failed */\n\terror?: string\n}\n\nexport interface TriggerDeployRequest {\n\t/** Environment ID to deploy */\n\tenvId: string\n\t/** Force rebuild without cache */\n\tforceRebuild?: boolean\n}\n\nexport interface RollbackDeployRequest {\n\t/** Environment ID to rollback */\n\tenvId: string\n\t/** Deployment ID to rollback to */\n\tdeploymentId: string\n}\n\nexport interface EnvVar {\n\t/** Environment variable key */\n\tkey: string\n\t/** Environment variable value */\n\tvalue: string\n\t/** Whether value is sensitive (masked in logs) */\n\tsensitive?: boolean\n}\n\nexport interface SetEnvVarRequest {\n\t/** Environment variable key */\n\tkey: string\n\t/** Environment variable value */\n\tvalue: string\n\t/** Whether value is sensitive/secret */\n\tsensitive?: boolean\n}\n\nexport interface DeployHistoryResponse {\n\t/** List of past deployments */\n\tdeployments: DeployInfo[]\n}\n\nexport interface BuildLog {\n\t/** Log line text */\n\ttext: string\n\t/** Log timestamp (ISO) */\n\ttimestamp?: string\n\t/** Log level */\n\tlevel?: 'info' | 'warn' | 'error'\n}\n\nexport interface BuildLogHistoryResponse {\n\t/** Log lines */\n\tlogs: BuildLog[]\n}\n\n// ============================================================================\n// Functions — Deployments\n// ============================================================================\n\n/**\n * Trigger a new deployment for an environment.\n *\n * @example\n * ```ts\n * const deploy = await triggerDeploy(config, { envId: 'env_prod_xxx' })\n * console.log(`Deployment ${deploy.deploymentId} started`)\n * ```\n */\nexport async function triggerDeploy(\n\tconfig: SylphxConfig,\n\trequest: TriggerDeployRequest,\n): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/trigger/${encodeURIComponent(request.envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: request.forceRebuild !== undefined ? { forceRebuild: request.forceRebuild } : undefined,\n\t})\n}\n\n/**\n * Get the current deployment status for an environment.\n *\n * @example\n * ```ts\n * const { status } = await getDeployStatus(config, 'env_prod_xxx')\n * if (status === 'success') {\n * console.log('Deployment succeeded!')\n * }\n * ```\n */\nexport async function getDeployStatus(config: SylphxConfig, envId: string): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/status/${encodeURIComponent(envId)}`, {\n\t\tmethod: 'GET',\n\t})\n}\n\n/**\n * Get deployment history for an environment.\n *\n * @example\n * ```ts\n * const { deployments } = await getDeployHistory(config, 'env_prod_xxx')\n * const lastDeploy = deployments[0]\n * ```\n */\nexport async function getDeployHistory(\n\tconfig: SylphxConfig,\n\tenvId: string,\n): Promise<DeployHistoryResponse> {\n\treturn callApi<DeployHistoryResponse>(\n\t\tconfig,\n\t\t`/sdk/deploy/history/${encodeURIComponent(envId)}`,\n\t\t{ method: 'GET' },\n\t)\n}\n\n/**\n * Rollback an environment to a previous deployment.\n *\n * @example\n * ```ts\n * await rollbackDeploy(config, {\n * envId: 'env_prod_xxx',\n * deploymentId: 'dep_abc123',\n * })\n * ```\n */\nexport async function rollbackDeploy(\n\tconfig: SylphxConfig,\n\trequest: RollbackDeployRequest,\n): Promise<DeployInfo> {\n\treturn callApi<DeployInfo>(config, `/sdk/deploy/rollback/${encodeURIComponent(request.envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: { deploymentId: request.deploymentId },\n\t})\n}\n\n/**\n * Get stored build log history for an environment.\n *\n * For live log streaming during an active build, use the SSE endpoint\n * directly or the `useDeployLogs` React hook.\n *\n * @example\n * ```ts\n * const { logs } = await getBuildLogHistory(config, 'env_prod_xxx')\n * for (const log of logs) {\n * console.log(log.text)\n * }\n * ```\n */\nexport async function getBuildLogHistory(\n\tconfig: SylphxConfig,\n\tenvId: string,\n): Promise<BuildLogHistoryResponse> {\n\treturn callApi<BuildLogHistoryResponse>(\n\t\tconfig,\n\t\t`/sdk/deploy/logs/${encodeURIComponent(envId)}/history`,\n\t\t{ method: 'GET' },\n\t)\n}\n\n// ============================================================================\n// Functions — Environment Variables\n// ============================================================================\n\n/**\n * List environment variables for a deployment environment.\n *\n * Sensitive values are masked in the response.\n *\n * @example\n * ```ts\n * const envVars = await listEnvVars(config, 'env_prod_xxx')\n * ```\n */\nexport async function listEnvVars(config: SylphxConfig, envId: string): Promise<EnvVar[]> {\n\tconst result = await callApi<{ envVars: EnvVar[] }>(\n\t\tconfig,\n\t\t`/sdk/deploy/envvars/${encodeURIComponent(envId)}`,\n\t\t{ method: 'GET' },\n\t)\n\treturn result.envVars\n}\n\n/**\n * Set (create or update) an environment variable.\n *\n * @example\n * ```ts\n * await setEnvVar(config, 'env_prod_xxx', {\n * key: 'DATABASE_URL',\n * value: 'postgresql://...',\n * sensitive: true,\n * })\n * ```\n */\nexport async function setEnvVar(\n\tconfig: SylphxConfig,\n\tenvId: string,\n\trequest: SetEnvVarRequest,\n): Promise<EnvVar> {\n\treturn callApi<EnvVar>(config, `/sdk/deploy/envvars/${encodeURIComponent(envId)}`, {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Delete an environment variable.\n *\n * @example\n * ```ts\n * await deleteEnvVar(config, 'env_prod_xxx', 'DATABASE_URL')\n * ```\n */\nexport async function deleteEnvVar(\n\tconfig: SylphxConfig,\n\tenvId: string,\n\tkey: string,\n): Promise<{ deleted: boolean }> {\n\treturn callApi<{ deleted: boolean }>(\n\t\tconfig,\n\t\t`/sdk/deploy/envvars/${encodeURIComponent(envId)}/${encodeURIComponent(key)}`,\n\t\t{ method: 'DELETE' },\n\t)\n}\n","/**\n * Monitoring Functions\n *\n * Pure functions for error tracking and log capture.\n * Works client-side and server-side.\n *\n * ## Industry Patterns\n * - **Error grouping via fingerprinting** — Same error only billed once (Sentry pattern)\n * - **Adaptive sampling** — Automatic sample rate adjustment based on quota usage\n * - **Breadcrumb trails** — Contextual trail leading to errors\n *\n * @example\n * ```ts\n * import { createConfig, captureException, captureMessage } from '@sylphx/sdk'\n *\n * const config = createConfig({ appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID! })\n *\n * // Capture errors\n * try {\n * await riskyOperation()\n * } catch (err) {\n * await captureException(config, err as Error)\n * }\n *\n * // Capture log messages\n * await captureMessage(config, 'User completed onboarding', { level: 'info' })\n * ```\n */\n\n// ADR-084 Wave 2d: contract now carries the Sentry-style envelope\n// (`exception: { values: ExceptionValue[] }`) plus the richer response\n// fields (`eventId`/`isNewError`/`quotaUsage`/`recommendedSampleRate`),\n// matching this SDK's production wire shape. Path is `/sdk/monitoring/*`\n// on both sides. Full contract rewire (runtime import + path derivation)\n// is Agent J's follow-up; this file keeps its SDK-native types until\n// then.\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type MonitoringSeverity = 'fatal' | 'error' | 'warning' | 'info'\n\nexport interface ExceptionFrame {\n\t/** Source filename */\n\tfilename?: string\n\t/** Function name */\n\tfunction?: string\n\t/** Line number */\n\tlineno?: number\n\t/** Column number */\n\tcolno?: number\n}\n\nexport interface ExceptionValue {\n\t/** Exception class/type (e.g., \"TypeError\") */\n\ttype: string\n\t/** Exception message */\n\tvalue: string\n\t/** Stack trace frames (innermost first) */\n\tstacktrace?: { frames?: ExceptionFrame[] }\n}\n\nexport interface Breadcrumb {\n\t/** Breadcrumb type (e.g., \"navigation\", \"http\", \"ui.click\") */\n\ttype?: string\n\t/** Log level */\n\tlevel?: MonitoringSeverity\n\t/** Breadcrumb message */\n\tmessage?: string\n\t/** Breadcrumb data */\n\tdata?: Record<string, unknown>\n\t/** Unix timestamp (seconds) */\n\ttimestamp?: number\n}\n\nexport interface CaptureExceptionRequest {\n\t/** Exception value(s) — first is primary, rest are chained causes */\n\texception: { values: ExceptionValue[] }\n\t/** Severity level (default: \"error\") */\n\tlevel?: MonitoringSeverity\n\t/** Current page route */\n\troute?: string\n\t/** User agent string */\n\tuserAgent?: string\n\t/** App release version */\n\trelease?: string\n\t/** Environment name (e.g., \"production\", \"staging\") */\n\tenvironment?: string\n\t/** Custom tags for filtering */\n\ttags?: Record<string, string>\n\t/** Extra context data */\n\textra?: Record<string, unknown>\n\t/** Breadcrumb trail leading to the error */\n\tbreadcrumbs?: Breadcrumb[]\n\t/** Custom fingerprint for grouping (overrides automatic grouping) */\n\tfingerprint?: string[]\n}\n\nexport interface CaptureMessageRequest {\n\t/** Log message */\n\tmessage: string\n\t/** Severity level (default: \"info\") */\n\tlevel?: MonitoringSeverity\n\t/** Current page route */\n\troute?: string\n\t/** App release version */\n\trelease?: string\n\t/** Custom tags for filtering */\n\ttags?: Record<string, string>\n\t/** Extra context data */\n\textra?: Record<string, unknown>\n}\n\nexport interface MonitoringResponse {\n\t/** Internal event ID */\n\teventId: string\n\t/** Whether this is a new unique error (true = billed, false = duplicate = free) */\n\tisNewError: boolean\n\t/** Current quota usage percentage (0-100+). Present when >= 50%. */\n\tquotaUsage?: number\n\t/**\n\t * Recommended client-side sample rate (0.0-1.0).\n\t * Reduce your error capture rate to this value when quota is high.\n\t * Present when quotaUsage >= 50%.\n\t */\n\trecommendedSampleRate?: number\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Convert a native Error object to ExceptionValue format.\n */\nfunction errorToExceptionValue(error: Error): ExceptionValue {\n\tconst frames: ExceptionFrame[] = []\n\n\tif (error.stack) {\n\t\t// Parse V8/SpiderMonkey style stack traces\n\t\tconst lines = error.stack.split('\\n').slice(1)\n\t\tfor (const line of lines) {\n\t\t\t// V8: \" at functionName (file:line:col)\"\n\t\t\tconst v8Match = line.match(/^\\s+at\\s+(.+?)\\s+\\((.+):(\\d+):(\\d+)\\)$/)\n\t\t\tif (v8Match) {\n\t\t\t\tframes.push({\n\t\t\t\t\tfunction: v8Match[1],\n\t\t\t\t\tfilename: v8Match[2],\n\t\t\t\t\tlineno: Number(v8Match[3]),\n\t\t\t\t\tcolno: Number(v8Match[4]),\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// V8 (no function): \" at file:line:col\"\n\t\t\tconst v8AnonMatch = line.match(/^\\s+at\\s+(.+):(\\d+):(\\d+)$/)\n\t\t\tif (v8AnonMatch) {\n\t\t\t\tframes.push({\n\t\t\t\t\tfilename: v8AnonMatch[1],\n\t\t\t\t\tlineno: Number(v8AnonMatch[2]),\n\t\t\t\t\tcolno: Number(v8AnonMatch[3]),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: error.name || 'Error',\n\t\tvalue: error.message,\n\t\tstacktrace: frames.length > 0 ? { frames } : undefined,\n\t}\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Capture an exception for error tracking.\n *\n * Errors with the same fingerprint are grouped automatically.\n * Duplicate occurrences of the same error are FREE (only new unique\n * errors count against your quota).\n *\n * @example\n * ```ts\n * try {\n * await processPayment(amount)\n * } catch (err) {\n * const result = await captureException(config, err as Error, {\n * tags: { paymentProvider: 'stripe' },\n * extra: { amount, userId },\n * })\n * }\n * ```\n */\nexport async function captureException(\n\tconfig: SylphxConfig,\n\terror: Error,\n\toptions: Omit<CaptureExceptionRequest, 'exception'> = {},\n): Promise<MonitoringResponse> {\n\t// Contract shape (Sentry envelope + rich response) reconciled with this\n\t// SDK in ADR-084 Wave 2d. Paths are already aligned (`/sdk/monitoring/*`).\n\tconst exceptionValue = errorToExceptionValue(error)\n\tconst request: CaptureExceptionRequest = {\n\t\t...options,\n\t\texception: { values: [exceptionValue] },\n\t}\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/exception', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Capture an exception with full control over the exception payload.\n *\n * Use this for structured exception capture with custom types, chained\n * causes, or when not working with native Error objects.\n */\nexport async function captureExceptionRaw(\n\tconfig: SylphxConfig,\n\trequest: CaptureExceptionRequest,\n): Promise<MonitoringResponse> {\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/exception', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n\n/**\n * Capture a log message for monitoring.\n *\n * Like `captureException` but for non-error events (warnings, info, etc.).\n * Messages with the same content are grouped automatically.\n *\n * @example\n * ```ts\n * // Info log\n * await captureMessage(config, 'Payment webhook received', {\n * level: 'info',\n * tags: { provider: 'stripe', event: 'payment.succeeded' },\n * })\n *\n * // Warning\n * await captureMessage(config, 'Slow query detected', {\n * level: 'warning',\n * extra: { queryMs: 2400, query: sql },\n * })\n * ```\n */\nexport async function captureMessage(\n\tconfig: SylphxConfig,\n\tmessage: string,\n\toptions: Omit<CaptureMessageRequest, 'message'> = {},\n): Promise<MonitoringResponse> {\n\tconst request: CaptureMessageRequest = { ...options, message }\n\treturn callApi<MonitoringResponse>(config, '/sdk/monitoring/message', {\n\t\tmethod: 'POST',\n\t\tbody: request,\n\t})\n}\n","/**\n * Sandbox Client\n *\n * Ephemeral compute sandbox API — SOTA direct-connection design.\n *\n * ## Architecture\n *\n * POST /sandboxes → Platform provisions sandbox, waits for runtime readiness,\n * returns { endpoint, token }. All subsequent exec/files/pty operations\n * go DIRECTLY to the sandbox exec-server — Platform is not in the data path.\n *\n * ```\n * SDK.create() ──→ Platform API (lifecycle only)\n * ↓ returns endpoint + token\n * SDK.exec() ──→ sandbox exec-server (direct, SSE stream)\n * SDK.files ──→ sandbox exec-server (direct, REST)\n * SDK.pty() ──→ sandbox exec-server (direct, WebSocket)\n * ```\n *\n * ## Usage\n *\n * ```typescript\n * import { createServerClient, SandboxClient } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Create sandbox (Platform waits for the runtime before returning)\n * const sandbox = await SandboxClient.create(config)\n *\n * // Stream exec output in real-time\n * for await (const event of sandbox.exec(['npm', 'install'])) {\n * if (event.type === 'stdout') process.stdout.write(event.data)\n * if (event.type === 'exit') console.log('exit', event.exitCode)\n * }\n *\n * // File operations\n * await sandbox.files.write('/workspace/app.py', 'print(\"hello\")')\n * const content = await sandbox.files.read('/workspace/output.txt')\n *\n * // Terminate when done\n * await sandbox.terminate()\n * ```\n *\n * ## Auth\n * Requires sk_* secret key (server-side only).\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\nimport { SANDBOX_CREATE_TIMEOUT_MS } from './constants'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SandboxOptions {\n\t/** OCI image from a connected registry. Omit to use the environment default. */\n\timage?: string\n\t/**\n\t * Request timeout for the create lifecycle call.\n\t * Defaults to the SDK's sandbox-create budget, which matches the Runtime\n\t * API readiness wait plus transport margin.\n\t */\n\tcreateTimeoutMs?: number\n\t/** Idle timeout in ms before auto-termination (default: 300_000 = 5 min) */\n\tidleTimeoutMs?: number\n\t/** Scratch storage size in GiB at /data, scoped to the sandbox lifecycle. */\n\tstorageGi?: number\n\t/** Managed machine size. Defaults to `standard`. */\n\tmachine?: SandboxMachineSize\n\t/** Environment variables injected into the sandbox container */\n\tenv?: Record<string, string>\n\t/**\n\t * Shared volume mounts from org-level managed volumes.\n\t * Use volumes created with sharing=\"shared\" when multiple sandboxes need\n\t * concurrent filesystem access.\n\t */\n\tvolumeMounts?: Array<{\n\t\t/** Volume resource ID (from `sylphx volumes list`) */\n\t\tvolumeId: string\n\t\t/** Absolute path inside the container (e.g. \"/shared\") */\n\t\tmountPath: string\n\t\t/** Optional sub-path within the volume */\n\t\tsubPath?: string\n\t\t/** Read-only mount (default: false) */\n\t\treadOnly?: boolean\n\t}>\n\t/**\n\t * Platform-managed workspace mounts.\n\t * Prefer this over raw volumeMounts for agent/IDE durable filesystems: the\n\t * Platform resolves the workspace's backing volume, PVC, quota, and lineage.\n\t */\n\tworkspaceMounts?: Array<{\n\t\t/** Workspace ID from the managed workspaces API. */\n\t\tworkspaceId: string\n\t\t/** Absolute path inside the container (e.g. \"/workspace\") */\n\t\tmountPath: string\n\t\t/** Optional workspace-relative sub-path view. */\n\t\tsubPath?: string\n\t\t/** Read-only mount (default: false) */\n\t\treadOnly?: boolean\n\t}>\n}\n\nexport type SandboxMachineSize = MachineSize\n\n/** SSE event emitted by sandbox.exec() */\nexport type ExecEvent =\n\t| { type: 'stdout'; data: string }\n\t| { type: 'stderr'; data: string }\n\t| { type: 'exit'; exitCode: number; durationMs: number; timedOut?: boolean }\n\t| { type: 'error'; message: string }\n\n/** Non-streaming exec result (all output collected, returned at once) */\nexport interface ExecResult {\n\tstdout: string\n\tstderr: string\n\texitCode: number\n\tdurationMs: number\n}\n\n/** @deprecated Use ExecResult */\nexport type CommandResult = ExecResult\n\n/** @deprecated File operations are now handled by SandboxFiles class */\nexport interface SandboxFile {\n\tpath: string\n\tcontent: string\n\tencoding?: 'utf8' | 'base64'\n}\n\nexport interface ExecOptions {\n\tcwd?: string\n\tenv?: Record<string, string>\n\ttimeout?: number\n\tstdin?: string\n}\n\n// =============================================================================\n// Process API Types\n// =============================================================================\n\nexport interface ProcessStartOptions {\n\t/** Command + args (e.g. ['npm', 'install']) */\n\tcommand: string[]\n\t/** Working directory */\n\tcwd?: string\n\t/** Environment variables */\n\tenv?: Record<string, string>\n\t/** Hard timeout in seconds (0 = no timeout) */\n\ttimeoutSeconds?: number\n\t/** Open stdin pipe for writing */\n\tstdin?: boolean\n}\n\nexport interface ProcessInfo {\n\tid: string\n\tpid: number\n\tcommand: string[]\n\tcwd: string\n\tstatus: 'running' | 'exited' | 'killed' | 'timeout'\n\texitCode: number | null\n\tsignal: string | null\n\tstartedAt: string\n\texitedAt: string | null\n\tdurationMs: number | null\n\tstdout: string\n\tstderr: string\n}\n\nexport interface ProcessSummary {\n\tid: string\n\tpid: number\n\tcommand: string[]\n\tstatus: 'running' | 'exited' | 'killed' | 'timeout'\n\texitCode: number | null\n\tstartedAt: string\n\texitedAt: string | null\n\tdurationMs: number | null\n}\n\n/** SSE event from a process stream */\nexport type ProcessEvent =\n\t| { type: 'stdout'; pid: number; data: string }\n\t| { type: 'stderr'; pid: number; data: string }\n\t| { type: 'exit'; pid: number; exitCode: number }\n\n// =============================================================================\n// Watch API Types\n// =============================================================================\n\nexport interface WatchOptions {\n\t/** Path to watch (relative to /workspace or absolute) */\n\tpath: string\n\t/** Watch subdirectories recursively (default: true) */\n\trecursive?: boolean\n\t/** Additional patterns to ignore */\n\tignore?: string[]\n}\n\nexport interface WatchEntry {\n\tpath: string\n\trecursive: boolean\n\tcreatedAt: string\n}\n\n/** File change event delivered via SSE /events stream */\nexport interface FileEvent {\n\ttype: 'file'\n\tpath: string\n\tevent: 'created' | 'modified' | 'deleted'\n}\n\n// =============================================================================\n// Browser API Types (persistent headful CDP sessions — Platform ADR-205)\n// =============================================================================\n\nexport type BrowserSessionStatus = 'starting' | 'ready' | 'exited'\n\n/** Persistent headful browser session running inside the sandbox. */\nexport interface BrowserSessionInfo {\n\tid: string\n\tstatus: BrowserSessionStatus\n\t/** OS pid of the Chrome browser process (0 if not surfaced). */\n\tpid: number\n\t/** Remote-debugging port the headful Chrome listens on (loopback, private to the sandbox). */\n\tdebugPort: number\n\t/**\n\t * Sandbox-internal loopback CDP/DevTools WebSocket endpoint (ws://127.0.0.1:…).\n\t * NOT reachable from outside the sandbox — connect via `connectUrl()` /\n\t * `connect()` which route through the authenticated proxy. Surfaced for\n\t * introspection only.\n\t */\n\tcdpEndpoint: string | null\n\t/** Persistent profile directory on the workspace volume. */\n\tuserDataDir: string\n\t/** Virtual display Chrome renders to. */\n\tdisplay: string\n\tstartedAt: string\n\texitedAt: string | null\n}\n\nexport interface BrowserCreateOptions {\n\t/**\n\t * Persistent profile to attach (defaults to the new session id). Pass an\n\t * explicit, stable value to deliberately reuse cookies/localStorage/auth\n\t * across sessions — the profile lives on the workspace volume and survives\n\t * session teardown.\n\t */\n\tprofile?: string\n}\n\nexport interface SandboxRecord {\n\tid: string\n\tstatus: 'starting' | 'running' | 'idle' | 'terminated' | 'error'\n\timage: string\n\t/** Managed machine size selected for this sandbox. */\n\tmachine: SandboxMachineSize | null\n\t/** Public HTTPS endpoint: https://sbx-xxx.sandboxes.sylphx.app */\n\tendpoint: string | null\n\t/** Per-sandbox RS256 JWT for direct exec-server authentication */\n\ttoken: string | null\n\tprojectId: string\n\tidleTimeoutMs: number\n\tcreatedAt: string\n\tstartedAt: string | null\n\texpiresAt: string | null\n\tterminatedAt: string | null\n}\n\n// =============================================================================\n// Files namespace\n// =============================================================================\n\nexport class SandboxFiles {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Write a file to the sandbox filesystem. */\n\tasync write(\n\t\tpath: string,\n\t\tcontent: string | Buffer,\n\t\tencoding: 'utf8' | 'base64' = 'utf8',\n\t): Promise<void> {\n\t\tconst contentStr = Buffer.isBuffer(content) ? content.toString('base64') : content\n\t\tconst effectiveEncoding = Buffer.isBuffer(content) ? 'base64' : encoding\n\n\t\tconst res = await fetch(`${this.endpoint}/files`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ path, content: contentStr, encoding: effectiveEncoding }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.write failed: ${await res.text()}`)\n\t}\n\n\t/** Read a file from the sandbox filesystem. Returns content as string. */\n\tasync read(path: string): Promise<string> {\n\t\tconst res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.read failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { content: string }\n\t\treturn data.content\n\t}\n\n\t/** Delete a file from the sandbox filesystem. */\n\tasync delete(path: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.delete failed: ${await res.text()}`)\n\t}\n\n\t/** List files in a directory. */\n\tasync list(path = '/'): Promise<string[]> {\n\t\tconst res = await fetch(`${this.endpoint}/list?path=${encodeURIComponent(path)}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`files.list failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { files: string[] }\n\t\treturn data.files\n\t}\n}\n\n// =============================================================================\n// Process namespace\n// =============================================================================\n\nexport class SandboxProcesses {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Spawn a new tracked process. Returns processId + pid immediately. */\n\tasync start(opts: ProcessStartOptions): Promise<{ id: string; pid: number }> {\n\t\tconst res = await fetch(`${this.endpoint}/process/start`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(opts),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.start failed: ${await res.text()}`)\n\t\treturn (await res.json()) as { id: string; pid: number }\n\t}\n\n\t/** List all tracked processes. */\n\tasync list(): Promise<ProcessSummary[]> {\n\t\tconst res = await fetch(`${this.endpoint}/process/list`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.list failed: ${await res.text()}`)\n\t\treturn (await res.json()) as ProcessSummary[]\n\t}\n\n\t/** Get full process info including buffered output. */\n\tasync get(processId: string): Promise<ProcessInfo> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.get failed: ${await res.text()}`)\n\t\treturn (await res.json()) as ProcessInfo\n\t}\n\n\t/** Send a signal to a process. */\n\tasync kill(processId: string, signal: string = 'SIGTERM'): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/kill`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ signal }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.kill failed: ${await res.text()}`)\n\t}\n\n\t/** Write to process stdin. */\n\tasync writeStdin(processId: string, data: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/input`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ data }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.writeStdin failed: ${await res.text()}`)\n\t}\n\n\t/**\n\t * Wait for a process to complete and return its final info.\n\t * Polls every 500ms until status is no longer 'running'.\n\t *\n\t * For real-time output, use stream() instead.\n\t */\n\tasync wait(processId: string, timeoutMs = 300_000): Promise<ProcessInfo> {\n\t\tconst deadline = Date.now() + timeoutMs\n\t\twhile (Date.now() < deadline) {\n\t\t\tconst info = await this.get(processId)\n\t\t\tif (info.status !== 'running') return info\n\t\t\tawait new Promise((r) => setTimeout(r, 500))\n\t\t}\n\t\tthrow new Error(`Timed out waiting for process ${processId} to complete (${timeoutMs}ms)`)\n\t}\n\n\t/** Stream process output as async iterable SSE events. */\n\tasync *stream(processId: string): AsyncGenerator<ProcessEvent> {\n\t\tconst res = await fetch(`${this.endpoint}/process/${processId}/stream`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`process.stream failed: ${await res.text()}`)\n\t\tif (!res.body) throw new Error('process.stream: no response body')\n\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? ''\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(line.slice(6)) as ProcessEvent\n\t\t\t\t\t\t\tyield event\n\t\t\t\t\t\t\tif (event.type === 'exit') return\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* skip malformed */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n}\n\n// =============================================================================\n// Watch namespace\n// =============================================================================\n\nexport class SandboxWatch {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/** Start watching a path. Events delivered via sandbox.events() SSE stream. */\n\tasync add(opts: WatchOptions): Promise<WatchEntry> {\n\t\tconst res = await fetch(`${this.endpoint}/watch`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(opts),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.add failed: ${await res.text()}`)\n\t\treturn (await res.json()) as WatchEntry\n\t}\n\n\t/** List active watches. */\n\tasync list(): Promise<WatchEntry[]> {\n\t\tconst res = await fetch(`${this.endpoint}/watch`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.list failed: ${await res.text()}`)\n\t\tconst data = (await res.json()) as { watches: WatchEntry[] }\n\t\treturn data.watches\n\t}\n\n\t/** Stop watching a path. */\n\tasync remove(path: string): Promise<void> {\n\t\tconst res = await fetch(`${this.endpoint}/watch?path=${encodeURIComponent(path)}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`watch.remove failed: ${await res.text()}`)\n\t}\n}\n\n// =============================================================================\n// Browser namespace (persistent headful CDP sessions — Platform ADR-205)\n// =============================================================================\n\n/**\n * Provision and drive persistent, headful, stealth browser sessions inside the\n * sandbox. The Platform owns the connectable browser primitive; the customer\n * app owns session→task mapping, navigation, and scraping — it drives the live\n * session over the Chrome DevTools Protocol (CDP).\n *\n * Lifecycle is thin pass-through to the sandbox exec-server's `/browser` routes\n * (direct connection, per-sandbox JWT — Platform is not in the data path). The\n * browser's CDP port is bound to loopback inside the sandbox; `connectUrl()` /\n * `connect()` reach it ONLY through the exec-server's authenticated CDP proxy.\n *\n * @example\n * ```typescript\n * const session = await sandbox.browser!.create({ profile: 'acme-login' })\n * // Drive it with your CDP client of choice:\n * import { chromium } from 'playwright'\n * const browser = await chromium.connectOverCDP(sandbox.browser!.connectUrl(session.id))\n * // ... navigate / scrape ...\n * await sandbox.browser!.destroy(session.id)\n * ```\n */\nexport class SandboxBrowser {\n\tconstructor(\n\t\tprivate readonly endpoint: string,\n\t\tprivate readonly token: string,\n\t) {}\n\n\tprivate authHeader(): Record<string, string> {\n\t\treturn { Authorization: `Bearer ${this.token}` }\n\t}\n\n\t/**\n\t * Create + start a persistent headful browser session. Resolves once the\n\t * session's CDP endpoint is available (201 from the exec-server).\n\t */\n\tasync create(opts: BrowserCreateOptions = {}): Promise<BrowserSessionInfo> {\n\t\tconst res = await fetch(`${this.endpoint}/browser`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.authHeader(), 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ profile: opts.profile }),\n\t\t})\n\t\tif (!res.ok) throw new Error(`browser.create failed (${res.status}): ${await res.text()}`)\n\t\treturn (await res.json()) as BrowserSessionInfo\n\t}\n\n\t/** List live browser sessions in this sandbox. */\n\tasync list(): Promise<BrowserSessionInfo[]> {\n\t\tconst res = await fetch(`${this.endpoint}/browser`, { headers: this.authHeader() })\n\t\tif (!res.ok) throw new Error(`browser.list failed (${res.status}): ${await res.text()}`)\n\t\treturn (await res.json()) as BrowserSessionInfo[]\n\t}\n\n\t/** Get full info for a session, or null if it does not exist. */\n\tasync get(sessionId: string): Promise<BrowserSessionInfo | null> {\n\t\tconst res = await fetch(`${this.endpoint}/browser/${encodeURIComponent(sessionId)}`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (res.status === 404) return null\n\t\tif (!res.ok) throw new Error(`browser.get failed (${res.status}): ${await res.text()}`)\n\t\treturn (await res.json()) as BrowserSessionInfo\n\t}\n\n\t/**\n\t * Build the authenticated CDP connect URL for a session — a `wss://…` URL on\n\t * the sandbox's public endpoint that proxies to the sandbox-internal loopback CDP. Pass\n\t * it to any CDP client (`chromium.connectOverCDP(url)` / raw `WebSocket`).\n\t *\n\t * The token is carried as a query param because WebSocket clients cannot set\n\t * custom headers in browsers (equally secure over TLS). The URL is derived\n\t * locally from the session id, so this is synchronous and does no I/O. For a\n\t * not-yet-ready session the upstream proxy returns HTTP 409 at connect time.\n\t */\n\tconnectUrl(sessionId: string): string {\n\t\tconst wsBase = this.endpoint.replace(/^http(s?):\\/\\//, (_m, s) => (s ? 'wss://' : 'ws://'))\n\t\tconst path = `/browser/${encodeURIComponent(sessionId)}/cdp/connect`\n\t\treturn `${wsBase}${path}?token=${encodeURIComponent(this.token)}`\n\t}\n\n\t/**\n\t * Open a raw WebSocket to a session's CDP endpoint through the authenticated\n\t * proxy. Most callers want `connectUrl()` to hand to a higher-level CDP\n\t * client; use this only for a hand-rolled CDP connection.\n\t */\n\tconnect(sessionId: string): WebSocket {\n\t\treturn new WebSocket(this.connectUrl(sessionId))\n\t}\n\n\t/**\n\t * Fetch the CDP connection details from the exec-server: the sandbox-internal loopback\n\t * `cdpEndpoint` (introspection only), the `debugPort`, and the authenticated\n\t * `connectUrl`. Throws 409 if the session is not ready yet, 404 if unknown.\n\t */\n\tasync cdp(\n\t\tsessionId: string,\n\t): Promise<{ cdpEndpoint: string; debugPort: number; connectUrl: string }> {\n\t\tconst res = await fetch(`${this.endpoint}/browser/${encodeURIComponent(sessionId)}/cdp`, {\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (!res.ok) throw new Error(`browser.cdp failed (${res.status}): ${await res.text()}`)\n\t\tconst data = (await res.json()) as { cdpEndpoint: string; debugPort: number }\n\t\treturn { ...data, connectUrl: this.connectUrl(sessionId) }\n\t}\n\n\t/**\n\t * Destroy a session. The browser is torn down; its persistent profile on the\n\t * workspace volume is preserved (reuse it via `create({ profile })`).\n\t * Returns false if the session did not exist.\n\t */\n\tasync destroy(sessionId: string): Promise<boolean> {\n\t\tconst res = await fetch(`${this.endpoint}/browser/${encodeURIComponent(sessionId)}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.authHeader(),\n\t\t})\n\t\tif (res.status === 404) return false\n\t\tif (!res.ok) throw new Error(`browser.destroy failed (${res.status}): ${await res.text()}`)\n\t\treturn true\n\t}\n}\n\n// =============================================================================\n// SandboxClient\n// =============================================================================\n\nexport class SandboxClient {\n\treadonly id: string\n\tprivate readonly config: SylphxConfig\n\n\t/** Public endpoint from Platform (may be null for sandboxes from pool pre-v2) */\n\treadonly endpoint: string | null\n\t/** Per-sandbox JWT for direct exec-server auth */\n\treadonly token: string | null\n\n\t/** File operations (direct to exec-server) */\n\treadonly files: SandboxFiles | null\n\n\t/** Concurrent process management (direct to exec-server) */\n\treadonly processes: SandboxProcesses | null\n\n\t/** Filesystem watch management (direct to exec-server) */\n\treadonly watch: SandboxWatch | null\n\n\t/** Persistent headful browser sessions (direct to exec-server — ADR-205) */\n\treadonly browser: SandboxBrowser | null\n\n\tprivate constructor(\n\t\tid: string,\n\t\tconfig: SylphxConfig,\n\t\tendpoint: string | null,\n\t\ttoken: string | null,\n\t) {\n\t\tthis.id = id\n\t\tthis.config = config\n\t\tthis.endpoint = endpoint\n\t\tthis.token = token\n\t\tthis.files = endpoint && token ? new SandboxFiles(endpoint, token) : null\n\t\tthis.processes = endpoint && token ? new SandboxProcesses(endpoint, token) : null\n\t\tthis.watch = endpoint && token ? new SandboxWatch(endpoint, token) : null\n\t\tthis.browser = endpoint && token ? new SandboxBrowser(endpoint, token) : null\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Factory\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Create a new sandbox.\n\t *\n\t * Platform provisions the sandbox runtime, waits for readiness, and returns\n\t * { endpoint, token } once the sandbox is fully ready to accept traffic.\n\t * No client-side polling required.\n\t */\n\tstatic async create(config: SylphxConfig, options?: SandboxOptions): Promise<SandboxClient> {\n\t\tconst record = await callApi<SandboxRecord>(config, '/sandboxes', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: {\n\t\t\t\timage: options?.image,\n\t\t\t\tidleTimeoutMs: options?.idleTimeoutMs ?? 300_000,\n\t\t\t\tmachine: options?.machine ?? 'standard',\n\t\t\t\tenv: options?.env,\n\t\t\t\tstorage:\n\t\t\t\t\toptions?.storageGi !== undefined\n\t\t\t\t\t\t? { enabled: true, sizeGi: options.storageGi }\n\t\t\t\t\t\t: undefined,\n\t\t\t\tvolumeMounts: options?.volumeMounts,\n\t\t\t\tworkspaceMounts: options?.workspaceMounts,\n\t\t\t},\n\t\t\ttimeout: options?.createTimeoutMs ?? SANDBOX_CREATE_TIMEOUT_MS,\n\t\t})\n\n\t\treturn new SandboxClient(record.id, config, record.endpoint, record.token)\n\t}\n\n\t/**\n\t * Reconnect to an existing sandbox by ID.\n\t * Fetches the current status to get the endpoint + token.\n\t */\n\tstatic async fromId(config: SylphxConfig, sandboxId: string): Promise<SandboxClient> {\n\t\tconst record = await callApi<SandboxRecord>(config, `/sandboxes/${sandboxId}`, {\n\t\t\tmethod: 'GET',\n\t\t})\n\t\treturn new SandboxClient(record.id, config, record.endpoint, record.token)\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Lifecycle\n\t// ---------------------------------------------------------------------------\n\n\tasync getStatus(): Promise<SandboxRecord> {\n\t\treturn callApi<SandboxRecord>(this.config, `/sandboxes/${this.id}`, { method: 'GET' })\n\t}\n\n\tasync terminate(): Promise<void> {\n\t\tawait callApi<{ success: boolean }>(this.config, `/sandboxes/${this.id}`, { method: 'DELETE' })\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Exec — SSE streaming (primary)\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Execute a command and stream output as async iterable SSE events.\n\t *\n\t * **Stateless mode**: each exec() call runs in an isolated bash invocation.\n\t * Shell state (CWD changes, exported env vars, functions) is NOT preserved\n\t * between calls.\n\t *\n\t * For state-preserving execution (CWD, env), use `run()` which runs in the\n\t * persistent active shell and returns the result once complete.\n\t *\n\t * For streaming + state-preserving (advanced), combine `sandbox.events()` with `run()`:\n\t * ```typescript\n\t * const eventStream = sandbox.events({ type: 'stdout' })\n\t * sandbox.run(['npm', 'install']) // don't await yet\n\t * for await (const ev of eventStream) { ... }\n\t * ```\n\t *\n\t * @example\n\t * ```typescript\n\t * for await (const event of sandbox.exec(['npm', 'install'])) {\n\t * if (event.type === 'stdout') process.stdout.write(event.data)\n\t * if (event.type === 'exit') console.log('Done:', event.exitCode)\n\t * }\n\t * ```\n\t */\n\tasync *exec(command: string[], options?: ExecOptions): AsyncGenerator<ExecEvent> {\n\t\tthis.assertDirect()\n\n\t\t// POST /exec with stateless:true, stream:true → exec-server returns SSE response.\n\t\t// 'stateless' runs in an isolated bash process (not the active shell).\n\t\t// Without stateless:true, /exec runs in the active shell and returns JSON — not SSE.\n\t\tconst res = await fetch(`${this.endpoint!}/exec`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${this.token!}`,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t},\n\t\t\tbody: JSON.stringify({ command, ...options, stateless: true, stream: true }),\n\t\t})\n\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`exec failed (${res.status}): ${await res.text()}`)\n\t\t}\n\t\tif (!res.body) throw new Error('exec: no response body')\n\n\t\t// Parse SSE stream\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? '' // Keep incomplete last line\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(line.slice(6)) as ExecEvent\n\t\t\t\t\t\t\tyield event\n\t\t\t\t\t\t\tif (event.type === 'exit' || event.type === 'error') return\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Malformed SSE data — skip\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n\n\t/**\n\t * Execute a command and collect all output (non-streaming).\n\t * Convenience wrapper over exec() for simple use cases.\n\t *\n\t * For long-running commands, prefer exec() to stream output incrementally.\n\t */\n\tasync run(command: string[], options?: ExecOptions): Promise<ExecResult> {\n\t\tlet stdout = ''\n\t\tlet stderr = ''\n\t\tlet exitCode = 1\n\t\tlet durationMs = 0\n\n\t\tfor await (const event of this.exec(command, options)) {\n\t\t\tif (event.type === 'stdout') stdout += event.data\n\t\t\telse if (event.type === 'stderr') stderr += event.data\n\t\t\telse if (event.type === 'exit') {\n\t\t\t\texitCode = event.exitCode\n\t\t\t\tdurationMs = event.durationMs\n\t\t\t}\n\t\t}\n\n\t\treturn { stdout, stderr, exitCode, durationMs }\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Events — Unified SSE stream\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Subscribe to the unified event stream (SSE).\n\t *\n\t * Receives all sandbox events: stdout, stderr, exit, port, file, shell, resource.\n\t * Filter by type/pid/shellId using query params.\n\t *\n\t * @example\n\t * ```typescript\n\t * for await (const event of sandbox.events({ type: 'file' })) {\n\t * console.log('File changed:', event.path, event.event)\n\t * }\n\t * ```\n\t */\n\tasync *events(filter?: {\n\t\ttype?: 'stdout' | 'stderr' | 'exit' | 'port' | 'file' | 'shell' | 'resource'\n\t\tpid?: number\n\t\tshellId?: string\n\t}): AsyncGenerator<Record<string, unknown>> {\n\t\tthis.assertDirect()\n\n\t\tconst params = new URLSearchParams()\n\t\tif (filter?.type) params.set('type', filter.type)\n\t\tif (filter?.pid !== undefined) params.set('pid', String(filter.pid))\n\t\tif (filter?.shellId) params.set('shellId', filter.shellId)\n\n\t\tconst qs = params.toString()\n\t\tconst url = `${this.endpoint!}/events${qs ? `?${qs}` : ''}`\n\n\t\tconst res = await fetch(url, {\n\t\t\theaders: { Authorization: `Bearer ${this.token!}` },\n\t\t})\n\n\t\tif (!res.ok) throw new Error(`events failed (${res.status}): ${await res.text()}`)\n\t\tif (!res.body) throw new Error('events: no response body')\n\n\t\tconst decoder = new TextDecoder()\n\t\tconst reader = res.body.getReader()\n\t\tlet buffer = ''\n\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split('\\n')\n\t\t\t\tbuffer = lines.pop() ?? ''\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('data: ')) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tyield JSON.parse(line.slice(6)) as Record<string, unknown>\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* skip malformed */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// PTY — Interactive terminal (WebSocket)\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * Open an interactive PTY session (WebSocket).\n\t *\n\t * Returns a WebSocket connected to a bash shell in the sandbox.\n\t * Token is passed as a query param (WebSocket doesn't support custom headers in browsers).\n\t *\n\t * @example\n\t * ```typescript\n\t * const ws = await sandbox.pty()\n\t * ws.on('message', (data) => process.stdout.write(JSON.parse(data).data))\n\t * ws.send(JSON.stringify({ type: 'input', data: 'ls -la\\n' }))\n\t * ws.send(JSON.stringify({ type: 'resize', cols: 120, rows: 40 }))\n\t * ```\n\t */\n\tpty(): WebSocket {\n\t\tthis.assertDirect()\n\n\t\tconst wsEndpoint = this.endpoint!.replace(/^https:\\/\\//, 'wss://').replace(\n\t\t\t/^http:\\/\\//,\n\t\t\t'ws://',\n\t\t)\n\n\t\treturn new WebSocket(`${wsEndpoint}/pty?token=${encodeURIComponent(this.token!)}`)\n\t}\n\n\t// ---------------------------------------------------------------------------\n\t// Private\n\t// ---------------------------------------------------------------------------\n\n\tprivate assertDirect(): void {\n\t\tif (!this.endpoint || !this.token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Sandbox endpoint/token not available. ' +\n\t\t\t\t\t'This sandbox was created with an older SDK version or does not have a public endpoint.',\n\t\t\t)\n\t\t}\n\t}\n}\n","/**\n * Runs Client (ADR-040, formerly Workers)\n *\n * Fire-and-forget batch compute API (Modal-style run-to-completion jobs).\n *\n * Runs are ephemeral isolated jobs that run to completion. Use them for:\n * - ML training folds (walk-forward cross-validation)\n * - Data processing pipelines\n * - Batch inference\n * - Any parallelisable CPU-heavy work\n *\n * ## Usage\n *\n * ### Single worker\n * ```typescript\n * import { createServerClient, RunsClient } from '@sylphx/sdk'\n *\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * const run = await RunsClient.run(config, {\n * image: 'ghcr.io/acme/my-trainer:sha-abc123',\n * command: ['python', 'train.py', '--fold', '0'],\n * machine: 'large',\n * timeoutSeconds: 3600,\n * })\n *\n * const result = await run.wait()\n * console.log(result.exitCode) // 0\n * console.log(result.stdout) // captured stdout\n * ```\n *\n * ### Parallel workers (walk-forward training)\n * ```typescript\n * const workers = await Promise.all(\n * folds.map((fold) =>\n * RunsClient.run(config, {\n * image: 'ghcr.io/acme/trainer:sha-abc123',\n * command: ['python', 'train.py', '--fold', String(fold.id)],\n * env: { FOLD_ID: String(fold.id), DATABASE_URL: process.env.DATABASE_URL! },\n * machine: 'large',\n * volumeMounts: [{ volumeId: sharedCacheVolumeId, mountPath: '/cache' }],\n * timeoutSeconds: 7200,\n * }),\n * ),\n * )\n *\n * const results = await Promise.all(workers.map((w) => w.wait()))\n * const failures = results.filter((r) => r.exitCode !== 0)\n * ```\n *\n * ## Architecture\n *\n * - Workers are isolated one-shot runs with no automatic restarts\n * - Images come from connected registries and are scanned before execution\n * - Volumes: org-level volume resources mounted into the run\n * - single-writer for one active writer at a time\n * - shared for concurrent runs and shared feature caches\n * - Auth: sk_* secret key (server-side only)\n * - Quota: 20 concurrent workers per org\n *\n * @module\n */\n\nimport type { MachineSize } from '@sylphx/contract'\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type RunStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'timeout'\nexport type RunMachineSize = MachineSize\n\nexport interface RunVolumeMount {\n\t/** UUID of the volumeResource to mount (must belong to this org) */\n\tvolumeId: string\n\t/** Absolute mount path inside the container (e.g. '/cache') */\n\tmountPath: string\n\t/** Optional sub-path within the volume (e.g. 'fold-3') */\n\tsubPath?: string\n\t/** Mount as read-only (default: false) */\n\treadOnly?: boolean\n}\n\nexport interface CreateRunOptions {\n\t/**\n\t * OCI image to run from a connected registry.\n\t *\n\t * @example 'ghcr.io/acme/my-trainer:sha-abc123'\n\t */\n\timage: string\n\n\t/**\n\t * Command + args to execute.\n\t *\n\t * @example ['python', 'train.py', '--fold', '3']\n\t * @example ['node', 'dist/process.js']\n\t */\n\tcommand: string[]\n\n\t/**\n\t * Environment variables to inject.\n\t * Use for fold IDs, feature flags, DB URLs, etc.\n\t */\n\tenv?: Record<string, string>\n\n\t/**\n\t * Managed machine size. The platform owns CPU, memory, scheduling, and isolation details.\n\t * Defaults to `standard`.\n\t */\n\tmachine?: RunMachineSize\n\n\t/**\n\t * Hard timeout in seconds (default: 3600 = 1 hour, max: 86400 = 24 hours).\n\t * The platform terminates the run when the deadline is reached (status: 'timeout').\n\t */\n\ttimeoutSeconds?: number\n\n\t/**\n\t * Volume mounts from org-level volumeResources.\n\t * Shared volumes allow concurrent access by multiple parallel workers.\n\t */\n\tvolumeMounts?: RunVolumeMount[]\n}\n\nexport interface Run {\n\t/** Worker run ID (e.g. 'worker_Vh3kJ9mNpQ2wXsL1') */\n\tid: string\n\t/** Current lifecycle status */\n\tstatus: RunStatus\n\t/** Docker image */\n\timage: string\n\t/** Command being executed */\n\tcommand: string[]\n\t/** Environment variables */\n\tenv: Record<string, string> | null\n\t/** Managed machine size selected for this run. */\n\tmachine: RunMachineSize | null\n\t/** Hard timeout in seconds */\n\ttimeoutSeconds: number\n\t/** Volume mounts */\n\tvolumeMounts: RunVolumeMount[] | null\n\t/** Exit code (only when succeeded or failed) */\n\texitCode: number | null\n\t/** Captured stdout (up to 1 MiB) */\n\tstdout: string | null\n\t/** Captured stderr (up to 1 MiB) */\n\tstderr: string | null\n\t/** Error message (e.g. OOMKilled, image pull failure) */\n\terrorMessage: string | null\n\t/** Duration in milliseconds (only when completed) */\n\tdurationMs: number | null\n\t/** When the worker runtime started */\n\tstartedAt: string | null\n\t/** When the worker completed */\n\tcompletedAt: string | null\n\t/** When this run was created */\n\tcreatedAt: string\n\t/** Last update timestamp */\n\tupdatedAt: string\n}\n\nexport interface RunResult {\n\t/** Exit code (0 = success) */\n\texitCode: number | null\n\t/** Status at completion */\n\tstatus: RunStatus\n\t/** Captured stdout */\n\tstdout: string | null\n\t/** Captured stderr */\n\tstderr: string | null\n\t/** Error message (OOMKilled, DeadlineExceeded, etc.) */\n\terrorMessage: string | null\n\t/** Wall-clock duration in milliseconds */\n\tdurationMs: number | null\n}\n\nexport interface RunLogsResult {\n\t/** Captured stdout (up to 1 MiB) */\n\tstdout: string\n\t/** Captured stderr (up to 1 MiB) */\n\tstderr: string\n\t/** Whether logs are still being captured (worker is running) */\n\tlive: boolean\n}\n\nexport interface ListRunsOptions {\n\t/** Filter by status */\n\tstatus?: RunStatus\n}\n\n/** OpenAI/Stripe-style list response */\nexport interface ListRunsResult {\n\tobject: 'list'\n\tdata: Run[]\n\t/** True if there are more results (limit was hit) */\n\thas_more: boolean\n}\n\n// ============================================================================\n// RunHandle — returned by RunsClient.run()\n// ============================================================================\n\nconst TERMINAL_STATUSES: ReadonlySet<RunStatus> = new Set([\n\t'succeeded',\n\t'failed',\n\t'cancelled',\n\t'timeout',\n])\nconst DEFAULT_POLL_INTERVAL_MS = 3_000\nconst DEFAULT_WAIT_TIMEOUT_MS = 7_200_000 // 2 hours\n\n/**\n * Handle to a running (or completed) worker.\n * Use `.wait()` to poll until completion, `.logs()` to stream logs, `.cancel()` to abort.\n */\nexport class RunHandle {\n\treadonly id: string\n\tprivate readonly config: SylphxConfig\n\n\tconstructor(id: string, config: SylphxConfig) {\n\t\tthis.id = id\n\t\tthis.config = config\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Status\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Get the current status of this worker.\n\t */\n\tasync status(): Promise<Run> {\n\t\treturn callApi<Run>(this.config, `/workers/${this.id}`, { method: 'GET' })\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Wait (poll to completion)\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Poll the worker until it reaches a terminal state (succeeded, failed, cancelled, timeout).\n\t *\n\t * @param options.pollIntervalMs - How often to poll in ms (default: 3000)\n\t * @param options.timeoutMs - Max time to wait before throwing (default: 7_200_000 = 2h)\n\t * @returns RunResult with exit code, status, stdout/stderr\n\t * @throws Error if waitTimeout is exceeded\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await run.wait()\n\t * if (result.exitCode !== 0) {\n\t * throw new Error(`Worker failed: ${result.errorMessage ?? result.stderr}`)\n\t * }\n\t * ```\n\t */\n\tasync wait(options?: { pollIntervalMs?: number; timeoutMs?: number }): Promise<RunResult> {\n\t\tconst pollMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS\n\t\tconst maxWaitMs = options?.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS\n\t\tconst deadline = Date.now() + maxWaitMs\n\n\t\twhile (true) {\n\t\t\tconst run = await this.status()\n\n\t\t\tif (TERMINAL_STATUSES.has(run.status)) {\n\t\t\t\treturn {\n\t\t\t\t\texitCode: run.exitCode,\n\t\t\t\t\tstatus: run.status,\n\t\t\t\t\tstdout: run.stdout,\n\t\t\t\t\tstderr: run.stderr,\n\t\t\t\t\terrorMessage: run.errorMessage,\n\t\t\t\t\tdurationMs: run.durationMs,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Worker ${this.id} did not complete within ${maxWaitMs}ms (current status: ${run.status})`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tawait sleep(pollMs)\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Logs\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Fetch captured logs for this worker.\n\t *\n\t * - For a running worker: returns live logs streamed from the runtime (may be incomplete)\n\t * - For a completed worker: returns the full captured output stored in DB\n\t *\n\t * @example\n\t * ```typescript\n\t * const { stdout, stderr, live } = await worker.logs()\n\t * console.log(stdout)\n\t * if (live) console.log('(worker still running, logs may be incomplete)')\n\t * ```\n\t */\n\tasync logs(): Promise<RunLogsResult> {\n\t\treturn callApi<RunLogsResult>(this.config, `/workers/${this.id}/logs`, { method: 'GET' })\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cancel\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Cancel this worker.\n\t *\n\t * - If still pending: immediately cancelled\n\t * - If running: runtime is terminated\n\t * - If already completed: no-op\n\t */\n\tasync cancel(): Promise<void> {\n\t\tawait callApi<{ success: boolean }>(this.config, `/workers/${this.id}`, { method: 'DELETE' })\n\t}\n}\n\n// ============================================================================\n// RunsClient\n// ============================================================================\n\n/**\n * Static client for the Workers BaaS service.\n *\n * @example\n * ```typescript\n * const config = createServerClient(process.env.SYLPHX_SECRET_URL!)\n *\n * // Run a worker and wait for completion\n * const result = await RunsClient.run(config, { ... }).then((run) => run.wait())\n *\n * // Run N workers in parallel, wait for all\n * const handles = await Promise.all(folds.map((fold) => RunsClient.run(config, { ... })))\n * const results = await Promise.all(handles.map(h => h.wait()))\n * ```\n */\nexport const RunsClient = {\n\t// --------------------------------------------------------------------------\n\t// Run\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Spawn a new worker and return a handle.\n\t *\n\t * The run is created immediately and starts pulling the image.\n\t * Use the returned handle to `.wait()` for completion or `.cancel()`.\n\t *\n\t * @example\n\t * ```typescript\n\t * const run = await RunsClient.run(config, {\n\t * image: 'ghcr.io/acme/trainer:sha-abc123',\n\t * command: ['python', 'train.py', '--fold', '3'],\n\t * machine: 'large',\n\t * volumeMounts: [{ volumeId: cacheVolumeId, mountPath: '/cache' }],\n\t * })\n\t * const result = await run.wait()\n\t * ```\n\t */\n\tasync run(config: SylphxConfig, options: CreateRunOptions): Promise<RunHandle> {\n\t\tconst run = await callApi<{ id: string }>(config, '/runs', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: {\n\t\t\t\timage: options.image,\n\t\t\t\tcommand: options.command,\n\t\t\t\tenv: options.env,\n\t\t\t\tmachine: options.machine ?? 'standard',\n\t\t\t\ttimeoutSeconds: options.timeoutSeconds ?? 3600,\n\t\t\t\tvolumeMounts: options.volumeMounts,\n\t\t\t},\n\t\t})\n\t\treturn new RunHandle(run.id, config)\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// Get\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Get a RunHandle for an existing run by ID.\n\t *\n\t * Useful for resuming monitoring across requests.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Store the worker ID, retrieve later\n\t * const handle = RunsClient.fromId(config, storedWorkerId)\n\t * const result = await handle.wait()\n\t * ```\n\t */\n\tfromId(config: SylphxConfig, workerId: string): RunHandle {\n\t\treturn new RunHandle(workerId, config)\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// List\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * List worker runs for this environment.\n\t *\n\t * @example\n\t * ```typescript\n\t * const { data } = await RunsClient.list(config, { status: 'running' })\n\t * console.log(`${data.length} runs currently running`)\n\t * ```\n\t */\n\tasync list(config: SylphxConfig, options?: ListRunsOptions): Promise<ListRunsResult> {\n\t\treturn callApi<ListRunsResult>(config, '/runs', {\n\t\t\tmethod: 'GET',\n\t\t\tquery: options?.status ? { status: options.status } : undefined,\n\t\t})\n\t},\n\n\t// --------------------------------------------------------------------------\n\t// Run-and-wait convenience\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Spawn a worker and wait for it to complete in one call.\n\t *\n\t * Equivalent to `(await RunsClient.run(config, options)).wait(waitOptions)`.\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await RunsClient.runAndWait(config, {\n\t * image: 'ghcr.io/acme/process:sha-abc123',\n\t * command: ['node', 'dist/process.js'],\n\t * })\n\t * if (result.exitCode !== 0) throw new Error(result.errorMessage ?? 'worker failed')\n\t * ```\n\t */\n\tasync runAndWait(\n\t\tconfig: SylphxConfig,\n\t\toptions: CreateRunOptions,\n\t\twaitOptions?: { pollIntervalMs?: number; timeoutMs?: number },\n\t): Promise<RunResult> {\n\t\tconst handle = await RunsClient.run(config, options)\n\t\treturn handle.wait(waitOptions)\n\t},\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// ── Backward-compat aliases (ADR-040) ────────────────────────────────────────\n/** @deprecated Use RunsClient */\nexport const WorkersClient = RunsClient\n/** @deprecated Use RunHandle */\nexport { RunHandle as WorkerHandle }\n/** @deprecated Use CreateRunOptions */\nexport type { CreateRunOptions as RunWorkerOptions }\n/** @deprecated Use Run */\nexport type { Run as WorkerRun }\n/** @deprecated Use RunLogsResult */\nexport type { RunLogsResult as WorkerLogsResult }\n/** @deprecated Use RunResult */\nexport type { RunResult as WorkerResult }\n/** @deprecated Use RunStatus */\nexport type { RunStatus as WorkerStatus }\n/** @deprecated Use RunVolumeMount */\nexport type { RunVolumeMount as WorkerVolumeMount }\n","/**\n * Application Logs SDK (ADR-089 Phase 4a)\n *\n * Pure functions for runtime application log ingestion and querying,\n * backed by `apps/runtime/src/server/runtime/routes/logs.ts`:\n *\n * POST /logs — ingest log entries (requires secret key / serverAuth)\n * GET /logs — query logs (requires any key / configAuth)\n *\n * Logs are persisted in the project's `appLogs` table. Ingestion is\n * batched via `{ logs: [...] }` — do not call once per line in a loop.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n\nexport interface LogEntry {\n\treadonly level: LogLevel\n\treadonly message: string\n\treadonly timestamp?: string\n\treadonly metadata?: Record<string, unknown>\n\treadonly source?: string\n\treadonly traceId?: string\n}\n\nexport interface StoredLogEntry {\n\treadonly id: string\n\treadonly level: LogLevel\n\treadonly message: string\n\treadonly timestamp: string\n\treadonly metadata: Record<string, unknown> | null\n\treadonly traceId: string | null\n\treadonly source: string | null\n\treadonly userId: string | null\n}\n\nexport interface IngestLogsResult {\n\treadonly ingested: number\n}\n\nexport interface QueryLogsOptions {\n\t/** Minimum level to return (levels at or above this are included). */\n\treadonly level?: LogLevel\n\t/** ISO 8601 lower bound (inclusive). */\n\treadonly since?: string\n\t/** ISO 8601 upper bound (inclusive). */\n\treadonly until?: string\n\t/** Max entries (default 100, server-enforced cap 1000). */\n\treadonly limit?: number\n\t/** Full-text search against `message` (case-insensitive). */\n\treadonly search?: string\n\t/** Filter by a specific trace correlation ID. */\n\treadonly traceId?: string\n}\n\nexport interface QueryLogsResult {\n\treadonly logs: readonly StoredLogEntry[]\n\treadonly total: number\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Ingest one or more log entries for the current project.\n *\n * @example\n * ```typescript\n * await ingestLogs(config, [\n * { level: 'info', message: 'user signed in', metadata: { userId } },\n * { level: 'warn', message: 'stripe webhook retry' },\n * ])\n * ```\n */\nexport async function ingestLogs(\n\tconfig: SylphxConfig,\n\tentries: readonly LogEntry[],\n): Promise<IngestLogsResult> {\n\treturn callApi<IngestLogsResult>(config, '/logs', {\n\t\tmethod: 'POST',\n\t\tbody: { logs: entries },\n\t})\n}\n\n/**\n * Query stored application logs with optional filtering.\n * Returns newest-first, capped at the server's limit (1000).\n *\n * @example\n * ```typescript\n * const { logs } = await queryLogs(config, { level: 'error', limit: 50 })\n * ```\n */\nexport async function queryLogs(\n\tconfig: SylphxConfig,\n\toptions: QueryLogsOptions = {},\n): Promise<QueryLogsResult> {\n\treturn callApi<QueryLogsResult>(config, '/logs', {\n\t\tmethod: 'GET',\n\t\tquery: {\n\t\t\tlevel: options.level,\n\t\t\tsince: options.since,\n\t\t\tuntil: options.until,\n\t\t\tlimit: options.limit,\n\t\t\tsearch: options.search,\n\t\t\ttraceId: options.traceId,\n\t\t},\n\t})\n}\n","/**\n * Miscellaneous SDK endpoints (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/misc.ts`:\n *\n * GET /app — project metadata (configAuth)\n * POST /challenge/verify — step-up identity verification (userAuth)\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProjectMetadata {\n\treadonly id: string\n\treadonly name: string\n\treadonly slug: string\n\treadonly captcha?: {\n\t\treadonly enabled: true\n\t\treadonly provider: 'turnstile' | 'hcaptcha'\n\t\treadonly siteKey: string\n\t\treadonly action: 'register'\n\t}\n\treadonly [key: string]: unknown\n}\n\nexport type ChallengeMethod = 'password' | 'email' | 'totp' | 'backup'\nexport type ChallengeType = 'identity' | 'mfa'\n\nexport interface ChallengeVerifyInput {\n\t/** Verification method to use. */\n\treadonly method: ChallengeMethod\n\t/** Verification value matching `method` (password, email code, TOTP code, backup code). */\n\treadonly value: string\n}\n\nexport interface ChallengeVerifyResult {\n\treadonly verified: boolean\n\treadonly method: ChallengeMethod\n\treadonly type: ChallengeType\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get app metadata for the current project (id, name, slug).\n * Cacheable — the BaaS emits `Cache-Control` headers for CDN reuse.\n */\nexport async function getProjectMetadata(config: SylphxConfig): Promise<ProjectMetadata> {\n\treturn callApi<ProjectMetadata>(config, '/app')\n}\n\n/**\n * Verify the authenticated user's identity for step-up authentication.\n *\n * Lockout is enforced server-side after repeated failures — respect the\n * `429` response. Methods accepted: password, email OTP, TOTP, backup code.\n *\n * @example\n * ```typescript\n * await verifyChallenge(config, { method: 'password', value: pw })\n * ```\n */\nexport async function verifyChallenge(\n\tconfig: SylphxConfig,\n\tinput: ChallengeVerifyInput,\n): Promise<ChallengeVerifyResult> {\n\treturn callApi<ChallengeVerifyResult>(config, '/challenge/verify', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n","/**\n * User SDK — customer-app-facing user management (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/user.ts`:\n *\n * GET /user/profile — full profile\n * PUT /user/profile — update profile\n * GET /user/security — 2FA / password status\n * GET /user/sessions — list active sessions\n * PUT /user/sessions/{id} — rename session device\n * DELETE /user/sessions/{id} — revoke session\n * GET /user/export — GDPR export\n * DELETE /user/account — GDPR erasure\n *\n * These routes are authenticated with a **user access token** (bearer) —\n * distinct from the platform-user namespace in `auth.ts` which targets\n * `/auth/platform-user/*` for Console / CLI operators. Customer apps\n * should use these; platform tooling should use `auth.user.*`.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UserFullProfile {\n\treadonly id: string\n\treadonly email: string\n\treadonly name: string | null\n\treadonly image: string | null\n\treadonly emailVerified: boolean\n\treadonly createdAt: string\n\treadonly updatedAt: string\n\treadonly [key: string]: unknown\n}\n\nexport interface UserUpdateProfileInput {\n\treadonly name?: string\n\treadonly image?: string\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface UserSecuritySettings {\n\treadonly hasPassword: boolean\n\treadonly twoFactorEnabled: boolean\n\treadonly passkeyCount: number\n\treadonly oauthProviders: readonly string[]\n\treadonly [key: string]: unknown\n}\n\nexport interface UserSession {\n\treadonly id: string\n\treadonly deviceName: string | null\n\treadonly ipAddress: string | null\n\treadonly userAgent: string | null\n\treadonly createdAt: string\n\treadonly lastActiveAt: string | null\n\treadonly expiresAt: string\n\treadonly current: boolean\n}\n\nexport interface UserSessionsList {\n\treadonly sessions: readonly UserSession[]\n}\n\nexport interface UserDataExport {\n\treadonly exportedAt: string\n\treadonly user: Record<string, unknown>\n\treadonly [key: string]: unknown\n}\n\nexport interface DeleteAccountResult {\n\treadonly success: boolean\n\treadonly deletedData: readonly string[]\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\nexport async function getUserProfile(config: SylphxConfig): Promise<UserFullProfile> {\n\treturn callApi<UserFullProfile>(config, '/user/profile')\n}\n\nexport async function updateUserProfile(\n\tconfig: SylphxConfig,\n\tinput: UserUpdateProfileInput,\n): Promise<UserFullProfile> {\n\treturn callApi<UserFullProfile>(config, '/user/profile', {\n\t\tmethod: 'PUT',\n\t\tbody: input,\n\t})\n}\n\nexport async function getUserSecurity(config: SylphxConfig): Promise<UserSecuritySettings> {\n\treturn callApi<UserSecuritySettings>(config, '/user/security')\n}\n\nexport async function listUserSessions(config: SylphxConfig): Promise<UserSessionsList> {\n\treturn callApi<UserSessionsList>(config, '/user/sessions')\n}\n\nexport async function revokeUserSession(\n\tconfig: SylphxConfig,\n\tsessionId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/user/sessions/${encodeURIComponent(sessionId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\nexport async function renameUserSession(\n\tconfig: SylphxConfig,\n\tsessionId: string,\n\tdeviceName: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, `/user/sessions/${encodeURIComponent(sessionId)}`, {\n\t\tmethod: 'PUT',\n\t\tbody: { deviceName },\n\t})\n}\n\n/**\n * Export every piece of personal data the platform holds about the\n * authenticated user (GDPR Article 20 — data portability).\n */\nexport async function exportUserData(config: SylphxConfig): Promise<UserDataExport> {\n\treturn callApi<UserDataExport>(config, '/user/export')\n}\n\n/**\n * Permanently delete the authenticated user's account and all\n * associated data (GDPR Article 17 — right to erasure). Callers\n * SHOULD gate this behind a `verifyChallenge()` step-up.\n */\nexport async function deleteUserAccount(config: SylphxConfig): Promise<DeleteAccountResult> {\n\treturn callApi<DeleteAccountResult>(config, '/user/account', {\n\t\tmethod: 'DELETE',\n\t})\n}\n","/**\n * Security SDK — customer-app-facing account security management (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/security/*`:\n *\n * /security/password/set — set or replace password\n * /security/oauth/disconnect — remove linked OAuth provider\n * /security/score — security posture score\n * /security/email/change + confirm — verified email change flow\n * /security/passkeys (+ register) — WebAuthn passkey CRUD\n * /security/2fa/setup|verify|disable — TOTP 2FA lifecycle\n * /security/backup-codes — view / regenerate recovery codes\n * /security/alerts — security alert inbox\n *\n * All routes require a user access token (bearer). Destructive verbs\n * (disable 2FA, delete passkey, disconnect OAuth) SHOULD be gated\n * behind a `verifyChallenge()` step-up from `@sylphx/sdk/misc`.\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SecurityScoreResult {\n\treadonly score: number\n\treadonly maxScore: number\n\treadonly factors: readonly {\n\t\treadonly key: string\n\t\treadonly label: string\n\t\treadonly satisfied: boolean\n\t\treadonly weight: number\n\t}[]\n}\n\nexport interface PasswordSetInput {\n\treadonly password: string\n\t/** Current password — required if one is already set. */\n\treadonly currentPassword?: string\n}\n\nexport interface EmailChangeInput {\n\treadonly newEmail: string\n}\n\nexport interface EmailConfirmInput {\n\treadonly code: string\n}\n\nexport interface PasskeySummary {\n\treadonly id: string\n\treadonly name: string | null\n\treadonly createdAt: string\n\treadonly lastUsedAt: string | null\n}\n\nexport interface PasskeysList {\n\treadonly passkeys: readonly PasskeySummary[]\n}\n\nexport interface PasskeyRegistrationOptions {\n\t/** WebAuthn `PublicKeyCredentialCreationOptions` — pass to `navigator.credentials.create()`. */\n\treadonly [key: string]: unknown\n}\n\nexport interface PasskeyRegistrationInput {\n\t/** WebAuthn credential response from `navigator.credentials.create()`. */\n\treadonly credential: Record<string, unknown>\n\treadonly deviceName?: string\n}\n\nexport interface TwoFactorSetupResult {\n\treadonly secret: string\n\treadonly qrCodeUri: string\n\treadonly recoveryHint?: string\n}\n\nexport interface TwoFactorEnableResult {\n\treadonly success: boolean\n\treadonly backupCodes: readonly string[]\n}\n\nexport interface BackupCodesResult {\n\treadonly codes: readonly string[]\n\treadonly remaining: number\n}\n\nexport interface SecurityAlert {\n\treadonly id: string\n\treadonly type: string\n\treadonly severity: 'info' | 'warning' | 'critical'\n\treadonly message: string\n\treadonly createdAt: string\n\treadonly readAt: string | null\n\treadonly metadata: Record<string, unknown> | null\n}\n\nexport interface SecurityAlertsList {\n\treadonly alerts: readonly SecurityAlert[]\n\treadonly unread: number\n}\n\n// ============================================================================\n// Password & OAuth linkage\n// ============================================================================\n\nexport async function setPassword(\n\tconfig: SylphxConfig,\n\tinput: PasswordSetInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/password/set', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function disconnectOAuthProvider(\n\tconfig: SylphxConfig,\n\tprovider: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/oauth/disconnect', {\n\t\tmethod: 'POST',\n\t\tbody: { provider },\n\t})\n}\n\nexport async function getSecurityScore(config: SylphxConfig): Promise<SecurityScoreResult> {\n\treturn callApi<SecurityScoreResult>(config, '/security/score')\n}\n\n// ============================================================================\n// Email change\n// ============================================================================\n\nexport async function requestEmailChange(\n\tconfig: SylphxConfig,\n\tinput: EmailChangeInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/email/change', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function confirmEmailChange(\n\tconfig: SylphxConfig,\n\tinput: EmailConfirmInput,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/email/confirm', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n// ============================================================================\n// Passkeys (WebAuthn)\n// ============================================================================\n\nexport async function listPasskeys(config: SylphxConfig): Promise<PasskeysList> {\n\treturn callApi<PasskeysList>(config, '/security/passkeys')\n}\n\nexport async function startPasskeyRegistration(\n\tconfig: SylphxConfig,\n): Promise<PasskeyRegistrationOptions> {\n\treturn callApi<PasskeyRegistrationOptions>(config, '/security/passkeys/register/start', {\n\t\tmethod: 'POST',\n\t})\n}\n\nexport async function verifyPasskeyRegistration(\n\tconfig: SylphxConfig,\n\tinput: PasskeyRegistrationInput,\n): Promise<{ success: boolean; passkey: PasskeySummary }> {\n\treturn callApi(config, '/security/passkeys/register/verify', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function renamePasskey(\n\tconfig: SylphxConfig,\n\tpasskeyId: string,\n\tname: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/passkeys/${encodeURIComponent(passkeyId)}`,\n\t\t{\n\t\t\tmethod: 'PUT',\n\t\t\tbody: { name },\n\t\t},\n\t)\n}\n\nexport async function deletePasskey(\n\tconfig: SylphxConfig,\n\tpasskeyId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/passkeys/${encodeURIComponent(passkeyId)}`,\n\t\t{ method: 'DELETE' },\n\t)\n}\n\n// ============================================================================\n// Two-factor authentication\n// ============================================================================\n\nexport async function setupTwoFactor(config: SylphxConfig): Promise<TwoFactorSetupResult> {\n\treturn callApi<TwoFactorSetupResult>(config, '/security/2fa/setup', { method: 'POST' })\n}\n\nexport async function verifyTwoFactorEnable(\n\tconfig: SylphxConfig,\n\tcode: string,\n): Promise<TwoFactorEnableResult> {\n\treturn callApi<TwoFactorEnableResult>(config, '/security/2fa/verify', {\n\t\tmethod: 'POST',\n\t\tbody: { code },\n\t})\n}\n\nexport async function disableTwoFactor(config: SylphxConfig): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(config, '/security/2fa/disable', { method: 'POST' })\n}\n\nexport async function getBackupCodes(config: SylphxConfig): Promise<BackupCodesResult> {\n\treturn callApi<BackupCodesResult>(config, '/security/backup-codes')\n}\n\nexport async function regenerateBackupCodes(config: SylphxConfig): Promise<BackupCodesResult> {\n\treturn callApi<BackupCodesResult>(config, '/security/backup-codes/regenerate', {\n\t\tmethod: 'POST',\n\t})\n}\n\n// ============================================================================\n// Security alerts\n// ============================================================================\n\nexport async function listSecurityAlerts(config: SylphxConfig): Promise<SecurityAlertsList> {\n\treturn callApi<SecurityAlertsList>(config, '/security/alerts')\n}\n\nexport async function markSecurityAlertRead(\n\tconfig: SylphxConfig,\n\talertId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi<{ success: boolean }>(\n\t\tconfig,\n\t\t`/security/alerts/${encodeURIComponent(alertId)}/read`,\n\t\t{ method: 'POST' },\n\t)\n}\n\nexport async function markAllSecurityAlertsRead(\n\tconfig: SylphxConfig,\n): Promise<{ success: boolean; updated: number }> {\n\treturn callApi(config, '/security/alerts/read-all', { method: 'POST' })\n}\n","/**\n * OAuth SDK — customer-app-facing social login (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/oauth.ts`:\n *\n * POST /oauth/authorize — initiate OAuth flow, returns provider URL\n *\n * The per-provider callback handler is a browser-side redirect endpoint\n * on the runtime (`GET /oauth/callback/{provider}`); SDK consumers\n * redirect the user to `authorizationUrl`, then receive a `code` on\n * their own `redirect_uri` which they exchange via `signIn()` or\n * `auth/login` with grant_type=authorization_code.\n *\n * PKCE is required per OAuth 2.1. Callers MUST generate `code_verifier`\n * + `code_challenge` and persist the verifier in session / cookie for\n * the subsequent token exchange — this SDK does not retain state.\n *\n * ## Distinction from `auth.oauth`\n *\n * `auth.oauth.*` (from `./auth`) targets platform-plane admin mint\n * (`/auth/platform-jwt/mint`) used by Console / CLI. The functions\n * here target the customer-app social-login flow.\n */\n\nimport type { TokenResponse } from './auth'\nimport { callApi, type SylphxConfig } from './config'\nimport { SylphxError } from './errors'\nimport type { OAuthProviderId } from './types'\n\n// ============================================================================\n// OIDC Discovery + UserInfo — ADR-089 Phase 5.6\n// ============================================================================\n\n/**\n * OIDC Discovery document shape (OIDC-Core §4). Every published field is\n * optional from the SDK's perspective because different providers\n * advertise different subsets; Sylphx's own doc is shipped fully\n * populated by {@link buildOidcDiscoveryDocument}.\n */\nexport interface OidcDiscoveryDocument {\n\treadonly issuer: string\n\treadonly authorization_endpoint?: string\n\treadonly token_endpoint?: string\n\treadonly userinfo_endpoint?: string\n\treadonly jwks_uri?: string\n\treadonly introspection_endpoint?: string\n\treadonly revocation_endpoint?: string\n\treadonly device_authorization_endpoint?: string\n\treadonly response_types_supported?: readonly string[]\n\treadonly subject_types_supported?: readonly string[]\n\treadonly id_token_signing_alg_values_supported?: readonly string[]\n\treadonly grant_types_supported?: readonly string[]\n\treadonly scopes_supported?: readonly string[]\n\treadonly claims_supported?: readonly string[]\n\treadonly token_endpoint_auth_methods_supported?: readonly string[]\n\treadonly code_challenge_methods_supported?: readonly string[]\n\treadonly dpop_signing_alg_values_supported?: readonly string[]\n\treadonly frontchannel_logout_supported?: boolean\n\treadonly backchannel_logout_supported?: boolean\n\treadonly [k: string]: unknown\n}\n\nexport interface OidcUserInfoResponse {\n\treadonly sub: string\n\treadonly email?: string\n\treadonly email_verified?: boolean\n\treadonly name?: string\n\treadonly picture?: string\n\treadonly updated_at?: number\n\treadonly [k: string]: unknown\n}\n\n/**\n * Fetch the provider's OIDC discovery document. Works against any\n * spec-compliant provider (Sylphx, Okta, Azure AD, etc.) — pass the\n * issuer URL (scheme + host, no path) as `baseUrl`.\n *\n * @example\n * ```ts\n * const doc = await getOidcDiscoveryDocument({ baseUrl: 'https://api.sylphx.com' })\n * console.log(doc.token_endpoint)\n * ```\n */\nexport async function getOidcDiscoveryDocument(opts: {\n\tbaseUrl: string\n}): Promise<OidcDiscoveryDocument> {\n\tconst url = `${opts.baseUrl.replace(/\\/$/, '')}/.well-known/openid-configuration`\n\tconst res = await fetch(url, { headers: { accept: 'application/json' } })\n\tif (!res.ok) {\n\t\tthrow new Error(`OIDC discovery failed: ${res.status} ${res.statusText}`)\n\t}\n\treturn (await res.json()) as OidcDiscoveryDocument\n}\n\n/**\n * Call the OIDC UserInfo endpoint (OIDC-Core §5.3). Returns identity\n * claims for the subject identified by `accessToken`. The token MUST\n * have been granted `openid` scope; otherwise the endpoint returns 403\n * `insufficient_scope`.\n *\n * @example\n * ```ts\n * const info = await userInfo({\n * baseUrl: 'https://api.sylphx.com',\n * accessToken: token.access_token,\n * })\n * console.log(info.sub, info.email)\n * ```\n */\nexport async function userInfo(opts: {\n\tbaseUrl: string\n\taccessToken: string\n\t/** Override the discovered endpoint (e.g. when testing against a staging AS). */\n\tendpoint?: string\n}): Promise<OidcUserInfoResponse> {\n\tconst url = opts.endpoint ?? `${opts.baseUrl.replace(/\\/$/, '')}/v1/auth/oauth/userinfo`\n\tconst res = await fetch(url, {\n\t\tmethod: 'GET',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${opts.accessToken}`,\n\t\t\taccept: 'application/json',\n\t\t},\n\t})\n\tif (!res.ok) {\n\t\tconst body = await res.text()\n\t\tthrow new Error(`userinfo failed: ${res.status} ${res.statusText} ${body}`)\n\t}\n\treturn (await res.json()) as OidcUserInfoResponse\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type OAuthProvider = OAuthProviderId | (string & {})\n\nexport type PkceMethod = 'S256' | 'plain'\n\nexport interface OAuthAuthorizeInput {\n\treadonly provider: OAuthProvider\n\t/** Where the OAuth provider sends the user after consent. */\n\treadonly redirectUri: string\n\t/** PKCE code challenge derived from `code_verifier` (S256 recommended). */\n\treadonly codeChallenge: string\n\treadonly codeChallengeMethod?: PkceMethod\n\t/** Override the default scopes for the provider. */\n\treadonly scopes?: readonly string[]\n\t/** Opaque state for CSRF protection. */\n\treadonly state?: string\n}\n\nexport interface OAuthAuthorizeResult {\n\t/** Fully-qualified URL to redirect the user to. */\n\treadonly authorizationUrl: string\n\t/** Opaque server-side state — echo on callback handler if present. */\n\treadonly state: string\n}\n\nexport interface OAuthProvidersResult {\n\treadonly providers: readonly OAuthProvider[]\n}\n\nexport interface OAuthCodeExchangeInput {\n\treadonly code: string\n\treadonly codeVerifier: string\n\treadonly anonymousId?: string\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Initiate an OAuth flow with the given provider. Returns the provider's\n * authorization URL — redirect the user to it.\n *\n * @example\n * ```typescript\n * import { generatePkce } from '@sylphx/sdk'\n * const { verifier, challenge } = await generatePkce()\n * sessionStorage.setItem('pkce_verifier', verifier)\n * const { authorizationUrl } = await authorizeOAuth(config, {\n * provider: 'google',\n * redirectUri: 'https://app.example.com/auth/callback',\n * codeChallenge: challenge,\n * codeChallengeMethod: 'S256',\n * })\n * window.location.href = authorizationUrl\n * ```\n */\nexport async function authorizeOAuth(\n\tconfig: SylphxConfig,\n\tinput: OAuthAuthorizeInput,\n): Promise<OAuthAuthorizeResult> {\n\treturn callApi<OAuthAuthorizeResult>(config, '/oauth/authorize', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tprovider: input.provider,\n\t\t\tredirect_uri: input.redirectUri,\n\t\t\tcode_challenge: input.codeChallenge,\n\t\t\tcode_challenge_method: input.codeChallengeMethod ?? 'S256',\n\t\t\t...(input.scopes && { scopes: input.scopes }),\n\t\t\t...(input.state && { state: input.state }),\n\t\t},\n\t})\n}\n\n/**\n * List OAuth providers enabled for the current customer app/environment.\n *\n * Uses the same `SylphxConfig` routing surface as every other BaaS call, so\n * customer apps do not need a second app-id or platformUrl configuration path.\n */\nexport async function listOAuthProviders(config: SylphxConfig): Promise<OAuthProvidersResult> {\n\treturn callApi<OAuthProvidersResult>(config, '/auth/oauth-providers')\n}\n\n/**\n * Exchange a social-login authorization code for Platform session tokens.\n *\n * This is the customer-app counterpart to {@link authorizeOAuth}. It uses the\n * app's publishable connection URL as the OAuth client id and keeps PKCE in\n * the caller's control.\n */\nexport async function exchangeOAuthCode(\n\tconfig: SylphxConfig,\n\tinput: OAuthCodeExchangeInput,\n): Promise<TokenResponse> {\n\tif (config.credentialType !== 'pk' || !config.publicKey) {\n\t\tthrow new SylphxError(\n\t\t\t'[Sylphx] exchangeOAuthCode() requires a publishable connection URL (pk_*).',\n\t\t\t{ code: 'BAD_REQUEST' },\n\t\t)\n\t}\n\n\treturn callApi<TokenResponse>(config, '/auth/token', {\n\t\tmethod: 'POST',\n\t\tbody: {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode: input.code,\n\t\t\tclient_id: config.publicKey,\n\t\t\tcode_verifier: input.codeVerifier,\n\t\t\t...(input.anonymousId ? { anonymous_id: input.anonymousId } : {}),\n\t\t},\n\t})\n}\n\n/**\n * Parse an OAuth callback URL (e.g. `window.location.href`) into the\n * fields a customer app needs to complete sign-in.\n *\n * Pure function — no network. Does NOT validate `state` against your\n * session store; callers MUST compare `parsed.state` to the opaque\n * value they persisted before redirect (CSRF mitigation).\n */\nexport function parseOAuthCallback(url: string): {\n\treadonly code: string | null\n\treadonly state: string | null\n\treadonly error: string | null\n\treadonly errorDescription: string | null\n} {\n\tconst parsed = new URL(url)\n\treturn {\n\t\tcode: parsed.searchParams.get('code'),\n\t\tstate: parsed.searchParams.get('state'),\n\t\terror: parsed.searchParams.get('error'),\n\t\terrorDescription: parsed.searchParams.get('error_description'),\n\t}\n}\n\n/**\n * Generate a cryptographically random PKCE verifier + S256 challenge\n * pair (RFC 7636 §4.1-4.2). Works in browsers (Web Crypto) and Bun /\n * Node 18+ (`globalThis.crypto`).\n */\nexport async function generatePkce(): Promise<{ verifier: string; challenge: string }> {\n\tconst bytes = new Uint8Array(32)\n\tglobalThis.crypto.getRandomValues(bytes)\n\tconst verifier = base64UrlEncode(bytes)\n\tconst digest = await globalThis.crypto.subtle.digest(\n\t\t'SHA-256',\n\t\tnew TextEncoder().encode(verifier),\n\t)\n\tconst challenge = base64UrlEncode(new Uint8Array(digest))\n\treturn { verifier, challenge }\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number)\n\treturn btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n","/**\n * Promo Code SDK (ADR-089 Phase 4a)\n *\n * Backed by `apps/runtime/src/server/runtime/routes/promo.ts`:\n *\n * POST /promo/validate — check code validity (projectAuth)\n * POST /promo/redeem — redeem for current user (userAuth)\n * GET /admin/promo — list codes (serverAuth)\n * POST /admin/promo — create code (serverAuth)\n * GET /admin/promo/{id} — get single code (serverAuth)\n * PATCH /admin/promo/{id} — update code (serverAuth)\n * DELETE /admin/promo/{id} — archive code (serverAuth)\n * GET /admin/promo/redemptions — redemption history (serverAuth)\n */\n\nimport { callApi, type SylphxConfig } from './config'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type PromoType = 'percent_off' | 'fixed_off' | 'free_trial' | 'feature_unlock'\nexport type PromoStatus = 'active' | 'paused' | 'expired' | 'archived'\n\nexport interface PromoCode {\n\treadonly id: string\n\treadonly code: string\n\treadonly name: string\n\treadonly description: string | null\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly maxUses: number | null\n\treadonly maxUsesPerUser: number | null\n\treadonly usedCount: number\n\treadonly status: PromoStatus\n\treadonly expiresAt: string | null\n\treadonly startsAt: string | null\n\treadonly minPurchaseAmount: number | null\n\treadonly applicablePlanIds: readonly string[] | null\n\treadonly metadata: Record<string, unknown> | null\n\treadonly createdAt: string\n\treadonly updatedAt: string\n}\n\nexport interface PromoValidationPreview {\n\treadonly code: string\n\treadonly name: string\n\treadonly description: string | null\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly expiresAt: string | null\n}\n\nexport interface ValidatePromoInput {\n\treadonly code: string\n\treadonly planId?: string\n\treadonly purchaseAmount?: number\n}\n\nexport interface ValidatePromoResult {\n\treadonly valid: boolean\n\treadonly promo: PromoValidationPreview | null\n\treadonly error?: string\n}\n\nexport interface RedeemPromoInput {\n\treadonly code: string\n\treadonly planId?: string\n\treadonly purchaseAmount?: number\n}\n\nexport interface RedeemPromoResult {\n\treadonly success: boolean\n\treadonly redemption: {\n\t\treadonly id: string\n\t\treadonly code: string\n\t\treadonly type: PromoType\n\t\treadonly appliedValue: number\n\t\treadonly discountAmount: number | null\n\t\treadonly trialDays: number | null\n\t\treadonly featureId: string | null\n\t}\n}\n\nexport interface CreatePromoInput {\n\treadonly code: string\n\treadonly name: string\n\treadonly description?: string\n\treadonly type: PromoType\n\treadonly value: number\n\treadonly maxUses?: number\n\treadonly maxUsesPerUser?: number\n\treadonly expiresAt?: string\n\treadonly startsAt?: string\n\treadonly minPurchaseAmount?: number\n\treadonly applicablePlanIds?: readonly string[]\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface UpdatePromoInput {\n\treadonly name?: string\n\treadonly description?: string | null\n\treadonly status?: PromoStatus\n\treadonly maxUses?: number | null\n\treadonly maxUsesPerUser?: number | null\n\treadonly expiresAt?: string | null\n\treadonly startsAt?: string | null\n\treadonly minPurchaseAmount?: number | null\n\treadonly applicablePlanIds?: readonly string[] | null\n\treadonly metadata?: Record<string, unknown>\n}\n\nexport interface ListPromosOptions {\n\treadonly status?: PromoStatus\n\treadonly limit?: number\n\treadonly offset?: number\n}\n\nexport interface ListPromosResult {\n\treadonly promos: readonly PromoCode[]\n\treadonly total: number\n}\n\nexport interface PromoRedemption {\n\treadonly id: string\n\treadonly promoId: string\n\treadonly code: string\n\treadonly userId: string\n\treadonly appliedValue: number\n\treadonly createdAt: string\n}\n\nexport interface ListRedemptionsOptions {\n\treadonly promoId?: string\n\treadonly userId?: string\n\treadonly limit?: number\n\treadonly offset?: number\n}\n\nexport interface ListRedemptionsResult {\n\treadonly redemptions: readonly PromoRedemption[]\n\treadonly total: number\n}\n\n// ============================================================================\n// Client functions (projectAuth + userAuth)\n// ============================================================================\n\n/** Validate a promo code — safe to call with a publishable key. */\nexport async function validatePromo(\n\tconfig: SylphxConfig,\n\tinput: ValidatePromoInput,\n): Promise<ValidatePromoResult> {\n\treturn callApi<ValidatePromoResult>(config, '/promo/validate', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n/** Redeem a promo for the authenticated user. Requires a user access token. */\nexport async function redeemPromo(\n\tconfig: SylphxConfig,\n\tinput: RedeemPromoInput,\n): Promise<RedeemPromoResult> {\n\treturn callApi<RedeemPromoResult>(config, '/promo/redeem', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\n// ============================================================================\n// Admin functions (serverAuth — secret key only)\n// ============================================================================\n\nexport async function listPromos(\n\tconfig: SylphxConfig,\n\toptions: ListPromosOptions = {},\n): Promise<ListPromosResult> {\n\treturn callApi<ListPromosResult>(config, '/admin/promo', {\n\t\tquery: {\n\t\t\tstatus: options.status,\n\t\t\tlimit: options.limit,\n\t\t\toffset: options.offset,\n\t\t},\n\t})\n}\n\nexport async function getPromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`)\n}\n\nexport async function createPromo(\n\tconfig: SylphxConfig,\n\tinput: CreatePromoInput,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, '/admin/promo', {\n\t\tmethod: 'POST',\n\t\tbody: input,\n\t})\n}\n\nexport async function updatePromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n\tinput: UpdatePromoInput,\n): Promise<{ promo: PromoCode }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`, {\n\t\tmethod: 'PATCH',\n\t\tbody: input,\n\t})\n}\n\nexport async function deletePromo(\n\tconfig: SylphxConfig,\n\tpromoId: string,\n): Promise<{ success: boolean }> {\n\treturn callApi(config, `/admin/promo/${encodeURIComponent(promoId)}`, {\n\t\tmethod: 'DELETE',\n\t})\n}\n\nexport async function listPromoRedemptions(\n\tconfig: SylphxConfig,\n\toptions: ListRedemptionsOptions = {},\n): Promise<ListRedemptionsResult> {\n\treturn callApi<ListRedemptionsResult>(config, '/admin/promo/redemptions', {\n\t\tquery: {\n\t\t\tpromoId: options.promoId,\n\t\t\tuserId: options.userId,\n\t\t\tlimit: options.limit,\n\t\t\toffset: options.offset,\n\t\t},\n\t})\n}\n"],"mappings":";;;;;;;;;;;AA+FA,SAAS,oBAAiD;AACzD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,gBAAgB;AACtB,MAAI,OAAO,cAAc,gBAAgB,YAAa,QAAO;AAC7D,SAAO;AACR;AApGA,IA0Da,cASA,sBAiBA,aAkBA,cAOA,oBAQA,2BAgBA,gCAGA,2BAOA,gCAYA,oBAOA,iCAOA,oBAGA,qBAcA,8BAWA,oBAOA,uBAWA,kBASA,qBASA,gCA4FA,iCAwGA,oBAWA,wBAOA,wBAOA,sBAKA,qBAuDA,uBAyBA,mCAKA,gCAKA,+BAKA,8BASA,kBAwJA,mCAOA,2BAOA,kCAWA,wBAOA;AA3uBb;AAAA;AAAA;AA0DO,IAAM,eAAe;AASrB,IAAM,uBAAuB;AAiB7B,IAAM,cAAc;AAkBpB,IAAM,eAAe,kBAAkB;AAOvC,IAAM,qBAAqB;AAQ3B,IAAM,4BAA4B;AAgBlC,IAAM,iCAAiC,IAAI;AAG3C,IAAM,4BAA4B,iCAAiC;AAOnE,IAAM,iCAAiC,KAAK,KAAK,KAAK;AAYtD,IAAM,qBAAqB,IAAI,KAAK;AAOpC,IAAM,kCAAkC,KAAK;AAO7C,IAAM,qBAAqB;AAG3B,IAAM,sBAAsB;AAc5B,IAAM,+BAA+B,KAAK,KAAK;AAW/C,IAAM,qBAAqB,IAAI,KAAK;AAOpC,IAAM,wBAAwB,KAAK;AAWnC,IAAM,mBAAmB,KAAK,KAAK;AASnC,IAAM,sBAAsB,IAAI,KAAK,KAAK,KAAK;AAS/C,IAAM,iCAAiC,KAAK,KAAK;AA4FjD,IAAM,kCAAkC,KAAK,KAAK;AAwGlD,IAAM,qBAAqB,KAAK,KAAK,KAAK,KAAK;AAW/C,IAAM,yBAAyB,KAAK;AAOpC,IAAM,yBAAyB,IAAI,KAAK;AAOxC,IAAM,uBAAuB,IAAI,KAAK;AAKtC,IAAM,sBAAsB,KAAK;AAuDjC,IAAM,wBAAwB,KAAK;AAyBnC,IAAM,oCAAoC,IAAI,OAAO;AAKrD,IAAM,iCAAiC,IAAI,OAAO;AAKlD,IAAM,gCAAgC,IAAI,OAAO;AAKjD,IAAM,+BAA+B,KAAK,OAAO;AASjD,IAAM,mBAAmB,KAAK,KAAK;AAwJnC,IAAM,oCAAoC;AAO1C,IAAM,4BAA4B;AAOlC,IAAM,mCAAmC;AAWzC,IAAM,yBAAyB;AAO/B,IAAM,oBAAoB,IAAI,KAAK;AAAA;AAAA;;;AC3uB1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAAA;AAAA,EAAA,uBAAAC;AAAA,EAAA,uBAAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAucO,SAAS,cAAc,OAAsC;AACnE,SAAO,iBAAiB;AACzB;AAKO,SAAS,iBAAiB,OAAyB;AACzD,MAAI,iBAAiB,aAAa;AACjC,WAAO,MAAM;AAAA,EACd;AAGA,MAAI,iBAAiB,OAAO;AAC3B,UAAM,UAAU,MAAM,QAAQ,YAAY;AAC1C,UAAM,OAAO,MAAM,KAAK,YAAY;AAGpC,QAAI,SAAS,eAAe,QAAQ,SAAS,OAAO,EAAG,QAAO;AAC9D,QAAI,SAAS,eAAgB,QAAO;AAGpC,QAAI,QAAQ,SAAS,SAAS,EAAG,QAAO;AACxC,QAAI,QAAQ,SAAS,WAAW,EAAG,QAAO;AAG1C,QAAI,QAAQ,SAAS,cAAc,EAAG,QAAO;AAC7C,QAAI,QAAQ,SAAS,YAAY,EAAG,QAAO;AAC3C,QAAI,QAAQ,SAAS,QAAQ,EAAG,QAAO;AAGvC,QAAI,QAAQ,SAAS,KAAK,EAAG,QAAO;AACpC,QAAI,QAAQ,SAAS,KAAK,EAAG,QAAO;AACpC,QAAI,QAAQ,SAAS,KAAK,EAAG,QAAO;AAAA,EACrC;AAEA,SAAO;AACR;AAKO,SAASA,iBAAgB,OAAgB,WAAW,6BAAqC;AAC/F,MAAI,iBAAiB,OAAO;AAC3B,WAAO,MAAM;AAAA,EACd;AACA,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO;AAAA,EACR;AACA,MAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;AAC7D,UAAM,UAAW,MAA+B;AAChD,QAAI,OAAO,YAAY,SAAU,QAAO;AAAA,EACzC;AACA,MAAI,SAAS,OAAO,UAAU,YAAY,cAAc,OAAO;AAC9D,UAAM,WAAY,MAChB;AACF,QAAI,UAAU,MAAM,QAAS,QAAO,SAAS,KAAK;AAClD,QAAI,UAAU,MAAM,MAAO,QAAO,SAAS,KAAK;AAAA,EACjD;AACA,SAAO;AACR;AAKO,SAASF,cAAa,OAAiC;AAC7D,MAAI,iBAAiB,aAAa;AACjC,WAAO,MAAM;AAAA,EACd;AACA,SAAO;AACR;AAEA,SAAS,eAAe,OAAoC;AAC3D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,YAAY,SAAS,OAAQ,MAA8B,WAAW,UAAU;AACnF,WAAQ,MAA6B;AAAA,EACtC;AACA,MAAI,gBAAgB,SAAS,OAAQ,MAAkC,eAAe,UAAU;AAC/F,WAAQ,MAAiC;AAAA,EAC1C;AACA,SAAO;AACR;AAEA,SAAS,gBAAgB,OAAoC;AAC5D,MAAI,iBAAiB,YAAa,QAAO,MAAM;AAC/C,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,UAAU,SAAS,OAAQ,MAA4B,SAAS,UAAU;AAC7E,WAAQ,MAA2B;AAAA,EACpC;AACA,QAAM,SAAS,eAAe,KAAK;AACnC,SAAO,UAAU,OAAO,SAAY,OAAO,MAAM;AAClD;AAWO,SAASC,iBACf,OACA,kBAAkB,6BACH;AACf,QAAM,UAAwB;AAAA,IAC7B,SAASC,iBAAgB,OAAO,eAAe;AAAA,EAChD;AACA,QAAM,OAAO,gBAAgB,KAAK;AAClC,MAAI,MAAM;AACT,WAAO,OAAO,SAAS,EAAE,KAAK,CAAC;AAAA,EAChC;AACA,QAAM,SAAS,eAAe,KAAK;AACnC,MAAI,UAAU,MAAM;AACnB,WAAO,OAAO,SAAS,EAAE,OAAO,CAAC;AAAA,EAClC;AACA,MAAI,iBAAiB,OAAO;AAC3B,WAAO,OAAO,SAAS;AAAA,MACtB,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC7C,CAAC;AAAA,EACF;AACA,SAAO;AACR;AAuDO,SAAS,oBACf,OACA,WAAW,2CACF;AACT,QAAM,YAAY,gBAAgB,KAAK;AACvC,MAAI,aAAa,oBAAoB,SAAS,EAAG,QAAO,oBAAoB,SAAS;AAErF,MAAI,SAAS,OAAO,UAAU,UAAU;AACvC,UAAM,QAAQ;AACd,QAAI,MAAM,MAAM,QAAQ,oBAAoB,MAAM,KAAK,IAAI,GAAG;AAC7D,aAAO,oBAAoB,MAAM,KAAK,IAAI;AAAA,IAC3C;AAAA,EACD;AAEA,QAAM,aAAaA,iBAAgB,OAAO,EAAE;AAC5C,MACC,cACA,WAAW,SAAS,OACpB,CAAC,WAAW,SAAS,IAAI,KACzB,CAAC,WAAW,SAAS,KAAK,KAC1B,CAAC,wBAAwB,KAAK,CAAC,YAAY,QAAQ,KAAK,UAAU,CAAC,GAClE;AACD,WAAO;AAAA,EACR;AAEA,SAAO;AACR;AAEO,SAAS,oBAAoB,KAAuB;AAC1D,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,MAAI,IAAI,QAAQ,SAAS,WAAW,EAAG,QAAO;AAC9C,QAAM,cAAc;AACpB,SAAO,YAAY,MAAM,SAAS;AACnC;AAKO,SAAS,cAAc,OAA6B;AAC1D,MAAI,iBAAiB,aAAa;AACjC,WAAO;AAAA,EACR;AAEA,MAAI,iBAAiB,OAAO;AAE3B,UAAM,UAAU,MAAM,QAAQ,YAAY;AAC1C,UAAM,OAAO,MAAM,KAAK,YAAY;AAGpC,QAAI,SAAS,eAAe,QAAQ,SAAS,OAAO,GAAG;AACtD,aAAO,IAAI,aAAa,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IACxD;AACA,QAAI,SAAS,gBAAgB,QAAQ,SAAS,SAAS,GAAG;AACzD,aAAO,IAAI,YAAY,MAAM,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,CAAC;AAAA,IACxE;AAGA,QAAI,QAAQ,SAAS,SAAS,GAAG;AAChC,aAAO,IAAI,aAAa,oBAAoB,EAAE,OAAO,MAAM,CAAC;AAAA,IAC7D;AAGA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,cAAc,GAAG;AAChE,aAAO,IAAI,oBAAoB,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IAC/D;AACA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG;AAC7D,aAAO,IAAI,mBAAmB,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IAC9D;AACA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG;AAC7D,aAAO,IAAI,cAAc,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IACzD;AACA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,YAAY,GAAG;AAC9D,aAAO,IAAI,eAAe,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IAC1D;AAEA,WAAO,IAAI,YAAY,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,EACvD;AAEA,SAAO,IAAI,YAAYA,iBAAgB,KAAK,CAAC;AAC9C;AAUO,SAAS,mBACf,SACA,YAAY,qBACZ,WAAW,oBACF;AAET,QAAM,mBAAmB,YAAY,KAAK;AAG1C,QAAM,cAAc,KAAK,IAAI,kBAAkB,QAAQ;AAGvD,QAAM,SAAS,cAAc,QAAQ,KAAK,OAAO,IAAI,IAAI;AAEzD,SAAO,KAAK,MAAM,cAAc,MAAM;AACvC;AAnuBA,IAgFa,mBAyBA,iBAsCA,aAsIA,cAUA,cAiBA,qBAUA,oBAUA,iBAyDA,gBA8CA,eA2JP,yBA0BA;AAhmBN;AAAA;AAAA;AAwBA;AAwDO,IAAM,oBAAqD;AAAA,MACjE,aAAa;AAAA,MACb,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,MACX,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,mBAAmB;AAAA,MACnB,gBAAgB;AAAA,MAChB,uBAAuB;AAAA,MACvB,iBAAiB;AAAA,MACjB,aAAa;AAAA,MACb,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,SAAS;AAAA,MACT,SAAS;AAAA,MACT,aAAa;AAAA,MACb,SAAS;AAAA,IACV;AAKO,IAAM,kBAAwC,oBAAI,IAAI;AAAA,MAC5D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,IACD,CAAC;AA8BM,IAAM,cAAN,MAAM,qBAAoB,MAAM;AAAA;AAAA,MAE7B;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA,MAET,YAAY,SAAiB,UAA8B,CAAC,GAAG;AAC9D,cAAM,SAAS,EAAE,OAAO,QAAQ,MAAM,CAAC;AACvC,aAAK,OAAO;AACZ,aAAK,OAAO,QAAQ,QAAQ;AAC5B,aAAK,SAAS,QAAQ,UAAU,kBAAkB,KAAK,IAAI;AAC3D,aAAK,OAAO,QAAQ;AACpB,aAAK,cAAc,gBAAgB,IAAI,KAAK,IAAI;AAChD,aAAK,aAAa,QAAQ;AAC1B,aAAK,YAAY,oBAAI,KAAK;AAG1B,YAAI,MAAM,mBAAmB;AAC5B,gBAAM,kBAAkB,MAAM,YAAW;AAAA,QAC1C;AAAA,MACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,OAAO,cAAc,KAAkE;AACtF,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,OAAO,gBACN,KAC4F;AAC5F,eACC,eAAe,gBACf,IAAI,SAAS,uBACb,IAAI,MAAM,SAAS;AAAA,MAErB;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,gBAAgB,KAA+D;AACrF,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,eAAe,KAA6D;AAClF,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,WAAW,KAA0D;AAC3E,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,YAAY,KAA0D;AAC5E,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,kBAAkB,KAAqE;AAC7F,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,eAAe,KAA8D;AACnF,eAAO,eAAe,gBAAe,IAAI,SAAS;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,OAAO,gBAAgB,KAAkC;AACxD,eACC,eAAe,iBACd,IAAI,SAAS,iBACb,IAAI,SAAS,qBACb,IAAI,SAAS;AAAA,MAEhB;AAAA;AAAA;AAAA;AAAA,MAKA,SAAkC;AACjC,eAAO;AAAA,UACN,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,MAAM,KAAK;AAAA,UACX,QAAQ,KAAK;AAAA,UACb,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,YAAY,KAAK;AAAA,UACjB,WAAW,KAAK,UAAU,YAAY;AAAA,QACvC;AAAA,MACD;AAAA,IACD;AAKO,IAAM,eAAN,cAA2B,YAAY;AAAA,MAC7C,YAAY,UAAU,0BAA0B,SAA4C;AAC3F,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,gBAAgB,CAAC;AACpD,aAAK,OAAO;AAAA,MACb;AAAA,IACD;AAKO,IAAM,eAAN,cAA2B,YAAY;AAAA;AAAA,MAEpC;AAAA,MAET,YAAY,SAAiB,SAA4C;AACxE,cAAM,2BAA2B,OAAO,MAAM;AAAA,UAC7C,GAAG;AAAA,UACH,MAAM;AAAA,QACP,CAAC;AACD,aAAK,OAAO;AACZ,aAAK,UAAU;AAAA,MAChB;AAAA,IACD;AAKO,IAAM,sBAAN,cAAkC,YAAY;AAAA,MACpD,YAAY,UAAU,2BAA2B,SAA4C;AAC5F,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,eAAe,CAAC;AACnD,aAAK,OAAO;AAAA,MACb;AAAA,IACD;AAKO,IAAM,qBAAN,cAAiC,YAAY;AAAA,MACnD,YAAY,UAAU,qBAAqB,SAA4C;AACtF,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,YAAY,CAAC;AAChD,aAAK,OAAO;AAAA,MACb;AAAA,IACD;AAKO,IAAM,kBAAN,cAA8B,YAAY;AAAA;AAAA,MAEvC;AAAA,MAET,YACC,SACA,SAGC;AACD,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,uBAAuB,CAAC;AAC3D,aAAK,OAAO;AACZ,aAAK,cAAc,SAAS;AAAA,MAC7B;AAAA;AAAA;AAAA;AAAA,MAKA,cAAc,OAAmC;AAChD,eAAO,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,MACrC;AAAA,IACD;AAoCO,IAAM,iBAAN,cAA6B,YAAY;AAAA;AAAA,MAEtC;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA,MAET,YACC,UAAU,qBACV,SACC;AACD,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,oBAAoB,CAAC;AACxD,aAAK,OAAO;AACZ,aAAK,QAAQ,SAAS;AACtB,aAAK,YAAY,SAAS;AAC1B,aAAK,UAAU,SAAS;AAAA,MACzB;AAAA;AAAA;AAAA;AAAA,MAKA,eAAiC;AAChC,eAAO,KAAK,UAAU,IAAI,KAAK,KAAK,UAAU,GAAI,IAAI;AAAA,MACvD;AAAA;AAAA;AAAA;AAAA,MAKA,kBAA0B;AACzB,YAAI,KAAK,YAAY;AACpB,iBAAO,sBAAsB,KAAK,UAAU;AAAA,QAC7C;AACA,YAAI,KAAK,SAAS;AACjB,gBAAM,UAAU,KAAK,IAAI,GAAG,KAAK,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,CAAC;AACxE,iBAAO,wBAAwB,OAAO;AAAA,QACvC;AACA,eAAO;AAAA,MACR;AAAA,IACD;AAKO,IAAM,gBAAN,cAA4B,YAAY;AAAA;AAAA,MAErC;AAAA;AAAA,MAGA;AAAA,MAET,YACC,UAAU,sBACV,SAIC;AACD,cAAM,SAAS,EAAE,GAAG,SAAS,MAAM,YAAY,CAAC;AAChD,aAAK,OAAO;AACZ,aAAK,eAAe,SAAS;AAC7B,aAAK,aAAa,SAAS;AAAA,MAC5B;AAAA,IACD;AAwIA,IAAM,0BAA0B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,IAAM,sBAA8C;AAAA,MACnD,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,aAAa;AAAA,MACb,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,MACX,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,uBAAuB;AAAA,MACvB,SAAS;AAAA,MACT,qBAAqB;AAAA,MACrB,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,qBAAqB;AAAA,MACrB,oBAAoB;AAAA,MACpB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IAClB;AAAA;AAAA;;;ACznBA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAIA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;;;ACKA,IAAM,sCAAsC;AAG5C,IAAM,qBAAqB;AAO3B,IAAM,0CAA0C;AAGhD,IAAM,kBAAkB;AAOxB,IAAM,qCAAqC;AAU3C,IAAM,qBAAqB;AAO3B,IAAM,kBAAkB;;;AC9CxB,IAAM,wBAAwB;AAO9B,IAAM,2BAA2B;AAGjC,IAAM,mBAAmB;AAGzB,IAAM,qBAAqB;AAOlC,IAAM,uBAAuB;AAM7B,IAAM,sBAAsB;AAarB,SAAS,uBAA+B;AAC9C,QAAM,QAAQ,IAAI,WAAW,oBAAoB;AACjD,SAAO,gBAAgB,KAAK;AAE5B,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,sBAAsB,KAAK;AAC9C,YAAQ,oBAAoB,MAAM,CAAC,IAAI,oBAAoB,MAAM;AAAA,EAClE;AACA,SAAO;AACR;;;AC5DO,SAAS,gBAAgB,OAAgB,WAAW,iBAAyB;AACnF,MAAI,iBAAiB,OAAO;AAC3B,WAAO,MAAM;AAAA,EACd;AAEA,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;AAC7D,UAAM,UAAW,MAA+B;AAChD,QAAI,OAAO,YAAY,UAAU;AAChC,aAAO;AAAA,IACR;AAAA,EACD;AAEA,MAAI,SAAS,OAAO,UAAU,YAAY,cAAc,OAAO;AAC9D,UAAM,WAAY,MAChB;AACF,QAAI,UAAU,MAAM,QAAS,QAAO,SAAS,KAAK;AAClD,QAAI,UAAU,MAAM,MAAO,QAAO,SAAS,KAAK;AAAA,EACjD;AAEA,SAAO;AACR;AAEA,SAAS,aAAa,OAAoC;AACzD,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACxC,WAAO;AAAA,EACR;AAEA,MAAI,UAAU,SAAS,OAAQ,MAA4B,SAAS,UAAU;AAC7E,WAAQ,MAA2B;AAAA,EACpC;AAEA,MAAI,YAAY,SAAS,OAAQ,MAA8B,WAAW,UAAU;AACnF,WAAO,OAAQ,MAA6B,MAAM;AAAA,EACnD;AAEA,MAAI,gBAAgB,SAAS,OAAQ,MAAkC,eAAe,UAAU;AAC/F,WAAO,OAAQ,MAAiC,UAAU;AAAA,EAC3D;AAEA,SAAO;AACR;AAWO,SAAS,gBAAgB,OAAgB,kBAAkB,iBAA+B;AAChG,QAAM,UAAwB;AAAA,IAC7B,SAAS,gBAAgB,OAAO,eAAe;AAAA,EAChD;AAEA,MAAI,iBAAiB,OAAO;AAC3B,YAAQ,OAAO,MAAM;AACrB,YAAQ,QAAQ,MAAM;AACtB,QAAI,MAAM,OAAO;AAChB,cAAQ,QAAQ,MAAM;AAAA,IACvB;AAAA,EACD;AAEA,QAAM,OAAO,aAAa,KAAK;AAC/B,MAAI,MAAM;AACT,YAAQ,OAAO;AAAA,EAChB;AAEA,MAAI,SAAS,OAAO,UAAU,UAAU;AACvC,QAAI,YAAY,SAAS,OAAQ,MAA8B,WAAW,UAAU;AACnF,cAAQ,SAAU,MAA6B;AAAA,IAChD,WACC,gBAAgB,SAChB,OAAQ,MAAkC,eAAe,UACxD;AACD,cAAQ,SAAU,MAAiC;AAAA,IACpD;AAAA,EACD;AAEA,SAAO;AACR;;;ACnBA,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAOjB,IAAM,mBAAmB;AAGhC,IAAM,gBAAgB;AAOtB,IAAM,aAAa;AAMZ,IAAM,4BAAN,MAAM,mCAAkC,MAAM;AAAA,EAC3C,OAAO;AAAA,EAEhB,YAAY,SAAiB;AAC5B,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,2BAA0B,SAAS;AAAA,EAChE;AACD;AAMA,SAAS,KAAK,QAAuB;AACpC,QAAM,IAAI,0BAA0B,kCAAkC,MAAM,EAAE;AAC/E;AAEA,SAAS,gBAAgB,KAGvB;AACD,QAAM,QAAQ,iBAAiB,KAAK,GAAG;AACvC,MAAI,CAAC,OAAO;AACX,SAAK,0EAA0E,GAAG,GAAG;AAAA,EACtF;AACA,SAAO;AAAA,IACN,gBAAgB,MAAM,CAAC;AAAA,IACvB,KAAK,MAAM,CAAC;AAAA,EACb;AACD;AAEA,SAAS,aAAa,WAA2B;AAChD,MAAI,CAAC,aAAa,UAAU,SAAS,MAAM,CAAC,WAAW,KAAK,SAAS,GAAG;AACvE,SAAK,SAAS,SAAS,oEAAoE;AAAA,EAC5F;AACA,SAAO;AACR;AAEA,SAAS,eAAe,QAAwB;AAC/C,MAAI,CAAC,UAAU,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,GAAG,GAAG;AACpF,SAAK,WAAW,MAAM,kCAAkC;AAAA,EACzD;AAEA,MAAI,OAAO,SAAS,KAAK,GAAG;AAC3B,SAAK,WAAW,MAAM,6BAA6B;AAAA,EACpD;AACA,SAAO,OAAO,YAAY;AAC3B;AAEA,SAAS,iBAAiB,SAAqC;AAC9D,MAAI,YAAY,OAAW,QAAO;AAClC,MAAI,YAAY,GAAI,QAAO;AAC3B,MAAI,CAAC,cAAc,KAAK,OAAO,GAAG;AACjC,SAAK,YAAY,OAAO,0BAA0B;AAAA,EACnD;AACA,SAAO;AACR;AAWO,SAAS,mBAAmB,OAAwC;AAC1E,QAAM,EAAE,YAAY,MAAM,SAAS,gBAAgB,UAAU,gBAAgB,IAAI;AAEjF,kBAAgB,UAAU;AAC1B,QAAM,WAAW,aAAa,IAAI;AAClC,QAAM,aAAa,eAAe,MAAM;AACxC,QAAM,cAAc,iBAAiB,OAAO;AAE5C,QAAM,OAAO,GAAG,QAAQ,IAAI,UAAU;AACtC,QAAM,aAAa,cAAc,IAAI,WAAW,KAAK;AACrD,SAAO,GAAG,eAAe,KAAK,UAAU,IAAI,IAAI,GAAG,UAAU;AAC9D;AAOO,SAAS,mBAAmB,KAAkC;AACpE,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAChD,SAAK,gCAAgC;AAAA,EACtC;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,IAAI,IAAI,GAAG;AAAA,EACrB,QAAQ;AACP,SAAK,qBAAqB,GAAG,GAAG;AAAA,EACjC;AAEA,MAAI,OAAO,aAAa,iBAAiB;AACxC,SAAK,oCAAoC,OAAO,QAAQ,GAAG;AAAA,EAC5D;AAKA,QAAM,aAAa,mBAAmB,OAAO,QAAQ;AACrD,MAAI,CAAC,YAAY;AAChB,SAAK,8DAA8D;AAAA,EACpE;AACA,MAAI,OAAO,UAAU;AACpB,SAAK,sDAAsD;AAAA,EAC5D;AAEA,QAAM,EAAE,gBAAgB,IAAI,IAAI,gBAAgB,UAAU;AAE1D,QAAM,OAAO,OAAO;AACpB,MAAI,CAAC,MAAM;AACV,SAAK,cAAc;AAAA,EACpB;AAGA,QAAM,WAAW,OAAO;AACxB,QAAM,WAAW,SAAS,QAAQ,GAAG;AACrC,MAAI,YAAY,GAAG;AAClB,SAAK,SAAS,QAAQ,+CAA+C;AAAA,EACtE;AACA,QAAM,gBAAgB,SAAS,MAAM,GAAG,QAAQ;AAChD,QAAM,eAAe,SAAS,MAAM,WAAW,CAAC;AAChD,MAAI,CAAC,cAAc;AAClB,SAAK,SAAS,QAAQ,2BAA2B;AAAA,EAClD;AACA,QAAM,OAAO,aAAa,aAAa;AAGvC,QAAM,UAAU,OAAO,SAAS,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;AACtE,MAAI,UAAU;AACd,MAAI,YAAY,IAAI;AACnB,QAAI,CAAC,cAAc,KAAK,OAAO,GAAG;AACjC,WAAK,SAAS,OAAO,QAAQ,gCAAgC;AAAA,IAC9D;AACA,cAAU;AAAA,EACX;AAEA,MAAI,OAAO,QAAQ;AAClB,SAAK,gDAAgD;AAAA,EACtD;AACA,MAAI,OAAO,MAAM;AAChB,SAAK,4CAA4C;AAAA,EAClD;AAEA,QAAM,aAAa,WAAW,IAAI,IAAI,OAAO;AAE7C,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;ACrOA;AACA;AA2BA,IAAM,8BAA8B;AAGpC,IAAM,yBAAyB;AAE/B,IAAM,oBACL;AAQD,SAAS,sBAAsB,OAAqB;AACnD,QAAM,UAAU,MAAM,KAAK,EAAE,YAAY;AAEzC,MAAI,uBAAuB,KAAK,OAAO,GAAG;AACzC,UAAM,IAAI,YAAY,YAAY,iBAAiB,IAAI,EAAE,MAAM,cAAc,CAAC;AAAA,EAC/E;AAEA,MAAI,4BAA4B,KAAK,OAAO,GAAG;AAC9C,UAAM,IAAI,YAAY,YAAY,iBAAiB,IAAI,EAAE,MAAM,cAAc,CAAC;AAAA,EAC/E;AACD;AAsEA,SAAS,aAAa,MAOL;AAChB,SAAO,OAAO,OAAO;AAAA,IACpB,YAAY,KAAK;AAAA,IACjB,gBAAgB,KAAK;AAAA,IACrB,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,aAAa,KAAK;AAAA;AAAA,IAElB,WAAW,KAAK,mBAAmB,OAAO,KAAK,aAAa;AAAA,IAC5D,WAAW,KAAK,mBAAmB,OAAO,KAAK,aAAa;AAAA,IAC5D,KAAK,KAAK;AAAA,EACX,CAAC;AACF;AA0BO,SAAS,aAAa,OAAiD;AAC7E,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO,oBAAoB,KAAK;AAAA,EACjC;AACA,SAAO,2BAA2B,KAAK;AACxC;AAsBO,SAAS,mBAAmB,OAAiD;AACnF,QAAM,SAAS,aAAa,KAAK;AAEjC,MAAI,OAAO,mBAAmB,MAAM;AACnC,UAAM,IAAI;AAAA,MACT;AAAA,MAEA,EAAE,MAAM,cAAc;AAAA,IACvB;AAAA,EACD;AAEA,SAAO;AACR;AAMA,SAAS,oBAAoB,KAA2B;AACvD,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACpC,UAAM,IAAI;AAAA,MACT;AAAA,MAEA,EAAE,MAAM,cAAc;AAAA,IACvB;AAAA,EACD;AAEA,QAAM,UAAU,IAAI,KAAK;AAGzB,wBAAsB,OAAO;AAG7B,MAAI,CAAC,QAAQ,WAAW,WAAW,GAAG;AAErC,QAAI,iBAAiB,KAAK,OAAO,GAAG;AACnC,YAAM,IAAI;AAAA,QACT;AAAA,QAGA,EAAE,MAAM,cAAc;AAAA,MACvB;AAAA,IACD;AACA,UAAM,IAAI;AAAA,MACT,6EAAwE,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,MAC5F,EAAE,MAAM,cAAc;AAAA,IACvB;AAAA,EACD;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,mBAAmB,OAAO;AAAA,EACpC,SAAS,KAAK;AACb,QAAI,eAAe,2BAA2B;AAC7C,YAAM,IAAI,YAAY,IAAI,SAAS,EAAE,MAAM,eAAe,OAAO,IAAI,CAAC;AAAA,IACvE;AACA,UAAM;AAAA,EACP;AAEA,SAAO,aAAa;AAAA,IACnB,YAAY,OAAO;AAAA,IACnB,gBAAgB,OAAO;AAAA,IACvB,KAAK,OAAO;AAAA,IACZ,MAAM,OAAO;AAAA,IACb,SAAS,OAAO;AAAA,EACjB,CAAC;AACF;AAEA,SAAS,2BAA2B,OAAwC;AAC3E,QAAM,aAAa,MAAM,aAAa,MAAM;AAC5C,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,YAAY,4DAA4D;AAAA,MACjF,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AAGA,QAAM,eAAe,MAAM,QAAQ,MAAM;AAEzC,MAAI,CAAC,cAAc;AAClB,UAAM,IAAI,YAAY,6DAA6D;AAAA,MAClF,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AAGA,QAAM,cAAc,WAAW,KAAK,EAAE,YAAY;AAElD,MAAI,iBAAiB,KAAK,WAAW,GAAG;AAEvC,UAAM,QAAQ,iBAAiB,KAAK,WAAW;AAC/C,UAAM,iBAAiB,MAAM,CAAC;AAC9B,UAAM,MAAM,MAAM,CAAC;AAEnB,UAAM,OAAO,aAAa,KAAK,EAAE,YAAY;AAO7C,UAAM,SAAS,MAAM,QAAQ,KAAK,KAAK;AACvC,UAAM,UAAU,WAAW,IAAI,IAAI,MAAM;AAEzC,WAAO,aAAa;AAAA,MACnB,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,MAAM;AAAA,IACpB,CAAC;AAAA,EACF;AAIA,QAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,QAAM,SAAS,MAAM,CAAC;AACtB,OAAK,WAAW,QAAQ,WAAW,SAAS,MAAM,UAAU,GAAG;AAC9D,UAAM,aAAa,MAAM,CAAC;AAC1B,UAAM,YAAY,CAAC,OAAO,OAAO,QAAQ,MAAM;AAC/C,UAAM,MAAM,UAAU,SAAS,UAAU,IACrC,aACD;AAEH,UAAM,OAAO,aAAa,KAAK,EAAE,YAAY;AAE7C,QAAI;AACJ,QAAI,MAAM,aAAa;AACtB,YAAM,WAAW,MAAM,YAAY,KAAK,EAAE,QAAQ,OAAO,EAAE;AAC3D,gBAAU,SAAS,SAAS,KAAK,IAAI,WAAW,GAAG,QAAQ;AAAA,IAC5D,OAAO;AACN,YAAM,SAAS,MAAM,QAAQ,KAAK,KAAK;AACvC,gBAAU,WAAW,IAAI,IAAI,MAAM;AAAA,IACpC;AAEA,WAAO,aAAa;AAAA,MACnB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,MAAM;AAAA,IACpB,CAAC;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACT,kGAAkG,YAAY,MAAM,GAAG,EAAE,CAAC;AAAA,IAC1H,EAAE,MAAM,cAAc;AAAA,EACvB;AACD;AAgBO,SAAS,UAAU,QAAsB,aAAmC;AAClF,SAAO,OAAO,OAAO;AAAA,IACpB,GAAG;AAAA,IACH;AAAA,EACD,CAAC;AACF;AAeO,IAAM,eAAe;AAS5B,SAAS,sBAAsB,QAAiC;AAC/D,UAAQ,QAAQ;AAAA,IACf,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR;AACC,aAAO,UAAU,MAAM,0BAA0B;AAAA,EACnD;AACD;AAOO,SAAS,aAAa,QAA8C;AAC1E,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,EACjB;AAEA,MAAI,OAAO,YAAY;AACtB,YAAQ,cAAc,IAAI,OAAO;AAAA,EAClC;AACA,MAAI,OAAO,aAAa;AACvB,YAAQ,gBAAgB,UAAU,OAAO,WAAW;AAAA,EACrD;AAEA,SAAO;AACR;AAOO,SAAS,YAAY,QAAsB,MAAsB;AACvE,QAAM,OAAO,OAAO,QAAQ,QAAQ,OAAO,EAAE;AAC7C,QAAM,YAAY,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AACxD,SAAO,GAAG,IAAI,GAAG,SAAS;AAC3B;AAWA,eAAsB,QACrB,QACA,MACA,UAQI,CAAC,GACc;AACnB,QAAM;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACV,IAAI;AAEJ,MAAI,MAAM,YAAY,QAAQ,IAAI;AAGlC,MAAI,OAAO;AACV,UAAM,SAAS,IAAI,gBAAgB;AACnC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,UAAI,UAAU,QAAW;AACxB,eAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,MAC9B;AAAA,IACD;AACA,UAAM,cAAc,OAAO,SAAS;AACpC,QAAI,aAAa;AAChB,aAAO,IAAI,WAAW;AAAA,IACvB;AAAA,EACD;AAGA,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAG9D,QAAM,iBAAiB,SAAS,YAAY,IAAI,CAAC,QAAQ,WAAW,MAAM,CAAC,IAAI,WAAW;AAE1F,QAAM,UAAU,aAAa,MAAM;AAGnC,MAAI,gBAAgB;AACnB,YAAQ,iBAAiB,IAAI;AAAA,EAC9B;AAGA,MAAI,cAAc;AACjB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,YAAY,GAAG;AAClD,cAAQ,CAAC,IAAI;AAAA,IACd;AAAA,EACD;AAEA,QAAM,eAA4B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACT;AAEA,MAAI,MAAM;AACT,iBAAa,OAAO,KAAK,UAAU,IAAI;AAAA,EACxC;AAEA,MAAI;AACJ,MAAI;AACH,eAAW,MAAM,MAAM,KAAK,YAAY;AAAA,EACzC,SAAS,OAAO;AACf,iBAAa,SAAS;AAGtB,QAAI,iBAAiB,OAAO;AAC3B,UAAI,MAAM,SAAS,cAAc;AAEhC,YAAI,WAAW,OAAO,WAAW,CAAC,QAAQ,SAAS;AAClD,gBAAM,IAAI,aAAa,OAAO;AAAA,QAC/B;AACA,cAAM,IAAI,YAAY,mBAAmB;AAAA,UACxC,MAAM;AAAA,UACN,OAAO;AAAA,QACR,CAAC;AAAA,MACF;AAEA,YAAM,IAAI,aAAa,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IACvD;AACA,UAAM,IAAI,aAAa,wBAAwB;AAAA,EAChD,UAAE;AACD,iBAAa,SAAS;AAAA,EACvB;AAEA,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,QAAI,eAAe;AACnB,QAAI;AAGJ,QAAI,WAAW;AACd,UAAI;AACH,cAAM,SAAS,KAAK,MAAM,SAAS;AAInC,uBAAe,OAAO,OAAO,WAAW,OAAO,WAAW;AAC1D,oBAAY,OAAO;AAAA,MACpB,QAAQ;AACP,uBAAe,SAAS,cAAc;AAAA,MACvC;AAAA,IACD;AAEA,UAAM,YAAY,sBAAsB,SAAS,MAAM;AAGvD,UAAM,mBAAmB,SAAS,QAAQ,IAAI,aAAa;AAC3D,UAAM,iBAAiB,SAAS,QAAQ,IAAI,mBAAmB;AAC/D,UAAM,qBAAqB,SAAS,QAAQ,IAAI,uBAAuB;AACvE,UAAM,iBAAiB,SAAS,QAAQ,IAAI,mBAAmB;AAE/D,UAAM,aAAa,mBAAmB,OAAO,SAAS,kBAAkB,EAAE,IAAI;AAG9E,QAAI,SAAS,WAAW,KAAK;AAC5B,YAAM,IAAI,eAAe,gBAAgB,qBAAqB;AAAA,QAC7D,QAAQ,SAAS;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA,OAAO,iBAAiB,OAAO,SAAS,gBAAgB,EAAE,IAAI;AAAA,QAC9D,WAAW,qBAAqB,OAAO,SAAS,oBAAoB,EAAE,IAAI;AAAA,QAC1E,SAAS,iBAAiB,OAAO,SAAS,gBAAgB,EAAE,IAAI;AAAA,MACjE,CAAC;AAAA,IACF;AAEA,UAAM,IAAI,YAAY,cAAc;AAAA,MACnC,MAAM;AAAA,MACN,QAAQ,SAAS;AAAA,MACjB,MAAM;AAAA,MACN;AAAA,IACD,CAAC;AAAA,EACF;AAGA,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,CAAC,MAAM;AACV,WAAO,CAAC;AAAA,EACT;AAGA,MAAI;AACH,WAAO,KAAK,MAAM,IAAI;AAAA,EACvB,SAAS,OAAO;AACf,UAAM,IAAI,YAAY,4BAA4B;AAAA,MACjD,MAAM;AAAA,MACN,OAAO,iBAAiB,QAAQ,QAAQ;AAAA,MACxC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE;AAAA,IAClC,CAAC;AAAA,EACF;AACD;;;AC5nBO,SAAS,eAAe,OAA0C;AACxE,MAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,WAAO;AAAA,EACR;AACA,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,GAAG;AACvE,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACrC;AACA,SAAO;AACR;;;ACQO,SAAS,oBAAoB,OAAe,OAAe,WAAW,GAAW;AACvF,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,aAAa,OAAO,WAAW;AACrC,SAAO,KAAK,MAAO,QAAQ,QAAS,UAAU,IAAI,MAAM;AACzD;AAWO,SAAS,mBACf,cACA,SACS;AACT,SAAO,IAAI,KAAK,aAAa,SAAS;AAAA,IACrC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,GAAG;AAAA,EACJ,CAAC,EAAE,OAAO,eAAe,GAAS;AACnC;AAYO,SAAS,YAAY,OAAuB;AAClD,SAAO,IAAI,KAAK,aAAa,SAAS;AAAA,IACrC,OAAO;AAAA,IACP,UAAU;AAAA,EACX,CAAC,EAAE,OAAO,QAAQ,GAAG;AACtB;AAuBO,SAAS,eACf,QACA,gBAAuF,OAC9E;AACT,QAAM,OAAO,OAAO,kBAAkB,YAAY,EAAE,SAAS,cAAc,IAAI;AAC/E,QAAM,YAAY,KAAK,YAAY,OAAO,YAAY;AACtD,QAAM,WAAW,KAAK,YAAY;AAClC,MAAI,KAAK,WAAW,KAAK,IAAI,MAAM,KAAK,KAAM;AAC7C,WAAO,IAAI,KAAK,aAAa,SAAS;AAAA,MACrC,OAAO;AAAA,MACP;AAAA,MACA,UAAU;AAAA,MACV,uBAAuB;AAAA,MACvB,uBAAuB;AAAA,IACxB,CAAC,EAAE,OAAO,MAAM;AAAA,EACjB;AACA,SAAO,IAAI,KAAK,aAAa,SAAS;AAAA,IACrC,OAAO;AAAA,IACP;AAAA,IACA,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,EACxB,CAAC,EAAE,OAAO,MAAM;AACjB;AAaO,SAAS,cAAc,OAAuB;AACpD,SAAO,GAAG,SAAS,IAAI,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC;AACnD;AAcO,SAAS,aAAa,KAAa,UAAU,OAAe;AAClE,MAAI,WAAW,OAAO,KAAM;AAC3B,WAAO,IAAI,KAAK,aAAa,SAAS;AAAA,MACrC,UAAU;AAAA,MACV,uBAAuB;AAAA,IACxB,CAAC,EAAE,OAAO,GAAG;AAAA,EACd;AACA,MAAI,OAAO,IAAe,QAAO,IAAI,MAAM,KAAe,QAAQ,CAAC,CAAC;AACpE,MAAI,OAAO,IAAW,QAAO,IAAI,MAAM,KAAW,QAAQ,CAAC,CAAC;AAC5D,MAAI,OAAO,IAAO,QAAO,IAAI,MAAM,KAAO,QAAQ,CAAC,CAAC;AACpD,SAAO,IAAI,eAAe;AAC3B;AAgBO,SAAS,eAAe,IAAoB;AAClD,MAAI,KAAK,EAAG,QAAO;AACnB,MAAI,KAAK,IAAM,QAAO,GAAG,KAAK,MAAM,EAAE,CAAC;AACvC,SAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AACjC;AAOO,SAAS,YAAY,OAAkC,WAAW,GAAW;AACnF,MAAI,SAAS,QAAQ,UAAU,KAAK,OAAO,MAAM,KAAK,EAAG,QAAO;AAChE,QAAM,IAAI;AACV,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,IAAI;AAChD,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AAClD,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC5E;AAaA,IAAM,0BAA6D,oBAAI,IAAI;AAAA;AAAA,EAE1E,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,UAAU,SAAS;AAAA;AAAA,EAEpB,CAAC,OAAO,WAAW;AAAA,EACnB,CAAC,WAAW,WAAW;AAAA;AAAA,EAEvB,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,aAAa,SAAS;AAAA;AAAA,EAEvB,CAAC,gBAAgB,SAAS;AAAA,EAC1B,CAAC,WAAW,SAAS;AAAA;AAAA,EAErB,CAAC,YAAY,OAAO;AAAA,EACpB,CAAC,WAAW,OAAO;AAAA,EACnB,CAAC,aAAa,OAAO;AAAA,EACrB,CAAC,UAAU,OAAO;AACnB,CAAC;AASM,SAAS,wBAAwB,QAA8B;AACrE,SAAO,wBAAwB,IAAI,MAAM,KAAK;AAC/C;AAMA,IAAM,0BAA6D,oBAAI,IAAI;AAAA,EAC1E,CAAC,SAAS,WAAW;AAAA,EACrB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,aAAa,OAAO;AACtB,CAAC;AASM,SAAS,wBAAwB,QAA8B;AACrE,SAAO,wBAAwB,IAAI,MAAM,KAAK;AAC/C;AAKA,SAAS,UAAU,MAAkC;AACpD,QAAM,IAAI,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;AAEtD,SAAO,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO;AAC3C;AAQO,SAAS,WACf,MACA,SACA,WAAW,KACF;AACT,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,mBAAmB,SAAS;AAAA,IACpC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,GAAG;AAAA,EACJ,CAAC;AACF;AAQO,SAAS,eACf,MACA,SACA,WAAW,KACF;AACT,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,eAAe,SAAS;AAAA,IAChC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,GAAG;AAAA,EACJ,CAAC;AACF;AAGA,IAAM,wBAAwB,IAAI,KAAK,mBAAmB,MAAM;AAAA,EAC/D,SAAS;AACV,CAAC;AAGD,IAAM,iBAA0E;AAAA,EAC/E,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,EAC3B,EAAE,QAAQ,GAAG,MAAM,MAAM;AAAA,EACzB,EAAE,QAAQ,SAAS,MAAM,OAAO;AAAA,EAChC,EAAE,QAAQ,IAAI,MAAM,QAAQ;AAAA,EAC5B,EAAE,QAAQ,OAAO,mBAAmB,MAAM,OAAO;AAClD;AASO,SAAS,mBAAmB,MAAoC;AACtE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,WAAW,EAAE,QAAQ,IAAI,KAAK,IAAI,KAAK;AAE3C,aAAW,EAAE,QAAQ,KAAK,KAAK,gBAAgB;AAC9C,QAAI,KAAK,IAAI,OAAO,IAAI,QAAQ;AAC/B,aAAO,sBAAsB,OAAO,KAAK,MAAM,OAAO,GAAG,IAAI;AAAA,IAC9D;AACA,eAAW;AAAA,EACZ;AAGA,SAAO,WAAW,CAAC;AACpB;AAkBO,SAAS,wBAAwB,MAAoC;AAC3E,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,SAAS,KAAK,IAAI,IAAI,EAAE,QAAQ;AACtC,QAAM,WAAW,KAAK,MAAM,SAAS,GAAI;AACzC,QAAM,WAAW,KAAK,MAAM,SAAS,GAAM;AAC3C,QAAM,YAAY,KAAK,MAAM,SAAS,IAAS;AAC/C,QAAM,WAAW,KAAK,MAAM,SAAS,KAAU;AAE/C,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,MAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,MAAI,YAAY,GAAI,QAAO,GAAG,SAAS;AACvC,MAAI,aAAa,EAAG,QAAO;AAC3B,MAAI,WAAW,EAAG,QAAO,GAAG,QAAQ;AACpC,MAAI,WAAW,GAAI,QAAO,GAAG,KAAK,MAAM,WAAW,CAAC,CAAC;AACrD,SAAO,EAAE,mBAAmB,SAAS,EAAE,OAAO,SAAS,KAAK,UAAU,CAAC;AACxE;AAQO,SAAS,gBAAgB,MAA4B,WAAW,KAAa;AACnF,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,mBAAmB,SAAS,EAAE,OAAO,QAAQ,MAAM,UAAU,CAAC;AACxE;AAQO,SAAS,WAAW,MAA4B,WAAW,KAAa;AAC9E,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,UAAU,IAAI;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,mBAAmB,SAAS,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAC5E;;;AC7YO,SAAS,cAA2B,OAAe,UAAwB;AACjF,MAAI;AACH,WAAO,KAAK,MAAM,KAAK;AAAA,EACxB,QAAQ;AACP,WAAO,YAAY;AAAA,EACpB;AACD;;;ACHO,SAAS,WAAW,OAA8B,YAAoB;AAC5E,MAAI,OAAQ,WAAoC,WAAW,aAAa;AAEvE,WAAO,SAAS,WACZ,WAA4D,OAAO,SAAS,SAC7E;AAAA,EACJ;AAGA,MAAI,QAAQ,IAAI,qBAAqB;AACpC,WAAO,QAAQ,IAAI;AAAA,EACpB;AAGA,QAAM,OAAO,QAAQ,IAAI,QAAQ;AACjC,SAAO,oBAAoB,IAAI;AAChC;AAKA,IAAM,gBAAwC;AAAA,EAC7C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACN;AAOO,SAAS,WAAW,KAAqB;AAC/C,SAAO,IAAI,QAAQ,YAAY,CAAC,SAAS,cAAc,IAAI,CAAC;AAC7D;AAkBO,SAAS,aAAa,MAAc,WAA4B;AACtE,QAAM,OAAO,KACX,YAAY,EACZ,KAAK,EACL,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,QAAQ,UAAU,GAAG;AAEvB,MAAI,aAAa,KAAK,SAAS,WAAW;AAEzC,WAAO,KAAK,MAAM,GAAG,SAAS,EAAE,QAAQ,OAAO,EAAE;AAAA,EAClD;AAEA,SAAO;AACR;;;AChEO,SAAS,eAAe,IAA6B;AAC3D,MAAI,CAAC,IAAI;AACR,WAAO,EAAE,SAAS,MAAM,IAAI,MAAM,YAAY,KAAK;AAAA,EACpD;AAEA,SAAO;AAAA,IACN,SAAS,cAAc,EAAE;AAAA,IACzB,IAAI,SAAS,EAAE;AAAA,IACf,YAAY,iBAAiB,EAAE;AAAA,EAChC;AACD;AAKA,SAAS,cAAc,IAA2B;AAEjD,MAAI,GAAG,SAAS,MAAM,GAAG;AACxB,UAAM,QAAQ,GAAG,MAAM,YAAY;AACnC,WAAO,QAAQ,QAAQ,MAAM,CAAC,CAAC,KAAK;AAAA,EACrC;AACA,MAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,OAAO,GAAG;AAChD,UAAM,QAAQ,GAAG,MAAM,YAAY,KAAK,GAAG,MAAM,cAAc;AAC/D,WAAO,QAAQ,SAAS,MAAM,CAAC,CAAC,KAAK;AAAA,EACtC;AACA,MAAI,GAAG,SAAS,OAAO,GAAG;AACzB,WAAO;AAAA,EACR;AACA,MAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,UAAM,QAAQ,GAAG,MAAM,eAAe;AACtC,WAAO,QAAQ,UAAU,MAAM,CAAC,CAAC,KAAK;AAAA,EACvC;AACA,MAAI,GAAG,SAAS,SAAS,KAAK,CAAC,GAAG,SAAS,QAAQ,GAAG;AACrD,UAAM,QAAQ,GAAG,MAAM,gBAAgB;AACvC,WAAO,QAAQ,UAAU,MAAM,CAAC,CAAC,KAAK;AAAA,EACvC;AACA,MAAI,GAAG,SAAS,UAAU,GAAG;AAC5B,UAAM,QAAQ,GAAG,MAAM,gBAAgB;AACvC,WAAO,QAAQ,WAAW,MAAM,CAAC,CAAC,KAAK;AAAA,EACxC;AACA,MAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,UAAU,GAAG;AACnD,UAAM,QAAQ,GAAG,MAAM,oBAAoB;AAC3C,WAAO,QAAQ,MAAM,MAAM,CAAC,CAAC,KAAK;AAAA,EACnC;AACA,SAAO;AACR;AAKA,SAAS,SAAS,IAA2B;AAE5C,MAAI,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,MAAM,GAAG;AACjD,UAAM,QAAQ,GAAG,MAAM,gBAAgB;AACvC,QAAI,OAAO;AACV,YAAM,UAAU,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG;AAC1C,aAAO,OAAO,OAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACR;AACA,MAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,UAAM,QAAQ,GAAG,MAAM,qBAAqB;AAC5C,WAAO,QAAQ,WAAW,MAAM,CAAC,CAAC,KAAK;AAAA,EACxC;AAGA,MAAI,GAAG,SAAS,UAAU,KAAK,GAAG,SAAS,OAAO,GAAG;AACpD,UAAM,QAAQ,GAAG,MAAM,sBAAsB,KAAK,GAAG,MAAM,mBAAmB;AAC9E,QAAI,OAAO;AACV,YAAM,UAAU,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG;AAC1C,aAAO,SAAS,OAAO;AAAA,IACxB;AACA,WAAO;AAAA,EACR;AACA,MAAI,GAAG,SAAS,YAAY,GAAG;AAC9B,UAAM,QAAQ,GAAG,MAAM,qBAAqB;AAC5C,QAAI,OAAO;AACV,YAAM,YAAY,MAAM,CAAC;AAEzB,YAAM,kBAA0C;AAAA,QAC/C,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACR;AACA,aAAO,gBAAgB,SAAS,KAAK,cAAc,SAAS;AAAA,IAC7D;AACA,WAAO;AAAA,EACR;AACA,MAAI,GAAG,SAAS,OAAO,GAAG;AACzB,QAAI,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClC,QAAI,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClC,QAAI,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClC,WAAO;AAAA,EACR;AACA,MAAI,GAAG,SAAS,MAAM,GAAG;AACxB,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAKA,SAAS,iBAAiB,IAAoD;AAE7E,MACC,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,QAAQ,KACnB,GAAG,SAAS,SAAS,KAAK,CAAC,GAAG,SAAS,QAAQ,GAC/C;AACD,WAAO;AAAA,EACR;AAGA,MACC,GAAG,SAAS,QAAQ,KACpB,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,SAAS,KACrB,GAAG,SAAS,QAAQ,KACpB,GAAG,SAAS,OAAO,KACnB,GAAG,SAAS,YAAY,KACxB,GAAG,SAAS,UAAU,KACtB,GAAG,SAAS,YAAY,GACvB;AACD,WAAO;AAAA,EACR;AAGA,MACC,GAAG,SAAS,YAAY,KACxB,GAAG,SAAS,UAAU,KACtB,GAAG,SAAS,OAAO,KACnB,GAAG,SAAS,OAAO,KACnB,GAAG,SAAS,MAAM,GACjB;AACD,WAAO;AAAA,EACR;AAEA,SAAO;AACR;;;ACnJA,eAAsB,eAAe,QAA2D;AAC/F,QAAM,SAAS,MAAM,QAA8B,QAAQ,aAAa;AACxE,SAAO,OAAO;AACf;AAEA,eAAsB,aACrB,QACA,aAC2B;AAC3B,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACA,eAAe,mBAAmB,WAAW,CAAC;AAAA,EAC/C;AACA,SAAO,OAAO;AACf;AAEA,eAAsB,gBACrB,QACA,OAC2B;AAC3B,QAAM,SAAS,MAAM,QAAyB,QAAQ,eAAe;AAAA,IACpE,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACV,CAAC;AACD,SAAO,OAAO;AACf;AAEA,eAAsB,cACrB,QACA,mBACA,QAA4B,CAAC,GACF;AAC3B,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACA,eAAe,mBAAmB,iBAAiB,CAAC;AAAA,IACpD;AAAA,MACC,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,IACd;AAAA,EACD;AACA,SAAO,OAAO;AACf;AAEA,eAAsB,gBAAgB,QAAsB,aAAoC;AAC/F,QAAM,QAAiC,QAAQ,eAAe,mBAAmB,WAAW,CAAC,IAAI;AAAA,IAChG,QAAQ;AAAA,EACT,CAAC;AACF;;;ACjDO,IAAM,sBAAsB;AAG5B,IAAM,sBAAsB;AAG5B,IAAM,wBAAwB;AAAA,EACpC,WAAW;AAAA,EACX,WAAW;AAAA,EACX,aAAa,oBAAoB,mBAAmB;AAAA,EACpD,aAAa,QAAQ,mBAAmB;AACzC;;;ACXO,IAAM,eAAe,OAAO,OAAO;AAGnC,IAAM,wBAAwB;AAG9B,IAAM,mBAAmB;AAczB,IAAM,kBAAkB;AAAA;AAAA,EAE9B,IAAI;AAAA,IACH,YAAY;AAAA;AAAA,IACZ,SAAS;AAAA;AAAA,EACV;AAAA;AAAA,EAEA,UAAU;AAAA,IACT,UAAU;AAAA;AAAA,IACV,aAAa;AAAA;AAAA,EACd;AAAA;AAAA,EAEA,IAAI;AAAA,IACH,QAAQ;AAAA,EACT;AAAA,EACA,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,iBAAiB;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,IACd,OAAO;AAAA,EACR;AAAA,EACA,WAAW;AAAA,IACV,QAAQ;AAAA,IACR,YAAY;AAAA,EACb;AAAA,EACA,SAAS;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACL,KAAK;AAAA,EACN;AAAA,EACA,OAAO;AAAA,IACN,aAAa;AAAA,EACd;AAAA,EACA,SAAS;AAAA,IACR,SAAS;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACV,aAAa;AAAA,EACd;AAAA,EACA,YAAY;AAAA,IACX,YAAY;AAAA,EACb;AAAA,EACA,SAAS;AAAA,IACR,eAAe;AAAA,IACf,cAAc;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACP,WAAW;AAAA,IACX,UAAU;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACT,YAAY;AAAA,EACb;AAAA,EACA,YAAY;AAAA,IACX,QAAQ;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACL,aAAa;AAAA,IACb,eAAe;AAAA,EAChB;AAAA,EACA,UAAU;AAAA,IACT,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,mBAAmB;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,IACP,cAAc;AAAA,EACf;AACD;AAOO,IAAM,wCAAwC;AAG9C,IAAM,sCAAsC;AAG5C,IAAM,gCAAgC;AAOtC,IAAM,sBAA8C;AAAA,EAC1D,UAAU;AAAA;AAAA,EACV,OAAO;AAAA;AAAA,EACP,QAAQ;AAAA;AACT;AAGO,IAAM,yBAAiD;AAAA,EAC7D,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AACT;AAOO,IAAM,yBAAiD;AAAA,EAC7D,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,YAAY;AACb;AAaO,IAAM,qCAAqC,oBAAoB;AAO/D,IAAM,4BAA4B,uBAAuB;AAazD,IAAM,sBAA8C;AAAA,EAC1D,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AACZ;AAGO,IAAM,4BAAoD;AAAA,EAChE,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AACT;AAGO,IAAM,sBAAsB,0BAA0B;AAYtD,IAAM,uBAAuB;AAO7B,IAAM,uBAAuB;AAO7B,IAAM,wBAAwB,CAAC,eAAe,SAAS,SAAS;AAIhE,SAAS,iBAAiB,MAAuB;AACvD,SAAO,sBAAsB,SAAS,IAA0B;AACjE;;;ACvNO,IAAM,mBAAmB;AAQzB,SAAS,eAAuC;AACtD,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,YAAY,cAAe,QAAO;AACtC,MAAI,YAAY,UAAW,QAAO;AAClC,MAAI,YAAY,aAAc,QAAO;AACrC,SAAO,QAAQ,IAAI,aAAa,eAAe,SAAS;AACzD;;;ACoDA,IAAM,mBAAmB;AAGzB,IAAM,kBAAkB;AAajB,IAAM,wBAAgD;AAAA,EAC5D,cAAc;AAAA,EACd,eAAe;AAAA,EACf,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,kBAAkB;AACnB;AAWO,SAAS,6BAA6B,IAAoB;AAChE,SAAO,sBAAsB,EAAE,KAAK;AACrC;AAMO,IAAM,iBAAiE;AAAA;AAAA,EAG7E,IAAI;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,QAAQ,WAAW,OAAO,QAAQ,YAAY;AAAA,IAC7D,cAAc;AAAA,IACd,YAAY,CAAC,0BAA0B,0BAA0B,uBAAuB;AAAA,EACzF;AAAA,EAEA,IAAI;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,WAAW,OAAO,QAAQ,YAAY;AAAA,IACrD,cAAc;AAAA,IACd,YAAY,CAAC,uBAAuB,iCAAiC,sBAAsB;AAAA,EAC5F;AAAA,EAEA,IAAI;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,WAAW,OAAO,QAAQ,YAAY;AAAA,IACrD,cAAc;AAAA,IACd,YAAY,CAAC,qBAAqB,8BAA8B,kBAAkB;AAAA,EACnF;AAAA,EAEA,IAAI;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,OAAO,QAAQ,YAAY;AAAA,IAC1C,cAAc;AAAA,IACd,YAAY,CAAC,sBAAsB,oCAAoC,mBAAmB;AAAA,EAC3F;AAAA,EAEA,IAAI;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,QAAQ,YAAY;AAAA,IACnC,cAAc;AAAA,IACd,YAAY,CAAC,sBAAsB,8BAA8B,yBAAyB;AAAA,EAC3F;AAAA,EAEA,OAAO;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,QAAQ,YAAY;AAAA,IACnC,cAAc;AAAA,IACd,YAAY,CAAC,uBAAuB,kCAAkC,iBAAiB;AAAA,EACxF;AAAA,EAEA,OAAO;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,YAAY;AAAA,IAC3B,cAAc;AAAA,IACd,YAAY,CAAC,wBAAwB,yBAAyB,sBAAsB;AAAA,EACrF;AAAA;AAAA;AAAA,EAKA,cAAc;AAAA,IACb,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,QAAQ,WAAW,OAAO,QAAQ,YAAY;AAAA,IAC7D,cAAc;AAAA,IACd,YAAY,CAAC,qBAAqB,0BAA0B,gCAAgC;AAAA,IAC5F,YAAY;AAAA,EACb;AAAA;AAAA,EAGA,eAAe;AAAA,IACd,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,WAAW,OAAO,QAAQ,YAAY;AAAA,IACrD,cAAc;AAAA,IACd,YAAY,CAAC,sBAAsB,8BAA8B,sBAAsB;AAAA,IACvF,YAAY;AAAA,EACb;AAAA;AAAA,EAGA,eAAe;AAAA,IACd,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,WAAW,OAAO,QAAQ,YAAY;AAAA,IACrD,cAAc;AAAA,IACd,YAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,YAAY;AAAA,EACb;AAAA;AAAA,EAGA,iBAAiB;AAAA,IAChB,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,OAAO,QAAQ,YAAY;AAAA,IAC1C,cAAc;AAAA,IACd,YAAY,CAAC,uBAAuB,oCAAoC,mBAAmB;AAAA,IAC3F,YAAY;AAAA,EACb;AAAA;AAAA,EAGA,iBAAiB;AAAA,IAChB,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,QAAQ,YAAY;AAAA,IACnC,cAAc;AAAA,IACd,YAAY,CAAC,uBAAuB,8BAA8B,yBAAyB;AAAA,IAC3F,YAAY;AAAA,EACb;AAAA;AAAA,EAGA,kBAAkB;AAAA,IACjB,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,cAAc,CAAC,YAAY;AAAA,IAC3B,cAAc;AAAA,IACd,YAAY,CAAC,wBAAwB,kCAAkC,sBAAsB;AAAA,IAC7F,YAAY;AAAA,EACb;AACD;AAOO,IAAM,sBAAwC,CAAC,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK;AAMzF,IAAM,6BAA+C;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAMA,IAAM,wBAAgE;AAAA,EACrE,MAAM;AAAA,EACN,SAAS;AAAA,EACT,KAAK;AAAA,EACL,MAAM;AAAA,EACN,YAAY;AACb;AAGO,SAAS,uBAAuB,MAAsC;AAC5E,SAAO,sBAAsB,IAAI;AAClC;AAGO,SAAS,0BAA0B,MAAgD;AACzF,SAAO,oBAAoB,IAAI,CAAC,OAAO,eAAe,EAAE,CAAC,EAAE;AAAA,IAC1D,CAAC,MAAM,EAAE,aAAa,SAAS,IAAI,KAAK,CAAC,EAAE;AAAA,EAC5C;AACD;AAGO,SAAS,iBAAiB,IAG/B;AACD,QAAM,IAAI,eAAe,EAAE;AAC3B,SAAO;AAAA,IACN,UAAU,EAAE,KAAK,EAAE,YAAY,QAAQ,EAAE,cAAc;AAAA,IACvD,QAAQ,EAAE,KAAK,EAAE,UAAU,QAAQ,EAAE,YAAY;AAAA,EAClD;AACD;AAGO,SAAS,oBAAoB,IAA4B;AAC/D,SAAO,eAAe,EAAE,EAAE;AAC3B;AAGO,IAAM,wBAAwB;AAG9B,SAAS,oBAAoB,IAAkC;AACrE,SAAO,MAAM;AACd;AAGO,SAAS,4BACf,IACA,MACqC;AAErC,QAAM,cAAc,6BAA6B,EAAE;AAEnD,MAAI,CAAC,oBAAoB,WAAW,GAAG;AACtC,WAAO,EAAE,OAAO,OAAO,OAAO,0BAA0B,EAAE,GAAG;AAAA,EAC9D;AAEA,QAAM,aAAa,eAAe,WAAW;AAC7C,MAAI,CAAC,WAAW,aAAa,SAAS,IAAI,GAAG;AAC5C,WAAO;AAAA,MACN,OAAO;AAAA,MACP,OAAO,kBAAkB,WAAW,IAAI,6BAA6B,IAAI;AAAA,IAC1E;AAAA,EACD;AAEA,SAAO,EAAE,OAAO,KAAK;AACtB;;;AC3UO,IAAM,iBAAiE;AAAA,EAC7E,MAAM;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,cAAc;AAAA,IACd,aAAa;AAAA,IACb,6BAA6B;AAAA;AAAA,IAC7B,UAAU;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACP,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,yBAAyB,KAAK;AAAA;AAAA,MAC9B,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACnB;AAAA,IACA,YAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,KAAK;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,cAAc;AAAA;AAAA,IACd,aAAa;AAAA;AAAA,IACb,6BAA6B;AAAA;AAAA,IAC7B,UAAU;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACP,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,yBAAyB,KAAK;AAAA;AAAA,MAC9B,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACnB;AAAA,IACA,YAAY,CAAC,kBAAkB,eAAe,kBAAkB,YAAY,eAAe;AAAA,IAC3F,KAAK;AAAA,EACN;AAAA,EAEA,KAAK;AAAA,IACJ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,cAAc;AAAA;AAAA,IACd,aAAa;AAAA;AAAA,IACb,6BAA6B;AAAA;AAAA,IAC7B,UAAU;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACP,aAAa;AAAA;AAAA,MACb,YAAY;AAAA,MACZ,kBAAkB;AAAA;AAAA,MAClB,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,cAAc;AAAA;AAAA,MACd,yBAAyB,IAAI,KAAK;AAAA;AAAA,MAClC,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACnB;AAAA,IACA,YAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,OAAO;AAAA,IACP,KAAK;AAAA,EACN;AAAA,EAEA,MAAM;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,cAAc;AAAA;AAAA,IACd,aAAa;AAAA;AAAA,IACb,6BAA6B;AAAA;AAAA,IAC7B,SAAS;AAAA,IACT,UAAU;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACP,aAAa;AAAA;AAAA,MACb,YAAY;AAAA;AAAA,MACZ,kBAAkB;AAAA;AAAA,MAClB,sBAAsB;AAAA;AAAA,MACtB,iBAAiB;AAAA;AAAA,MACjB,cAAc;AAAA;AAAA,MACd,yBAAyB,IAAI,KAAK;AAAA;AAAA,MAClC,oBAAoB;AAAA,MACpB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACnB;AAAA,IACA,YAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,KAAK;AAAA,EACN;AAAA,EAEA,YAAY;AAAA,IACX,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,cAAc;AAAA;AAAA,IACd,aAAa;AAAA;AAAA,IACb,6BAA6B;AAAA;AAAA,IAC7B,UAAU;AAAA,IACV,UAAU;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,KAAK;AAAA,MACL,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACP,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,yBAAyB,KAAK,KAAK;AAAA,MACnC,oBAAoB;AAAA;AAAA,MACpB,cAAc;AAAA,MACd,cAAc;AAAA;AAAA,MACd,sBAAsB;AAAA;AAAA,MACtB,qBAAqB;AAAA;AAAA,MACrB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACnB;AAAA,IACA,YAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,KAAK;AAAA,EACN;AACD;AAOO,IAAM,sBAAwC,CAAC,QAAQ,OAAO,QAAQ,YAAY;AAMlF,IAAM,0BAA4C;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAGO,SAAS,iBAAiB,QAAiC;AACjE,SAAO,eAAe,MAAM,EAAE,eAAe;AAC9C;AAGO,SAAS,iBAA2C;AAC1D,SAAO,oBAAoB,IAAI,CAAC,OAAO,eAAe,EAAE,CAAC;AAC1D;AAGO,SAAS,gBAAgB,cAA8B;AAC7D,SAAO,KAAK,eAAe,KAAW,QAAQ,CAAC,CAAC;AACjD;AAGO,SAAS,eAAe,OAAuB;AACrD,SAAO,KAAK,QAAQ,KAAK,QAAQ,CAAC,CAAC;AACpC;AAGO,SAAS,oBAAoB,MAA8B,SAAS,OAAe;AACzF,MAAI,KAAK,SAAU,QAAO;AAC1B,QAAM,QACL,UAAU,KAAK,eAAe,OAAO,KAAK,MAAM,KAAK,cAAc,EAAE,IAAI,KAAK;AAC/E,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,OAAO,eAAe,KAAK;AACjC,SAAO,KAAK,UAAU,GAAG,IAAI,UAAU;AACxC;;;AC5WA,IAAM,oBAAoB;AAU1B,SAAS,iBAA0B;AAElC,MAAI,OAAO,WAAW,eAAe,OAAO,iBAAiB,aAAa;AACzE,QAAI;AACH,aAAO,aAAa,QAAQ,iBAAiB,MAAM;AAAA,IACpD,QAAQ;AAEP,aAAO;AAAA,IACR;AAAA,EACD;AAGA,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AAClD,WAAO,QAAQ,IAAI,iBAAiB;AAAA,EACrC;AAEA,SAAO;AACR;AAGA,IAAI,iBAAiC;AAO9B,SAAS,eAAwB;AACvC,MAAI,mBAAmB,MAAM;AAC5B,qBAAiB,eAAe;AAAA,EACjC;AACA,SAAO;AACR;AAKO,SAAS,sBAA4B;AAC3C,mBAAiB;AAClB;AA2BO,SAAS,SAAS,UAAyB,SAAiB,MAAsB;AACxF,MAAI,CAAC,aAAa,EAAG;AAErB,QAAM,UAAU,WAAW,QAAQ;AAEnC,MAAI,SAAS,QAAW;AAAA,EACxB,OAAO;AAAA,EACP;AACD;AAKO,SAAS,UAAU,UAAyB,SAAiB,MAAsB;AACzF,MAAI,CAAC,aAAa,EAAG;AAErB,QAAM,SAAS,WAAW,QAAQ;AAElC,MAAI,SAAS,QAAW;AACvB,YAAQ,KAAK,QAAQ,SAAS,IAAI;AAAA,EACnC,OAAO;AACN,YAAQ,KAAK,QAAQ,OAAO;AAAA,EAC7B;AACD;AAQO,SAAS,WAAW,UAAyB,SAAiB,OAAuB;AAC3F,MAAI,CAAC,aAAa,EAAG;AAErB,QAAM,SAAS,WAAW,QAAQ;AAElC,MAAI,UAAU,QAAW;AACxB,YAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EACrC,OAAO;AACN,YAAQ,MAAM,QAAQ,OAAO;AAAA,EAC9B;AACD;AAgBO,SAAS,WAAW,UAAyB,WAAwC;AAC3F,MAAI,CAAC,aAAa,GAAG;AACpB,WAAO,EAAE,KAAK,MAAM;AAAA,IAAC,EAAE;AAAA,EACxB;AAEA,QAAM,QAAQ,YAAY,IAAI;AAE9B,SAAO;AAAA,IACN,MAAM;AACL,YAAM,WAAW,YAAY,IAAI,IAAI;AACrC,eAAS,UAAU,GAAG,SAAS,cAAc;AAAA,QAC5C,YAAY,KAAK,MAAM,QAAQ;AAAA,MAChC,CAAC;AAAA,IACF;AAAA,EACD;AACD;AAcO,SAAS,cAAoB;AACnC,MAAI,OAAO,iBAAiB,aAAa;AACxC,YAAQ,KAAK,iEAAiE;AAC9E;AAAA,EACD;AAEA,MAAI;AACH,iBAAa,QAAQ,mBAAmB,MAAM;AAC9C,qBAAiB;AAAA,EAClB,SAAS,GAAG;AACX,YAAQ,KAAK,yCAAyC,CAAC;AAAA,EACxD;AACD;AAKO,SAAS,eAAqB;AACpC,MAAI,OAAO,iBAAiB,aAAa;AACxC,YAAQ,KAAK,kEAAkE;AAC/E;AAAA,EACD;AAEA,MAAI;AACH,iBAAa,WAAW,iBAAiB;AACzC,qBAAiB;AAAA,EAClB,SAAS,GAAG;AACX,YAAQ,KAAK,0CAA0C,CAAC;AAAA,EACzD;AACD;AAYO,SAAS,4BAAkC;AACjD,MAAI,OAAO,WAAW,YAAa;AAGnC,QAAM,IAAI;AAQV,IAAE,WAAW;AAAA,IACZ;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,EACjB;AACD;;;ACpNA;AAcA;;;ACgDA,IAAM,qBAAqB;AAG3B,IAAM,iBAAkD;AAAA,EACvD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AACP;AASA,SAAS,gBAAgB,KAAuB;AAC/C,QAAM,SAAmB,CAAC;AAC1B,MAAI,QAAQ,IAAI,KAAK,EAAG,QAAO,KAAK,YAAY;AAChD,MAAI,IAAI,SAAS,IAAI,EAAG,QAAO,KAAK,SAAS;AAC7C,MAAI,IAAI,SAAS,IAAI,EAAG,QAAO,KAAK,iBAAiB;AACrD,MAAI,IAAI,SAAS,GAAG,EAAG,QAAO,KAAK,OAAO;AAC1C,MAAI,QAAQ,IAAI,YAAY,EAAG,QAAO,KAAK,iBAAiB;AAC5D,SAAO;AACR;AAKA,SAAS,0BAA0B,SAAkB,QAAkB,YAA4B;AAClG,QAAM,cAAc,YAAY,UAAU,WAAW;AACrD,SACC,YAAY,WAAW,aAAa,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,UAI1C,UAAU;AAAA;AAAA;AAAA;AAAA;AAKvB;AAKA,SAAS,sBAAsB,SAAkB,KAAa,YAA4B;AACzF,QAAM,YAAY,IAAI,SAAS,KAAK,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,QAAQ;AAC/D,QAAM,aACL,YAAY,UACT,uEACA;AACJ,QAAM,cAAc,YAAY,UAAU,WAAW;AAErD,SACC,oBAAoB,WAAW;AAAA;AAAA,mBACX,UAAU;AAAA,aAChB,SAAS;AAAA;AAAA,oBACF,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQjC;AAKA,SAAS,mBAAmB,KAA0C;AAErE,QAAM,QAAQ,IAAI,MAAM,qCAAqC;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,eAAe,MAAM,CAAC,CAAC;AAC/B;AAKA,SAAS,mBACR,KACA,SACA,SACA,YACsB;AACtB,QAAM,cAAc,YAAY,UAAU,WAAW;AAGrD,MAAI,CAAC,KAAK;AACT,WAAO;AAAA,MACN,OAAO;AAAA,MACP,cAAc;AAAA,MACd,OACC,YAAY,WAAW,qBAChB,UAAU;AAAA,MAClB,QAAQ,CAAC,SAAS;AAAA,IACnB;AAAA,EACD;AAGA,QAAM,SAAS,gBAAgB,GAAG;AAGlC,MAAI,QAAQ,KAAK,GAAG,GAAG;AACtB,WAAO;AAAA,MACN,OAAO;AAAA,MACP,cAAc;AAAA,MACd;AAAA,MACA,aAAa,mBAAmB,GAAG;AAAA,MACnC,QAAQ,CAAC;AAAA,IACV;AAAA,EACD;AAGA,QAAM,YAAY,IAAI,KAAK,EAAE,YAAY;AAEzC,MAAI,QAAQ,KAAK,SAAS,GAAG;AAE5B,WAAO;AAAA,MACN,OAAO;AAAA,MACP,cAAc;AAAA,MACd;AAAA,MACA,aAAa,mBAAmB,SAAS;AAAA,MACzC,SAAS,0BAA0B,SAAS,QAAQ,UAAU;AAAA,MAC9D;AAAA,IACD;AAAA,EACD;AAGA,SAAO;AAAA,IACN,OAAO;AAAA,IACP,cAAc;AAAA,IACd,OAAO,sBAAsB,SAAS,KAAK,UAAU;AAAA,IACrD,QAAQ,CAAC,GAAG,QAAQ,gBAAgB;AAAA,EACrC;AACD;AAqFO,SAAS,kBAAkB,KAAqD;AACtF,SAAO,mBAAmB,KAAK,UAAU,oBAAoB,mBAAmB;AACjF;AAQO,SAAS,6BAA6B,KAAwC;AACpF,QAAM,SAAS,kBAAkB,GAAG;AAEpC,MAAI,CAAC,OAAO,OAAO;AAClB,UAAM,IAAI,MAAM,OAAO,KAAK;AAAA,EAC7B;AAEA,MAAI,OAAO,SAAS;AACnB,YAAQ,KAAK,OAAO,OAAO;AAAA,EAC5B;AAEA,SAAO,OAAO;AACf;;;ADzIA,eAAe,YACd,aACA,SACoB;AACpB,MAAI,UAAU;AACd,aAAW,MAAM,aAAa;AAC7B,QAAI,GAAG,WAAW;AACjB,YAAM,OAAO,MAAM,GAAG,UAAU,EAAE,QAAQ,CAAC;AAC3C,UAAI,KAAM,WAAU;AAAA,IACrB;AAAA,EACD;AAEA,QAAM,sBAAsB,YAAY;AAAA,IACvC,CAAC,MAAM,OACN,GAAG,UACA,OAAOC,aAAY;AACnB,YAAMC,YAAW,MAAM,GAAG,UAAU,EAAE,SAAAD,UAAS,KAAK,CAAC;AACrD,aAAOC,aAAY,KAAKD,QAAO;AAAA,IAChC,IACC;AAAA,IACJ,CAACA,aAAY,MAAMA,QAAO;AAAA,EAC3B;AAEA,MAAI,WAAW,MAAM,oBAAoB,OAAO;AAEhD,aAAW,MAAM,aAAa;AAC7B,QAAI,GAAG,YAAY;AAClB,YAAM,OAAO,MAAM,GAAG,WAAW,EAAE,SAAS,SAAS,CAAC;AACtD,UAAI,KAAM,YAAW;AAAA,IACtB;AAAA,EACD;AAEA,SAAO;AACR;AAOA,SAAS,SAAS,SAAiB,MAAc,QAA0C;AAC1F,QAAM,MAAM,GAAG,OAAO,GAAG,IAAI;AAC7B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,MAAS;AACxE,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAME,UAAS,IAAI;AAAA,IAClB,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAqB;AAAA,EAC3D,EAAE,SAAS;AACX,SAAO,GAAG,GAAG,IAAIA,OAAM;AACxB;AAEA,SAAS,wBACR,SACA,eACU;AACV,QAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAC3C,gBAAc,OAAO;AACrB,SAAO,IAAI,QAAQ,SAAS,EAAE,QAAQ,CAAC;AACxC;AAgCA,SAAS,gBAAgB,MAAc,YAA6C;AACnF,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,KAAK,QAAQ,cAAc,CAAC,QAAQ,QAAgB;AAC1D,UAAM,QAAQ,WAAW,GAAG;AAC5B,QAAI,UAAU,OAAW,QAAO,IAAI,GAAG;AACvC,WAAO,mBAAmB,KAAK;AAAA,EAChC,CAAC;AACF;AAEA,eAAe,eACd,QACA,SACA,MACA,SACA,aACA,aACsC;AACtC,QAAM,YAAY,gBAAgB,MAAM,SAAS,QAAQ,IAAI;AAC7D,QAAM,MAAM,SAAS,SAAS,WAAW,SAAS,QAAQ,KAAK;AAE/D,QAAM,UAAkC,EAAE,GAAG,aAAa,GAAG,SAAS,QAAQ;AAC9E,QAAM,OAAoB,EAAE,QAAQ,QAAQ;AAC5C,MAAI,SAAS,SAAS,QAAW;AAChC,SAAK,OAAO,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI;AACzF,QAAI,CAAC,QAAQ,cAAc,KAAK,CAAC,QAAQ,cAAc,GAAG;AACzD,cAAQ,cAAc,IAAI;AAAA,IAC3B;AAAA,EACD;AAEA,QAAM,UAAU,IAAI,QAAQ,KAAK,IAAI;AACrC,QAAM,WAAW,MAAM,YAAY,aAAa,OAAO;AAOvD,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,MAAI;AACJ,MAAI,YAAY,SAAS,MAAM,GAAG;AACjC,QAAI;AACH,eAAS,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,IACtC,QAAQ;AACP,eAAS;AAAA,IACV;AAAA,EACD,WAAW,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AAC9D,QAAI;AACH,YAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,eAAS,SAAS,KAAK,SAAY;AAAA,IACpC,QAAQ;AACP,eAAS;AAAA,IACV;AAAA,EACD;AAEA,MAAI,SAAS,IAAI;AAChB,WAAO,EAAE,MAAM,QAAe,SAAS;AAAA,EACxC;AACA,SAAO,EAAE,OAAO,QAAkB,SAAS;AAC5C;AA6CA,SAAS,YACR,SACA,aACoD;AACpD,QAAM,cAA4B,CAAC;AACnC,QAAM,WACL,CAAc,WACd,CAAC,MAAc,YACd,eAA4B,QAAQ,SAAS,MAAM,SAAS,aAAa,WAAW;AAEtF,QAAM,SAAqB;AAAA,IAC1B,KAAK,SAAS,KAAK;AAAA,IACnB,MAAM,SAAS,MAAM;AAAA,IACrB,KAAK,SAAS,KAAK;AAAA,IACnB,OAAO,SAAS,OAAO;AAAA,IACvB,QAAQ,SAAS,QAAQ;AAAA,IACzB,MAAM,SAAS,MAAM;AAAA,IACrB,SAAS,SAAS,SAAS;AAAA,IAC3B,OAAO,KAA4B;AAClC,kBAAY,KAAK,GAAG,GAAG;AAAA,IACxB;AAAA,EACD;AAEA,SAAO,EAAE,QAAQ,YAAY;AAC9B;AAKA,SAAS,qBAAqB,QAAuC;AACpE,SAAO;AAAA,IACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAC5B,aAAO,wBAAwB,SAAS,CAAC,YAAY;AAEpD,gBAAQ,IAAI,iBAAiB,WAAW;AACxC,gBAAQ,IAAI,kBAAkB,YAAY;AAG1C,YAAI,OAAO,WAAW;AACrB,kBAAQ,IAAI,gBAAgB,OAAO,SAAS;AAAA,QAC7C;AAGA,cAAM,QAAQ,OAAO,iBAAiB;AACtC,YAAI,OAAO;AACV,kBAAQ,IAAI,iBAAiB,UAAU,KAAK,EAAE;AAAA,QAC/C;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AACD;AAKA,SAAS,kBAAkB,QAAyB;AACnD,SAAO,UAAU,OAAO,WAAW;AACpC;AASA,eAAe,cAAc,SAAmC;AAC/D,QAAM,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,EAAE,KAAK,IAAI;AAC3D,SAAO,GAAG,QAAQ,MAAM,IAAI,QAAQ,GAAG,IAAI,IAAI;AAChD;AAaA,SAAS,8BACR,SAA2F,CAAC,GAC/E;AACb,QAAM,EAAE,UAAU,MAAM,UAAU,CAAC,KAAK,EAAE,IAAI;AAE9C,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,mBAAmB,oBAAI,IAA+B;AAE5D,SAAO;AAAA,IACN,MAAM,QAAQ,EAAE,SAAS,KAAK,GAAG;AAEhC,YAAM,SAAS,QAAQ,OAAO,YAAY;AAC1C,UAAI,CAAC,QAAQ,SAAS,MAAM,GAAG;AAC9B,eAAO,KAAK,OAAO;AAAA,MACpB;AAEA,YAAM,MAAM,MAAM,cAAc,OAAO;AACvC,YAAM,WAAW,iBAAiB,IAAI,GAAG;AACzC,UAAI,UAAU;AACb,cAAM,iBAAiB,MAAM;AAC7B,eAAO,eAAe,MAAM;AAAA,MAC7B;AAEA,YAAM,kBAAkB,KAAK,OAAO,EAAE,KAAK,CAAC,aAAa,SAAS,MAAM,CAAC;AACzE,uBAAiB,IAAI,KAAK,eAAe;AAEzC,UAAI;AACH,cAAM,WAAW,MAAM;AACvB,eAAO,SAAS,MAAM;AAAA,MACvB,UAAE;AACD,yBAAiB,OAAO,GAAG;AAAA,MAC5B;AAAA,IACD;AAAA,EACD;AACD;AAkBO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACzC;AAAA,EAET,YAAY,aAAqB;AAChC,UAAM,wCAAwC,KAAK,KAAK,cAAc,GAAI,CAAC,GAAG;AAC9E,SAAK,OAAO;AACZ,SAAK,cAAc;AAAA,EACpB;AACD;AAkBA,SAAS,6BAA6B,SAA+B,CAAC,GAAmB;AACxF,SAAO;AAAA,IACN,OAAO;AAAA,IACP,UAAU,CAAC;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,MACP,SAAS,OAAO,WAAW;AAAA,MAC3B,kBAAkB,OAAO,oBAAoB;AAAA,MAC7C,UAAU,OAAO,YAAY;AAAA,MAC7B,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,WAAW,OAAO,cAAc,CAAC,WAAW,UAAU,OAAO,WAAW;AAAA,IACzE;AAAA,EACD;AACD;AAKA,SAAS,cAAc,IAA0B;AAChD,QAAM,MAAM,KAAK,IAAI;AAGrB,KAAG,WAAW,GAAG,SAAS,OAAO,CAAC,MAAM,MAAM,IAAI,GAAG,OAAO,QAAQ;AAGpE,KAAG,SAAS,KAAK,GAAG;AAGpB,MAAI,GAAG,SAAS,UAAU,GAAG,OAAO,kBAAkB;AACrD,OAAG,QAAQ;AACX,OAAG,WAAW;AAAA,EACf;AACD;AAKA,SAAS,cAAc,IAA0B;AAChD,MAAI,GAAG,UAAU,aAAa;AAE7B,OAAG,QAAQ;AACX,OAAG,WAAW,CAAC;AACf,OAAG,WAAW;AAAA,EACf;AACD;AAKA,SAAS,mBAAmB,IAG1B;AACD,QAAM,MAAM,KAAK,IAAI;AAErB,UAAQ,GAAG,OAAO;AAAA,IACjB,KAAK;AACJ,aAAO,EAAE,SAAS,KAAK;AAAA,IAExB,KAAK,QAAQ;AACZ,YAAM,UAAU,OAAO,GAAG,YAAY;AACtC,UAAI,WAAW,GAAG,OAAO,gBAAgB;AAExC,WAAG,QAAQ;AACX,eAAO,EAAE,SAAS,KAAK;AAAA,MACxB;AACA,aAAO;AAAA,QACN,SAAS;AAAA,QACT,aAAa,GAAG,OAAO,iBAAiB;AAAA,MACzC;AAAA,IACD;AAAA,IAEA,KAAK;AAGJ,aAAO,EAAE,SAAS,KAAK;AAAA,IAExB;AACC,aAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACD;AAYA,SAAS,+BACR,QACa;AACb,MAAI,WAAW,OAAO;AACrB,WAAO;AAAA,MACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAC5B,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAGA,QAAM,KAAK,6BAA6B,UAAU,CAAC,CAAC;AAGpD,wBAAsB;AAEtB,SAAO;AAAA,IACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAC5B,UAAI,CAAC,GAAG,OAAO,SAAS;AACvB,eAAO;AAAA,MACR;AAEA,YAAM,QAAQ,mBAAmB,EAAE;AACnC,UAAI,CAAC,MAAM,SAAS;AACnB,cAAM,IAAI,wBAAwB,MAAM,WAAY;AAAA,MACrD;AAEA,aAAO;AAAA,IACR;AAAA,IACA,MAAM,WAAW,EAAE,SAAS,GAAG;AAC9B,UAAI,CAAC,GAAG,OAAO,SAAS;AACvB,eAAO;AAAA,MACR;AAEA,UAAI,GAAG,OAAO,UAAU,SAAS,MAAM,GAAG;AACzC,sBAAc,EAAE;AAAA,MACjB,OAAO;AACN,sBAAc,EAAE;AAAA,MACjB;AAEA,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAWA,IAAI,sBAA6C;AAO1C,SAAS,sBAA4B;AAC3C,MAAI,qBAAqB;AACxB,wBAAoB,QAAQ;AAC5B,wBAAoB,WAAW,CAAC;AAChC,wBAAoB,WAAW;AAAA,EAChC;AACA,wBAAsB;AACvB;AAMO,SAAS,yBAIP;AACR,MAAI,CAAC,oBAAqB,QAAO;AACjC,SAAO;AAAA,IACN,OAAO,oBAAoB;AAAA,IAC3B,UAAU,oBAAoB,SAAS;AAAA,IACvC,UAAU,oBAAoB;AAAA,EAC/B;AACD;AAkBA,IAAM,YAAY,oBAAI,IAA4B;AAKlD,SAAS,gBAAgB,SAA0B;AAClD,SAAO,GAAG,QAAQ,MAAM,IAAI,QAAQ,GAAG;AACxC;AAKA,SAAS,gBAAgB,YAAoB,OAAqB;AACjE,QAAM,MAAM,KAAK,IAAI;AAGrB,aAAW,CAAC,KAAK,KAAK,KAAK,WAAW;AACrC,QAAI,MAAM,MAAM,YAAY,OAAO;AAClC,gBAAU,OAAO,GAAG;AAAA,IACrB;AAAA,EACD;AAGA,MAAI,UAAU,OAAO,YAAY;AAChC,UAAM,UAAU,MAAM,KAAK,UAAU,QAAQ,CAAC;AAC9C,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS;AAEtD,UAAM,WAAW,QAAQ,MAAM,GAAG,QAAQ,SAAS,UAAU;AAC7D,eAAW,CAAC,GAAG,KAAK,UAAU;AAC7B,gBAAU,OAAO,GAAG;AAAA,IACrB;AAAA,EACD;AACD;AAYA,SAAS,qBAAqB,QAAoD;AACjF,MAAI,WAAW,OAAO;AACrB,WAAO;AAAA,MACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAC5B,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,QAAM;AAAA,IACL,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,EACT,IAAI,UAAU,CAAC;AAEf,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,MACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAC5B,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAE5B,UAAI,QAAQ,WAAW,OAAO;AAC7B,eAAO;AAAA,MACR;AAEA,YAAM,WAAW,gBAAgB,OAAO;AACxC,YAAM,SAAS,UAAU,IAAI,QAAQ;AAErC,UAAI,QAAQ;AAEX,YAAI,KAAK,IAAI,IAAI,OAAO,YAAY,OAAO;AAC1C,oBAAU,OAAO,QAAQ;AAAA,QAC1B,OAAO;AACN,iBAAO,wBAAwB,SAAS,CAAC,YAAY;AACpD,oBAAQ,IAAI,iBAAiB,OAAO,IAAI;AAAA,UACzC,CAAC;AAAA,QACF;AAAA,MACD;AAEA,aAAO;AAAA,IACR;AAAA,IACA,MAAM,WAAW,EAAE,SAAS,SAAS,GAAG;AAEvC,UAAI,QAAQ,WAAW,OAAO;AAC7B,eAAO;AAAA,MACR;AAEA,YAAM,WAAW,gBAAgB,OAAO;AAGxC,UAAI,SAAS,WAAW,KAAK;AAC5B,cAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,YAAI,QAAQ;AAEX,iBAAO,YAAY,KAAK,IAAI;AAG5B,iBAAO,IAAI,SAAS,OAAO,MAAM;AAAA,YAChC,QAAQ;AAAA,YACR,SAAS,SAAS;AAAA,UACnB,CAAC;AAAA,QACF;AAEA,eAAO;AAAA,MACR;AAGA,UAAI,SAAS,IAAI;AAChB,cAAM,OAAO,SAAS,QAAQ,IAAI,MAAM;AACxC,YAAI,MAAM;AAET,gBAAM,SAAS,SAAS,MAAM;AAC9B,gBAAM,OAAO,MAAM,OAAO,KAAK;AAG/B,0BAAgB,YAAY,KAAK;AAGjC,oBAAU,IAAI,UAAU;AAAA,YACvB;AAAA,YACA;AAAA,YACA,WAAW,KAAK,IAAI;AAAA,UACrB,CAAC;AAAA,QACF;AAAA,MACD;AAEA,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAiCA,IAAM,eAAe,oBAAI,QAAgC;AAWzD,SAAS,sBAAsB,aAA0D;AACxF,MAAI,gBAAgB,OAAO;AAE1B,WAAO;AAAA,MACN,MAAM,WAAW,EAAE,SAAS,GAAG;AAC9B,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,QAAM;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,cAAc;AAAA,IACd,UAAU;AAAA,EACX,IAAI,eAAe,CAAC;AAEpB,SAAO;AAAA,IACN,MAAM,UAAU,EAAE,QAAQ,GAAG;AAE5B,YAAM,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,EAAE,KAAK,IAAI;AAG3D,YAAM,aAAa,IAAI,gBAAgB;AACvC,iBAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE5C,YAAM,aAAa,IAAI,QAAQ,QAAQ,KAAK;AAAA,QAC3C,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,QACjB;AAAA,QACA,QAAQ,WAAW;AAAA,MACpB,CAAC;AAGD,mBAAa,IAAI,YAAY,IAAI;AAEjC,aAAO;AAAA,IACR;AAAA,IACA,MAAM,WAAW,EAAE,UAAU,QAAQ,GAAG;AAEvC,YAAM,eAAe,aAAa,IAAI,OAAO,KAAK;AAElD,UAAI,UAAU;AACd,UAAI,kBAAkB;AAGtB,aAAO,UAAU,cAAc,YAAY,gBAAgB,QAAQ,OAAO,GAAG;AAC5E,cAAM,aAAa,gBAAgB,QAAQ,IAAI,aAAa;AAC5D,cAAM,QAAQ,aACX,OAAO,SAAS,YAAY,EAAE,IAAI,MAClC,mBAAmB,SAAS,WAAW,QAAQ;AAElD,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AACzD;AAGA,cAAM,aAAa,IAAI,gBAAgB;AACvC,cAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,YAAI;AAEH,gBAAM,eAAe,IAAI,QAAQ,QAAQ,KAAK;AAAA,YAC7C,QAAQ,QAAQ;AAAA,YAChB,SAAS,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,QAAQ,WAAW;AAAA,UACpB,CAAC;AAED,gBAAM,cAAc,MAAM,MAAM,YAAY;AAC5C,uBAAa,SAAS;AAGtB,cAAI,YAAY,MAAM,CAAC,YAAY,YAAY,QAAQ,OAAO,GAAG;AAChE,yBAAa,OAAO,OAAO;AAC3B,mBAAO;AAAA,UACR;AAEA,4BAAkB;AAAA,QACnB,SAAS,OAAO;AACf,uBAAa,SAAS;AAEtB,cAAI,WAAW,YAAY;AAC1B,yBAAa,OAAO,OAAO;AAC3B,kBAAM;AAAA,UACP;AAAA,QACD;AAAA,MACD;AAEA,mBAAa,OAAO,OAAO;AAC3B,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAKA,SAAS,qBAAqB,QAAsD;AACnF,SAAO;AAAA,IACN,WAAW,6BAA6B,OAAO,SAAS;AAAA,IACxD,UAAU,OAAO,eAAe,WAAW,oBAAoB,IAAI,KAAK;AAAA,EACzE;AACD;AAsBO,SAAS,iBAAiB,QAAsC;AACtE,QAAM,EAAE,WAAW,QAAQ,IAAI,qBAAqB,MAAM;AAE1D,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,GAAG,OAAO,GAAG,YAAY,IAAI;AAAA,IACxE,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,EACjB,CAAC;AAGD,MAAI,OAAO,kBAAkB,OAAO;AACnC,gBAAY,KAAK,8BAA8B,OAAO,aAAa,CAAC;AAAA,EACrE;AAGA,MAAI,OAAO,mBAAmB,OAAO;AACpC,gBAAY,KAAK,+BAA+B,OAAO,cAAc,CAAC;AAAA,EACvE;AAGA,MAAI,OAAO,SAAS,OAAO;AAC1B,gBAAY,KAAK,qBAAqB,OAAO,IAAI,CAAC;AAAA,EACnD;AAGA,cAAY,KAAK,sBAAsB,OAAO,KAAK,CAAC;AAEpD,SAAO;AACR;AAiBO,SAAS,wBAAwB,QAA8C;AACrF,QAAM,EAAE,WAAW,QAAQ,IAAI,qBAAqB,MAAM;AAE1D,QAAM,kBAAqC;AAAA,IAC1C,GAAG;AAAA,IACH;AAAA,IACA,aAAa;AAAA,EACd;AAEA,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,GAAG,OAAO,GAAG,YAAY,IAAI;AAAA,IACxE,gBAAgB;AAAA,EACjB,CAAC;AAGD,MAAI,OAAO,kBAAkB,OAAO;AACnC,gBAAY,KAAK,8BAA8B,OAAO,aAAa,CAAC;AAAA,EACrE;AAGA,cAAY,KAAK,qBAAqB,eAAe,CAAC;AAGtD,MAAI,OAAO,mBAAmB,OAAO;AACpC,gBAAY,KAAK,+BAA+B,OAAO,cAAc,CAAC;AAAA,EACvE;AAGA,MAAI,OAAO,SAAS,OAAO;AAC1B,gBAAY,KAAK,qBAAqB,OAAO,IAAI,CAAC;AAAA,EACnD;AAGA,cAAY,KAAK,sBAAsB,OAAO,KAAK,CAAC;AAEpD,SAAO;AACR;AAKO,SAAS,SAAe,UAM7B;AACD,SAAO,SAAS,UAAU;AAC3B;AAKO,SAAS,oBAAoB,OAAwB;AAC3D,MAAI,SAAS,OAAO,UAAU,YAAY,WAAW,OAAO;AAC3D,UAAM,MAAM;AACZ,WAAO,IAAI,OAAO,WAAW;AAAA,EAC9B;AACA,MAAI,iBAAiB,OAAO;AAC3B,WAAO,MAAM;AAAA,EACd;AACA,SAAO;AACR;;;AEv2BA;;;AC7PA,SAAS,qBAAqB;;;AC3BvB,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnB,MAAM,kBAIH;AACF,UAAM,EAAE,YAAY,UAAU,IAAK,MAAM,OAAO,OAAO;AAAA,MACtD,EAAE,MAAM,SAAS,YAAY,QAAQ;AAAA,MACrC;AAAA,MACA,CAAC,QAAQ,QAAQ;AAAA,IAClB;AACA,UAAM,YAAY,MAAM,OAAO,OAAO,UAAU,OAAO,SAAS;AAChE,UAAM,aAAa,MAAM,kBAAkB,kBAAkB,SAAS,CAAC;AACvE,WAAO,EAAE,YAAY,WAAW,WAAW;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,MAOA;AACnB,UAAM,eAAe,MAAM,OAAO,OAAO,UAAU,OAAO,KAAK,SAAS;AACxE,UAAM,YAAY,kBAAkB,YAAY;AAEhD,UAAM,SAAS,EAAE,KAAK,YAAY,KAAK,SAAS,KAAK,UAAU;AAC/D,UAAM,UAAmC;AAAA,MACxC,KAAK,UAAU;AAAA,MACf,KAAK,KAAK,OAAO,YAAY;AAAA,MAC7B,KAAK,sBAAsB,KAAK,GAAG;AAAA,MACnC,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IAClC;AACA,QAAI,KAAK,aAAa;AACrB,cAAQ,MAAM,MAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,KAAK,WAAW,CAAC;AAAA,IAC/E;AACA,QAAI,KAAK,MAAO,SAAQ,QAAQ,KAAK;AAErC,UAAM,YAAY,gBAAgB,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;AAClF,UAAM,aAAa,gBAAgB,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AACpF,UAAM,eAAe,GAAG,SAAS,IAAI,UAAU;AAE/C,UAAM,eAAe,IAAI,YAAY,EAAE,OAAO,YAAY;AAC1D,UAAM,aAAa,IAAI,WAAW,aAAa,UAAU;AACzD,eAAW,IAAI,YAAY;AAC3B,UAAM,SAAS,MAAM,OAAO,OAAO;AAAA,MAClC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,KAAK;AAAA,MACL,WAAW;AAAA,IACZ;AACA,UAAM,SAAS,gBAAgB,IAAI,WAAW,MAAM,CAAC;AACrD,WAAO,GAAG,YAAY,IAAI,MAAM;AAAA,EACjC;AACD;AAEA,SAAS,kBAAkB,KAAqE;AAC/F,MAAI,IAAI,QAAQ,QAAQ,IAAI,QAAQ,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG;AAChE,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AACA,SAAO,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,GAAG,GAAG,IAAI,EAAE;AACzD;AAEA,eAAe,kBAAkB,KAKb;AACnB,QAAM,YAAY,KAAK,UAAU,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,GAAG,GAAG,IAAI,EAAE,CAAC;AACnF,SAAO,gBAAgB,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AAC3D;AAEA,eAAe,gBAAgB,MAAmC;AACjE,QAAM,OAAO,IAAI,WAAW,KAAK,UAAU;AAC3C,OAAK,IAAI,IAAI;AACb,QAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,MAAM;AAChE,SAAO,gBAAgB,IAAI,WAAW,MAAM,CAAC;AAC9C;AAEA,SAAS,gBAAgB,OAA2B;AACnD,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,MAAK,OAAO,aAAa,MAAM,CAAC,CAAE;AACzE,SAAO,KAAK,CAAC,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACzE;AAEA,SAAS,sBAAsB,KAAqB;AACnD,MAAI;AACH,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,WAAO,GAAG,EAAE,QAAQ,KAAK,EAAE,IAAI,GAAG,EAAE,QAAQ;AAAA,EAC7C,QAAQ;AACP,UAAM,IAAI,IAAI,QAAQ,GAAG;AACzB,UAAM,IAAI,IAAI,QAAQ,GAAG;AACzB,UAAM,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC;AAChE,WAAO,QAAQ,SAAY,MAAM,IAAI,MAAM,GAAG,GAAG;AAAA,EAClD;AACD;AAEA,SAAS,YAAoB;AAC5B,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,gBAAgB,KAAK;AAC7B;;;ACjHA;AAMO,IAAM,eAAe;AAAA,EAC3B,MAAM,QAAQ,MAWqB;AAClC,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,KAAK,UAAW,SAAQ,YAAY,IAAI,KAAK;AACjD,UAAM,SAAS,KAAK,aAAa;AACjC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,GAAG,MAAM,iBAAiB;AAAA,MACnF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,aAAa,CAAgC;AAAA,IACzF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,sBAAsB;AACtE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,MAMK;AACjB,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,KAAK,UAAW,SAAQ,YAAY,IAAI,KAAK;AACjD,UAAM,SAAS,KAAK,aAAa;AACjC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,GAAG,MAAM,gBAAgB;AAAA,MAClF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,aAAa,CAA+B;AAAA,IACxF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,qBAAqB;AAAA,EACtE;AACD;AAEA,eAAe,kBAAkB,KAAe,WAAmC;AAClF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAI9B,QAAI,OAAO,OAAO,UAAU,SAAU,QAAO,OAAO;AAAA,aAC3C,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU;AAC1D,UAAI,OAAO,MAAM,KAAM,QAAO,OAAO,MAAM;AAC3C,UAAI,OAAO,MAAM,QAAS,WAAU,OAAO,MAAM;AAAA,IAClD,WAAW,OAAO,SAAS;AAC1B,gBAAU,OAAO;AAAA,IAClB;AAAA,EACD,QAAQ;AACP,QAAI,KAAM,WAAU;AAAA,EACrB;AACA,MAAI;AAMJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;ACnFA;AAsHO,IAAM,gBAAgB;AAAA,EAC5B,MAAM,MAAM,MAM0B;AACrC,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE,MAAM,KAAK,UAAU;AAAA,UACpB,cAAc,KAAK;AAAA,UACnB,GAAI,KAAK,cAAc,UAAa,EAAE,WAAW,KAAK,UAAU;AAAA,UAChE,GAAI,KAAK,cAAc,UAAa,EAAE,WAAW,KAAK,UAAU;AAAA,QACjE,CAAC;AAAA,MACF;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,qBAAqB;AACtE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,IAAI,MAK0B;AACnC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,oCAAoC;AAAA,MAC7F,QAAQ;AAAA,MACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,MACnE,MAAM,KAAK,UAAU,KAAK,cAAc,SAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC,CAAC;AAAA,IACvF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,mBAAmB;AACpE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,KAAK,MAK2B;AACrC,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,qCAAqC,mBAAmB,KAAK,SAAS,CAAC;AAAA,MACzG;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,MACpE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,oBAAoB;AACrE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,MAI+B;AAC3C,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,MACpE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,sBAAsB;AACvE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,eAAe,MAAyE;AAC7F,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE,MAAM,KAAK,UAAU;AAAA,UACpB,cAAc,KAAK;AAAA,UACnB,QAAQ,KAAK;AAAA,QACd,CAAC;AAAA,MACF;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,8BAA8B;AAC/E,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,YAAY,MAA8E;AAC/F,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE,MAAM,KAAK,UAAU;AAAA,UACpB,WAAW,KAAK;AAAA,UAChB,cAAc,KAAK;AAAA,UACnB,WAAW,KAAK;AAAA,UAChB,GAAI,KAAK,kBAAkB,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAAA,QACzD,CAAC;AAAA,MACF;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,2BAA2B;AAC5E,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,eAAe,MAMqB;AACzC,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,+BAA+B,mBAAmB,KAAK,SAAS,CAAC;AAAA,MACnG;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE,MAAM,KAAK,UAAU,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,MACjD;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,8BAA8B;AAC/E,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,aAAa,MAU6B;AAC/C,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,KAAK,QAAQ,WAAY,QAAO,IAAI,cAAc,KAAK,OAAO,UAAU;AAC5E,QAAI,KAAK,QAAQ,aAAc,QAAO,IAAI,gBAAgB,KAAK,OAAO,YAAY;AAClF,QAAI,KAAK,QAAQ,OAAQ,QAAO,IAAI,UAAU,KAAK,OAAO,MAAM;AAChE,QAAI,KAAK,QAAQ,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,KAAK,OAAO,KAAK,CAAC;AAC7E,UAAM,KAAK,OAAO,SAAS;AAC3B,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,wCAAwC,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MAC5F;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,MACpE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,4BAA4B;AAC7E,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,WAAW,MAK2D;AAC3E,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,oCAAoC,mBAAmB,KAAK,SAAS,CAAC;AAAA,MACxG;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,0BAA0B,KAAK,aAAa,KAAK,SAAS;AAAA,MACpE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,0BAA0B;AAC3E,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;AAEA,SAAS,0BACR,aACA,WACyB;AACzB,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,IAChB,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,mBAAmB,KAAe,WAAmC;AACnF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAOJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;ACvUA,IAAI,eAAiC;AAO9B,SAAS,yBAA+B;AAC9C,iBAAe;AAChB;AAEA,IAAM,uBAAuB,KAAK,KAAK;AAEvC,eAAe,kBAAkB,SAAqC;AACrE,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,gBAAgB,aAAa,YAAY,IAAK,QAAO,aAAa;AAEtE,QAAM,OAAO,QAAQ,QAAQ,OAAO,EAAE,EAAE,QAAQ,SAAS,EAAE;AAC3D,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,wBAAwB;AACvD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AACpE,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,MAAI,CAAC,MAAM,QAAQ,KAAK,IAAI,EAAG,OAAM,IAAI,MAAM,8BAA8B;AAE7E,iBAAe,EAAE,MAAM,KAAK,MAAM,WAAW,MAAM,qBAAqB;AACxE,SAAO,KAAK;AACb;AAuDA,eAAsB,kBACrB,OACA,MAIqC;AACrC,QAAM,EAAE,WAAW,WAAW,sBAAsB,IAAI,MAAM,OAAO,MAAM;AAE3E,MAAI,OAAO,MAAM,kBAAkB,KAAK,OAAO;AAI/C,MAAI;AACJ,MAAI;AACH,UAAM,sBAAsB,KAAK,EAAE;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,QAAM,SAAS,CAAC,YACf,QAAQ,KAAK,CAAC,MAAO,GAA+B,QAAQ,GAAG;AAEhE,MAAI,OAAO,CAAC,OAAO,IAAI,GAAG;AACzB,2BAAuB;AACvB,WAAO,MAAM,kBAAkB,KAAK,OAAO;AAAA,EAC5C;AAEA,MAAI,YAAqB;AACzB,aAAW,OAAO,MAAM;AACvB,UAAM,YAAY;AAClB,QAAI,OAAO,UAAU,OAAO,UAAU,QAAQ,IAAK;AACnD,QAAI;AACH,YAAM,MAAM,MAAM,UAAU,KAAwC,OAAO;AAC3E,YAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,KAAK;AAAA,QAC/C,UAAU,KAAK;AAAA,MAChB,CAAC;AASD,YAAM,SAAS,QAAQ;AACvB,YAAM,MACL,UAAU,OAAO,WAAW,YAAY,SAAS,SAC9C,EAAE,KAAK,OAAO,OAAO,QAAQ,WAAW,OAAO,MAAM,OAAU,IAC/D;AAEJ,aAAO;AAAA,QACN,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,QACb,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,gBAAgB,QAAQ,QAAQ,cAAc;AAAA,QAC9C,QAAQ,QAAQ;AAAA,QAChB,MAAO,QAAQ,QAAmB;AAAA,QAClC,QAAQ,QAAQ;AAAA,QAChB,UAAU,QAAQ;AAAA,QAClB,UAAU,QAAQ;AAAA,QAClB,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,QACb,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACtB;AAAA,IACD,SAAS,KAAK;AACb,kBAAY;AAAA,IACb;AAAA,EACD;AAEA,QAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,kCAAkC;AAC5F;AA0BA,IAAM,yBAAyB,KAAK;AACpC,IAAM,sBAAsB,oBAAI,IAG9B;AAEK,SAAS,2BAAiC;AAChD,sBAAoB,MAAM;AAC3B;AAMO,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBtB,MAAM,oBAAoB,MAIiB;AAC1C,QAAI,CAAC,KAAK,aAAc,QAAO;AAE/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,oBAAoB,IAAI,KAAK,YAAY;AACxD,QAAI,UAAU,OAAO,YAAY,IAAK,QAAO,OAAO;AAEpD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,kCAAkC;AAAA,MAC3F,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,QAAQ,KAAK;AAAA,QACb,GAAI,KAAK,YAAY,EAAE,cAAc,KAAK,UAAU,IAAI,CAAC;AAAA,MAC1D;AAAA,IACD,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AAGZ,0BAAoB,IAAI,KAAK,cAAc;AAAA,QAC1C,QAAQ;AAAA,QACR,WAAW,MAAM;AAAA,MAClB,CAAC;AACD,aAAO;AAAA,IACR;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,wBAAoB,IAAI,KAAK,cAAc;AAAA,MAC1C,QAAQ;AAAA,MACR,WAAW,MAAM;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACR;AACD;;;AC1PA;;;ACEA;AAUA,IAAM,0BAA0B,oBAAI,IAA6B;AAAA,EAChE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,SAAS,SAAS,OAAkD;AACnE,SAAO,OAAO,UAAU,YAAY,UAAU;AAC/C;AAEA,SAAS,cAAc,QAAiC,KAAqB;AAC5E,QAAM,QAAQ,OAAO,GAAG;AACxB,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,8BAA8B,GAAG,EAAE;AAClF,SAAO;AACR;AAEA,SAAS,eAAe,QAAiC,KAAiC;AACzF,QAAM,QAAQ,OAAO,GAAG;AACxB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,8BAA8B,GAAG,EAAE;AAClF,SAAO;AACR;AAEA,SAAS,cAAc,KAAa,OAAmD;AACtF,SAAO,UAAU,SAAY,CAAC,IAAI,EAAE,CAAC,GAAG,GAAG,MAAM;AAClD;AAEA,SAAS,cAAc,QAAiC,KAAqB;AAC5E,QAAM,QAAQ,OAAO,GAAG;AACxB,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,8BAA8B,GAAG,EAAE;AAClF,SAAO;AACR;AAEA,SAAS,gBACR,QACA,WACgE;AAChE,MAAI,OAAO,eAAe,WAAW;AACpC,UAAM,IAAI,MAAM,sCAAsC,SAAS,EAAE;AAAA,EAClE;AACD;AAEA,SAAS,wBAAwB,OAA6D;AAC7F,MAAI,CAAC,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,6BAA6B;AACnE,UAAQ,MAAM,YAAY;AAAA,IACzB,KAAK;AACJ,sBAAgB,OAAO,oBAAoB;AAC3C,aAAO;AAAA,QACN,YAAY;AAAA,QACZ,MAAM,cAAc,OAAO,MAAM;AAAA,QACjC,cAAc,cAAc,OAAO,cAAc;AAAA,QACjD,WAAW,cAAc,OAAO,WAAW;AAAA,QAC3C,eAAe,cAAc,OAAO,eAAe;AAAA,QACnD,GAAG,cAAc,iBAAiB,eAAe,OAAO,eAAe,CAAC;AAAA,MACzE;AAAA,IACD,KAAK;AACJ,sBAAgB,OAAO,eAAe;AACtC,aAAO;AAAA,QACN,YAAY;AAAA,QACZ,eAAe,cAAc,OAAO,eAAe;AAAA,QACnD,WAAW,cAAc,OAAO,WAAW;AAAA,QAC3C,GAAG,cAAc,iBAAiB,eAAe,OAAO,eAAe,CAAC;AAAA,QACxE,GAAG,cAAc,SAAS,eAAe,OAAO,OAAO,CAAC;AAAA,MACzD;AAAA,IACD,KAAK;AACJ,sBAAgB,OAAO,8CAA8C;AACrE,aAAO;AAAA,QACN,YAAY;AAAA,QACZ,aAAa,cAAc,OAAO,aAAa;AAAA,QAC/C,WAAW,cAAc,OAAO,WAAW;AAAA,QAC3C,GAAG,cAAc,iBAAiB,eAAe,OAAO,eAAe,CAAC;AAAA,MACzE;AAAA,IACD,KAAK;AACJ,sBAAgB,OAAO,oBAAoB;AAC3C,aAAO;AAAA,QACN,YAAY;AAAA,QACZ,WAAW,cAAc,OAAO,WAAW;AAAA,QAC3C,eAAe,cAAc,OAAO,eAAe;AAAA,QACnD,GAAG,cAAc,SAAS,eAAe,OAAO,OAAO,CAAC;AAAA,MACzD;AAAA,IACD;AACC,YAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACD;AAEO,SAAS,mBAAmB,OAA0C;AAC5E,QAAM,UAAU,wBAAwB,KAAK;AAC7C,SAAO,IAAI;AAAA,IACV,OAAO,QAAQ,OAAO,EAAE;AAAA,MACvB,CAAC,UAAqC,OAAO,MAAM,CAAC,MAAM;AAAA,IAC3D;AAAA,EACD,EAAE,SAAS;AACZ;AAEO,SAAS,uBAAuB,OAAkC;AACxE,MAAI,CAAC,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACpE,QAAM,YAAY,cAAc,OAAO,YAAY;AACnD,MAAI,cAAc,SAAU,OAAM,IAAI,MAAM,0BAA0B;AACtE,SAAO;AAAA,IACN,cAAc,cAAc,OAAO,cAAc;AAAA,IACjD,YAAY;AAAA,IACZ,YAAY,cAAc,OAAO,YAAY;AAAA,IAC7C,eAAe,cAAc,OAAO,eAAe;AAAA,IACnD,oBAAoB,cAAc,OAAO,oBAAoB;AAAA,IAC7D,OAAO,cAAc,OAAO,OAAO;AAAA,EACpC;AACD;AAEO,SAAS,mCAAmC,OAA8C;AAChG,MAAI,CAAC,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAM,YAAY,cAAc,OAAO,YAAY;AACnD,MAAI,cAAc,SAAU,OAAM,IAAI,MAAM,0BAA0B;AACtE,SAAO;AAAA,IACN,cAAc,cAAc,OAAO,cAAc;AAAA,IACjD,YAAY;AAAA,IACZ,YAAY,cAAc,OAAO,YAAY;AAAA,IAC7C,OAAO,cAAc,OAAO,OAAO;AAAA,EACpC;AACD;AAEO,SAAS,sBAAsB,OAAiD;AACtF,MAAI,CAAC,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,oCAAoC;AAC1E,QAAM,QAAQ,cAAc,OAAO,OAAO;AAC1C,MAAI,CAAC,wBAAwB,IAAI,KAAgC,GAAG;AACnE,UAAM,IAAI,MAAM,gCAAgC;AAAA,EACjD;AACA,SAAO;AAAA,IACN;AAAA,IACA,GAAG,cAAc,qBAAqB,eAAe,OAAO,mBAAmB,CAAC;AAAA,IAChF,GAAG,cAAc,aAAa,eAAe,OAAO,WAAW,CAAC;AAAA,EACjE;AACD;AAEA,eAAsB,gBAAgB,KAAe,WAAmC;AACvF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAuB;AAC3B,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,sBAAsB,KAAK,MAAM,IAAI,CAAC;AACrD,WAAO,OAAO;AACd,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,EAChD,QAAQ;AAAA,EAER;AAEA,MAAI;AACJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AAEjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,YAAY,KAAK;AAAA,EAC1B,CAAC;AACF;;;AD1HO,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASpB,MAAM,gBAAgB,MAKa;AAClC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,2BAA2B;AAAA,MACpF,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,aAAa;AAAA,QAC3C,GAAI,KAAK,YAAY,EAAE,cAAc,KAAK,UAAU,IAAI,CAAC;AAAA,MAC1D;AAAA,MACA,MAAM,KAAK,UAAU,KAAK,MAAM;AAAA,IACjC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,iBAAiB,KAAK,uBAAuB;AACtE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,0BAA0B,MAOF;AAC7B,UAAM,OAAO,mBAAmB;AAAA,MAC/B,YAAY;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,eAAe,KAAK;AAAA,MACpB,GAAI,KAAK,eAAe,EAAE,eAAe,KAAK,aAAa,IAAI,CAAC;AAAA,IACjE,CAAC;AAED,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,gBAAgB;AAAA,MACzE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D;AAAA,IACD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,gBAAgB,KAAK,iCAAiC;AAC/E,WAAO,uBAAuB,MAAM,IAAI,KAAK,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,mBAAmB,MAMK;AAC7B,UAAM,OAAO,mBAAmB;AAAA,MAC/B,YAAY;AAAA,MACZ,eAAe,KAAK;AAAA,MACpB,WAAW,KAAK;AAAA,MAChB,GAAI,KAAK,eAAe,EAAE,eAAe,KAAK,aAAa,IAAI,CAAC;AAAA,MAChE,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAC3C,CAAC;AAED,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,gBAAgB;AAAA,MACzE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D;AAAA,IACD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,gBAAgB,KAAK,0BAA0B;AACxE,WAAO,uBAAuB,MAAM,IAAI,KAAK,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,gBAAgB,MAIO;AAC5B,UAAM,OAAO,mBAAmB;AAAA,MAC/B,YAAY;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,IACjB,CAAC;AACD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,gBAAgB;AAAA,MACzE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D;AAAA,IACD,CAAC;AACD,QAAI,IAAI,GAAI,QAAO,EAAE,IAAI,MAAe,QAAQ,uBAAuB,MAAM,IAAI,KAAK,CAAC,EAAE;AAEzF,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,QAAI,OAAuB;AAC3B,QAAI;AACH,aAAO,sBAAsB,KAAK,MAAM,IAAI,CAAC,EAAE;AAAA,IAChD,QAAQ;AAAA,IAER;AACA,WAAO,EAAE,IAAI,OAAgB,OAAO,MAAM,QAAQ,IAAI,OAAO;AAAA,EAC9D;AAAA,EAEA,MAAM,uBAAuB,MAKa;AACzC,UAAM,OAAO,mBAAmB;AAAA,MAC/B,YAAY;AAAA,MACZ,WAAW,KAAK;AAAA,MAChB,eAAe,KAAK;AAAA,MACpB,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAC3C,CAAC;AAED,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,gBAAgB;AAAA,MACzE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D;AAAA,IACD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,gBAAgB,KAAK,8BAA8B;AAC5E,WAAO,mCAAmC,MAAM,IAAI,KAAK,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,YAAY,MAA0C;AAC3D,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,iBAAiB;AAAA,MAC1E,QAAQ;AAAA,MACR,SAAS,iBAAiB,KAAK,SAAS;AAAA,MACxC,MAAM,KAAK,UAAU;AAAA,QACpB,OAAO,KAAK;AAAA,QACZ,iBAAiB,KAAK;AAAA,QACtB,WAAW,KAAK;AAAA,QAChB,eAAe,KAAK;AAAA,MACrB,CAA4B;AAAA,IAC7B,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACZ,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,IACpE;AAAA,EACD;AAAA,EAEA,MAAM,gBAAgB,MAA2D;AAChF,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,qBAAqB;AAAA,MAC9E,QAAQ;AAAA,MACR,SAAS,iBAAiB,KAAK,SAAS;AAAA,MACxC,MAAM,KAAK,UAAU;AAAA,QACpB,OAAO,KAAK;AAAA,QACZ,iBAAiB,KAAK;AAAA,QACtB,WAAW,KAAK;AAAA,QAChB,eAAe,KAAK;AAAA,MACrB,CAAgC;AAAA,IACjC,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACZ,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAM,IAAI,MAAM,iCAAiC,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,IACxE;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;AAEA,SAAS,iBAAiB,WAAuD;AAChF,SAAO;AAAA,IACN,gBAAgB;AAAA,IAChB,GAAI,YAAY,EAAE,cAAc,UAAU,IAAI,CAAC;AAAA,EAChD;AACD;AAEA,eAAe,iBAAiB,KAAe,WAAmC;AACjF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AACJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;AErPA;AAQO,IAAM,WAAW;AAAA,EACvB,MAAM,OAAO,MAI6B;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,kCAAkC;AAAA,MAC3F,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,IACvE,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,sBAAsB,KAAK,iBAAiB;AACrE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,IAAI,MAK6B;AACtC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,+BAA+B;AAAA,MACxF,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACtE,MAAM,KAAK,UAAU;AAAA,QACpB,UAAU,KAAK;AAAA,MAChB,CAAoC;AAAA,IACrC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,sBAAsB,KAAK,cAAc;AAClE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,MAM6B;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,kCAAkC;AAAA,MAC3F,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACtE,MAAM,KAAK,UAAU;AAAA,QACpB,iBAAiB,KAAK;AAAA,QACtB,aAAa,KAAK;AAAA,MACnB,CAAuC;AAAA,IACxC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,sBAAsB,KAAK,iBAAiB;AACrE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;AAEA,SAAS,6BACR,aACA,WACyB;AACzB,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,IAChB,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,sBAAsB,KAAe,WAAmC;AACtF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAMJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;ACtGA;AAUO,IAAM,WAAW;AAAA,EACvB,MAAM,KAAK,MAI6B;AACvC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,2BAA2B;AAAA,MACpF,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,IACvE,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,qBAAqB,KAAK,eAAe;AAClE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,MAK4B;AACxC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,kCAAkC;AAAA,MAC3F,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACtE,MAAM,KAAK,UAAU,EAAE,WAAW,KAAK,UAAU,CAAsC;AAAA,IACxF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,qBAAqB,KAAK,iBAAiB;AACpE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,YAAY,MAI4B;AAC7C,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACvE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,qBAAqB,KAAK,sBAAsB;AACzE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,UAAU,MAI4B;AAC3C,UAAM,MAAM,MAAM;AAAA,MACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACvE;AAAA,IACD;AACA,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,qBAAqB,KAAK,oBAAoB;AACvE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,MAM4B;AACxC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,kCAAkC;AAAA,MAC3F,QAAQ;AAAA,MACR,SAAS,6BAA6B,KAAK,aAAa,KAAK,SAAS;AAAA,MACtE,MAAM,KAAK,UAAU;AAAA,QACpB,WAAW,KAAK;AAAA,QAChB,MAAM,KAAK;AAAA,MACZ,CAAsC;AAAA,IACvC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,qBAAqB,KAAK,iBAAiB;AACpE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;AAEA,SAAS,6BACR,aACA,WACyB;AACzB,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,IAChB,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,qBAAqB,KAAe,WAAmC;AACrF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAMJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;AC3IA;AAWO,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnB,MAAM,WAAW,MAIqB;AACrC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,8BAA8B;AAAA,MACvF,QAAQ;AAAA,MACR,SAAS,yBAAyB,KAAK,aAAa,KAAK,SAAS;AAAA,IACnE,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,iBAAiB;AACjE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,MAKkB;AACrC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,+BAA+B;AAAA,MACxF,QAAQ;AAAA,MACR,SAAS,yBAAyB,KAAK,aAAa,KAAK,SAAS;AAAA,MAClE,MAAM,KAAK,UAAU;AAAA,QACpB,GAAI,KAAK,WAAW,UAAa,EAAE,QAAQ,KAAK,OAAO;AAAA,MACxD,CAAmC;AAAA,IACpC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,oBAAoB;AACpE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAKR,MAAM,SAAS,MAKY;AAC1B,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,8BAA8B;AAAA,QACvF,QAAQ;AAAA,QACR,SAAS,yBAAyB,KAAK,aAAa,KAAK,SAAS;AAAA,QAClE,MAAM,KAAK,UAAU,KAAK,WAAW,SAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC;AAAA,MAC9E,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,uBAAuB;AACvE,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,OAAO,MAKc;AAC1B,YAAM,MAAM,MAAM;AAAA,QACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,8BAA8B;AAAA,UAC/D,KAAK;AAAA,QACN,CAAC;AAAA,QACD;AAAA,UACC,QAAQ;AAAA,UACR,SAAS,yBAAyB,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE;AAAA,MACD;AACA,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,qBAAqB;AACrE,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,SAAS,MAK8D;AAC5E,YAAM,MAAM,MAAM;AAAA,QACjB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,8BAA8B;AAAA,UAC/D,KAAK;AAAA,QACN,CAAC;AAAA,QACD;AAAA,UACC,QAAQ;AAAA,UACR,SAAS,yBAAyB,KAAK,aAAa,KAAK,SAAS;AAAA,QACnE;AAAA,MACD;AACA,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,kBAAkB,KAAK,uBAAuB;AACvE,YAAM,SAAS,IAAI,QAAQ,IAAI,wBAAwB;AACvD,YAAM,aAAa,IAAI,QAAQ,IAAI,sBAAsB;AACzD,YAAM,YAAY,aAAa,OAAO,SAAS,YAAY,EAAE,IAAI;AACjE,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,EAAE,MAAM,QAAQ,WAAW,OAAO,SAAS,SAAS,IAAI,YAAY,KAAK;AAAA,IACjF;AAAA,EACD;AACD;AAkBA,SAAS,yBACR,aACA,WACyB;AACzB,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,IAChB,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,kBAAkB,KAAe,WAAmC;AAClF,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAMJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAI,YAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;ATgFA,eAAsB,OAAO,QAAsB,OAA6C;AAE/F,QAAM,OAAO;AACb,QAAM,WAAW,cAAc;AAC/B,SAAO,QAAuB,QAAQ,SAAS,MAAM;AAAA,IACpD,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAeA,eAAsB,OACrB,QACA,OAC4B;AAE5B,QAAM,WAAW,cAAc;AAC/B,SAAO,QAA0B,QAAQ,SAAS,MAAM;AAAA,IACvD,QAAQ,SAAS;AAAA,IACjB,MAAM;AAAA,EACP,CAAC;AACF;AAUA,eAAsB,QAAQ,QAAqC;AAElE,QAAM,WAAW,cAAc;AAC/B,QAAM,QAAc,QAAQ,SAAS,MAAM,EAAE,QAAQ,SAAS,OAAO,CAAC;AACvE;AAWA,eAAsB,aAAa,QAAsB,OAAuC;AAC/F,SAAO,QAAuB,QAAQ,eAAe;AAAA,IACpD,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,eAAe,OAAO;AAAA,IACvB;AAAA,EACD,CAAC;AACF;AAUA,eAAsB,YAAY,QAAsB,OAA8B;AACrF,QAAM,QAAc,QAAQ,sBAAsB;AAAA,IACjD,QAAQ;AAAA,IACR,MAAM,EAAE,MAAM;AAAA,EACf,CAAC;AACF;AAYA,eAAsB,eACrB,QACA,OACA,UAAoC,CAAC,GACrB;AAChB,QAAM,QAA8B,QAAQ,yBAAyB;AAAA,IACpE,QAAQ;AAAA,IACR,MAAM;AAAA,MACL;AAAA,MACA,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,IACnE;AAAA,EACD,CAAC;AACF;AAaA,eAAsB,wBAAwB,QAAsB,OAA8B;AACjG,QAAM,WAAW,cAAc;AAC/B,QAAM,OAAO,EAAE,MAAM;AACrB,QAAM,QAAyC,QAAQ,SAAS,MAAM;AAAA,IACrE,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAUA,eAAsB,cACrB,QACA,OACgB;AAChB,QAAM,QAA8B,QAAQ,wBAAwB;AAAA,IACnE,QAAQ;AAAA,IACR,MAAM,EAAE,OAAO,MAAM,OAAO,UAAU,MAAM,SAAS;AAAA,EACtD,CAAC;AACF;AAaA,eAAsB,WAAW,QAA8C;AAC9E,MAAI,CAAC,OAAO,aAAa;AACxB,WAAO,EAAE,MAAM,KAAK;AAAA,EACrB;AAQA,QAAM,WAAW,cAAc;AAC/B,MAAI;AACH,UAAMC,QAAO,MAAM,QAA+B,QAAQ,SAAS,MAAM;AAAA,MACxE,QAAQ,SAAS;AAAA,IAClB,CAAC;AACD,WAAO,EAAE,MAAAA,MAAK;AAAA,EACf,QAAQ;AACP,WAAO,EAAE,MAAM,KAAK;AAAA,EACrB;AACD;AAaA,eAAsB,gBACrB,QACA,QACA,MACyB;AACzB,SAAO,QAAuB,QAAQ,oBAAoB;AAAA,IACzD,QAAQ;AAAA,IACR,MAAM,EAAE,QAAQ,KAAK;AAAA,EACtB,CAAC;AACF;AAmBA,eAAsB,gBACrB,QACA,OACA,eACoC;AACpC,QAAM,WAAW,MAAM,MAAM,YAAY,QAAQ,kBAAkB,GAAG;AAAA,IACrE,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,gBAAgB;AAAA;AAAA,MAEhB,gBAAgB,OAAO,aAAa;AAAA,IACrC;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACpB;AAAA,MACA,iBAAiB;AAAA,IAClB,CAAC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAEjB,WAAO,EAAE,QAAQ,MAAM;AAAA,EACxB;AAEA,SAAO,SAAS,KAAK;AACtB;AAmBA,eAAsB,YACrB,QACA,OACA,SACgB;AAChB,QAAM,MAAM,YAAY,QAAQ,cAAc,GAAG;AAAA,IAChD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACpB,OAAO,SAAS,YAAY,SAAY;AAAA,MACxC,eAAe,OAAO;AAAA,MACtB,SAAS,SAAS;AAAA,MAClB,YAAY,SAAS;AAAA,IACtB,CAAC;AAAA,EACF,CAAC;AAEF;AAaA,eAAsB,gBAAgB,QAAsB,QAA+B;AAC1F,QAAM,YAAY,QAAQ,IAAI,EAAE,WAAW,MAAM,OAAO,CAAC;AAC1D;AAoBA,eAAsB,eACrB,QACA,OAC4B;AAC5B,SAAO,QAA0B,QAAQ,kBAAkB;AAAA,IAC1D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAiBA,eAAsB,WACrB,QACA,OAC8B;AAC9B,SAAO,QAA4B,QAAQ,gBAAgB;AAAA,IAC1D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,SAAS,gCAAgC,MAA0D;AAClG,QAAM,cAAc,KAAK,eAAe,KAAK;AAC7C,MAAI,CAAC,aAAa;AACjB,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC1E;AAEA,SAAO;AAAA,IACN,OAAO;AAAA,IACP;AAAA,IACA,WAAW,KAAK,aAAa,KAAK;AAAA,IAClC,WAAW,KAAK,aAAa,KAAK;AAAA,IAClC,MAAM,KAAK;AAAA,EACZ;AACD;AASA,eAAsB,kBACrB,QACA,OACkC;AAClC,QAAM,OAAO,MAAM,QAAoC,QAAQ,oBAAoB;AAAA,IAClF,QAAQ;AAAA,IACR,MAAM,EAAE,MAAM;AAAA,EACf,CAAC;AACD,SAAO,gCAAgC,IAAI;AAC5C;AAMA,eAAsB,UACrB,QACA,OACkC;AAClC,SAAO,kBAAkB,QAAQ,KAAK;AACvC;AAiCO,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBrB,MAAM,KAAK,MAKc;AACxB,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,gBAAgB;AAAA,MACzE,QAAQ;AAAA,MACR,SAAS,mBAAmB,KAAK,SAAS;AAAA,MAC1C,MAAM,KAAK,UAAU;AAAA,QACpB,WAAW,KAAK;AAAA,QAChB,OAAO,KAAK,SAAS,CAAC;AAAA,MACvB,CAAC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,YAAY,KAAK,aAAa;AACvD,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,KAAK,MAImB;AAC7B,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,mBAAmB;AACzE,QAAI,aAAa,IAAI,eAAe,KAAK,UAAU;AACnD,UAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS,mBAAmB,KAAK,SAAS;AAAA,IAC3C,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,YAAY,KAAK,aAAa;AACvD,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,QAAQ,MAKmB;AAChC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,wBAAwB;AAAA,MACjF,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,GAAG,mBAAmB,KAAK,SAAS;AAAA,QACpC,eAAe,UAAU,KAAK,WAAW;AAAA,MAC1C;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,WAAW,KAAK,SAAS,CAAC;AAAA,IAClD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,YAAY,KAAK,gBAAgB;AAC1D,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,MAKmB;AAC7B,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,qBAAqB;AAAA,MAC9E,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,GAAG,mBAAmB,KAAK,SAAS;AAAA,QACpC,eAAe,UAAU,KAAK,WAAW;AAAA,MAC1C;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,WAAW,KAAK,SAAS,CAAC;AAAA,IAClD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,YAAY,KAAK,aAAa;AACvD,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;AAEA,SAAS,mBAAmB,WAAuD;AAClF,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,EACjB;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAOA,eAAe,YAAY,KAAe,WAAmC;AAG5E,QAAM,EAAE,aAAAC,aAAY,IAAI,MAAM;AAC9B,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,EAChD,QAAQ;AAAA,EAER;AACA,SAAO,IAAIA,aAAY,SAAS;AAAA,IAC/B,MAAM,IAAI,WAAW,MAAM,sBAAsB;AAAA,IACjD,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;;;AU/yBA,SAASC,cAAa,aAAqB,WAAuD;AACjG,QAAM,UAAkC;AAAA,IACvC,QAAQ;AAAA,IACR,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,WAAW,KAAe,WAAmC;AAC3E,QAAM,EAAE,aAAAC,aAAY,IAAI,MAAM;AAC9B,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAMJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAIA,aAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;AAEO,IAAM,QAAQ;AAAA,EACpB,MAAM,MAAM,MAKkB;AAC7B,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,cAAc;AACpE,UAAM,IAAI,KAAK,UAAU,CAAC;AAC1B,QAAI,EAAE,MAAO,KAAI,aAAa,IAAI,SAAS,EAAE,KAAK;AAClD,QAAI,EAAE,MAAO,KAAI,aAAa,IAAI,SAAS,EAAE,KAAK;AAClD,QAAI,EAAE,aAAc,KAAI,aAAa,IAAI,iBAAiB,EAAE,YAAY;AACxE,QAAI,EAAE,WAAY,KAAI,aAAa,IAAI,eAAe,EAAE,UAAU;AAClE,QAAI,EAAE,OAAQ,KAAI,aAAa,IAAI,UAAU,EAAE,MAAM;AACrD,QAAI,EAAE,KAAM,KAAI,aAAa,IAAI,QAAQ,EAAE,IAAI;AAC/C,QAAI,EAAE,GAAI,KAAI,aAAa,IAAI,MAAM,EAAE,EAAE;AACzC,QAAI,EAAE,OAAQ,KAAI,aAAa,IAAI,UAAU,EAAE,MAAM;AACrD,QAAI,EAAE,UAAU,OAAW,KAAI,aAAa,IAAI,SAAS,OAAO,EAAE,KAAK,CAAC;AAExE,UAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MACvC,QAAQ;AAAA,MACR,SAASD,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,IACvD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,WAAW,KAAK,aAAa;AACtD,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;;;AChDA,SAASE,cAAa,aAAqB,WAAuD;AACjG,QAAM,UAAkC;AAAA,IACvC,QAAQ;AAAA,IACR,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,eAAe,KAAe,WAAmC;AAC/E,QAAM,EAAE,aAAAC,aAAY,IAAI,MAAM;AAC9B,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAOJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAIA,aAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;AAEO,IAAM,aAAa;AAAA,EACzB,MAAM,OAAO,MAKsB;AAClC,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,qBAAqB;AAC3E,UAAM,IAAI,KAAK,UAAU,CAAC;AAC1B,QAAI,EAAE,MAAO,KAAI,aAAa,IAAI,SAAS,EAAE,KAAK;AAClD,QAAI,EAAE,SAAU,KAAI,aAAa,IAAI,YAAY,EAAE,QAAQ;AAC3D,QAAI,EAAE,UAAW,KAAI,aAAa,IAAI,aAAa,EAAE,SAAS;AAC9D,UAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MACvC,QAAQ;AAAA,MACR,SAASD,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,IACvD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,eAAe,KAAK,mBAAmB;AAChE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA,EAEA,YAAY;AAAA,IACX,MAAM,KAAK,MAK4B;AACtC,YAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,yBAAyB;AAC/E,YAAM,IAAI,KAAK,UAAU,CAAC;AAC1B,UAAI,EAAE,MAAO,KAAI,aAAa,IAAI,SAAS,EAAE,KAAK;AAClD,UAAI,EAAE,SAAU,KAAI,aAAa,IAAI,YAAY,EAAE,QAAQ;AAC3D,YAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,QACvC,QAAQ;AAAA,QACR,SAASA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,MACvD,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,eAAe,KAAK,4BAA4B;AACzE,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,IAAI,MAMiC;AAC1C,YAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,2BAA2B,mBAAmB,KAAK,SAAS,CAAC;AAC3G,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS;AAAA,UACR,GAAGA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,UAChD,gBAAgB;AAAA,QACjB;AAAA,QACA,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,MAC/B,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,eAAe,KAAK,2BAA2B;AACxE,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,OAAO,MAM8B;AAC1C,YAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,2BAA2B,mBAAmB,KAAK,SAAS,CAAC;AAC3G,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS;AAAA,UACR,GAAGA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,UAChD,gBAAgB;AAAA,QACjB;AAAA,QACA,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,MAC/B,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,eAAe,KAAK,8BAA8B;AAC3E,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA,EACD;AACD;;;AC1JA,eAAe,oBAAoB,KAAe,WAAmC;AACpF,QAAM,EAAE,aAAAE,aAAY,IAAI,MAAM;AAC9B,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AACJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAIA,aAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;AAOO,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmB7B,MAAM,eAAe,MAK8B;AAClD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,oCAAoC;AAAA,MAC7F,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,aAAa;AAAA,QAC3C,GAAI,KAAK,YAAY,EAAE,cAAc,KAAK,UAAU,IAAI,CAAC;AAAA,MAC1D;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,aAAa,KAAK,YAAY,CAAC;AAAA,IACvD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,oBAAoB,KAAK,gCAAgC;AAClF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AACD;;;ACxEA,SAAS,yBAAyB;AA6DlC,eAAsB,aACrB,QACA,SACgC;AAEhC,QAAM,OAAO;AACb,QAAM,WAAW,kBAAkB;AACnC,SAAO,QAAoC,QAAQ,SAAS,MAAM;AAAA,IACjE,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAiBA,eAAsB,mBACrB,QACA,SACmC;AAInC,QAAM,WAAW,kBAAkB;AACnC,SAAO,QAAiC,QAAQ,SAAS,MAAM;AAAA,IAC9D,QAAQ,SAAS;AAAA,IACjB,OAAO;AAAA,MACN,SAAS,QAAQ;AAAA,MACjB,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,OAAO,QAAQ,KAAK,EAAE;AAAA,MAClE,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,QAAQ,MAAM;AAAA,IAC3D;AAAA,EACD,CAAC;AACF;;;AC9EA,SAASC,cAAa,aAAqB,WAAuD;AACjG,QAAM,UAAkC;AAAA,IACvC,gBAAgB;AAAA,IAChB,eAAe,UAAU,WAAW;AAAA,EACrC;AACA,MAAI,UAAW,SAAQ,YAAY,IAAI;AACvC,SAAO;AACR;AAEA,eAAe,mBAAmB,KAAe,WAAmC;AACnF,QAAM,EAAE,aAAAC,aAAY,IAAI,MAAM;AAC9B,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG,SAAS,iBAAiB,IAAI,MAAM;AACrD,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,IAAI;AAK9B,QAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAI,OAAO,kBAAmB,WAAU,OAAO;AAAA,aACtC,OAAO,QAAS,WAAU,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AACA,MAAI;AAOJ,MAAI,IAAI,WAAW,IAAK,aAAY;AAAA,WAC3B,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,WAAW,IAAK,aAAY;AAAA,WAChC,IAAI,UAAU,IAAK,aAAY;AAAA,MACnC,aAAY;AACjB,SAAO,IAAIA,aAAY,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,MAAM,EAAE,WAAW,KAAK;AAAA,EACzB,CAAC;AACF;AAQO,IAAM,gBAAgB;AAAA,EAC5B,UAAU;AAAA;AAAA;AAAA;AAAA,IAIT,MAAM,OAAO,MAK6B;AACzC,YAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,wBAAwB;AAC9E,UAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,YAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,QACvC,QAAQ;AAAA,QACR,SAASD,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,MACvD,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,gCAAgC;AACjF,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM,KAAK,MAKqC;AAC/C,YAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,0BAA0B;AAChF,UAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,YAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,QACvC,QAAQ;AAAA,QACR,SAASA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,MACvD,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,8BAA8B;AAC/E,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,MAAM,OAAO,MAMoC;AAChD,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,4BAA4B;AAAA,QACrF,QAAQ;AAAA,QACR,SAASA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,QACtD,MAAM,KAAK,UAAU,EAAE,WAAW,KAAK,WAAW,MAAM,KAAK,KAAK,CAAC;AAAA,MACpE,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,gCAAgC;AACjF,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,OAAO,MAMoC;AAChD,YAAM,MAAM,IAAI;AAAA,QACf,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC,4BAA4B;AAAA,UAC7D,KAAK;AAAA,QACN,CAAC;AAAA,MACF;AACA,UAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,YAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,QACvC,QAAQ;AAAA,QACR,SAASA,cAAa,KAAK,aAAa,KAAK,SAAS;AAAA,MACvD,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,MAAM,mBAAmB,KAAK,gCAAgC;AACjF,aAAQ,MAAM,IAAI,KAAK;AAAA,IACxB;AAAA,EACD;AACD;;;ACzJA,eAAsB,UACrB,QACA,MAC2B;AAC3B,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,MAAM,MAAO,QAAO,IAAI,SAAS,KAAK,KAAK;AAC/C,MAAI,MAAM,OAAQ,QAAO,IAAI,UAAU,KAAK,MAAM;AAClD,MAAI,MAAM,MAAO,QAAO,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC;AACvD,MAAI,MAAM,OAAQ,QAAO,IAAI,UAAU,OAAO,KAAK,MAAM,CAAC;AAC1D,QAAM,KAAK,OAAO,SAAS;AAC3B,SAAO,QAAyB,QAAQ,eAAe,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE;AAC5E;AAMA,eAAsB,QAAQ,QAAsB,QAAoC;AACvF,SAAO,QAAmB,QAAQ,gBAAgB,MAAM,EAAE;AAC3D;AAYA,eAAsB,eACrB,QACA,OAC4B;AAC5B,QAAM,SAAS,MAAM,UAAU,QAAQ,EAAE,OAAO,OAAO,EAAE,CAAC;AAC1D,SAAO,OAAO,MAAM,CAAC,KAAK;AAC3B;AAWA,eAAsB,WACrB,QACA,QACA,OACqB;AACrB,SAAO,QAAmB,QAAQ,gBAAgB,MAAM,IAAI;AAAA,IAC3D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAWA,eAAsB,mBACrB,QACA,QACA,UACqB;AACrB,SAAO,QAAmB,QAAQ,gBAAgB,MAAM,aAAa;AAAA,IACpE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAWA,eAAsB,YACrB,QACA,QACA,QACgB;AAChB,QAAM,QAAc,QAAQ,gBAAgB,MAAM,YAAY;AAAA,IAC7D,QAAQ;AAAA,IACR,MAAM,EAAE,OAAO;AAAA,EAChB,CAAC;AACF;AAWA,eAAsB,WAAW,QAAsB,QAA+B;AACrF,QAAM,QAAc,QAAQ,gBAAgB,MAAM,WAAW;AAAA,IAC5D,QAAQ;AAAA,EACT,CAAC;AACF;;;ACnIA,SAAS,0BAA0B;AAyEnC,eAAsB,MAAM,QAAsB,OAAkC;AAEnF,QAAM,WAAW,mBAAmB;AACpC,QAAM,OAAO;AAAA,IACZ,OAAO,MAAM;AAAA,IACb,YAAY,MAAM,cAAc,CAAC;AAAA,IACjC,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,WAAW,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,EACtD;AACA,QAAM,QAAQ,QAAQ,SAAS,MAAM;AAAA,IACpC,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAaA,eAAsB,KAAK,QAAsB,OAAiC;AAIjF,QAAM,WAAW,mBAAmB;AACpC,QAAM,OAAO;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ,YAAY,MAAM,cAAc,CAAC;AAAA,IACjC,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,EACpB;AACA,QAAM,QAAQ,QAAQ,SAAS,MAAM;AAAA,IACpC,QAAQ,SAAS;AAAA,IACjB,MAAM,EAAE,GAAG,MAAM,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAAA,EACtD,CAAC;AACF;AAcA,eAAsB,SAAS,QAAsB,OAAqC;AAEzF,QAAM,WAAW,mBAAmB;AACpC,QAAM,OAAO;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB,aAAa,MAAM;AAAA,EACpB;AACA,QAAM,QAAQ,QAAQ,SAAS,MAAM;AAAA,IACpC,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAcA,eAAsB,WAAW,QAAsB,QAAqC;AAE3F,QAAM,WAAW,mBAAmB;AACpC,QAAM,OAAO;AAAA,IACZ,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,MAC1B,OAAO,EAAE,SAAS,UAAW,EAAE,SAAS,KAAM,EAAE,SAAS,SAAS,cAAc;AAAA,MAChF,YAAY;AAAA,QACX,GAAG,EAAE;AAAA,QACL,GAAI,EAAE,SAAS,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,QACtD,GAAI,EAAE,SAAS,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;AAAA,MACjE;AAAA,MACA,QAAQ,EAAE;AAAA,MACV,aAAa,EAAE;AAAA,MACf,WAAW,EAAE,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClD,EAAE;AAAA,EACH;AACA,QAAM,QAAQ,QAAQ,SAAS,MAAM;AAAA,IACpC,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAmBO,SAAS,sBAA8B;AAE7C,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACvD,WAAO,OAAO,WAAW;AAAA,EAC1B;AAEA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACrE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACrB,CAAC;AACF;AAkBO,SAAS,cAAc,QAAsB,oBAA6B;AAChF,QAAM,cAAc,sBAAsB,oBAAoB;AAE9D,SAAO;AAAA,IACN,OAAO,CAAC,OAAe,YAAsC,WAC5D,MAAM,QAAQ,EAAE,OAAO,YAAY,QAAQ,YAAY,CAAC;AAAA,IAEzD,MAAM,CAAC,MAAc,YAAsC,WAC1D,KAAK,QAAQ,EAAE,MAAM,YAAY,QAAQ,YAAY,CAAC;AAAA,IAEvD,UAAU,CAAC,QAAgB,WAC1B,SAAS,QAAQ,EAAE,QAAQ,QAAQ,YAAY,CAAC;AAAA,IAEjD,OAAO,CAAC,WACP;AAAA,MACC;AAAA,MACA,OAAO,IAAI,CAAC,OAAO;AAAA,QAClB,GAAG;AAAA,QACH,aAAa,EAAE,eAAe;AAAA,MAC/B,EAAE;AAAA,IACH;AAAA;AAAA,IAGD,gBAAgB,MAAM;AAAA,EACvB;AACD;;;AC1PA;AA+IA,eAAsB,KAAK,QAAsB,OAAuC;AACvF,QAAM,WAAW,MAAM,MAAM,YAAY,QAAQ,mBAAmB,GAAG;AAAA,IACtE,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,GAAG,aAAa,MAAM;AAAA,MACtB,eAAe,UAAU,OAAO,SAAS;AAAA,IAC1C;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACpB,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,YAAY,MAAM;AAAA,MAClB,OAAO,MAAM;AAAA,MACb,mBAAmB,MAAM;AAAA,MACzB,kBAAkB,MAAM;AAAA,MACxB,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,IACpB,CAAC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,QAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,EAAE,SAAS,sBAAsB,EAAE,EAAE;AAC/F,UAAM,IAAI,YAAY,OAAO,OAAO,WAAW,uBAAuB;AAAA,MACrE,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,SAAO;AAAA,IACN,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK,QAAQ,IAAI,CAAC,OAAgC;AAAA,MAC1D,OAAO,EAAE;AAAA,MACT,SAAS;AAAA,QACR,MAAM;AAAA,QACN,SAAU,EAAE,SAAqC;AAAA,QACjD,YAAa,EAAE,SAAqC;AAAA,MACrD;AAAA,MACA,cAAc,EAAE;AAAA,IACjB,EAAE;AAAA,IACF,OAAO;AAAA,MACN,cAAc,KAAK,MAAM;AAAA,MACzB,kBAAkB,KAAK,MAAM;AAAA,MAC7B,aAAa,KAAK,MAAM;AAAA,IACzB;AAAA,EACD;AACD;AAiBO,SAAS,WAAW,QAAsB,OAAkD;AAClG,SAAO;AAAA,IACN,CAAC,OAAO,aAAa,GAAG,mBAAmB;AAC1C,YAAM,WAAW,MAAM,MAAM,YAAY,QAAQ,mBAAmB,GAAG;AAAA,QACtE,QAAQ;AAAA,QACR,SAAS;AAAA,UACR,GAAG,aAAa,MAAM;AAAA,UACtB,eAAe,UAAU,OAAO,SAAS;AAAA,QAC1C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACpB,OAAO,MAAM;AAAA,UACb,UAAU,MAAM;AAAA,UAChB,aAAa,MAAM;AAAA,UACnB,YAAY,MAAM;AAAA,UAClB,OAAO,MAAM;AAAA,UACb,mBAAmB,MAAM;AAAA,UACzB,kBAAkB,MAAM;AAAA,UACxB,MAAM,MAAM;AAAA,UACZ,OAAO,MAAM;AAAA,UACb,aAAa,MAAM;AAAA,UACnB,QAAQ;AAAA,QACT,CAAC;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACjB,cAAM,QAAQ,MAAM,SAClB,KAAK,EACL,MAAM,OAAO,EAAE,OAAO,EAAE,SAAS,wBAAwB,EAAE,EAAE;AAC/D,cAAM,IAAI,YAAY,OAAO,OAAO,WAAW,yBAAyB;AAAA,UACvE,MAAM;AAAA,QACP,CAAC;AAAA,MACF;AAEA,YAAM,SAAS,SAAS,MAAM,UAAU;AACxC,UAAI,CAAC,QAAQ;AACZ,cAAM,IAAI,YAAY,oBAAoB;AAAA,UACzC,MAAM;AAAA,QACP,CAAC;AAAA,MACF;AAEA,YAAM,UAAU,IAAI,YAAY;AAChC,UAAI,SAAS;AAEb,UAAI;AACH,eAAO,MAAM;AACZ,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,cAAI,KAAM;AAEV,oBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,gBAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,mBAAS,MAAM,IAAI,KAAK;AAExB,qBAAW,QAAQ,OAAO;AACzB,gBAAI,KAAK,WAAW,QAAQ,GAAG;AAC9B,oBAAM,OAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAChC,kBAAI,SAAS,SAAU;AAEvB,kBAAI;AACH,sBAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,sBAAM;AAAA,kBACL,IAAI,MAAM,MAAM;AAAA,kBAChB,OAAO,MAAM,SAAS,MAAM;AAAA,kBAC5B,UAAU,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,OAAgC;AAAA,oBACnE,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;AAAA,oBAC/C,OAAO;AAAA,sBACN,MAAO,EAAE,OAAmC;AAAA,sBAC5C,SAAU,EAAE,OAAmC;AAAA,sBAC/C,YAAa,EAAE,OAAmC;AAAA,oBAGnD;AAAA,oBACA,cACE,EAAE,iBAAmE;AAAA,kBACxE,EAAE;AAAA,gBACH;AAAA,cACD,QAAQ;AAAA,cAER;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD,UAAE;AACD,eAAO,YAAY;AAAA,MACpB;AAAA,IACD;AAAA,EACD;AACD;AAeA,eAAsB,MAAM,QAAsB,OAAyC;AAC1F,QAAM,WAAW,MAAM,MAAM,YAAY,QAAQ,aAAa,GAAG;AAAA,IAChE,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,GAAG,aAAa,MAAM;AAAA,MACtB,eAAe,UAAU,OAAO,SAAS;AAAA,IAC1C;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACpB,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,YAAY,MAAM;AAAA,IACnB,CAAC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,QAAQ,MAAM,SAClB,KAAK,EACL,MAAM,OAAO,EAAE,OAAO,EAAE,SAAS,2BAA2B,EAAE,EAAE;AAClE,UAAM,IAAI,YAAY,OAAO,OAAO,WAAW,4BAA4B;AAAA,MAC1E,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,SAAO;AAAA,IACN,OAAO,KAAK;AAAA,IACZ,MAAM,KAAK;AAAA,IACX,OAAO;AAAA,MACN,cAAc,KAAK,MAAM;AAAA,MACzB,aAAa,KAAK,MAAM;AAAA,IACzB;AAAA,EACD;AACD;AAcA,eAAsB,SACrB,QACA,OACA,QACA,SACkB;AAClB,QAAM,WAAW,MAAM,KAAK,QAAQ;AAAA,IACnC;AAAA,IACA,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC5C,GAAG;AAAA,EACJ,CAAC;AACD,SAAO,SAAS,QAAQ,CAAC,GAAG,QAAQ,WAAW;AAChD;AAaA,eAAsB,eAAe,QAAsB,OAAmC;AAC7F,MAAI,SAAS;AACb,mBAAiB,SAAS,WAAW,QAAQ,KAAK,GAAG;AACpD,cAAU,MAAM,QAAQ,CAAC,GAAG,MAAM,WAAW;AAAA,EAC9C;AACA,SAAO;AACR;;;ACrWA,eAAsB,SAAS,QAAuC;AACrE,SAAO,QAAgB,QAAQ,gBAAgB;AAChD;AAaA,eAAsB,gBACrB,QACA,QAC+B;AAC/B,SAAO,QAA6B,QAAQ,yBAAyB;AAAA,IACpE,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;AAkBA,eAAsB,eACrB,QACA,OACmC;AACnC,SAAO,QAAiC,QAAQ,qBAAqB;AAAA,IACpE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAeA,eAAsB,oBACrB,QACA,OACiC;AACjC,SAAO,QAA+B,QAAQ,mBAAmB;AAAA,IAChE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAWA,eAAsB,kBAAkB,QAAuD;AAC9F,SAAO,QAAgC,QAAQ,kBAAkB;AAClE;AAUA,eAAsB,gBACrB,QACA,SACgC;AAChC,SAAO,QAA8B,QAAQ,kBAAkB;AAAA,IAC9D,OAAO,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI;AAAA,EACpD,CAAC;AACF;;;ACtHA;;;AC1BA;AAeO,IAAM,uBAAoC;AAAA,EAChD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,YAAY;AACb;AAUO,SAAS,sBACf,SACA,SAAsB,sBACb;AACT,QAAM,MAAM,OAAO,cAAc,KAAK;AACtC,QAAM,SAAS,KAAK,IAAI,KAAK,OAAO,UAAU;AAC9C,SAAO,KAAK,OAAO,IAAI;AACxB;AAGO,SAAS,MAAM,IAAY,QAAqC;AACtE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,QAAI,QAAQ,SAAS;AACpB,aAAO,aAAa,CAAC;AACrB;AAAA,IACD;AACA,UAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,YAAQ;AAAA,MACP;AAAA,MACA,MAAM;AACL,qBAAa,KAAK;AAClB,eAAO,aAAa,CAAC;AAAA,MACtB;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACd;AAAA,EACD,CAAC;AACF;AAOO,IAAM,qBAA0C,oBAAI,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC;AAQvE,SAASE,kBAAiB,OAAyB;AACzD,MAAI,aAAa,KAAK,EAAG,QAAO;AAChC,MAAI,iBAAiB,UAAW,QAAO;AACvC,MAAI,iBAAiB,SAAS,YAAY,OAAO;AAChD,UAAM,SAAU,MAA6B;AAC7C,WAAO,UAAU,OAAO,mBAAmB,IAAI,MAAM;AAAA,EACtD;AACA,SAAO;AACR;AAGO,SAAS,aAAa,OAAyB;AACrD,MAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAAc,QAAO;AACzE,MAAI,iBAAiB,SAAS,MAAM,SAAS,aAAc,QAAO;AAClE,SAAO;AACR;AAGO,SAAS,aAAa,UAAU,WAAkB;AACxD,MAAI,OAAO,iBAAiB,aAAa;AACxC,WAAO,IAAI,aAAa,SAAS,YAAY;AAAA,EAC9C;AACA,QAAM,MAAM,IAAI,MAAM,OAAO;AAC7B,MAAI,OAAO;AACX,SAAO;AACR;AAkBA,eAAsB,UAAa,IAAsB,UAAwB,CAAC,GAAe;AAChG,QAAM,MAAmB;AAAA,IACxB,YAAY,QAAQ,cAAc,qBAAqB;AAAA,IACvD,aAAa,QAAQ,eAAe,qBAAqB;AAAA,IACzD,YAAY,QAAQ,cAAc,qBAAqB;AAAA,EACxD;AACA,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,IAAI,YAAY,WAAW;AAC3D,QAAI,QAAQ,QAAQ,QAAS,OAAM,aAAa,SAAS;AACzD,QAAI;AACH,aAAO,MAAM,GAAG;AAAA,IACjB,SAAS,KAAK;AACb,kBAAY;AACZ,UAAI,aAAa,GAAG,EAAG,OAAM;AAC7B,UAAI,YAAY,IAAI,WAAY;AAChC,UAAI,CAACA,kBAAiB,GAAG,EAAG,OAAM;AAElC,YAAM,aAAa,kBAAkB,GAAG;AACxC,YAAM,QAAQ,cAAc,sBAAsB,SAAS,GAAG;AAC9D,cAAQ,UAAU,SAAS,OAAO,GAAG;AACrC,YAAM,MAAM,OAAO,QAAQ,MAAM;AAAA,IAClC;AAAA,EACD;AACA,QAAM;AACP;AAEA,SAAS,kBAAkB,KAAkC;AAC5D,MAAI,OAAO,OAAO,QAAQ,YAAY,gBAAgB,KAAK;AAC1D,UAAM,IAAK,IAAgC;AAC3C,QAAI,OAAO,MAAM,YAAY,IAAI,EAAG,QAAO,IAAI;AAAA,EAChD;AACA,SAAO;AACR;AAcO,SAAS,SAAiB;AAChC,QAAM,KAAK,OAAO,KAAK,IAAI,CAAC;AAE5B,QAAM,QAAQ,YAAY,EAAE;AAE5B,QAAM,CAAC,IAAI,OAAQ,MAAM,MAAO,KAAK;AACrC,QAAM,CAAC,IAAI,OAAQ,MAAM,MAAO,KAAK;AACrC,QAAM,CAAC,IAAI,OAAQ,MAAM,MAAO,KAAK;AACrC,QAAM,CAAC,IAAI,OAAQ,MAAM,MAAO,KAAK;AACrC,QAAM,CAAC,IAAI,OAAQ,MAAM,KAAM,KAAK;AACpC,QAAM,CAAC,IAAI,OAAO,KAAK,KAAK;AAE5B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAE/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAE/B,QAAM,MAAM,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC7E,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC;AAC7G;AAEA,SAAS,YAAY,GAAuB;AAC3C,QAAM,MAAM,IAAI,WAAW,CAAC;AAC5B,QAAM,IAAK,WAAgF;AAC3F,MAAI,GAAG,iBAAiB;AACvB,MAAE,gBAAgB,GAAG;AACrB,WAAO;AAAA,EACR;AACA,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,KAAI,CAAC,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AACnE,SAAO;AACR;;;ADzEA,IAAM,QAAQ;AAAA,EACb,SAAS;AAAA,EACT,QAAQ,CAAC,OAA0B,oBAAoB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACrF,gBAAgB,CAAC,OAChB,oBAAoB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACnD,YAAY,CAAC,IAAuB,MACnC,oBAAoB,mBAAmB,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC;AAAA,EAC9D,OAAO;AAAA,EACP,MAAM,CAAC,OAAwB,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EAC/E,cAAc,CAAC,OACd,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACjD,aAAa,CAAC,OAAwB,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACtF,eAAe,CAAC,OACf,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACjD,UAAU,CAAC,OAAwB,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACnF,UAAU,CAAC,OAAwB,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC;AAAA,EACnF,gBAAgB,CAAC,IAAqB,QACrC,kBAAkB,mBAAmB,OAAO,EAAE,CAAC,CAAC,aAAa,mBAAmB,OAAO,GAAG,CAAC,CAAC;AAC9F;AAMA,IAAM,SAAS,MACd,OAAQ,WAA4C,mBAAmB;AAExE,IAAM,kBAAkB,MAAe;AACtC,MAAI;AACH,UAAM,KAAM,WAA0C;AACtD,WAAO,QAAQ,MAAM,OAAO,GAAG,YAAY,UAAU;AAAA,EACtD,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAMA,eAAe,iBAAiB,MAA6B;AAC5D,QAAM,SAAU,WACd,QAAQ;AACV,MAAI,CAAC,QAAQ,QAAQ;AACpB,UAAM,IAAI,YAAY,8DAA8D;AAAA,MACnF,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,KAAK,YAAY;AACnC,QAAM,SAAS,MAAM,OAAO,OAAO,WAAW,GAAG;AACjD,SAAO,WAAW,IAAI,WAAW,MAAM,CAAC;AACzC;AAEA,SAAS,WAAW,GAAuB;AAC1C,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,MAAK,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACzE,SAAO;AACR;AAYA,IAAM,oBAAoB;AAE1B,SAAS,UAAU,UAA0B;AAC5C,SAAO,GAAG,iBAAiB,GAAG,QAAQ;AACvC;AAEA,SAAS,cAAc,KAAyB;AAC/C,MAAI,gBAAgB,GAAG;AACtB,QAAI;AACH,mBAAa,QAAQ,UAAU,IAAI,QAAQ,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,IAClE,QAAQ;AAAA,IAER;AAAA,EACD;AACD;AAEA,SAAS,YAAY,UAAwB;AAC5C,MAAI,gBAAgB,GAAG;AACtB,QAAI;AACH,mBAAa,WAAW,UAAU,QAAQ,CAAC;AAAA,IAC5C,QAAQ;AAAA,IAER;AAAA,EACD;AACD;AAgBA,SAAS,QACR,KACA,MACA,SACA,QACA,YACqB;AACrB,MAAI,OAAO,EAAG,QAAO,WAAW,KAAK,MAAM,SAAS,QAAQ,UAAU;AACtE,SAAO,aAAa,KAAK,MAAM,SAAS,QAAQ,UAAU;AAC3D;AAEA,SAAS,WACR,KACA,MACA,SACA,QACA,YACqB;AACrB,SAAO,IAAI,QAAmB,CAAC,SAAS,WAAW;AAClD,UAAM,MAAM,IAAI,eAAe;AAE/B,UAAM,cAAc,MAAM;AACzB,UAAI;AACH,YAAI,MAAM;AAAA,MACX,QAAQ;AAAA,MAER;AACA,aAAO,aAAa,gBAAgB,CAAC;AAAA,IACtC;AAEA,QAAI,QAAQ,SAAS;AACpB,aAAO,aAAa,gBAAgB,CAAC;AACrC;AAAA,IACD;AACA,YAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;AAE7D,QAAI,YAAY;AACf,UAAI,OAAO,iBAAiB,YAAY,CAAC,MAAM;AAC9C,YAAI,EAAE,iBAAkB,YAAW,EAAE,MAAM;AAAA,MAC5C,CAAC;AAAA,IACF;AAEA,QAAI,iBAAiB,QAAQ,MAAM;AAClC,cAAQ,oBAAoB,SAAS,WAAW;AAChD,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AAC1C,cAAM,OAAO,eAAe,GAAG;AAC/B,gBAAQ,EAAE,MAAM,YAAY,IAAI,GAAG,QAAQ,IAAI,OAAO,CAAC;AAAA,MACxD,OAAO;AACN,cAAM,MAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,EAAE;AAG5D,YAAI,SAAS,IAAI;AACjB,eAAO,GAAG;AAAA,MACX;AAAA,IACD,CAAC;AACD,QAAI,iBAAiB,SAAS,MAAM;AACnC,cAAQ,oBAAoB,SAAS,WAAW;AAChD,aAAO,IAAI,UAAU,6BAA6B,CAAC;AAAA,IACpD,CAAC;AACD,QAAI,iBAAiB,SAAS,MAAM;AACnC,cAAQ,oBAAoB,SAAS,WAAW;AAChD,aAAO,aAAa,gBAAgB,CAAC;AAAA,IACtC,CAAC;AAED,QAAI,KAAK,OAAO,GAAG;AACnB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC7C,UAAI;AACH,YAAI,iBAAiB,GAAG,CAAC;AAAA,MAC1B,QAAQ;AAAA,MAER;AAAA,IACD;AACA,QAAI,KAAK,IAAI;AAAA,EACd,CAAC;AACF;AAEA,SAAS,eAAe,KAA6B;AACpD,QAAM,YAAY,IAAI,kBAAkB,MAAM;AAC9C,MAAI,cAAc,KAAM,QAAO;AAC/B,SAAO,IAAI,kBAAkB,MAAM,KAAK;AACzC;AAEA,eAAe,aACd,KACA,MACA,SACA,QACA,YACqB;AAErB,MAAI,SAAmB;AACvB,MAAI,cAAc,OAAO,oBAAoB,eAAe,OAAO,KAAK,WAAW,YAAY;AAC9F,QAAI,SAAS;AACb,UAAM,UAAU,IAAI,gBAAwC;AAAA,MAC3D,UAAU,OAAO,YAAY;AAC5B,kBAAU,MAAM;AAChB,mBAAW,MAAM;AACjB,mBAAW,QAAQ,KAAK;AAAA,MACzB;AAAA,IACD,CAAC;AACD,aAAS,KAAK,OAAO,EAAE,YAAY,OAAO;AAAA,EAC3C;AAEA,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC5B,QAAQ;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,GAAI,EAAE,QAAQ,OAAO;AAAA,EACtB,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACZ,UAAM,MAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,EAAE;AAC5D,QAAI,SAAS,IAAI;AACjB,UAAM;AAAA,EACP;AACA,QAAM,OAAO,iBAAiB,IAAI,OAAO;AACzC,MAAI,WAAY,YAAW,KAAK,IAAI;AACpC,SAAO,EAAE,MAAM,YAAY,IAAI,GAAG,QAAQ,IAAI,OAAO;AACtD;AAEA,SAAS,iBAAiB,SAA0B;AACnD,QAAM,YAAY,QAAQ,IAAI,MAAM;AACpC,MAAI,cAAc,KAAM,QAAO;AAC/B,SAAO,QAAQ,IAAI,MAAM,KAAK;AAC/B;AAEA,SAAS,YAAY,GAAmB;AACvC,MAAI,EAAE,UAAU,KAAK,EAAE,WAAW,GAAG,KAAK,EAAE,SAAS,GAAG,EAAG,QAAO,EAAE,MAAM,GAAG,EAAE;AAC/E,SAAO;AACR;AAMA,eAAe,aACd,KACA,MACA,SACA,QACA,YACqB;AACrB,MAAI;AACJ,QAAM,aAAa;AACnB,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACvD,QAAI,QAAQ,QAAS,OAAM,aAAa;AACxC,QAAI;AACH,aAAO,MAAM,QAAQ,KAAK,MAAM,SAAS,QAAQ,UAAU;AAAA,IAC5D,SAAS,KAAK;AACb,UAAI,aAAa,GAAG,EAAG,OAAM;AAC7B,gBAAU;AACV,UAAI,YAAY,cAAc,CAACC,kBAAiB,GAAG,EAAG,OAAM;AAC5D,YAAM,MAAM,sBAAsB,OAAO,GAAG,MAAM;AAAA,IACnD;AAAA,EACD;AACA,QAAM;AACP;AAMA,eAAe,cACd,QACA,MACA,SACwB;AACxB,QAAM,SAAS,QAAQ;AACvB,MAAI,QAAQ,QAAS,OAAM,aAAa;AAExC,QAAM,WAAW,QAAQ,aAAa,OAAO,IAAI,IAAI,KAAK,OAAO,WAAc;AAC/E,QAAM,cACL,QAAQ,gBACP,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO;AAClD,QAAM,OAAO,KAAK;AAElB,QAAM,iBAAiB,QAAQ,kBAAmB,MAAM,iBAAiB,IAAI;AAE7E,QAAM,iBAAiB,QAAQ,kBAAkB,OAAO;AAExD,QAAM,aAAkC;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,YAAY,QAAQ;AAAA,IACpB,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,aAAa,QAAQ;AAAA,EACtB;AAGA,QAAM,UAAU,MAAM,QAA4B,QAAQ,MAAM,SAAS;AAAA,IACxE,QAAQ;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACD,CAAC;AAED,QAAM,WAAW,QAAQ;AAGzB,QAAM,YAAY,YAAY;AAC7B,QAAI;AACH,YAAM,QAAQ,QAAQ,MAAM,OAAO,QAAQ,GAAG,EAAE,QAAQ,SAAS,CAAC;AAAA,IACnE,QAAQ;AAAA,IAER;AACA,gBAAY,OAAO,QAAQ,CAAC;AAAA,EAC7B;AAEA,MAAI;AACH,QAAI,QAAQ,WAAW,OAAO;AAC7B,aAAO,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,cAAc;AAAA,IAC1E;AACA,WAAO,MAAM,aAAa,QAAQ,MAAM,SAAS,SAAS,cAAc;AAAA,EACzE,SAAS,KAAK;AACb,QAAI,aAAa,GAAG,GAAG;AACtB,YAAM,UAAU;AAAA,IACjB;AACA,UAAM;AAAA,EACP;AACD;AAEA,SAAS,OAAO,GAA2B;AAC1C,SAAO,OAAQ,EAAW,SAAS;AACpC;AAEA,eAAe,cACd,QACA,MACA,SACA,SACA,gBACwB;AACxB,QAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,QAAM,QAAQ,KAAK;AAEnB,QAAM,gBAAgB,aACnB,CAAC,WAAmB;AACpB,eAAW,EAAE,QAAQ,OAAO,gBAAgB,GAAG,YAAY,EAAE,CAAC;AAAA,EAC/D,IACC;AAEH,QAAM,MAAM,MAAM,aAAa,QAAQ,KAAK,MAAM,QAAQ,SAAS,QAAQ,aAAa;AAExF,MAAI,WAAY,YAAW,EAAE,QAAQ,OAAO,OAAO,gBAAgB,GAAG,YAAY,EAAE,CAAC;AAErF,QAAM,aAAa,MAAM;AAAA,IACxB;AAAA,IACA,MAAM,eAAe,QAAQ,QAAQ;AAAA,IACrC;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,MACnD,gBAAgB,GAAG,cAAc;AAAA,MACjC;AAAA,IACD;AAAA,EACD;AAEA,cAAY,OAAO,QAAQ,QAAQ,CAAC;AACpC,SAAO,MAAM,UAAU,QAAQ,WAAW,MAAM;AACjD;AAEA,eAAe,aACd,QACA,MACA,SACA,SACA,gBACwB;AACxB,QAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,QAAM,WAAW,QAAQ;AACzB,QAAM,QAAQ,KAAK;AACnB,QAAM,aAAa,QAAQ;AAC3B,QAAM,iBAA8D,CAAC;AACrE,QAAM,aAAa,oBAAI,IAAoB;AAE3C,QAAM,iBAAiB,MAAM;AAC5B,QAAI,CAAC,WAAY;AACjB,QAAI,SAAS;AACb,eAAW,KAAK,WAAW,OAAO,EAAG,WAAU;AAC/C,eAAW,EAAE,QAAQ,OAAO,gBAAgB,eAAe,QAAQ,WAAW,CAAC;AAAA,EAChF;AAEA,aAAW,QAAQ,QAAQ,OAAO;AACjC,QAAI,QAAQ,QAAS,OAAM,aAAa;AACxC,UAAM,UAAU,KAAK,aAAa,KAAK;AACvC,UAAM,MAAM,KAAK,IAAI,SAAS,UAAU,KAAK;AAC7C,UAAM,QAAQ,KAAK,MAAM,QAAQ,GAAG;AAEpC,UAAM,gBAAgB,aACnB,CAAC,WAAmB;AACpB,iBAAW,IAAI,KAAK,YAAY,MAAM;AACtC,qBAAe;AAAA,IAChB,IACC;AAEH,UAAM,SAAS,MAAM,aAAa,KAAK,KAAK,OAAO,CAAC,GAAG,QAAQ,aAAa;AAC5E,mBAAe,KAAK,EAAE,YAAY,KAAK,YAAY,MAAM,OAAO,KAAK,CAAC;AACtE,eAAW,IAAI,KAAK,YAAY,MAAM,IAAI;AAC1C,mBAAe;AAGf,kBAAc;AAAA,MACb,UAAU,OAAO,QAAQ,QAAQ;AAAA,MACjC,gBAAgB,CAAC,GAAG,cAAc;AAAA,MAClC,WAAW,KAAK,IAAI;AAAA,IACrB,CAAC;AAAA,EACF;AAEA,QAAM,aAAa,MAAM;AAAA,IACxB;AAAA,IACA,MAAM,eAAe,QAAQ,QAAQ;AAAA,IACrC;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,EAAE,OAAO,eAAe;AAAA,MAC9B,gBAAgB,GAAG,cAAc;AAAA,MACjC;AAAA,IACD;AAAA,EACD;AAEA,cAAY,OAAO,QAAQ,QAAQ,CAAC;AACpC,SAAO,MAAM,UAAU,QAAQ,WAAW,MAAM;AACjD;AAEA,eAAe,UAAU,QAAsB,QAAgD;AAC9F,SAAO,QAAsB,QAAQ,MAAM,KAAK,MAAM,GAAG,EAAE,QAAQ,MAAM,CAAC;AAC3E;AAMA,eAAe,aAAa,QAAsB,UAA4C;AAC7F,QAAM,UAAU,MAAM,QAAQ,QAAQ,MAAM,OAAO,QAAQ,GAAG,EAAE,QAAQ,SAAS,CAAC,GAAG;AAAA,IACpF,YAAY;AAAA,EACb,CAAC;AACD,cAAY,OAAO,QAAQ,CAAC;AAC7B;AAWA,SAAS,cACR,QACA,SACA,QACoB;AACpB,QAAM,QAAwB;AAAA,IAC7B,QAAQ,QAAQ;AAAA,IAChB,QAAQ,UAAU,QAAQ;AAAA,IAC1B,OAAO,QAAQ;AAAA,IACf,gBAAgB,QAAQ;AAAA,EACzB;AACA,SAAO,QAAkB,QAAQ,MAAM,OAAO;AAAA,IAC7C,QAAQ;AAAA,IACR,OAAO;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,gBAAgB,MAAM;AAAA,IACvB;AAAA,EACD,CAAC;AACF;AAEA,SAAS,UACR,QACA,UAA4B,CAAC,GAG5B;AACD,QAAM,YAAY,CAAC,WAAoB,cAAc,QAAQ,SAAS,MAAM;AAC5E,QAAM,OAAoC;AAAA,IACzC,CAAC,OAAO,aAAa,GAAG,mBAAmB;AAC1C,UAAI,SAAoC,QAAQ;AAChD,SAAG;AACF,cAAMC,QAAO,MAAM,UAAU,UAAU,MAAS;AAChD,mBAAW,KAAKA,MAAK,MAAO,OAAM;AAClC,iBAASA,MAAK;AAAA,MACf,SAAS;AAAA,IACV;AAAA,EACD;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,UAAU,CAAC;AACzC;AAEA,eAAe,SAAS,QAAsB,QAAgD;AAC7F,SAAO,QAAsB,QAAQ,MAAM,KAAK,MAAM,GAAG,EAAE,QAAQ,MAAM,CAAC;AAC3E;AAEA,eAAe,YACd,QACA,QAC2C;AAC3C,SAAO,QAAQ,QAAQ,MAAM,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,CAAC;AAChE;AAEA,eAAe,cACd,QACA,QACA,SAC8B;AAC9B,SAAO,QAA4B,QAAQ,MAAM,aAAa,MAAM,GAAG;AAAA,IACtE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAe,aAAa,QAAsB,QAAgD;AACjG,SAAO,QAAsB,QAAQ,MAAM,YAAY,MAAM,GAAG,EAAE,QAAQ,OAAO,CAAC;AACnF;AAEA,eAAe,eACd,QACA,QACA,UAA4B,CAAC,GACqC;AAClE,QAAM,OAAyB;AAAA,IAC9B,WAAW,QAAQ;AAAA,IACnB,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,EACjB;AACA,SAAO,QAAQ,QAAQ,MAAM,cAAc,MAAM,GAAG;AAAA,IACnD,QAAQ;AAAA,IACR;AAAA,EACD,CAAC;AACF;AAEA,eAAe,UACd,QACA,QACA,SACwB;AACxB,QAAM,OAAwB;AAAA,IAC7B,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,YAAY,QAAQ;AAAA,IACpB,UAAU,QAAQ;AAAA,EACnB;AACA,SAAO,QAAsB,QAAQ,MAAM,SAAS,MAAM,GAAG;AAAA,IAC5D,QAAQ;AAAA,IACR;AAAA,EACD,CAAC;AACF;AAEA,eAAe,kBACd,QACA,QACyB;AACzB,QAAM,OAAO,MAAM,QAAqC,QAAQ,MAAM,SAAS,MAAM,GAAG;AAAA,IACvF,QAAQ;AAAA,EACT,CAAC;AACD,SAAO,KAAK;AACb;AAEA,eAAe,qBACd,QACA,QACA,WACwD;AACxD,SAAO,QAAQ,QAAQ,MAAM,eAAe,QAAQ,SAAS,GAAG,EAAE,QAAQ,OAAO,CAAC;AACnF;AAwBO,IAAM,UAAU;AAAA,EACtB,SAAS;AAAA,IACR,QAAQ;AAAA,IACR,OAAO;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,WAAW;AAAA,IACX,MAAM;AAAA,IACN,UAAU;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,IACV;AAAA,EACD;AACD;;;AE7kBO,SAAS,sBAAsB,SAAkC,CAAC,GAAS;AACjF,QAAM,EAAE,aAAa,cAAc,qBAAqB,oBAAoB,IAAI;AAGhF,OAAK,iBAAiB,QAAQ,CAAC,UAAU;AACxC,QAAI,CAAC,MAAM,MAAM;AAChB,cAAQ,KAAK,8CAA8C;AAC3D;AAAA,IACD;AAEA,QAAI;AACJ,QAAI;AACH,gBAAU,MAAM,KAAK,KAAK;AAAA,IAC3B,QAAQ;AAEP,gBAAU;AAAA,QACT,OAAO;AAAA,QACP,MAAM,MAAM,KAAK,KAAK;AAAA,MACvB;AAAA,IACD;AAGA,UAAM,sBAAsB;AAAA,MAC3B,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ,QAAQ;AAAA,MACtB,OAAO,QAAQ,SAAS;AAAA,MACxB,OAAO,QAAQ;AAAA,MACf,MAAM;AAAA,QACL,GAAG,QAAQ;AAAA,QACX,KAAK,QAAQ;AAAA,QACb,gBAAgB;AAAA,MACjB;AAAA,MACA,KAAK,QAAQ;AAAA,MACb,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ,UAAU;AAAA,MAC1B,SAAS,QAAQ;AAAA,IAClB;AAEA,UAAM,UAAU,KAAK,aAAa,iBAAiB,QAAQ,OAAO,mBAAmB,CAAC;AAAA,EACvF,CAAC;AAGD,OAAK,iBAAiB,qBAAqB,CAAC,UAAU;AACrD,UAAM,aAAa,MAAM;AAEzB,UAAM,OAAO,MAAM,aAAa;AAChC,UAAM,UAAU,MAAM;AACtB,UAAM,MAAM,MAAM;AAGlB,QAAI,uBAAuB,SAAS;AACnC,0BAAoB,OAAO;AAAA,IAC5B;AAGA,QAAI,MAAM,QAAQ;AAAA,IAClB;AAGA,QAAI,KAAK;AACR,YAAM;AAAA,QACL,KAAK,QAAQ,SAAS,EAAE,MAAM,UAAU,qBAAqB,KAAK,CAAC,EAAE,KAAK,CAAC,eAAe;AAEzF,qBAAW,UAAU,YAAY;AAChC,gBAAI,OAAO,QAAQ,OAAO,WAAW,QAAQ;AAC5C,qBAAO,OAAO,MAAM;AAAA,YACrB;AAAA,UACD;AAEA,iBAAO,KAAK,QAAQ,WAAW,GAAG;AAAA,QACnC,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD,CAAC;AAGD,OAAK,iBAAiB,qBAAqB,CAAC,UAAU;AACrD,UAAM,OAAO,MAAM,aAAa;AAChC,UAAM,UAAU,MAAM;AAEtB,QAAI,uBAAuB,SAAS;AACnC,0BAAoB,OAAO;AAAA,IAC5B;AAAA,EACD,CAAC;AAGD,OAAK,iBAAiB,YAAY,CAAC,UAAU;AAC5C,UAAM;AAAA;AAAA,MAEL,KAAK,QAAQ,MAAM;AAAA,IACpB;AAAA,EACD,CAAC;AACF;AAqBO,SAAS,0BAA0B,SAAkC,CAAC,GAAW;AACvF,QAAM,EAAE,cAAc,iBAAiB,eAAe,gBAAgB,IAAI;AAE1E,SAAO;AAAA;AAAA;AAAA;AAAA,wBAIgB,WAAW;AAAA,yBACV,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuDnC,KAAK;AACP;AAiBA,eAAsB,0BACrB,SAAS,UACmC;AAC5C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI,EAAE,mBAAmB,YAAY;AACpC,YAAQ,KAAK,wCAAwC;AACrD,WAAO;AAAA,EACR;AAEA,MAAI;AACH,UAAM,eAAe,MAAM,UAAU,cAAc,SAAS,MAAM;AAClE,WAAO;AAAA,EACR,SAAS,OAAO;AACf,YAAQ,MAAM,gDAAgD,KAAK;AACnE,WAAO;AAAA,EACR;AACD;;;AC1RA,SAAS,iBAAiB,QAAoC;AAC7D,MAAI,OAAO,SAAS,OAAW,QAAO,OAAO;AAC7C,MAAI,OAAO,WAAW,OAAW,QAAO,OAAO;AAC/C,SAAO,OAAO,WAAW,cAAc,IAAI;AAC5C;AAEA,SAAS,mBAAmB,QAAoC;AAC/D,MAAI,OAAO,WAAW,OAAW,QAAO,OAAO;AAC/C,MAAI,OAAO,YAAY,OAAW,QAAO,OAAO;AAChD,SAAO,OAAO,WAAW,WAAW,IAAI;AACzC;AA4BA,eAAsB,aACrB,QACA,cACgB;AAGhB,QAAM,QAAQ,QAAQ,2BAA2B;AAAA,IAChD,QAAQ;AAAA,IACR,MAAM,EAAE,aAAa;AAAA,EACtB,CAAC;AACF;AAUA,eAAsB,eAAe,QAAsB,UAAiC;AAC3F,QAAM,QAAQ,QAAQ,6BAA6B;AAAA,IAClD,QAAQ;AAAA,IACR,MAAM,EAAE,SAAS;AAAA,EAClB,CAAC;AACF;AAcA,eAAsB,SACrB,QACA,QACA,cAC0B;AAQ1B,QAAM,OAA8B;AAAA,IACnC;AAAA,IACA,OAAO,aAAa;AAAA,IACpB,MAAM,aAAa;AAAA,IACnB,GAAI,aAAa,SAAS,UAAa,EAAE,MAAM,aAAa,KAAK;AAAA,IACjE,GAAI,aAAa,QAAQ,UAAa,EAAE,KAAK,aAAa,IAAI;AAAA,EAC/D;AACA,QAAM,SAAS,MAAM,QAA4B,QAAQ,uBAAuB;AAAA,IAC/E,QAAQ;AAAA,IACR;AAAA,EACD,CAAC;AACD,QAAM,OAAO,iBAAiB,MAAM;AACpC,QAAM,SAAS,mBAAmB,MAAM;AACxC,SAAO;AAAA,IACN,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,UAAU;AAAA,IACzB,SAAS,OAAO,WAAW;AAAA,EAC5B;AACD;AAUA,eAAsB,mBACrB,QACqE;AACrE,SAAO,QAAQ,QAAQ,8BAA8B,EAAE,QAAQ,MAAM,CAAC;AACvE;AAaA,eAAsB,sBACrB,QACA,aACgB;AAChB,QAAM,QAAQ,QAAQ,8BAA8B;AAAA,IACnD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAoEO,IAAM,WAAW;AAAA,EACvB,MAAM,OACL,QACA,OACuB;AACvB,WAAO,QAAQ,QAAQ,2BAA2B,EAAE,QAAQ,QAAQ,MAAM,MAAM,CAAC;AAAA,EAClF;AAAA,EACA,MAAM,KAAK,QAA4D;AACtE,WAAO,QAAQ,QAAQ,2BAA2B,EAAE,QAAQ,MAAM,CAAC;AAAA,EACpE;AAAA,EACA,MAAM,IAAI,QAAsB,IAAkC;AACjE,WAAO,QAAQ,QAAQ,2BAA2B,EAAE,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1E;AAAA,EACA,MAAM,OAAO,QAAsB,IAA2B;AAC7D,UAAM,QAAQ,QAAQ,2BAA2B,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC5E;AACD;AAOO,IAAM,YAAY;AAAA,EACxB,MAAM,OACL,QACA,OAMwB;AACxB,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,QAAQ,QAAQ,MAAM,MAAM,CAAC;AAAA,EACnF;AAAA,EACA,MAAM,IAAI,QAAsB,IAAmC;AAClE,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC3E;AAAA,EACA,MAAM,SAAS,QAAsB,IAAY,aAA4C;AAC5F,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,aAAa;AAAA,MACjE,QAAQ;AAAA,MACR,MAAM,EAAE,YAAY;AAAA,IACrB,CAAC;AAAA,EACF;AAAA,EACA,MAAM,KAAK,QAAsB,IAAmC;AACnE,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,SAAS,EAAE,QAAQ,OAAO,CAAC;AAAA,EACjF;AAAA,EACA,MAAM,OAAO,QAAsB,IAAmC;AACrE,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,WAAW,EAAE,QAAQ,OAAO,CAAC;AAAA,EACnF;AAAA,EACA,MAAM,MAAM,QAAsB,IAAwC;AACzE,WAAO,QAAQ,QAAQ,4BAA4B,EAAE,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,EACjF;AACD;;;ACpNA,eAAe,cACd,QACA,SACmB;AACnB,SAAO,QAAiB,QAAQ,aAAa;AAAA,IAC5C,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAGA,eAAe,aAAa,QAAmD;AAC9E,SAAO,QAA4B,QAAQ,WAAW;AACvD;AAGA,eAAe,WAAW,QAAsB,WAAqC;AACpF,SAAO,QAAiB,QAAQ,aAAa,SAAS,EAAE;AACzD;AAGA,eAAe,cACd,QACA,WACA,SACmB;AACnB,SAAO,QAAiB,QAAQ,aAAa,SAAS,IAAI;AAAA,IACzD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAGA,eAAe,cACd,QACA,WACgC;AAChC,SAAO,QAA8B,QAAQ,aAAa,SAAS,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC5F;AAGA,eAAe,aAAa,QAAsB,WAAqC;AACtF,SAAO,QAAiB,QAAQ,aAAa,SAAS,UAAU,EAAE,QAAQ,OAAO,CAAC;AACnF;AAGA,eAAe,cAAc,QAAsB,WAAqC;AACvF,SAAO,QAAiB,QAAQ,aAAa,SAAS,WAAW,EAAE,QAAQ,OAAO,CAAC;AACpF;AAGA,eAAe,YACd,QACA,WACiD;AACjD,SAAO,QAA+C,QAAQ,aAAa,SAAS,SAAS;AAAA,IAC5F,QAAQ;AAAA,EACT,CAAC;AACF;AAWA,eAAe,aACd,QACA,WACA,SAC8B;AAC9B,SAAO,QAA4B,QAAQ,oBAAoB;AAAA,IAC9D,QAAQ;AAAA,IACR,MAAM,EAAE,WAAW,SAAS,WAAW,CAAC,EAAE;AAAA,EAC3C,CAAC;AACF;AAMO,IAAM,iBAAiB;AAAA,EAC7B,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN;AACD;;;AC5MA,SAAS,YAAY,uBAAuB;AAWrC,IAAM,qBAAN,MAAyB;AAAA,EAG/B,YACiB,UACA,QACf;AAFe;AACA;AAAA,EACd;AAAA,EALM,wBAAwB;AAMlC;AAMO,IAAM,kBAAN,MAAsB;AAAA,EAG5B,YACiB,UACA,UACf;AAFe;AACA;AAAA,EACd;AAAA,EALM,qBAAqB;AAM/B;AAQO,IAAM,sBAAN,MAA0B;AAAA,EAGhC,YACiB,UACA,WACA,UAKZ,CAAC,GACJ;AARe;AACA;AACA;AAAA,EAMd;AAAA,EAXM,yBAAyB;AAYnC;AAYO,SAAS,kBACf,gBACA,eACoB;AACpB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAON,MAAM,IAAO,MAAc,IAAsC;AAChE,UAAI,eAAe,IAAI,IAAI,GAAG;AAC7B,eAAO,eAAe,IAAI,IAAI;AAAA,MAC/B;AAEA,YAAM,SAAS,MAAM,GAAG;AACxB,YAAM,IAAI,mBAAmB,MAAM,MAAM;AAAA,IAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,MAAM,MAAM,MAAc,UAAiC;AAC1D,UAAI,cAAc,IAAI,IAAI,GAAG;AAC5B;AAAA,MACD;AAEA,YAAM,IAAI,gBAAgB,MAAM,QAAQ;AAAA,IACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAsBA,MAAM,aACL,MACA,WACA,UAAkE,CAAC,GAC/C;AACpB,UAAI,cAAc,IAAI,IAAI,GAAG;AAE5B,eAAQ,cAAc,IAAI,IAAI,KAAkB;AAAA,MACjD;AAEA,YAAM,IAAI,oBAAoB,MAAM,WAAW,OAAO;AAAA,IACvD;AAAA,EACD;AACD;AAUO,SAAS,gBAAgB,MAAc,WAAmB,QAAyB;AACzF,MAAI;AACH,UAAM,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK;AAC3E,UAAM,aAAa,UAAU,QAAQ,YAAY,EAAE;AAEnD,UAAM,WAAW,OAAO,KAAK,MAAM,KAAK;AACxC,UAAM,WAAW,OAAO,KAAK,YAAY,KAAK;AAE9C,QAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAChD,WAAO,gBAAgB,UAAU,QAAQ;AAAA,EAC1C,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAqDO,SAAS,mBACf,UACA,UAA+B,CAAC,GACX;AACrB,QAAM,UAAU,oBAAI,IAAkC;AACtD,aAAW,OAAO,UAAU;AAC3B,YAAQ,IAAI,IAAI,MAAM,GAAG;AAAA,EAC1B;AAGA,QAAM,MAAM,OAAO,SAAqC;AACvD,WAAO,SAAS,KAAK;AAAA,MACpB,IAAI;AAAA,MACJ,OAAO,MAAM,KAAK,QAAQ,KAAK,CAAC;AAAA,IACjC,CAAC;AAAA,EACF;AAGA,QAAM,OAAO,OAAO,QAAoC;AAIvD,QAAI;AACJ,QAAI;AACH,gBAAU,MAAM,IAAI,KAAK;AAAA,IAC1B,QAAQ;AACP,aAAO,SAAS;AAAA,QACf,EAAE,QAAQ,SAAS,SAAS,8BAA8B;AAAA,QAC1D,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAKA,UAAM,gBAAgB,QAAQ,iBAAiB,QAAQ,IAAI,yBAAyB;AAEpF,QAAI,eAAe;AAClB,YAAM,YAAY,IAAI,QAAQ,IAAI,oBAAoB,KAAK;AAC3D,UAAI,CAAC,WAAW;AACf,eAAO,SAAS;AAAA,UACf,EAAE,QAAQ,SAAS,SAAS,oCAAoC;AAAA,UAChE,EAAE,QAAQ,IAAI;AAAA,QACf;AAAA,MACD;AAEA,UAAI,CAAC,gBAAgB,SAAS,WAAW,aAAa,GAAG;AACxD,eAAO,SAAS,KAAK,EAAE,QAAQ,SAAS,SAAS,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MACxF;AAAA,IACD;AAKA,QAAI;AACJ,QAAI;AACH,iBAAW,KAAK,MAAM,OAAO;AAAA,IAC9B,QAAQ;AACP,aAAO,SAAS;AAAA,QACf,EAAE,QAAQ,SAAS,SAAS,iCAAiC;AAAA,QAC7D,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM,EAAE,UAAU,SAAS,QAAQ,IAAI;AAEvC,QAAI,CAAC,YAAY,OAAO,aAAa,UAAU;AAC9C,aAAO,SAAS;AAAA,QACf,EAAE,QAAQ,SAAS,SAAS,sCAAsC;AAAA,QAClE,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAKA,UAAM,UAAU,QAAQ,IAAI,QAAQ;AACpC,QAAI,CAAC,SAAS;AACb,aAAO,SAAS;AAAA,QACf;AAAA,UACC,QAAQ;AAAA,UACR,SAAS,SAAS,QAAQ,kCAAkC,MAAM,KAAK,QAAQ,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,QAClG;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAKA,UAAM,iBAAiB,oBAAI,IAAqB;AAChD,eAAW,QAAQ,SAAS,SAAS,CAAC,GAAG;AACxC,qBAAe,IAAI,KAAK,MAAM,KAAK,MAAM;AAAA,IAC1C;AAGA,UAAM,gBAAgB,oBAAI,IAAqB;AAC/C,eAAW,QAAQ,SAAS,SAAS,CAAC,GAAG;AACxC,oBAAc,IAAI,KAAK,MAAM,KAAK,UAAU,MAAS;AAAA,IACtD;AAEA,UAAM,UAAU,kBAAkB,gBAAgB,aAAa;AAK/D,QAAI;AACH,YAAM,SAAS,MAAM,QAAQ,QAAQ,SAAS,EAAE,MAAM,QAAQ,CAAC;AAG/D,aAAO,SAAS,KAAK,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,IACpD,SAAS,KAAc;AAItB,UAAI,eAAe,sBAAuB,KAA4B,uBAAuB;AAC5F,cAAM,SAAS;AACf,eAAO,SAAS,KAAK;AAAA,UACpB,QAAQ;AAAA,UACR,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,QAChB,CAAC;AAAA,MACF;AAKA,UAAI,eAAe,mBAAoB,KAAyB,oBAAoB;AACnF,cAAM,SAAS;AACf,eAAO,SAAS,KAAK;AAAA,UACpB,QAAQ;AAAA,UACR,UAAU,OAAO;AAAA,UACjB,UAAU,OAAO;AAAA,QAClB,CAAC;AAAA,MACF;AAKA,UACC,eAAe,uBACd,KAA6B,wBAC7B;AACD,cAAM,SAAS;AACf,eAAO,SAAS,KAAK;AAAA,UACpB,QAAQ;AAAA,UACR,UAAU,OAAO;AAAA,UACjB,WAAW,OAAO;AAAA,UAClB,SAAS,OAAO,QAAQ,WAAW;AAAA,UACnC,QAAQ,OAAO,QAAQ,UAAU;AAAA,QAClC,CAAC;AAAA,MACF;AAKA,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,MAAM,wBAAwB,QAAQ,qBAAqB,GAAG;AACtE,aAAO,SAAS;AAAA,QACf;AAAA,UACC,QAAQ;AAAA,UACR;AAAA,UACA,WAAW;AAAA,QACZ;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAAA,EACD;AAEA,SAAO,EAAE,KAAK,KAAK;AACpB;;;AClTA,eAAsB,aAAa,QAAsB,OAAuC;AAC/F,SAAO,QAAoB,QAAQ,mBAAmB;AAAA,IACrD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAKA,eAAsB,QAAQ,QAAsB,QAAqC;AACxF,SAAO,QAAoB,QAAQ,UAAU,MAAM,IAAI,EAAE,QAAQ,MAAM,CAAC;AACzE;AAKA,eAAsB,WAAW,QAAsB,QAAkC;AACxF,QAAM,SAAS,MAAM,QAA8B,QAAQ,UAAU,MAAM,WAAW;AAAA,IACrF,QAAQ;AAAA,EACT,CAAC;AACD,SAAO,OAAO;AACf;AAKA,eAAsB,UACrB,QACA,SACkD;AAClD,SAAO,QAAQ,QAAQ,UAAU;AAAA,IAChC,QAAQ;AAAA,IACR,OAAO;AAAA,EACR,CAAC;AACF;AAKA,eAAsB,WAAW,QAAsB,OAAyC;AAC/F,SAAO,QAAsB,QAAQ,eAAe;AAAA,IACnD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAKA,eAAsB,UAAU,QAAsB,YAAsC;AAC3F,QAAM,SAAS,MAAM,QAA8B,QAAQ,eAAe,UAAU,UAAU;AAAA,IAC7F,QAAQ;AAAA,EACT,CAAC;AACD,SAAO,OAAO;AACf;AAKA,eAAsB,WAAW,QAAsB,YAAsC;AAC5F,QAAM,SAAS,MAAM,QAA8B,QAAQ,eAAe,UAAU,WAAW;AAAA,IAC9F,QAAQ;AAAA,EACT,CAAC;AACD,SAAO,OAAO;AACf;AAKA,eAAsB,WAAW,QAAsB,YAAsC;AAC5F,QAAM,SAAS,MAAM,QAA8B,QAAQ,eAAe,UAAU,IAAI;AAAA,IACvF,QAAQ;AAAA,EACT,CAAC;AACD,SAAO,OAAO;AACf;;;AC9JA,SAAS,sBAAsB;AAwD/B,eAAsB,UACrB,QACA,SACA,SACsB;AAEtB,QAAM,WAAW,eAAe;AAChC,QAAM,OAAO;AAAA,IACZ,SAAS;AAAA,MACR,QAAQ,SAAS;AAAA,MACjB,aAAa,SAAS;AAAA,MACtB,YAAY,SAAS;AAAA,IACtB;AAAA,IACA,MAAM,CAAC,OAAO;AAAA,EACf;AACA,QAAM,WAAW,MAAM,QAA+B,QAAQ,SAAS,MAAM;AAAA,IAC5E,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AAGD,SACC,SAAS,KAAK,OAAO,KAAK;AAAA,IACzB,KAAK;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AAEF;AAmBA,eAAsB,SACrB,QACA,UACA,SACsC;AAEtC,QAAM,WAAW,eAAe;AAChC,QAAM,OAAO;AAAA,IACZ,SAAS;AAAA,MACR,QAAQ,SAAS;AAAA,MACjB,aAAa,SAAS;AAAA,MACtB,YAAY,SAAS;AAAA,IACtB;AAAA,IACA,MAAM;AAAA,EACP;AACA,QAAM,WAAW,MAAM,QAA+B,QAAQ,SAAS,MAAM;AAAA,IAC5E,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AAED,SAAO,SAAS;AACjB;AAmBA,eAAsB,YACrB,QACA,SACsC;AAEtC,QAAM,WAAW,eAAe;AAChC,QAAM,OAAO;AAAA,IACZ,SAAS;AAAA,MACR,QAAQ,SAAS;AAAA,MACjB,aAAa,SAAS;AAAA,MACtB,YAAY,SAAS;AAAA,IACtB;AAAA;AAAA,EAED;AACA,QAAM,WAAW,MAAM,QAA+B,QAAQ,SAAS,MAAM;AAAA,IAC5E,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AAED,SAAO,SAAS;AACjB;AAYA,eAAsB,UACrB,QACA,SACA,SACmB;AACnB,QAAM,OAAO,MAAM,UAAU,QAAQ,SAAS,OAAO;AACrD,SAAO,KAAK;AACb;AAwBA,eAAsB,WACrB,QACA,SACA,SAC8B;AAC9B,QAAM,OAAO,MAAM,UAAU,QAAQ,SAAS,OAAO;AACrD,SAAO,KAAK;AACb;AAcA,eAAsB,eACrB,QACA,SACA,SACyB;AACzB,QAAM,OAAO,MAAM,UAAU,QAAQ,SAAS,OAAO;AACrD,SAAO,KAAK;AACb;;;ACvIA,eAAsB,iBAAiB,QAA8C;AACpF,SAAO,QAAQ,QAAQ,oBAAoB,EAAE,QAAQ,MAAM,CAAC;AAC7D;AAaA,eAAsB,oBACrB,QACA,MACgB;AAChB,SAAO,QAAQ,QAAQ,oBAAoB,EAAE,QAAQ,OAAO,MAAM,KAAK,CAAC;AACzE;AAaA,eAAsB,qBACrB,QACA,SACmC;AACnC,SAAO,QAAQ,QAAQ,wBAAwB;AAAA,IAC9C,QAAQ;AAAA,IACR,OAAO;AAAA,EACR,CAAC;AACF;AAWA,eAAsB,mBACrB,QACA,YAC2B;AAC3B,SAAO,QAAQ,QAAQ,wBAAwB,UAAU,IAAI;AAAA,IAC5D,QAAQ;AAAA,EACT,CAAC;AACF;AAUA,eAAsB,sBACrB,QACA,YACgB;AAChB,SAAO,QAAQ,QAAQ,wBAAwB,UAAU,WAAW;AAAA,IACnE,QAAQ;AAAA,EACT,CAAC;AACF;AAWA,eAAsB,gBACrB,QACA,eACwB;AACxB,SAAO,QAAQ,QAAQ,mBAAmB;AAAA,IACzC,QAAQ;AAAA,IACR,OAAO,gBAAgB,EAAE,cAAc,IAAI;AAAA,EAC5C,CAAC;AACF;;;AC7LA,SAAS,sBAAsB;AAyK/B,eAAsB,kBAAkB,QAAwC;AAE/E,QAAM,WAAW,eAAe;AAChC,SAAO,QAAQ,QAAQ,SAAS,MAAM,EAAE,QAAQ,SAAS,OAAO,CAAC;AAClE;AAeA,eAAsB,UACrB,QACA,SACsB;AAItB,QAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,QAAM,WAAW,eAAe;AAChC,SAAO,QAAiC,QAAQ,SAAS,MAAM;AAAA,IAC9D,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,EACD,CAAC;AACF;AAeA,eAAsB,mBACrB,QACA,SACsB;AAEtB,QAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,QAAM,WAAW,eAAe;AAChC,SAAO,QAAiC,QAAQ,SAAS,MAAM;AAAA,IAC9D,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,EACD,CAAC;AACF;AAeA,eAAsB,gBACrB,QACA,SACsB;AAEtB,QAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,QAAM,WAAW,eAAe;AAChC,SAAO,QAAiC,QAAQ,SAAS,MAAM;AAAA,IAC9D,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,EACD,CAAC;AACF;AAeA,eAAsB,cACrB,QACA,SAC0B;AAC1B,SAAO,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,QAAQ,MAAM,QAAQ,CAAC;AAC5E;AAaA,eAAsB,oBACrB,QACA,SACiC;AACjC,SAAO,QAAQ,QAAQ,oBAAoB;AAAA,IAC1C,QAAQ;AAAA,IACR,OAAO;AAAA,EACR,CAAC;AACF;AAWA,eAAsB,kBACrB,QACA,SAC0B;AAC1B,SAAO,QAAQ,QAAQ,oBAAoB,OAAO,IAAI,EAAE,QAAQ,MAAM,CAAC;AACxE;AAUA,eAAsB,qBAAqB,QAAsB,SAAgC;AAChG,SAAO,QAAQ,QAAQ,oBAAoB,OAAO,WAAW;AAAA,IAC5D,QAAQ;AAAA,EACT,CAAC;AACF;AAUA,eAAsB,gBACrB,QACA,SACA,cAC0B;AAC1B,SAAO,QAAQ,QAAQ,oBAAoB,OAAO,eAAe;AAAA,IAChE,QAAQ;AAAA,IACR,MAAM,EAAE,aAAa;AAAA,EACtB,CAAC;AACF;AAWA,eAAsB,uBAAuB,QAAoD;AAChG,SAAO,QAAQ,QAAQ,0BAA0B,EAAE,QAAQ,MAAM,CAAC;AACnE;;;ACzMA,eAAsB,gBAAgB,QAA8C;AACnF,SAAO,QAAQ,QAAQ,kBAAkB,EAAE,QAAQ,MAAM,CAAC;AAC3D;AAmCA,eAAsB,WACrB,QACA,aACA,OACA,UACmB;AACnB,SAAO,QAAQ,QAAQ,kBAAkB;AAAA,IACxC,QAAQ;AAAA,IACR,MAAM,EAAE,aAAa,GAAG,OAAO,SAAS;AAAA,EACzC,CAAC;AACF;AAcA,eAAsB,gBACrB,QACA,OACyB;AACzB,SAAO,QAAQ,QAAQ,iBAAiB;AAAA,IACvC,QAAQ;AAAA,IACR,OAAO;AAAA,EACR,CAAC;AACF;AAgBA,eAAsB,YAAY,QAAsB,OAAwC;AAC/F,SAAO,QAAQ,QAAQ,gBAAgB,EAAE,QAAQ,QAAQ,MAAM,MAAM,CAAC;AACvE;AAUA,eAAsB,kBACrB,QACA,OACgB;AAChB,SAAO,QAAQ,QAAQ,uBAAuB;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAUA,eAAsB,wBACrB,QACA,OACgB;AAChB,SAAO,QAAQ,QAAQ,6BAA6B;AAAA,IACnD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAeA,eAAsB,sBACrB,QACA,OACgB;AAChB,SAAO,QAAQ,QAAQ,2BAA2B;AAAA,IACjD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AA0BA,eAAsB,kBACrB,QACA,OACgC;AAChC,SAAO,QAAQ,QAAQ,oBAAoB;AAAA,IAC1C,QAAQ;AAAA,IACR,OAAO;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,aAAa,MAAM;AAAA,MACnB,OAAO,MAAM,OAAO,SAAS;AAAA,MAC7B,QAAQ,MAAM;AAAA,IACf;AAAA,EACD,CAAC;AACF;;;AC7OA,eAAsB,kBACrB,QACA,QACwB;AACxB,SAAO,QAAQ,QAAQ,mBAAmB;AAAA,IACzC,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;AAWA,eAAsB,iBACrB,QACA,QACyB;AACzB,SAAO,QAAQ,QAAQ,oBAAoB;AAAA,IAC1C,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;AAkCA,eAAsB,mBACrB,QACA,OACA,UACwB;AACxB,SAAO,QAAQ,QAAQ,qBAAqB;AAAA,IAC3C,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,OAAO,SAAS;AAAA,EAC5B,CAAC;AACF;AAcA,eAAsB,uBACrB,QACA,QACA,SAC6B;AAC7B,SAAO,QAAQ,QAAQ,0BAA0B;AAAA,IAChD,QAAQ;AAAA,IACR,OAAO,EAAE,QAAQ,GAAG,QAAQ;AAAA,EAC7B,CAAC;AACF;AAaA,eAAsB,uBACrB,QACA,QACwB;AACxB,SAAO,QAAQ,QAAQ,8BAA8B;AAAA,IACpD,QAAQ;AAAA,IACR,MAAM,EAAE,OAAO;AAAA,EAChB,CAAC;AACF;;;ACwEO,IAAM,0BAA0B;AAAA,EACtC,QAAQ,EAAE,OAAO,WAAW,QAAQ,GAAG;AAAA,EACvC,QAAQ,EAAE,OAAO,WAAW,QAAQ,GAAG;AAAA,EACvC,MAAM,EAAE,OAAO,WAAW,QAAQ,GAAG;AAAA,EACrC,UAAU,EAAE,OAAO,WAAW,QAAQ,IAAI;AAAA,EAC1C,SAAS,EAAE,OAAO,WAAW,QAAQ,IAAI;AAC1C;;;AChOA,eAAsB,UACrB,QACA,UACA,QACuB;AACvB,SAAO,QAAQ,QAAQ,2BAA2B;AAAA,IACjD,QAAQ;AAAA,IACR,OAAO,EAAE,UAAU,OAAO;AAAA,EAC3B,CAAC;AACF;AAaA,eAAsB,cAAc,QAAsB,QAAwC;AACjG,SAAO,QAAQ,QAAQ,uBAAuB;AAAA,IAC7C,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;AA+BA,eAAsB,qBACrB,QACA,OACA,QACA,UACgC;AAChC,QAAM,EAAE,gBAAgB,GAAG,UAAU,IAAI;AACzC,SAAO,QAAQ,QAAQ,8BAA8B;AAAA,IACpD,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,WAAW,QAAQ,SAAS;AAAA,IACvC;AAAA,EACD,CAAC;AACF;AAaA,eAAsB,cACrB,QACA,UACA,QACqD;AACrD,SAAO,QAAQ,QAAQ,+BAA+B;AAAA,IACrD,QAAQ;AAAA,IACR,MAAM,EAAE,UAAU,OAAO;AAAA,EAC1B,CAAC;AACF;AAiCA,eAAsB,eACrB,QACA,eACA,QACA,SAC6B;AAC7B,SAAO,QAAQ,QAAQ,gCAAgC;AAAA,IACtD,QAAQ;AAAA,IACR,OAAO,EAAE,eAAe,QAAQ,UAAU,QAAW,GAAG,QAAQ;AAAA,EAIjE,CAAC;AACF;AAkCA,eAAsB,YACrB,QACA,OACA,QACA,UAC6B;AAC7B,SAAO,QAAQ,QAAQ,mCAAmC;AAAA,IACzD,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,OAAO,QAAQ,SAAS;AAAA,EACpC,CAAC;AACF;AAaA,eAAsB,uBACrB,QACA,eACA,QACkD;AAClD,SAAO,QAAQ,QAAQ,iCAAiC;AAAA,IACvD,QAAQ;AAAA,IACR,OAAO,EAAE,eAAe,OAAO;AAAA,EAChC,CAAC;AACF;AA4BA,eAAsB,gBACrB,QACA,QAC6B;AAC7B,SAAO,QAAQ,QAAQ,4BAA4B;AAAA,IAClD,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;AAaA,eAAsB,eACrB,QACA,eACA,QACkC;AAClC,SAAO,QAAQ,QAAQ,gCAAgC;AAAA,IACtD,QAAQ;AAAA,IACR,OAAO,EAAE,eAAe,OAAO;AAAA,EAChC,CAAC;AACF;AA0BA,eAAsB,kBACrB,QACA,eACA,QACA,UACkC;AAClC,SAAO,QAAQ,QAAQ,mCAAmC;AAAA,IACzD,QAAQ;AAAA,IACR,MAAM,EAAE,eAAe,QAAQ,SAAS;AAAA,EACzC,CAAC;AACF;AAgCA,eAAsB,6BACrB,QACA,eACA,QACA,QACA,UAC2B;AAC3B,SAAO,QAAQ,QAAQ,qCAAqC;AAAA,IAC3D,QAAQ;AAAA,IACR,MAAM,EAAE,eAAe,QAAQ,QAAQ,SAAS;AAAA,EACjD,CAAC;AACF;AAYA,eAAsB,qBACrB,QACA,QACqE;AACrE,SAAO,QAAQ,QAAQ,mCAAmC;AAAA,IACzD,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO;AAAA,EACjB,CAAC;AACF;;;ACzbA;AAAA,EASC;AAAA,OAGM;AAkDP,eAAsB,iBAAiB,QAA0D;AAChG,QAAM,WAAW,uBAAuB;AACxC,SAAO,QAAmC,QAAQ,SAAS,MAAM;AAAA,IAChE,QAAQ,SAAS;AAAA,EAClB,CAAC;AACF;AAaA,eAAsB,kBACrB,QACA,UAA+C,CAAC,GACb;AACnC,QAAM,WAAW,uBAAuB;AACxC,SAAO,QAAiC,QAAQ,SAAS,MAAM;AAAA,IAC9D,QAAQ,SAAS;AAAA,IACjB,OAAO;AAAA,EACR,CAAC;AACF;AAUA,eAAsB,gBACrB,QACA,aAIE;AACF,SAAO,QAGJ,QAAQ,SAAS,WAAW,EAAE;AAClC;AAaA,eAAsB,mBACrB,QACA,OAC0C;AAC1C,QAAM,WAAW,uBAAuB;AACxC,QAAM,eAAe,MAAM,QAAsB,QAAQ,SAAS,MAAM;AAAA,IACvE,QAAQ,SAAS;AAAA,IACjB,MAAM;AAAA,EACP,CAAC;AACD,SAAO,EAAE,aAAa;AACvB;AAYA,eAAsB,mBACrB,QACA,aACA,OAC0C;AAC1C,SAAO,QAAwC,QAAQ,SAAS,WAAW,IAAI;AAAA,IAC9E,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAYA,eAAsB,mBACrB,QACA,aACgC;AAChC,SAAO,QAA8B,QAAQ,SAAS,WAAW,IAAI;AAAA,IACpE,QAAQ;AAAA,EACT,CAAC;AACF;AAcA,eAAsB,uBACrB,QACA,aAC6C;AAC7C,SAAO,QAA2C,QAAQ,SAAS,WAAW,UAAU;AACzF;AAeA,eAAsB,yBACrB,QACA,aACA,OACkD;AAClD,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW;AAAA,IACpB;AAAA,MACC,QAAQ;AAAA,MACR,MAAM;AAAA,IACP;AAAA,EACD;AACD;AAYA,eAAsB,6BACrB,QACA,aACA,UACA,MAC0C;AAC1C,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW,YAAY,QAAQ;AAAA,IACxC;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,EAAE,KAAK;AAAA,IACd;AAAA,EACD;AACD;AAYA,eAAsB,yBACrB,QACA,aACA,UACgC;AAChC,SAAO,QAA8B,QAAQ,SAAS,WAAW,YAAY,QAAQ,IAAI;AAAA,IACxF,QAAQ;AAAA,EACT,CAAC;AACF;AAUA,eAAsB,kBACrB,QACA,aACgC;AAChC,SAAO,QAA8B,QAAQ,SAAS,WAAW,UAAU;AAAA,IAC1E,QAAQ;AAAA,EACT,CAAC;AACF;AAgBA,eAAsB,2BACrB,QACA,aACqD;AACrD,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW;AAAA,EACrB;AACD;AAUA,eAAsB,6BACrB,QACA,OAC0C;AAC1C,SAAO,QAAwC,QAAQ,4BAA4B;AAAA,IAClF,QAAQ;AAAA,IACR,MAAM,EAAE,MAAM;AAAA,EACf,CAAC;AACF;AAYA,eAAsB,6BACrB,QACA,aACA,cACgC;AAChC,SAAO,QAA8B,QAAQ,SAAS,WAAW,gBAAgB,YAAY,IAAI;AAAA,IAChG,QAAQ;AAAA,EACT,CAAC;AACF;AASO,SAAS,QAAQ,YAA2C,aAA+B;AACjG,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,gBAA2B;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAEA,QAAM,gBAAgB,cAAc,QAAQ,WAAW,IAAI;AAC3D,QAAM,oBAAoB,cAAc,QAAQ,WAAW;AAE3D,SAAO,iBAAiB;AACzB;AAKO,SAAS,iBAAiB,YAAoD;AACpF,SAAO,QAAQ,YAAY,OAAO;AACnC;AAKO,SAAS,kBAAkB,YAAoD;AACrF,SAAO,QAAQ,YAAY,OAAO;AACnC;AAKO,SAAS,sBAAsB,YAAoD;AACzF,SAAO,QAAQ,YAAY,aAAa;AACzC;;;ACrUA,eAAsB,gBACrB,QACyC;AACzC,SAAO,QAAuC,QAAQ,cAAc;AACrE;AAkBA,eAAsB,iBACrB,QACA,OACsC;AACtC,SAAO,QAAoC,QAAQ,gBAAgB;AAAA,IAClE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAcA,eAAsB,iBACrB,QACA,eACgC;AAChC,SAAO,QAA8B,QAAQ,gBAAgB,aAAa,IAAI;AAAA,IAC7E,QAAQ;AAAA,EACT,CAAC;AACF;AAsBA,eAAsB,qBACrB,QACA,aACA,UACmC;AACnC,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW,YAAY,QAAQ;AAAA,EACzC;AACD;AAmBO,SAAS,cAAc,aAAuB,UAA2B;AAC/E,SAAO,YAAY,SAAS,QAAQ;AACrC;AAeO,SAAS,iBAAiB,aAAuB,UAA6B;AACpF,SAAO,SAAS,KAAK,CAAC,SAAS,YAAY,SAAS,IAAI,CAAC;AAC1D;AAeO,SAAS,kBAAkB,aAAuB,UAA6B;AACrF,SAAO,SAAS,MAAM,CAAC,SAAS,YAAY,SAAS,IAAI,CAAC;AAC3D;;;ACnHA,eAAsB,UAAU,QAAkD;AACjF,SAAO,QAA2B,QAAQ,QAAQ;AACnD;AAcA,eAAsB,QAAQ,QAAsB,SAA0C;AAC7F,SAAO,QAAwB,QAAQ,UAAU,OAAO,EAAE;AAC3D;AAmBA,eAAsB,WACrB,QACA,OAC0B;AAE1B,QAAM,OAAgC;AAAA,IACrC,KAAK,MAAM;AAAA,IACX,MAAM,MAAM;AAAA,EACb;AACA,MAAI,MAAM,gBAAgB,OAAW,MAAK,cAAc,MAAM;AAC9D,MAAI,MAAM,gBAAgB,OAAW,MAAK,iBAAiB,MAAM;AACjE,MAAI,MAAM,cAAc,OAAW,MAAK,YAAY,MAAM;AAC1D,MAAI,MAAM,cAAc,OAAW,MAAK,YAAY,MAAM;AAE1D,SAAO,QAAwB,QAAQ,UAAU;AAAA,IAChD,QAAQ;AAAA,IACR;AAAA,EACD,CAAC;AACF;AAiBA,eAAsB,WACrB,QACA,SACA,OAC0B;AAE1B,QAAM,OAAgC,CAAC;AACvC,MAAI,MAAM,SAAS,OAAW,MAAK,OAAO,MAAM;AAChD,MAAI,MAAM,gBAAgB,OAAW,MAAK,cAAc,MAAM;AAC9D,MAAI,MAAM,gBAAgB,OAAW,MAAK,iBAAiB,MAAM;AACjE,MAAI,MAAM,cAAc,OAAW,MAAK,YAAY,MAAM;AAC1D,MAAI,MAAM,cAAc,OAAW,MAAK,YAAY,MAAM;AAE1D,SAAO,QAAwB,QAAQ,UAAU,OAAO,IAAI;AAAA,IAC3D,QAAQ;AAAA,IACR;AAAA,EACD,CAAC;AACF;AAcA,eAAsB,WACrB,QACA,SACgC;AAChC,SAAO,QAA8B,QAAQ,UAAU,OAAO,IAAI;AAAA,IACjE,QAAQ;AAAA,EACT,CAAC;AACF;AAiBA,eAAsB,iBACrB,QACA,aACA,UACA,SACgC;AAChC,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW,YAAY,QAAQ;AAAA,IACxC;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,EAAE,QAAQ;AAAA,IACjB;AAAA,EACD;AACD;;;ACpJA,eAAsB,UACrB,QACA,OAC2B;AAC3B,SAAO,QAAyB,QAAQ,gBAAgB;AAAA,IACvD,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,KAAK,MAAM;AAAA,MACX,eAAe,MAAM;AAAA,IACtB;AAAA,EACD,CAAC;AACF;AAqBA,eAAsB,WACrB,QACA,OAC4B;AAC5B,SAAO,QAA0B,QAAQ,oBAAoB;AAAA,IAC5D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,eAAe,MAAM;AAAA,IACtB;AAAA,EACD,CAAC;AACF;AAkBA,eAAsB,eACrB,QACA,QAA6B,CAAC,GACH;AAC3B,SAAO,QAAyB,QAAQ,qBAAqB;AAAA,IAC5D,QAAQ;AAAA,IACR,OAAO,MAAM,gBAAgB,EAAE,eAAe,MAAM,cAAc,IAAI;AAAA,EACvE,CAAC;AACF;AAgBA,eAAsB,UAAU,QAAsB,KAA+B;AACpF,MAAI;AACH,UAAM,OAAO,MAAM,eAAe,MAAM;AACxC,WAAO,KAAK,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AAAA,EACtC,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAkBA,eAAsB,cACrB,QACA,eAC4B;AAC5B,QAAM,OAAO,MAAM,eAAe,QAAQ,EAAE,cAAc,CAAC;AAC3D,MAAI,KAAK,WAAW,GAAG;AACtB,WAAO,CAAC;AAAA,EACT;AACA,SAAO,WAAW,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,cAAc,CAAC;AAC1E;;;ACqDA,eAAsB,cACrB,QACA,OAC+B;AAC/B,SAAO,QAA6B,QAAQ,iBAAiB;AAAA,IAC5D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,WAAW,MAAM,aAAa;AAAA,MAC9B,YAAY,MAAM;AAAA,MAClB,KAAK,MAAM;AAAA,MACX,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,UAAU,MAAM,YAAY;AAAA,MAC5B,eAAe,MAAM,iBAAiB;AAAA,MACtC,gBAAgB,MAAM,kBAAkB;AAAA,IACzC;AAAA,EACD,CAAC;AACF;AA0BA,eAAsB,WACrB,QACA,OAC4B;AAC5B,SAAO,QAA0B,QAAQ,sBAAsB;AAAA,IAC9D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM,aAAa;AAAA,MAC9B,UAAU,MAAM,YAAY;AAAA,MAC5B,eAAe,MAAM,iBAAiB;AAAA,MACtC,gBAAgB,MAAM,kBAAkB;AAAA,IACzC;AAAA,EACD,CAAC;AACF;AA8BA,eAAsB,OAAO,QAAsB,OAA6C;AAC/F,SAAO,QAAwB,QAAQ,kBAAkB;AAAA,IACxD,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,OAAO,MAAM;AAAA,MACb,WAAW,MAAM,aAAa;AAAA,MAC9B,YAAY,MAAM,cAAc;AAAA,MAChC,OAAO,MAAM,SAAS;AAAA,MACtB,QAAQ,MAAM,UAAU;AAAA,MACxB,eAAe,MAAM;AAAA,MACrB,eAAe,MAAM,iBAAiB;AAAA,MACtC,UAAU,MAAM,YAAY;AAAA,MAC5B,SAAS,MAAM;AAAA,MACf,WAAW,MAAM,aAAa;AAAA,MAC9B,gBAAgB,MAAM,kBAAkB;AAAA,MACxC,YAAY,MAAM,cAAc;AAAA,MAChC,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM;AAAA,IACf;AAAA,EACD,CAAC;AACF;AAuBA,eAAsB,UACrB,QACA,QAAwB,CAAC,GACC;AAC1B,SAAO,QAAwB,QAAQ,qBAAqB;AAAA,IAC3D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,WAAW,MAAM,aAAa;AAAA,MAC9B,QAAQ,MAAM,UAAU,CAAC,YAAY,MAAM;AAAA,MAC3C,SAAS,MAAM;AAAA,IAChB;AAAA,EACD,CAAC;AACF;AAqBA,eAAsB,eACrB,QACA,OAC+B;AAC/B,SAAO,QAA6B,QAAQ,kBAAkB;AAAA,IAC7D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,IAAI,MAAM;AAAA,MACV,YAAY,MAAM;AAAA,MAClB,WAAW,MAAM,aAAa;AAAA,IAC/B;AAAA,EACD,CAAC;AACF;AAuBA,eAAsB,eACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,kBAAkB;AAAA,IAC9D,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,WAAW,MAAM,aAAa;AAAA,MAC9B,YAAY,MAAM;AAAA,MAClB,KAAK,MAAM;AAAA,MACX,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,UAAU,MAAM,YAAY;AAAA,MAC5B,eAAe,MAAM,iBAAiB;AAAA,MACtC,gBAAgB,MAAM,kBAAkB;AAAA,IACzC;AAAA,EACD,CAAC;AACF;AAgBA,eAAsB,eACrB,QACA,WAC6B;AAC7B,SAAO,QAA2B,QAAQ,oBAAoB;AAAA,IAC7D,QAAQ;AAAA,IACR,MAAM,EAAE,UAAU;AAAA,EACnB,CAAC;AACF;AAoBA,eAAsB,WACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,sBAAsB;AAAA,IAClE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;;;ACndA,eAAsB,4BACrB,QACkC;AAClC,SAAO,QAAgC,QAAQ,mCAAmC;AAAA,IACjF,QAAQ;AAAA,EACT,CAAC;AACF;AAgBA,eAAsB,kBAAkB,QAAmD;AAC1F,SAAO,QAA4B,QAAQ,wBAAwB;AAAA,IAClE,QAAQ;AAAA,EACT,CAAC;AACF;;;ACnEA,SAAS,mBAAmB;AAY5B,SAASC,iBAAgB,UAAkB,QAAwC;AAClF,MAAI,OAAO;AACX,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,WAAO,KAAK,QAAQ,IAAI,GAAG,IAAI,mBAAmB,KAAK,CAAC;AAAA,EACzD;AACA,SAAO;AACR;AAEA,IAAM,WAAW;AAAA,EAChB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AACT;AAIA,SAAS,iBAAiB,QAA4C;AACrE,MAAI,OAAO,YAAY,OAAW,QAAO,EAAE,IAAI,OAAO,QAAQ;AAC9D,MAAI,OAAO,OAAO,OAAW,QAAO,EAAE,IAAI,OAAO,GAAG;AACpD,SAAO,EAAE,IAAI,MAAM;AACpB;AAEA,SAAS,oBAAoB,QAAsD;AAClF,MAAI,OAAO,YAAY,OAAW,QAAO,OAAO;AAChD,MAAI,OAAO,UAAU,OAAW,QAAO,OAAO;AAC9C,SAAO;AACR;AA2HA,eAAsB,MAAM,QAAsB,SAAiD;AAGlG,QAAM,EAAE,KAAK,GAAG,KAAK,IAAI;AACzB,QAAM,OAAO;AAAA,IACZ,GAAG;AAAA,IACH,GAAI,KAAK,OAAO,UAAa,QAAQ,SAAY,EAAE,IAAI,IAAI,IAAI,CAAC;AAAA,EACjE;AACA,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,MAAM,QAA6B,QAAQ,SAAS,MAAM;AAAA,IACxE,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACD,SAAO,iBAAiB,MAAM;AAC/B;AAeA,eAAsB,MAAmB,QAAsB,KAAgC;AAE9F,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACAA,iBAAgB,SAAS,MAAM,EAAE,IAAI,CAAC;AAAA,IACtC,EAAE,QAAQ,SAAS,OAAO;AAAA,EAC3B;AACA,SAAO,OAAO;AACf;AAWA,eAAsB,SAAS,QAAsB,KAA2C;AAE/F,QAAM,WAAW,YAAY;AAC7B,SAAO,QAAgC,QAAQA,iBAAgB,SAAS,MAAM,EAAE,IAAI,CAAC,GAAG;AAAA,IACvF,QAAQ,SAAS;AAAA,EAClB,CAAC;AACF;AAUA,eAAsB,SAAS,QAAsB,KAA2C;AAE/F,QAAM,WAAW,YAAY;AAC7B,SAAO,QAAgC,QAAQA,iBAAgB,SAAS,MAAM,EAAE,IAAI,CAAC,GAAG;AAAA,IACvF,QAAQ,SAAS;AAAA,EAClB,CAAC;AACF;AAUA,eAAsB,SACrB,QACA,SAC2B;AAE3B,QAAM,OAAO;AACb,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,MAAM,QAA6B,QAAQ,SAAS,MAAM;AAAA,IACxE,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACD,SAAO,iBAAiB,MAAM;AAC/B;AAUA,eAAsB,OACrB,QACA,SAC6B;AAE7B,QAAM,OAAO;AACb,QAAM,WAAW,YAAY;AAC7B,SAAO,QAA8B,QAAQ,SAAS,MAAM;AAAA,IAC3D,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AASA,eAAsB,OACrB,QACA,SAC2B;AAC3B,QAAM,SAAS,MAAM,QAA2B,QAAQ,SAAS,MAAM;AAAA,IACtE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,SAAO,iBAAiB,MAAM;AAC/B;AAOA,eAAsB,OACrB,QACA,SAC2B;AAC3B,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACA,SAAS;AAAA,IACT;AAAA,MACC,QAAQ;AAAA,MACR,MAAM;AAAA,IACP;AAAA,EACD;AACA,QAAM,SAAS,OAAO;AACtB,MAAI,MAAM,QAAQ,MAAM,GAAG;AAC1B,WAAO;AAAA,EACR;AACA,SAAO,QAAQ,KAAK,IAAI,CAAC,QAAQ,OAAO,GAAG,KAAK,IAAI;AACrD;AAcA,eAAsB,OACrB,QACA,SAC6B;AAC7B,QAAM,SAAS,MAAM,QAA8C,QAAQ,SAAS,MAAM;AAAA,IACzF,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,SAAO,EAAE,OAAO,oBAAoB,MAAM,EAAE;AAC7C;AAKA,eAAsB,OACrB,QACA,SACoB;AACpB,QAAM,SAAS,MAAM,QAA6B,QAAQ,SAAS,MAAM;AAAA,IACxE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,SAAO,OAAO;AACf;AAKA,eAAsB,UACrB,QACA,SACoB;AACpB,QAAM,SAAS,MAAM,QAAiD,QAAQ,SAAS,SAAS;AAAA,IAC/F,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,MAAI,OAAO,WAAW,OAAW,QAAO,OAAO;AAC/C,MAAI,OAAO,UAAU,OAAW,QAAO,OAAO;AAC9C,SAAO;AACR;AAcA,eAAsB,QACrB,QACA,SAC8B;AAC9B,SAAO,QAA4B,QAAQ,SAAS,OAAO;AAAA,IAC1D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAWA,eAAsB,SACrB,QACA,SACe;AACf,QAAM,SAAS,MAAM,QAAuC,QAAQ,SAAS,QAAQ;AAAA,IACpF,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,MAAI,OAAO,WAAW,OAAW,QAAO,OAAO;AAC/C,MAAI,OAAO,UAAU,OAAW,QAAO,OAAO;AAC9C,SAAO,CAAC;AACT;AAkBA,eAAsB,OACrB,QACA,SAC6B;AAC7B,SAAO,QAA2B,QAAQ,SAAS,MAAM;AAAA,IACxD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAiBA,eAAsB,SACrB,QACA,SACqD;AACrD,QAAM,SAAS,MAAM,QAElB,QAAQ,SAAS,QAAQ;AAAA,IAC3B,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACD,SAAO,OAAO;AACf;AAyBA,eAAsB,YACrB,QACA,SAC6B;AAE7B,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,MAAI,CAAC,KAAK;AACT,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC7C;AACA,QAAM,OAAO;AAAA,IACZ;AAAA,IACA,OAAO,QAAQ;AAAA,IACf,QAAQ,OAAO,QAAQ,WAAW,WAAW,GAAG,QAAQ,MAAM,MAAM,QAAQ;AAAA,EAC7E;AACA,QAAM,WAAW,YAAY;AAC7B,SAAO,QAA2B,QAAQ,SAAS,MAAM;AAAA,IACxD,QAAQ,SAAS;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAyCA,eAAsB,OAAO,QAAsB,SAAgD;AAIlG,QAAM,WAAW,YAAY;AAC7B,SAAO,QAA8B,QAAQ,SAAS,MAAM;AAAA,IAC3D,QAAQ,SAAS;AAAA,IACjB,OAAO;AAAA,MACN,GAAI,SAAS,YAAY,UAAa,EAAE,SAAS,QAAQ,QAAQ;AAAA,MACjE,GAAI,SAAS,WAAW,UAAa,EAAE,QAAQ,QAAQ,OAAO;AAAA,MAC9D,GAAI,SAAS,UAAU,UAAa,EAAE,OAAO,OAAO,QAAQ,KAAK,EAAE;AAAA,IACpE;AAAA,EACD,CAAC;AACF;AAiBA,eAAsB,UAAuB,QAAsB,KAAgC;AAClG,QAAM,MAAM,MAAM,MAAc,QAAQ,GAAG;AAC3C,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI;AACH,WAAO,KAAK,MAAM,GAAG;AAAA,EACtB,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAUA,eAAsB,UACrB,QACA,KACA,OACA,SAC2B;AAC3B,SAAO,MAAM,QAAQ,EAAE,KAAK,OAAO,KAAK,UAAU,KAAK,GAAG,GAAG,QAAQ,CAAC;AACvE;;;ACrhBA,eAAsB,cACrB,QACA,SACsB;AACtB,SAAO,QAAoB,QAAQ,uBAAuB,mBAAmB,QAAQ,KAAK,CAAC,IAAI;AAAA,IAC9F,QAAQ;AAAA,IACR,MAAM,QAAQ,iBAAiB,SAAY,EAAE,cAAc,QAAQ,aAAa,IAAI;AAAA,EACrF,CAAC;AACF;AAaA,eAAsB,gBAAgB,QAAsB,OAAoC;AAC/F,SAAO,QAAoB,QAAQ,sBAAsB,mBAAmB,KAAK,CAAC,IAAI;AAAA,IACrF,QAAQ;AAAA,EACT,CAAC;AACF;AAWA,eAAsB,iBACrB,QACA,OACiC;AACjC,SAAO;AAAA,IACN;AAAA,IACA,uBAAuB,mBAAmB,KAAK,CAAC;AAAA,IAChD,EAAE,QAAQ,MAAM;AAAA,EACjB;AACD;AAaA,eAAsB,eACrB,QACA,SACsB;AACtB,SAAO,QAAoB,QAAQ,wBAAwB,mBAAmB,QAAQ,KAAK,CAAC,IAAI;AAAA,IAC/F,QAAQ;AAAA,IACR,MAAM,EAAE,cAAc,QAAQ,aAAa;AAAA,EAC5C,CAAC;AACF;AAgBA,eAAsB,mBACrB,QACA,OACmC;AACnC,SAAO;AAAA,IACN;AAAA,IACA,oBAAoB,mBAAmB,KAAK,CAAC;AAAA,IAC7C,EAAE,QAAQ,MAAM;AAAA,EACjB;AACD;AAgBA,eAAsB,YAAY,QAAsB,OAAkC;AACzF,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACA,uBAAuB,mBAAmB,KAAK,CAAC;AAAA,IAChD,EAAE,QAAQ,MAAM;AAAA,EACjB;AACA,SAAO,OAAO;AACf;AAcA,eAAsB,UACrB,QACA,OACA,SACkB;AAClB,SAAO,QAAgB,QAAQ,uBAAuB,mBAAmB,KAAK,CAAC,IAAI;AAAA,IAClF,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAUA,eAAsB,aACrB,QACA,OACA,KACgC;AAChC,SAAO;AAAA,IACN;AAAA,IACA,uBAAuB,mBAAmB,KAAK,CAAC,IAAI,mBAAmB,GAAG,CAAC;AAAA,IAC3E,EAAE,QAAQ,SAAS;AAAA,EACpB;AACD;;;ACxIA,SAAS,sBAAsB,OAA8B;AAC5D,QAAM,SAA2B,CAAC;AAElC,MAAI,MAAM,OAAO;AAEhB,UAAM,QAAQ,MAAM,MAAM,MAAM,IAAI,EAAE,MAAM,CAAC;AAC7C,eAAW,QAAQ,OAAO;AAEzB,YAAM,UAAU,KAAK,MAAM,wCAAwC;AACnE,UAAI,SAAS;AACZ,eAAO,KAAK;AAAA,UACX,UAAU,QAAQ,CAAC;AAAA,UACnB,UAAU,QAAQ,CAAC;AAAA,UACnB,QAAQ,OAAO,QAAQ,CAAC,CAAC;AAAA,UACzB,OAAO,OAAO,QAAQ,CAAC,CAAC;AAAA,QACzB,CAAC;AACD;AAAA,MACD;AAEA,YAAM,cAAc,KAAK,MAAM,4BAA4B;AAC3D,UAAI,aAAa;AAChB,eAAO,KAAK;AAAA,UACX,UAAU,YAAY,CAAC;AAAA,UACvB,QAAQ,OAAO,YAAY,CAAC,CAAC;AAAA,UAC7B,OAAO,OAAO,YAAY,CAAC,CAAC;AAAA,QAC7B,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM,MAAM,QAAQ;AAAA,IACpB,OAAO,MAAM;AAAA,IACb,YAAY,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI;AAAA,EAC9C;AACD;AAyBA,eAAsB,iBACrB,QACA,OACA,UAAsD,CAAC,GACzB;AAG9B,QAAM,iBAAiB,sBAAsB,KAAK;AAClD,QAAM,UAAmC;AAAA,IACxC,GAAG;AAAA,IACH,WAAW,EAAE,QAAQ,CAAC,cAAc,EAAE;AAAA,EACvC;AACA,SAAO,QAA4B,QAAQ,6BAA6B;AAAA,IACvE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAQA,eAAsB,oBACrB,QACA,SAC8B;AAC9B,SAAO,QAA4B,QAAQ,6BAA6B;AAAA,IACvE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAuBA,eAAsB,eACrB,QACA,SACA,UAAkD,CAAC,GACrB;AAC9B,QAAM,UAAiC,EAAE,GAAG,SAAS,QAAQ;AAC7D,SAAO,QAA4B,QAAQ,2BAA2B;AAAA,IACrE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;;;ACrNA;AAiOO,IAAM,eAAN,MAAmB;AAAA,EACzB,YACkB,UACA,OAChB;AAFgB;AACA;AAAA,EACf;AAAA,EAEK,aAAqC;AAC5C,WAAO,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,MACL,MACA,SACA,WAA8B,QACd;AAChB,UAAM,aAAa,OAAO,SAAS,OAAO,IAAI,QAAQ,SAAS,QAAQ,IAAI;AAC3E,UAAM,oBAAoB,OAAO,SAAS,OAAO,IAAI,WAAW;AAEhE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,UAAU;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,EAAE,MAAM,SAAS,YAAY,UAAU,kBAAkB,CAAC;AAAA,IAChF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,EACvE;AAAA;AAAA,EAGA,MAAM,KAAK,MAA+B;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,eAAe,mBAAmB,IAAI,CAAC,IAAI;AAAA,MAClF,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,sBAAsB,MAAM,IAAI,KAAK,CAAC,EAAE;AACrE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,KAAK;AAAA,EACb;AAAA;AAAA,EAGA,MAAM,OAAO,MAA6B;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,eAAe,mBAAmB,IAAI,CAAC,IAAI;AAAA,MAClF,QAAQ;AAAA,MACR,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,wBAAwB,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,EACxE;AAAA;AAAA,EAGA,MAAM,KAAK,OAAO,KAAwB;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,cAAc,mBAAmB,IAAI,CAAC,IAAI;AAAA,MACjF,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,sBAAsB,MAAM,IAAI,KAAK,CAAC,EAAE;AACrE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,KAAK;AAAA,EACb;AACD;AAMO,IAAM,mBAAN,MAAuB;AAAA,EAC7B,YACkB,UACA,OAChB;AAFgB;AACA;AAAA,EACf;AAAA,EAEK,aAAqC;AAC5C,WAAO,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,MAAM,MAAiE;AAC5E,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,kBAAkB;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,IAAI;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,yBAAyB,MAAM,IAAI,KAAK,CAAC,EAAE;AACxE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,OAAkC;AACvC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,iBAAiB;AAAA,MACxD,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,wBAAwB,MAAM,IAAI,KAAK,CAAC,EAAE;AACvE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,IAAI,WAAyC;AAClD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,SAAS,IAAI;AAAA,MAChE,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB,MAAM,IAAI,KAAK,CAAC,EAAE;AACtE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,KAAK,WAAmB,SAAiB,WAA0B;AACxE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,SAAS,SAAS;AAAA,MACrE,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;AAAA,IAChC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,wBAAwB,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,EACxE;AAAA;AAAA,EAGA,MAAM,WAAW,WAAmB,MAA6B;AAChE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,SAAS,UAAU;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC9B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,8BAA8B,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,WAAmB,YAAY,KAA+B;AACxE,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC7B,YAAM,OAAO,MAAM,KAAK,IAAI,SAAS;AACrC,UAAI,KAAK,WAAW,UAAW,QAAO;AACtC,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAAA,IAC5C;AACA,UAAM,IAAI,MAAM,iCAAiC,SAAS,iBAAiB,SAAS,KAAK;AAAA,EAC1F;AAAA;AAAA,EAGA,OAAO,OAAO,WAAiD;AAC9D,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,SAAS,WAAW;AAAA,MACvE,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,0BAA0B,MAAM,IAAI,KAAK,CAAC,EAAE;AACzE,QAAI,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,kCAAkC;AAEjE,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,SAAS,IAAI,KAAK,UAAU;AAClC,QAAI,SAAS;AAEb,QAAI;AACH,aAAO,MAAM;AACZ,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AACV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,cAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAS,MAAM,IAAI,KAAK;AACxB,mBAAW,QAAQ,OAAO;AACzB,cAAI,KAAK,WAAW,QAAQ,GAAG;AAC9B,gBAAI;AACH,oBAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC;AACtC,oBAAM;AACN,kBAAI,MAAM,SAAS,OAAQ;AAAA,YAC5B,QAAQ;AAAA,YAER;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD,UAAE;AACD,aAAO,YAAY;AAAA,IACpB;AAAA,EACD;AACD;AAMO,IAAM,eAAN,MAAmB;AAAA,EACzB,YACkB,UACA,OAChB;AAFgB;AACA;AAAA,EACf;AAAA,EAEK,aAAqC;AAC5C,WAAO,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,IAAI,MAAyC;AAClD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,UAAU;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,IAAI;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,qBAAqB,MAAM,IAAI,KAAK,CAAC,EAAE;AACpE,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,OAA8B;AACnC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,UAAU;AAAA,MACjD,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,sBAAsB,MAAM,IAAI,KAAK,CAAC,EAAE;AACrE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,KAAK;AAAA,EACb;AAAA;AAAA,EAGA,MAAM,OAAO,MAA6B;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,eAAe,mBAAmB,IAAI,CAAC,IAAI;AAAA,MAClF,QAAQ;AAAA,MACR,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,wBAAwB,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,EACxE;AACD;AA2BO,IAAM,iBAAN,MAAqB;AAAA,EAC3B,YACkB,UACA,OAChB;AAFgB;AACA;AAAA,EACf;AAAA,EAEK,aAAqC;AAC5C,WAAO,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,OAA6B,CAAC,GAAgC;AAC1E,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY;AAAA,MACnD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,WAAW,GAAG,gBAAgB,mBAAmB;AAAA,MACpE,MAAM,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,CAAC;AAAA,IAC/C,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AACzF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,OAAsC;AAC3C,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,EAAE,SAAS,KAAK,WAAW,EAAE,CAAC;AAClF,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,wBAAwB,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AACvF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,IAAI,WAAuD;AAChE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,mBAAmB,SAAS,CAAC,IAAI;AAAA,MACpF,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AACtF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,WAAW,WAA2B;AACrC,UAAM,SAAS,KAAK,SAAS,QAAQ,kBAAkB,CAAC,IAAI,MAAO,IAAI,WAAW,OAAQ;AAC1F,UAAM,OAAO,YAAY,mBAAmB,SAAS,CAAC;AACtD,WAAO,GAAG,MAAM,GAAG,IAAI,UAAU,mBAAmB,KAAK,KAAK,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,WAA8B;AACrC,WAAO,IAAI,UAAU,KAAK,WAAW,SAAS,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACL,WAC0E;AAC1E,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,mBAAmB,SAAS,CAAC,QAAQ;AAAA,MACxF,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AACtF,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,EAAE,GAAG,MAAM,YAAY,KAAK,WAAW,SAAS,EAAE;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,WAAqC;AAClD,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,YAAY,mBAAmB,SAAS,CAAC,IAAI;AAAA,MACpF,QAAQ;AAAA,MACR,SAAS,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AAC1F,WAAO;AAAA,EACR;AACD;AAMO,IAAM,gBAAN,MAAM,eAAc;AAAA,EACjB;AAAA,EACQ;AAAA;AAAA,EAGR;AAAA;AAAA,EAEA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EAED,YACP,IACA,QACA,UACA,OACC;AACD,SAAK,KAAK;AACV,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,QAAQ;AACb,SAAK,QAAQ,YAAY,QAAQ,IAAI,aAAa,UAAU,KAAK,IAAI;AACrE,SAAK,YAAY,YAAY,QAAQ,IAAI,iBAAiB,UAAU,KAAK,IAAI;AAC7E,SAAK,QAAQ,YAAY,QAAQ,IAAI,aAAa,UAAU,KAAK,IAAI;AACrE,SAAK,UAAU,YAAY,QAAQ,IAAI,eAAe,UAAU,KAAK,IAAI;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,OAAO,QAAsB,SAAkD;AAC3F,UAAM,SAAS,MAAM,QAAuB,QAAQ,cAAc;AAAA,MACjE,QAAQ;AAAA,MACR,MAAM;AAAA,QACL,OAAO,SAAS;AAAA,QAChB,eAAe,SAAS,iBAAiB;AAAA,QACzC,SAAS,SAAS,WAAW;AAAA,QAC7B,KAAK,SAAS;AAAA,QACd,SACC,SAAS,cAAc,SACpB,EAAE,SAAS,MAAM,QAAQ,QAAQ,UAAU,IAC3C;AAAA,QACJ,cAAc,SAAS;AAAA,QACvB,iBAAiB,SAAS;AAAA,MAC3B;AAAA,MACA,SAAS,SAAS,mBAAmB;AAAA,IACtC,CAAC;AAED,WAAO,IAAI,eAAc,OAAO,IAAI,QAAQ,OAAO,UAAU,OAAO,KAAK;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,OAAO,QAAsB,WAA2C;AACpF,UAAM,SAAS,MAAM,QAAuB,QAAQ,cAAc,SAAS,IAAI;AAAA,MAC9E,QAAQ;AAAA,IACT,CAAC;AACD,WAAO,IAAI,eAAc,OAAO,IAAI,QAAQ,OAAO,UAAU,OAAO,KAAK;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAoC;AACzC,WAAO,QAAuB,KAAK,QAAQ,cAAc,KAAK,EAAE,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EACtF;AAAA,EAEA,MAAM,YAA2B;AAChC,UAAM,QAA8B,KAAK,QAAQ,cAAc,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,OAAO,KAAK,SAAmB,SAAkD;AAChF,SAAK,aAAa;AAKlB,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAS,SAAS;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,eAAe,UAAU,KAAK,KAAM;AAAA,QACpC,gBAAgB;AAAA,MACjB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,GAAG,SAAS,WAAW,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC5E,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACZ,YAAM,IAAI,MAAM,gBAAgB,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,IACnE;AACA,QAAI,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,wBAAwB;AAGvD,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,SAAS,IAAI,KAAK,UAAU;AAClC,QAAI,SAAS;AAEb,QAAI;AACH,aAAO,MAAM;AACZ,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,cAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAS,MAAM,IAAI,KAAK;AAExB,mBAAW,QAAQ,OAAO;AACzB,cAAI,KAAK,WAAW,QAAQ,GAAG;AAC9B,gBAAI;AACH,oBAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC;AACtC,oBAAM;AACN,kBAAI,MAAM,SAAS,UAAU,MAAM,SAAS,QAAS;AAAA,YACtD,QAAQ;AAAA,YAER;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD,UAAE;AACD,aAAO,YAAY;AAAA,IACpB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAI,SAAmB,SAA4C;AACxE,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,aAAa;AAEjB,qBAAiB,SAAS,KAAK,KAAK,SAAS,OAAO,GAAG;AACtD,UAAI,MAAM,SAAS,SAAU,WAAU,MAAM;AAAA,eACpC,MAAM,SAAS,SAAU,WAAU,MAAM;AAAA,eACzC,MAAM,SAAS,QAAQ;AAC/B,mBAAW,MAAM;AACjB,qBAAa,MAAM;AAAA,MACpB;AAAA,IACD;AAEA,WAAO,EAAE,QAAQ,QAAQ,UAAU,WAAW;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,OAAO,OAAO,QAI8B;AAC3C,SAAK,aAAa;AAElB,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,IAAI;AAChD,QAAI,QAAQ,QAAQ,OAAW,QAAO,IAAI,OAAO,OAAO,OAAO,GAAG,CAAC;AACnE,QAAI,QAAQ,QAAS,QAAO,IAAI,WAAW,OAAO,OAAO;AAEzD,UAAM,KAAK,OAAO,SAAS;AAC3B,UAAM,MAAM,GAAG,KAAK,QAAS,UAAU,KAAK,IAAI,EAAE,KAAK,EAAE;AAEzD,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC5B,SAAS,EAAE,eAAe,UAAU,KAAK,KAAM,GAAG;AAAA,IACnD,CAAC;AAED,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AACjF,QAAI,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,0BAA0B;AAEzD,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,SAAS,IAAI,KAAK,UAAU;AAClC,QAAI,SAAS;AAEb,QAAI;AACH,aAAO,MAAM;AACZ,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AACV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,cAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAS,MAAM,IAAI,KAAK;AACxB,mBAAW,QAAQ,OAAO;AACzB,cAAI,KAAK,WAAW,QAAQ,GAAG;AAC9B,gBAAI;AACH,oBAAM,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC;AAAA,YAC/B,QAAQ;AAAA,YAER;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD,UAAE;AACD,aAAO,YAAY;AAAA,IACpB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAiB;AAChB,SAAK,aAAa;AAElB,UAAM,aAAa,KAAK,SAAU,QAAQ,eAAe,QAAQ,EAAE;AAAA,MAClE;AAAA,MACA;AAAA,IACD;AAEA,WAAO,IAAI,UAAU,GAAG,UAAU,cAAc,mBAAmB,KAAK,KAAM,CAAC,EAAE;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAqB;AAC5B,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,OAAO;AAClC,YAAM,IAAI;AAAA,QACT;AAAA,MAED;AAAA,IACD;AAAA,EACD;AACD;;;AC9sBA,IAAM,oBAA4C,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AACD,IAAM,2BAA2B;AACjC,IAAM,0BAA0B;AAMzB,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACQ;AAAA,EAEjB,YAAY,IAAY,QAAsB;AAC7C,SAAK,KAAK;AACV,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SAAuB;AAC5B,WAAO,QAAa,KAAK,QAAQ,YAAY,KAAK,EAAE,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,KAAK,SAA+E;AACzF,UAAM,SAAS,SAAS,kBAAkB;AAC1C,UAAM,YAAY,SAAS,aAAa;AACxC,UAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAO,MAAM;AACZ,YAAM,MAAM,MAAM,KAAK,OAAO;AAE9B,UAAI,kBAAkB,IAAI,IAAI,MAAM,GAAG;AACtC,eAAO;AAAA,UACN,UAAU,IAAI;AAAA,UACd,QAAQ,IAAI;AAAA,UACZ,QAAQ,IAAI;AAAA,UACZ,QAAQ,IAAI;AAAA,UACZ,cAAc,IAAI;AAAA,UAClB,YAAY,IAAI;AAAA,QACjB;AAAA,MACD;AAEA,UAAI,KAAK,IAAI,KAAK,UAAU;AAC3B,cAAM,IAAI;AAAA,UACT,UAAU,KAAK,EAAE,4BAA4B,SAAS,uBAAuB,IAAI,MAAM;AAAA,QACxF;AAAA,MACD;AAEA,YAAMC,OAAM,MAAM;AAAA,IACnB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,OAA+B;AACpC,WAAO,QAAuB,KAAK,QAAQ,YAAY,KAAK,EAAE,SAAS,EAAE,QAAQ,MAAM,CAAC;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,SAAwB;AAC7B,UAAM,QAA8B,KAAK,QAAQ,YAAY,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC7F;AACD;AAqBO,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBzB,MAAM,IAAI,QAAsB,SAA+C;AAC9E,UAAM,MAAM,MAAM,QAAwB,QAAQ,SAAS;AAAA,MAC1D,QAAQ;AAAA,MACR,MAAM;AAAA,QACL,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ;AAAA,QACjB,KAAK,QAAQ;AAAA,QACb,SAAS,QAAQ,WAAW;AAAA,QAC5B,gBAAgB,QAAQ,kBAAkB;AAAA,QAC1C,cAAc,QAAQ;AAAA,MACvB;AAAA,IACD,CAAC;AACD,WAAO,IAAI,UAAU,IAAI,IAAI,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,OAAO,QAAsB,UAA6B;AACzD,WAAO,IAAI,UAAU,UAAU,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,KAAK,QAAsB,SAAoD;AACpF,WAAO,QAAwB,QAAQ,SAAS;AAAA,MAC/C,QAAQ;AAAA,MACR,OAAO,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI;AAAA,IACvD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,WACL,QACA,SACA,aACqB;AACrB,UAAM,SAAS,MAAM,WAAW,IAAI,QAAQ,OAAO;AACnD,WAAO,OAAO,KAAK,WAAW;AAAA,EAC/B;AACD;AAMA,SAASA,OAAM,IAA2B;AACzC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACxD;AAIO,IAAM,gBAAgB;;;ACxX7B,eAAsB,WACrB,QACA,SAC4B;AAC5B,SAAO,QAA0B,QAAQ,SAAS;AAAA,IACjD,QAAQ;AAAA,IACR,MAAM,EAAE,MAAM,QAAQ;AAAA,EACvB,CAAC;AACF;AAWA,eAAsB,UACrB,QACA,UAA4B,CAAC,GACF;AAC3B,SAAO,QAAyB,QAAQ,SAAS;AAAA,IAChD,QAAQ;AAAA,IACR,OAAO;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,IAClB;AAAA,EACD,CAAC;AACF;;;AC9DA,eAAsB,mBAAmB,QAAgD;AACxF,SAAO,QAAyB,QAAQ,MAAM;AAC/C;AAaA,eAAsB,gBACrB,QACA,OACiC;AACjC,SAAO,QAA+B,QAAQ,qBAAqB;AAAA,IAClE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;;;ACMA,eAAsB,eAAe,QAAgD;AACpF,SAAO,QAAyB,QAAQ,eAAe;AACxD;AAEA,eAAsB,kBACrB,QACA,OAC2B;AAC3B,SAAO,QAAyB,QAAQ,iBAAiB;AAAA,IACxD,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,gBAAgB,QAAqD;AAC1F,SAAO,QAA8B,QAAQ,gBAAgB;AAC9D;AAEA,eAAsB,iBAAiB,QAAiD;AACvF,SAAO,QAA0B,QAAQ,gBAAgB;AAC1D;AAEA,eAAsB,kBACrB,QACA,WACgC;AAChC,SAAO,QAA8B,QAAQ,kBAAkB,mBAAmB,SAAS,CAAC,IAAI;AAAA,IAC/F,QAAQ;AAAA,EACT,CAAC;AACF;AAEA,eAAsB,kBACrB,QACA,WACA,YACgC;AAChC,SAAO,QAA8B,QAAQ,kBAAkB,mBAAmB,SAAS,CAAC,IAAI;AAAA,IAC/F,QAAQ;AAAA,IACR,MAAM,EAAE,WAAW;AAAA,EACpB,CAAC;AACF;AAMA,eAAsB,eAAe,QAA+C;AACnF,SAAO,QAAwB,QAAQ,cAAc;AACtD;AAOA,eAAsB,kBAAkB,QAAoD;AAC3F,SAAO,QAA6B,QAAQ,iBAAiB;AAAA,IAC5D,QAAQ;AAAA,EACT,CAAC;AACF;;;ACjCA,eAAsB,YACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,0BAA0B;AAAA,IACtE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,wBACrB,QACA,UACgC;AAChC,SAAO,QAA8B,QAAQ,8BAA8B;AAAA,IAC1E,QAAQ;AAAA,IACR,MAAM,EAAE,SAAS;AAAA,EAClB,CAAC;AACF;AAEA,eAAsB,iBAAiB,QAAoD;AAC1F,SAAO,QAA6B,QAAQ,iBAAiB;AAC9D;AAMA,eAAsB,mBACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,0BAA0B;AAAA,IACtE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,mBACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,2BAA2B;AAAA,IACvE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAMA,eAAsB,aAAa,QAA6C;AAC/E,SAAO,QAAsB,QAAQ,oBAAoB;AAC1D;AAEA,eAAsB,yBACrB,QACsC;AACtC,SAAO,QAAoC,QAAQ,qCAAqC;AAAA,IACvF,QAAQ;AAAA,EACT,CAAC;AACF;AAEA,eAAsB,0BACrB,QACA,OACyD;AACzD,SAAO,QAAQ,QAAQ,sCAAsC;AAAA,IAC5D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,cACrB,QACA,WACA,MACgC;AAChC,SAAO;AAAA,IACN;AAAA,IACA,sBAAsB,mBAAmB,SAAS,CAAC;AAAA,IACnD;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,EAAE,KAAK;AAAA,IACd;AAAA,EACD;AACD;AAEA,eAAsB,cACrB,QACA,WACgC;AAChC,SAAO;AAAA,IACN;AAAA,IACA,sBAAsB,mBAAmB,SAAS,CAAC;AAAA,IACnD,EAAE,QAAQ,SAAS;AAAA,EACpB;AACD;AAMA,eAAsB,eAAe,QAAqD;AACzF,SAAO,QAA8B,QAAQ,uBAAuB,EAAE,QAAQ,OAAO,CAAC;AACvF;AAEA,eAAsB,sBACrB,QACA,MACiC;AACjC,SAAO,QAA+B,QAAQ,wBAAwB;AAAA,IACrE,QAAQ;AAAA,IACR,MAAM,EAAE,KAAK;AAAA,EACd,CAAC;AACF;AAEA,eAAsB,iBAAiB,QAAqD;AAC3F,SAAO,QAA8B,QAAQ,yBAAyB,EAAE,QAAQ,OAAO,CAAC;AACzF;AAEA,eAAsB,eAAe,QAAkD;AACtF,SAAO,QAA2B,QAAQ,wBAAwB;AACnE;AAEA,eAAsB,sBAAsB,QAAkD;AAC7F,SAAO,QAA2B,QAAQ,qCAAqC;AAAA,IAC9E,QAAQ;AAAA,EACT,CAAC;AACF;AAMA,eAAsB,mBAAmB,QAAmD;AAC3F,SAAO,QAA4B,QAAQ,kBAAkB;AAC9D;AAEA,eAAsB,sBACrB,QACA,SACgC;AAChC,SAAO;AAAA,IACN;AAAA,IACA,oBAAoB,mBAAmB,OAAO,CAAC;AAAA,IAC/C,EAAE,QAAQ,OAAO;AAAA,EAClB;AACD;AAEA,eAAsB,0BACrB,QACiD;AACjD,SAAO,QAAQ,QAAQ,6BAA6B,EAAE,QAAQ,OAAO,CAAC;AACvE;;;AC5OA;AAyDA,eAAsB,yBAAyB,MAEZ;AAClC,QAAM,MAAM,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAC9C,QAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;AACxE,MAAI,CAAC,IAAI,IAAI;AACZ,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EACzE;AACA,SAAQ,MAAM,IAAI,KAAK;AACxB;AAiBA,eAAsB,SAAS,MAKG;AACjC,QAAM,MAAM,KAAK,YAAY,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAC/D,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC5B,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,eAAe,UAAU,KAAK,WAAW;AAAA,MACzC,QAAQ;AAAA,IACT;AAAA,EACD,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACZ,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,IAAI,MAAM,oBAAoB,IAAI,MAAM,IAAI,IAAI,UAAU,IAAI,IAAI,EAAE;AAAA,EAC3E;AACA,SAAQ,MAAM,IAAI,KAAK;AACxB;AA8DA,eAAsB,eACrB,QACA,OACgC;AAChC,SAAO,QAA8B,QAAQ,oBAAoB;AAAA,IAChE,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,UAAU,MAAM;AAAA,MAChB,cAAc,MAAM;AAAA,MACpB,gBAAgB,MAAM;AAAA,MACtB,uBAAuB,MAAM,uBAAuB;AAAA,MACpD,GAAI,MAAM,UAAU,EAAE,QAAQ,MAAM,OAAO;AAAA,MAC3C,GAAI,MAAM,SAAS,EAAE,OAAO,MAAM,MAAM;AAAA,IACzC;AAAA,EACD,CAAC;AACF;AAQA,eAAsB,mBAAmB,QAAqD;AAC7F,SAAO,QAA8B,QAAQ,uBAAuB;AACrE;AASA,eAAsB,kBACrB,QACA,OACyB;AACzB,MAAI,OAAO,mBAAmB,QAAQ,CAAC,OAAO,WAAW;AACxD,UAAM,IAAI;AAAA,MACT;AAAA,MACA,EAAE,MAAM,cAAc;AAAA,IACvB;AAAA,EACD;AAEA,SAAO,QAAuB,QAAQ,eAAe;AAAA,IACpD,QAAQ;AAAA,IACR,MAAM;AAAA,MACL,YAAY;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,WAAW,OAAO;AAAA,MAClB,eAAe,MAAM;AAAA,MACrB,GAAI,MAAM,cAAc,EAAE,cAAc,MAAM,YAAY,IAAI,CAAC;AAAA,IAChE;AAAA,EACD,CAAC;AACF;AAUO,SAAS,mBAAmB,KAKjC;AACD,QAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,SAAO;AAAA,IACN,MAAM,OAAO,aAAa,IAAI,MAAM;AAAA,IACpC,OAAO,OAAO,aAAa,IAAI,OAAO;AAAA,IACtC,OAAO,OAAO,aAAa,IAAI,OAAO;AAAA,IACtC,kBAAkB,OAAO,aAAa,IAAI,mBAAmB;AAAA,EAC9D;AACD;AAOA,eAAsB,eAAiE;AACtF,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,aAAW,OAAO,gBAAgB,KAAK;AACvC,QAAM,WAAWC,iBAAgB,KAAK;AACtC,QAAM,SAAS,MAAM,WAAW,OAAO,OAAO;AAAA,IAC7C;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,QAAQ;AAAA,EAClC;AACA,QAAM,YAAYA,iBAAgB,IAAI,WAAW,MAAM,CAAC;AACxD,SAAO,EAAE,UAAU,UAAU;AAC9B;AAEA,SAASA,iBAAgB,OAA2B;AACnD,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,WAAU,OAAO,aAAa,MAAM,CAAC,CAAW;AACvF,SAAO,KAAK,MAAM,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC9E;;;AC9IA,eAAsB,cACrB,QACA,OAC+B;AAC/B,SAAO,QAA6B,QAAQ,mBAAmB;AAAA,IAC9D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAGA,eAAsB,YACrB,QACA,OAC6B;AAC7B,SAAO,QAA2B,QAAQ,iBAAiB;AAAA,IAC1D,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAMA,eAAsB,WACrB,QACA,UAA6B,CAAC,GACF;AAC5B,SAAO,QAA0B,QAAQ,gBAAgB;AAAA,IACxD,OAAO;AAAA,MACN,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IACjB;AAAA,EACD,CAAC;AACF;AAEA,eAAsB,SACrB,QACA,SACgC;AAChC,SAAO,QAAQ,QAAQ,gBAAgB,mBAAmB,OAAO,CAAC,EAAE;AACrE;AAEA,eAAsB,YACrB,QACA,OACgC;AAChC,SAAO,QAAQ,QAAQ,gBAAgB;AAAA,IACtC,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,YACrB,QACA,SACA,OACgC;AAChC,SAAO,QAAQ,QAAQ,gBAAgB,mBAAmB,OAAO,CAAC,IAAI;AAAA,IACrE,QAAQ;AAAA,IACR,MAAM;AAAA,EACP,CAAC;AACF;AAEA,eAAsB,YACrB,QACA,SACgC;AAChC,SAAO,QAAQ,QAAQ,gBAAgB,mBAAmB,OAAO,CAAC,IAAI;AAAA,IACrE,QAAQ;AAAA,EACT,CAAC;AACF;AAEA,eAAsB,qBACrB,QACA,UAAkC,CAAC,GACF;AACjC,SAAO,QAA+B,QAAQ,4BAA4B;AAAA,IACzE,OAAO;AAAA,MACN,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IACjB;AAAA,EACD,CAAC;AACF;;;AlDwNO,IAAM,WAAW;AAAA,EACvB,OAAO,cAAc;AAAA,EACrB,MAAuB;AAAA,EACvB,SAA0B;AAC3B;AAOO,IAAM,YAAY;AAAA,EACxB,OAAO;AACR;","names":["getErrorCode","getErrorDetails","getErrorMessage","request","response","search","user","SylphxError","buildHeaders","SylphxError","buildHeaders","SylphxError","SylphxError","buildHeaders","SylphxError","isRetryableError","isRetryableError","page","interpolatePath","sleep","base64UrlEncode"]}