@sylphx/sdk 0.8.0-rc.1 → 0.8.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,1842 +0,0 @@
1
- import { SdkBillingPlan, SdkConsentType } from '@sylphx/contract';
2
- import { OAuthProvider } from '@sylphx/ui';
3
- export { OAuthProvider } from '@sylphx/ui';
4
-
5
- /**
6
- * REST Client for Sylphx Platform
7
- *
8
- * Type-safe REST API client built on plain `fetch` (no runtime `openapi-fetch`
9
- * dependency — ADR-084 routes types through `@sylphx/contract` so the codegen
10
- * layer no longer needs a transport library of its own). Public surface is
11
- * preserved: consumers still call `client.GET('/path')`, `client.POST(...)`,
12
- * etc. The middleware chain (deduplication → circuit breaker → ETag →
13
- * retry) runs in the same order as the previous implementation.
14
- *
15
- * @example
16
- * ```typescript
17
- * import { createRestClient } from '@sylphx/sdk'
18
- *
19
- * const client = createRestClient({
20
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
21
- * })
22
- *
23
- * const { data: user, error } = await client.GET('/auth/me')
24
- * const { data: result } = await client.POST('/auth/login', {
25
- * body: { email, password },
26
- * })
27
- * ```
28
- */
29
- /**
30
- * Retry configuration for automatic request retries
31
- */
32
- interface RetryConfig {
33
- /** Maximum number of retries (default: 3) */
34
- maxRetries?: number;
35
- /** Base delay in milliseconds (default: 1000) */
36
- baseDelay?: number;
37
- /** Maximum delay in milliseconds (default: 30000) */
38
- maxDelay?: number;
39
- /** Custom function to determine if error is retryable */
40
- shouldRetry?: (status: number, attempt: number) => boolean;
41
- /** Request timeout in milliseconds (default: 30000) */
42
- timeout?: number;
43
- }
44
- /**
45
- * Request deduplication configuration
46
- */
47
- interface DeduplicationConfig {
48
- /** Enable request deduplication (default: true) */
49
- enabled?: boolean;
50
- /** HTTP methods to deduplicate (default: ['GET']) */
51
- methods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')[];
52
- }
53
- /**
54
- * Circuit breaker configuration (AWS/Resilience4j pattern)
55
- *
56
- * Prevents cascade failures by fast-failing when service is unhealthy.
57
- * States: CLOSED (normal) → OPEN (failing) → HALF_OPEN (testing)
58
- */
59
- interface CircuitBreakerConfig {
60
- /** Enable circuit breaker (default: true) */
61
- enabled?: boolean;
62
- /** Number of failures before opening circuit (default: 5) */
63
- failureThreshold?: number;
64
- /** Time window for counting failures in ms (default: 10000) */
65
- windowMs?: number;
66
- /** How long circuit stays open in ms (default: 30000) */
67
- openDurationMs?: number;
68
- /** Custom function to determine if response is a failure */
69
- isFailure?: (status: number) => boolean;
70
- }
71
- /**
72
- * ETag/Conditional request configuration (HTTP caching pattern)
73
- *
74
- * Enables HTTP conditional requests with If-None-Match header
75
- * to avoid re-downloading unchanged data (saves bandwidth).
76
- */
77
- interface ETagConfig {
78
- /** Enable ETag caching (default: true for GET requests) */
79
- enabled?: boolean;
80
- /** Maximum cache entries (default: 100) */
81
- maxEntries?: number;
82
- /** Cache TTL in milliseconds (default: 5 minutes) */
83
- ttlMs?: number;
84
- }
85
- /**
86
- * Configuration for the REST client
87
- *
88
- * The app key identifies the app — no separate app ID needed.
89
- */
90
- interface RestClientConfig {
91
- /**
92
- * Your app key — identifies the app and environment.
93
- *
94
- * Accepts either:
95
- * - Secret key (sk_dev_, sk_stg_, sk_prod_) — full access, server-side only
96
- * - Publishable key (app_dev_, app_stg_, app_prod_) — limited access, safe for client
97
- */
98
- secretKey: string;
99
- /** Platform URL (default: https://sylphx.com) */
100
- platformUrl?: string;
101
- /** Retry configuration (default: 3 retries with exponential backoff) */
102
- retry?: RetryConfig | false;
103
- /**
104
- * Request deduplication configuration (default: enabled for GET)
105
- *
106
- * Prevents duplicate concurrent requests for the same resource.
107
- * When multiple components request the same data simultaneously,
108
- * only one API call is made and the result is shared.
109
- */
110
- deduplication?: DeduplicationConfig | false;
111
- /**
112
- * Circuit breaker configuration (default: enabled)
113
- *
114
- * Prevents cascade failures by fast-failing when service is unhealthy.
115
- * Opens after 5 failures in 10s, stays open for 30s, then allows test request.
116
- */
117
- circuitBreaker?: CircuitBreakerConfig | false;
118
- /**
119
- * ETag caching configuration (default: enabled for GET)
120
- *
121
- * Uses HTTP conditional requests to avoid re-downloading unchanged data.
122
- * Saves bandwidth by returning 304 Not Modified when content hasn't changed.
123
- */
124
- etag?: ETagConfig | false;
125
- }
126
- /**
127
- * Middleware contract — kept structurally identical to the previous
128
- * `openapi-fetch` shape so existing implementations (dedup / circuit breaker
129
- * / ETag / retry) compose without change.
130
- */
131
- interface Middleware {
132
- onRequest?: (ctx: {
133
- request: Request;
134
- }) => Promise<Request | undefined> | Request | undefined;
135
- onResponse?: (ctx: {
136
- request: Request;
137
- response: Response;
138
- }) => Promise<Response | undefined> | Response | undefined;
139
- }
140
- /**
141
- * Options for an HTTP request — structural subset of `openapi-fetch`'s
142
- * `FetchOptions` so existing callsites (`client.GET('/path', { params: {...} })`)
143
- * continue to compile.
144
- */
145
- interface RequestOptions {
146
- body?: unknown;
147
- params?: {
148
- path?: Record<string, string>;
149
- query?: Record<string, unknown>;
150
- };
151
- headers?: Record<string, string>;
152
- }
153
- /**
154
- * Response envelope — `{ data, error, response }` mirroring `openapi-fetch`.
155
- * `data` is present on 2xx, `error` on non-2xx; both carry the parsed JSON
156
- * body when the server returns one. `response` is always the raw `Response`
157
- * so consumers can read headers (Content-Type, Retry-After, etc.).
158
- */
159
- interface FetchResponse<TOk, TError> {
160
- data?: TOk;
161
- error?: TError;
162
- response: Response;
163
- }
164
- /**
165
- * The typed method surface. Callers pass a path + options; return envelope
166
- * follows openapi-fetch's shape so migration is drop-in for the small number
167
- * of internal callers that relied on `.GET` / `.POST` etc.
168
- */
169
- interface RestClient {
170
- GET<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
171
- POST<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
172
- PUT<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
173
- PATCH<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
174
- DELETE<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
175
- HEAD<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
176
- OPTIONS<TOk = unknown, TError = unknown>(path: string, options?: RequestOptions): Promise<FetchResponse<TOk, TError>>;
177
- use(...middleware: readonly Middleware[]): void;
178
- }
179
-
180
- declare class InvalidConnectionUrlError extends Error {
181
- readonly code: "INVALID_CONNECTION_URL";
182
- constructor(message: string);
183
- }
184
-
185
- /**
186
- * SDK Configuration — ADR-055 Connection URL API
187
- *
188
- * v0.5.0: The primary entry point is `createClient(url)` which accepts
189
- * a `sylphx://` connection URL. The old `createConfig({ ref, publicKey })`
190
- * API is removed.
191
- *
192
- * @example
193
- * ```typescript
194
- * import { createClient } from '@sylphx/sdk'
195
- *
196
- * const sylphx = createClient(process.env.SYLPHX_URL!)
197
- * // Parses: sylphx://pk_prod_{hex}@bold-river-a1b2c3.sylphx.com
198
- * ```
199
- */
200
-
201
- /**
202
- * SDK Configuration object — immutable, frozen.
203
- *
204
- * Created by `createClient()` or `createServerClient()`.
205
- * Passed to all pure SDK functions (`track()`, `signIn()`, etc.).
206
- */
207
- interface SylphxConfig {
208
- /** The credential string (pk_* or sk_*) */
209
- readonly credential: string;
210
- /** Credential type: 'pk' (publishable) or 'sk' (secret) */
211
- readonly credentialType: 'pk' | 'sk';
212
- /** Target environment: dev, stg, prod, or prev */
213
- readonly env: 'dev' | 'stg' | 'prod' | 'prev';
214
- /** Resource slug (first DNS label), e.g. 'bold-river-a1b2c3' */
215
- readonly slug: string;
216
- /** Pre-computed API base URL, e.g. 'https://bold-river-a1b2c3.sylphx.com/v1' */
217
- readonly baseUrl: string;
218
- /** Optional access token for authenticated requests */
219
- readonly accessToken?: string;
220
- /**
221
- * Secret key — populated when credentialType is 'sk'.
222
- * Backward-compatible alias for `credential` when credential is sk_*.
223
- */
224
- readonly secretKey?: string;
225
- /**
226
- * Publishable key — populated when credentialType is 'pk'.
227
- * Backward-compatible alias for `credential` when credential is pk_*.
228
- */
229
- readonly publicKey?: string;
230
- /**
231
- * @deprecated Use `slug`. Backward-compatible alias.
232
- */
233
- readonly ref: string;
234
- }
235
- /**
236
- * Explicit components input — alternative to connection URL string.
237
- *
238
- * For multi-tenant apps or cases where components are stored separately.
239
- */
240
- interface SylphxClientInput {
241
- /** Resource slug, e.g. 'bold-river-a1b2c3' */
242
- slug?: string;
243
- /** Publishable key (pk_*) — client-safe */
244
- publicKey?: string;
245
- /** Secret key (sk_*) — server-side only */
246
- secretKey?: string;
247
- /** Optional access token */
248
- accessToken?: string;
249
- /** API domain override (default: api.sylphx.com) */
250
- domain?: string;
251
- /**
252
- * @deprecated Use `slug`. Accepted for backward compatibility during migration.
253
- */
254
- ref?: string;
255
- /**
256
- * @deprecated Use `domain`. Accepted for backward compatibility during migration.
257
- */
258
- platformUrl?: string;
259
- }
260
- /**
261
- * Create a Sylphx client from a connection URL or explicit components.
262
- *
263
- * This is the primary SDK entry point for client-side (browser) usage.
264
- * Accepts a `sylphx://` connection URL or an explicit components object.
265
- *
266
- * @example Connection URL (recommended)
267
- * ```typescript
268
- * const sylphx = createClient(process.env.NEXT_PUBLIC_SYLPHX_URL!)
269
- * // Parses: sylphx://pk_prod_{hex}@bold-river-a1b2c3.sylphx.com
270
- * ```
271
- *
272
- * @example Explicit components
273
- * ```typescript
274
- * const sylphx = createClient({
275
- * slug: 'bold-river-a1b2c3',
276
- * publicKey: 'pk_prod_f19e...',
277
- * })
278
- * ```
279
- */
280
- declare function createClient(input: string | SylphxClientInput): SylphxConfig;
281
- /**
282
- * Create a Sylphx server client from a connection URL or explicit components.
283
- *
284
- * Equivalent to `createClient()` but validates that a secret key (sk_*) is provided.
285
- * Use this for server-side operations that require elevated permissions.
286
- *
287
- * @example Connection URL (recommended)
288
- * ```typescript
289
- * const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
290
- * // Parses: sylphx://sk_prod_{hex}@bold-river-a1b2c3.sylphx.com
291
- * ```
292
- *
293
- * @example Explicit components
294
- * ```typescript
295
- * const sylphx = createServerClient({
296
- * slug: 'bold-river-a1b2c3',
297
- * secretKey: 'sk_prod_5120...',
298
- * })
299
- * ```
300
- */
301
- declare function createServerClient(input: string | SylphxClientInput): SylphxConfig;
302
-
303
- /**
304
- * Billing Functions
305
- *
306
- * Pure functions for billing and subscriptions.
307
- * Uses REST API at /api/sdk/billing/* for all operations.
308
- *
309
- * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The
310
- * contract is the single source of truth for `GET /billing/plans`,
311
- * `/billing/subscription`, `POST /billing/checkout`, `/billing/portal`,
312
- * `GET /billing/balance`, `/billing/usage`.
313
- */
314
-
315
- type Plan = SdkBillingPlan;
316
-
317
- /**
318
- * Consent Functions
319
- *
320
- * Pure functions for GDPR/CCPA consent management.
321
- *
322
- * ## Architecture (ADR-004)
323
- *
324
- * Consent uses **Inline Defaults + Auto-Discovery + Console Override**:
325
- * - Code provides optional inline defaults when checking consent
326
- * - Platform auto-discovers/creates consent types when first referenced
327
- * - Console can override names, descriptions, requirements without deployment
328
- *
329
- * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The
330
- * contract is the single source of truth for `GET /consent/types`,
331
- * `GET /consent`, `POST /consent`, and friends. SDK-specific input types
332
- * (history pagination, anonymous linking) remain local — they describe
333
- * ergonomic helpers on top of the wire shapes rather than the wire
334
- * shapes themselves.
335
- *
336
- * @example
337
- * ```typescript
338
- * import { hasConsent, getUserConsents, setConsents } from '@sylphx/sdk'
339
- *
340
- * // Check consent with inline defaults (auto-discovered if doesn't exist)
341
- * if (await hasConsent(config, 'analytics', { userId: 'user-123' }, {
342
- * name: 'Analytics Cookies',
343
- * description: 'Help us understand how visitors use our site',
344
- * category: 'analytics',
345
- * required: false,
346
- * })) {
347
- * track('pageview')
348
- * }
349
- *
350
- * // Get user's current consents
351
- * const consents = await getUserConsents(config, { userId: 'user-123' })
352
- *
353
- * // Set specific consents
354
- * await setConsents(config, {
355
- * userId: 'user-123',
356
- * consents: { analytics: true, marketing: false }
357
- * })
358
- * ```
359
- */
360
-
361
- type ConsentType = SdkConsentType;
362
-
363
- /**
364
- * AI Functions
365
- *
366
- * Pure functions for AI completions - Vercel AI SDK style.
367
- * Direct API calls with natural tree-shaking.
368
- *
369
- * Wire-shape types are re-exported from `@sylphx/contract` (ADR-084). The
370
- * contract is the single source of truth for `GET /ai/models`,
371
- * `/ai/usage`, `/ai/rate-limit`. Chat-completion / embedding envelopes
372
- * remain local to this module — they pass through the Vercel AI SDK
373
- * bridge and evolve independently of the platform contract.
374
- */
375
-
376
- interface ChatMessage {
377
- role: 'system' | 'user' | 'assistant' | 'tool';
378
- content: string | ContentPart[];
379
- name?: string;
380
- tool_call_id?: string;
381
- tool_calls?: ToolCall[];
382
- /** Timestamp for UI display */
383
- timestamp?: Date;
384
- }
385
- interface ContentPart {
386
- type: 'text' | 'image_url';
387
- text?: string;
388
- image_url?: {
389
- url: string;
390
- detail?: 'auto' | 'low' | 'high';
391
- };
392
- }
393
- interface ToolCall {
394
- id: string;
395
- type: 'function';
396
- function: {
397
- name: string;
398
- arguments: string;
399
- };
400
- }
401
-
402
- /**
403
- * SDK-specific types — cross-layer helpers and server-first configuration.
404
- *
405
- * Wire-shape types (API request/response envelopes) live in
406
- * `@sylphx/contract` and are re-exported per namespace from their SDK
407
- * module (e.g. `Plan` / `Subscription` from `./billing`, `ConsentType`
408
- * from `./consent`, `TokenResponse` from `./auth`). React-hook wrapper
409
- * shapes live in `./react/types` (tRPC-like convenience shapes that
410
- * are not part of the platform wire).
411
- *
412
- * History: pre-ADR-084 this file mirrored every wire shape the SDK
413
- * exposed; those aliases now come directly from `@sylphx/contract`.
414
- */
415
-
416
- declare const OAUTH_PROVIDERS: readonly ["google", "github", "apple", "microsoft", "facebook", "twitter", "discord", "linkedin", "slack", "gitlab", "bitbucket", "twitch", "spotify"];
417
- type OAuthProviderId = (typeof OAUTH_PROVIDERS)[number];
418
- interface OAuthProviderInfo$1 {
419
- id: OAuthProviderId;
420
- name: string;
421
- }
422
- interface FeatureFlagDefinition$1 {
423
- key: string;
424
- name: string;
425
- description: string | null;
426
- enabled: boolean;
427
- rolloutPercentage: number;
428
- targetPremiumOnly: boolean;
429
- targetAdminOnly: boolean;
430
- }
431
- interface AppMetadata$1 {
432
- id: string;
433
- name: string;
434
- slug: string;
435
- }
436
- /** Fetched server-side via `getAppConfig()`, passed to `SylphxProvider`. */
437
- interface AppConfig {
438
- plans: Plan[];
439
- consentTypes: ConsentType[];
440
- oauthProviders: OAuthProviderInfo$1[];
441
- featureFlags: FeatureFlagDefinition$1[];
442
- app: AppMetadata$1;
443
- fetchedAt: string;
444
- }
445
- interface AccessTokenPayload {
446
- /** Raw UUID (OAuth/OIDC spec). */
447
- sub: string;
448
- /** Prefixed platform ID (e.g. `user_xxx`). */
449
- pid?: string;
450
- email: string;
451
- name?: string;
452
- picture?: string;
453
- email_verified: boolean;
454
- app_id: string;
455
- role?: string;
456
- iat: number;
457
- exp: number;
458
- }
459
-
460
- /**
461
- * API Key Validation — Single Source of Truth
462
- *
463
- * OAuth 2.0 standard key validation for Sylphx Platform.
464
- * ALL key validation, sanitization, and environment detection logic lives here.
465
- *
466
- * Principles:
467
- * 1. Fail fast - Invalid input rejected immediately with clear errors
468
- * 2. Helpful errors - Tell users exactly what's wrong and how to fix it
469
- * 3. Development warnings - Warn about issues that would fail in production
470
- * 4. No silent fixes - Transparency over convenience (but warn + continue)
471
- * 5. Single Source of Truth - All key logic in one place
472
- *
473
- * Key Formats (ADR-021):
474
- * - Publishable key: pk_(dev|stg|prod)_{ref}_{32hex} — client-safe (new)
475
- * - Secret Key: sk_(dev|stg|prod)_{ref}_{64hex} — server-side only
476
- *
477
- * Legacy Key Formats (backward-compat):
478
- * - App ID (old): app_(dev|stg|prod)_[identifier] — Public identifier
479
- *
480
- * Special Internal Formats (NOT rotated):
481
- * - Console bootstrap: app_prod_platform_{slug} / sk_prod_platform_{slug}
482
- */
483
- /** Environment type derived from key prefix */
484
- type EnvironmentType = 'development' | 'staging' | 'production';
485
- /** Key type - publicKey (pk_*), appId (legacy app_*), or secret (sk_*) */
486
- type KeyType = 'publicKey' | 'appId' | 'secret';
487
- /** Validation result with clear error information */
488
- interface KeyValidationResult {
489
- /** Whether the key is valid (possibly after sanitization) */
490
- valid: boolean;
491
- /** The sanitized key to use (only if valid) */
492
- sanitizedKey: string;
493
- /** Detected key type */
494
- keyType?: KeyType;
495
- /** Detected environment */
496
- environment?: EnvironmentType;
497
- /** Error message if invalid */
498
- error?: string;
499
- /** Warning message if key was auto-fixed */
500
- warning?: string;
501
- /** Detected issues for debugging */
502
- issues?: string[];
503
- }
504
- /**
505
- * Validate a legacy App ID (app_*) and return detailed results.
506
- *
507
- * @deprecated Use validatePublicKey() for new pk_* keys (ADR-021).
508
- *
509
- * @example
510
- * ```typescript
511
- * const result = validateAppId(process.env.NEXT_PUBLIC_SYLPHX_APP_ID)
512
- * if (!result.valid) {
513
- * throw new Error(result.error)
514
- * }
515
- * if (result.warning) {
516
- * console.warn(result.warning)
517
- * }
518
- * ```
519
- */
520
- declare function validateAppId(key: string | undefined | null): KeyValidationResult;
521
- /**
522
- * Validate and sanitize App ID, logging warnings.
523
- *
524
- * @deprecated Use validateAndSanitizePublicKey() for new pk_* keys (ADR-021).
525
- * @throws Error if the key is invalid and cannot be sanitized
526
- * @returns The sanitized App ID
527
- */
528
- declare function validateAndSanitizeAppId(key: string | undefined | null): string;
529
- /**
530
- * Validate a secret key and return detailed results
531
- *
532
- * @example
533
- * ```typescript
534
- * const result = validateSecretKey(process.env.SYLPHX_SECRET_KEY)
535
- * if (!result.valid) {
536
- * throw new Error(result.error)
537
- * }
538
- * ```
539
- */
540
- declare function validateSecretKey(key: string | undefined | null): KeyValidationResult;
541
- /**
542
- * Validate and sanitize secret key, logging warnings
543
- *
544
- * @throws Error if the key is invalid and cannot be sanitized
545
- * @returns The sanitized secret key
546
- */
547
- declare function validateAndSanitizeSecretKey(key: string | undefined | null): string;
548
- /**
549
- * Detect environment type from any key (App ID or Secret Key)
550
- *
551
- * @example
552
- * ```typescript
553
- * detectEnvironment('sk_dev_abc123') // 'development'
554
- * detectEnvironment('app_prod_xyz789') // 'production'
555
- * detectEnvironment('sk_stg_qwe456') // 'staging'
556
- * ```
557
- *
558
- * @throws Error if key format is invalid
559
- */
560
- declare function detectEnvironment(key: string): EnvironmentType;
561
- /**
562
- * Check if running in development environment based on key
563
- */
564
- declare function isDevelopmentKey(key: string): boolean;
565
- /**
566
- * Check if running in production environment based on key
567
- */
568
- declare function isProductionKey(key: string): boolean;
569
- /**
570
- * Get the cookie namespace for a given secret key
571
- *
572
- * Used by auth middleware to namespace cookies per environment.
573
- * This prevents dev/staging/prod cookies from conflicting.
574
- *
575
- * @example
576
- * ```typescript
577
- * getCookieNamespace('sk_dev_abc123') // 'sylphx_dev'
578
- * getCookieNamespace('sk_prod_xyz789') // 'sylphx_prod'
579
- * ```
580
- */
581
- declare function getCookieNamespace(secretKey: string): string;
582
- /**
583
- * Detect the type of key.
584
- *
585
- * @returns 'publicKey' (pk_*), 'appId' (legacy app_*), 'secret' (sk_*), or null if unknown
586
- */
587
- declare function detectKeyType(key: string): KeyType | null;
588
- /**
589
- * Check if a key is an App ID (legacy app_* format)
590
- *
591
- * @deprecated Use isPublishableKey() to also accept new pk_* keys
592
- */
593
- declare function isAppId(key: string): boolean;
594
- /**
595
- * Check if a key is a secret key (sk_*)
596
- */
597
- declare function isSecretKey(key: string): boolean;
598
- /**
599
- * Validate any key (auto-detects type)
600
- *
601
- * Use this when you accept either App ID or Secret Key.
602
- * The function auto-detects the key type and validates accordingly.
603
- *
604
- * @example
605
- * ```typescript
606
- * const result = validateKey(process.env.SYLPHX_SECRET_KEY)
607
- * if (!result.valid) {
608
- * throw new Error(result.error)
609
- * }
610
- * const sanitizedKey = result.sanitizedKey
611
- * ```
612
- */
613
- declare function validateKey(key: string | undefined | null): KeyValidationResult;
614
- /**
615
- * Validate any key and return sanitized version (throws on error)
616
- *
617
- * Use this when you need the key value and want to throw on invalid input.
618
- */
619
- declare function validateAndSanitizeKey(key: string | undefined | null): string;
620
- /**
621
- * Check if we're in development mode (based on NODE_ENV or hostname)
622
- */
623
- declare function isDevelopmentRuntime(): boolean;
624
-
625
- /**
626
- * Functions Client — Serverless V8 Isolate Functions (ADR-043)
627
- *
628
- * Deploy and manage serverless functions powered by Supabase Edge Runtime.
629
- * Functions run in isolated V8 contexts with < 5ms cold starts.
630
- *
631
- * ## Key Concepts
632
- *
633
- * - Functions are TypeScript/JavaScript handlers deployed to the Sylphx edge runtime
634
- * - Invoked via HTTP: GET/POST https://{project}.sylphx.app/functions/{name}
635
- * - Can access all Sylphx BaaS services via the SDK inside the handler
636
- * - Isolated per project — no shared state between projects
637
- *
638
- * ## Quick Start
639
- *
640
- * ```typescript
641
- * // functions/hello-world/index.ts — your function code
642
- * import { createServerClient } from '@sylphx/sdk/server'
643
- *
644
- * export default async function handler(req: Request): Promise<Response> {
645
- * const { name } = await req.json()
646
- * return Response.json({ message: `Hello, ${name}!` })
647
- * }
648
- * ```
649
- *
650
- * ```bash
651
- * # Deploy via CLI
652
- * sylphx functions deploy hello-world
653
- * # → https://my-project.sylphx.app/functions/hello-world
654
- * ```
655
- *
656
- * ```typescript
657
- * // Or deploy programmatically via SDK
658
- * import { createConfig, FunctionsClient } from '@sylphx/sdk'
659
- *
660
- * const config = createConfig({ secretKey: process.env.SYLPHX_SECRET_KEY! })
661
- *
662
- * await FunctionsClient.deploy(config, {
663
- * name: 'hello-world',
664
- * code: `export default async (req) => Response.json({ ok: true })`,
665
- * })
666
- *
667
- * // Invoke the function
668
- * const result = await FunctionsClient.invoke(config, 'hello-world', { name: 'world' })
669
- * ```
670
- *
671
- * ## Available in handler
672
- *
673
- * All Web Standard APIs are available inside function handlers:
674
- * - `fetch()` — HTTP requests
675
- * - `Request`, `Response`, `Headers`, `URL`
676
- * - `crypto` — Web Crypto API
677
- * - `TextEncoder`, `TextDecoder`
678
- * - `Deno.env.get('KEY')` — access injected env vars
679
- *
680
- * ## Constraints
681
- *
682
- * - Memory: 150 MB default (max 512 MB)
683
- * - Timeout: 30s default (max 120s)
684
- * - No filesystem access (`fs` module unavailable)
685
- * - No raw socket access (`net` module unavailable)
686
- * - npm packages via CDN URL: `import { z } from 'https://esm.sh/zod'`
687
- */
688
-
689
- type FunctionStatus = 'active' | 'deploying' | 'failed' | 'disabled';
690
- type FunctionRuntime = 'deno';
691
- interface FunctionDeployOptions {
692
- /**
693
- * Function slug — URL-safe name (lowercase kebab-case).
694
- * Used in the invocation URL: /functions/{name}
695
- */
696
- name: string;
697
- /**
698
- * TypeScript/JavaScript source code.
699
- * Must export a default handler function:
700
- * export default async function(req: Request): Promise<Response> { ... }
701
- *
702
- * For functions > 100KB, use the CLI: sylphx functions deploy
703
- */
704
- code: string;
705
- /** Human-readable display name */
706
- displayName?: string;
707
- /** Short description shown in console */
708
- description?: string;
709
- /** Memory limit in MB (default: 150, max: 512) */
710
- memoryMb?: number;
711
- /** Execution timeout in seconds (default: 30, max: 120) */
712
- timeoutSeconds?: number;
713
- /** Per-function env var overrides (merged with project env vars) */
714
- envVars?: Record<string, string>;
715
- /** Whether invocations require Sylphx platform auth (default: false) */
716
- requireAuth?: boolean;
717
- /** Target environment slug (default: 'production') */
718
- environment?: string;
719
- }
720
- interface FunctionRecord {
721
- id: string;
722
- name: string;
723
- displayName: string | null;
724
- description: string | null;
725
- status: FunctionStatus;
726
- version: number;
727
- runtime: FunctionRuntime;
728
- memoryMb: number;
729
- timeoutSeconds: number;
730
- requireAuth: boolean;
731
- invocationCount: number;
732
- errorCount: number;
733
- lastInvokedAt: string | null;
734
- /** Invocation URL */
735
- url: string;
736
- createdAt: string;
737
- updatedAt: string;
738
- }
739
- interface FunctionDeployResult {
740
- id: string;
741
- name: string;
742
- version: number;
743
- status: FunctionStatus;
744
- /** The live invocation URL */
745
- url: string;
746
- }
747
- interface FunctionInvokeOptions {
748
- /** HTTP method (default: POST) */
749
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
750
- /** Request headers */
751
- headers?: Record<string, string>;
752
- /** Request body (JSON-serialized automatically) */
753
- body?: unknown;
754
- /** Query parameters appended to the URL */
755
- query?: Record<string, string>;
756
- }
757
- interface FunctionListOptions {
758
- /** Filter by status */
759
- status?: FunctionStatus;
760
- /** Environment slug (default: 'production') */
761
- environment?: string;
762
- /** Max results (default: 50) */
763
- limit?: number;
764
- }
765
- interface FunctionLogsOptions {
766
- /** Maximum log entries to return (default: 100) */
767
- limit?: number;
768
- /** Only return errors */
769
- errorsOnly?: boolean;
770
- /** ISO timestamp — only logs after this time */
771
- since?: string;
772
- }
773
- interface FunctionLogEntry {
774
- id: string;
775
- status: 'success' | 'error' | 'timeout';
776
- statusCode: number | null;
777
- durationMs: number | null;
778
- method: string;
779
- path: string | null;
780
- errorMessage: string | null;
781
- createdAt: string;
782
- }
783
- declare const FunctionsClient: {
784
- /**
785
- * Deploy (create or update) a function.
786
- *
787
- * If a function with the same name already exists, it is updated
788
- * (version number incremented, code replaced atomically).
789
- *
790
- * @example
791
- * ```typescript
792
- * const fn = await FunctionsClient.deploy(config, {
793
- * name: 'send-webhook',
794
- * code: `
795
- * export default async (req: Request) => {
796
- * const body = await req.json()
797
- * await fetch(body.url, { method: 'POST', body: JSON.stringify(body.payload) })
798
- * return Response.json({ ok: true })
799
- * }
800
- * `,
801
- * timeoutSeconds: 10,
802
- * })
803
- * console.log('Deployed:', fn.url)
804
- * ```
805
- */
806
- readonly deploy: (config: SylphxConfig, options: FunctionDeployOptions) => Promise<FunctionDeployResult>;
807
- /**
808
- * Get a function by name.
809
- */
810
- readonly get: (config: SylphxConfig, name: string) => Promise<FunctionRecord>;
811
- /**
812
- * List all functions for this project.
813
- */
814
- readonly list: (config: SylphxConfig, options?: FunctionListOptions) => Promise<FunctionRecord[]>;
815
- /**
816
- * Delete a function permanently.
817
- */
818
- readonly delete: (config: SylphxConfig, name: string) => Promise<void>;
819
- /**
820
- * Invoke a function from server-side code.
821
- *
822
- * For client-side invocations, call the function URL directly via fetch().
823
- *
824
- * @example
825
- * ```typescript
826
- * const result = await FunctionsClient.invoke(config, 'process-order', {
827
- * body: { orderId: '123', userId: 'user_abc' },
828
- * })
829
- * ```
830
- */
831
- readonly invoke: <T = unknown>(config: SylphxConfig, name: string, body?: unknown, options?: FunctionInvokeOptions) => Promise<T>;
832
- /**
833
- * Retrieve recent invocation logs for a function.
834
- *
835
- * @example
836
- * ```typescript
837
- * const logs = await FunctionsClient.logs(config, 'send-webhook', { errorsOnly: true })
838
- * for (const entry of logs) {
839
- * console.log(`${entry.status} ${entry.durationMs}ms`, entry.errorMessage)
840
- * }
841
- * ```
842
- */
843
- readonly logs: (config: SylphxConfig, name: string, options?: FunctionLogsOptions) => Promise<FunctionLogEntry[]>;
844
- };
845
-
846
- /**
847
- * Platform ID utilities for the Sylphx SDK
848
- *
849
- * Converts between raw UUIDs (used internally / in JWT sub claims) and
850
- * prefixed TypeID strings (used in all API responses and JWT pid claims).
851
- *
852
- * Uses TypeID spec v0.3.0 (Crockford base32, case-insensitive).
853
- * Also accepts legacy base58 format for backward compatibility.
854
- *
855
- * No external dependencies — pure TypeScript implementation of Crockford base32.
856
- *
857
- * @example
858
- * ```ts
859
- * import { encodeUserId, decodeUserId } from '@sylphx/sdk/nextjs'
860
- *
861
- * const prefixed = encodeUserId('018f4a3b-1c2d-7000-9abc-def012345678')
862
- * // => 'user_01h2xcejqtf2nbrexx3vqjhp41'
863
- *
864
- * const uuid = decodeUserId('user_01h2xcejqtf2nbrexx3vqjhp41')
865
- * // => '018f4a3b-1c2d-7000-9abc-def012345678'
866
- * ```
867
- */
868
- /**
869
- * Encode a raw UUID as a prefixed TypeID user ID.
870
- *
871
- * @param uuid - Raw UUID string (with or without dashes)
872
- * @returns Prefixed TypeID: `user_<crockford_base32>`
873
- */
874
- declare function encodeUserId(uuid: string): string;
875
- /**
876
- * Decode a prefixed user ID back to a raw UUID.
877
- * Accepts both TypeID (current) and base58 (legacy) formats.
878
- *
879
- * @param prefixedId - Prefixed user ID: `user_<encoded>`
880
- * @returns Raw UUID string, or null if invalid
881
- */
882
- declare function decodeUserId(prefixedId: string): string | null;
883
-
884
- /**
885
- * Server-side AI Client
886
- *
887
- * Creates an OpenAI-compatible client configured for Sylphx Platform.
888
- * Use this in Server Components, API routes, and server actions.
889
- *
890
- * @example
891
- * ```ts
892
- * import { createAI } from '@sylphx/platform-sdk/server'
893
- *
894
- * const ai = createAI()
895
- *
896
- * // Chat completion
897
- * const response = await ai.chat({
898
- * model: 'anthropic/claude-3.5-sonnet',
899
- * messages: [{ role: 'user', content: 'Hello!' }],
900
- * })
901
- *
902
- * // Embeddings
903
- * const embeddings = await ai.embed({
904
- * model: 'openai/text-embedding-3-small',
905
- * input: 'Hello world',
906
- * })
907
- *
908
- * // Streaming
909
- * const stream = await ai.chat({
910
- * model: 'anthropic/claude-3.5-sonnet',
911
- * messages: [{ role: 'user', content: 'Tell me a story' }],
912
- * stream: true,
913
- * })
914
- *
915
- * for await (const chunk of stream) {
916
- * process.stdout.write(chunk.choices[0]?.delta?.content || '')
917
- * }
918
- * ```
919
- */
920
- interface AIClientOptions {
921
- /** Secret key for authentication (default: SYLPHX_SECRET_KEY env var) */
922
- secretKey?: string;
923
- /** Platform URL (default: https://api.sylphx.com) */
924
- platformUrl?: string;
925
- }
926
-
927
- interface ChatCompletionOptions {
928
- model: string;
929
- messages: ChatMessage[];
930
- temperature?: number;
931
- max_tokens?: number;
932
- top_p?: number;
933
- frequency_penalty?: number;
934
- presence_penalty?: number;
935
- stop?: string | string[];
936
- stream?: false;
937
- user?: string;
938
- }
939
- interface ChatCompletionStreamOptions extends Omit<ChatCompletionOptions, 'stream'> {
940
- stream: true;
941
- }
942
- interface ChatCompletionResponse {
943
- id: string;
944
- object: 'chat.completion';
945
- created: number;
946
- model: string;
947
- choices: Array<{
948
- index: number;
949
- message: {
950
- role: 'assistant';
951
- content: string;
952
- };
953
- finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null;
954
- }>;
955
- usage: {
956
- prompt_tokens: number;
957
- completion_tokens: number;
958
- total_tokens: number;
959
- };
960
- }
961
- interface ChatCompletionChunk {
962
- id: string;
963
- object: 'chat.completion.chunk';
964
- created: number;
965
- model: string;
966
- choices: Array<{
967
- index: number;
968
- delta: {
969
- role?: 'assistant';
970
- content?: string;
971
- };
972
- finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null;
973
- }>;
974
- }
975
- interface EmbeddingOptions {
976
- model: string;
977
- input: string | string[];
978
- dimensions?: number;
979
- user?: string;
980
- }
981
- interface EmbeddingResponse {
982
- object: 'list';
983
- data: Array<{
984
- object: 'embedding';
985
- index: number;
986
- embedding: number[];
987
- }>;
988
- model: string;
989
- usage: {
990
- prompt_tokens: number;
991
- total_tokens: number;
992
- };
993
- }
994
- interface ModelInfo {
995
- id: string;
996
- name: string;
997
- context_length: number;
998
- pricing: {
999
- prompt: string;
1000
- completion: string;
1001
- };
1002
- capabilities: string[];
1003
- }
1004
- interface ModelsResponse {
1005
- object: 'list';
1006
- data: ModelInfo[];
1007
- }
1008
- interface AIClient {
1009
- /** Create a chat completion */
1010
- chat(options: ChatCompletionOptions): Promise<ChatCompletionResponse>;
1011
- /** Create a streaming chat completion */
1012
- chat(options: ChatCompletionStreamOptions): Promise<AsyncIterable<ChatCompletionChunk>>;
1013
- /** Create embeddings */
1014
- embed(options: EmbeddingOptions): Promise<EmbeddingResponse>;
1015
- /** List available models */
1016
- listModels(options?: {
1017
- capability?: string;
1018
- search?: string;
1019
- }): Promise<ModelsResponse>;
1020
- }
1021
- /**
1022
- * Create a server-side AI client
1023
- *
1024
- * Uses environment variables by default:
1025
- *
1026
- * - SYLPHX_SECRET_KEY: Your app's secret key (sk_dev_xxx, sk_stg_xxx, sk_prod_xxx)
1027
- */
1028
- declare function createAI(options?: AIClientOptions): AIClient;
1029
- /**
1030
- * Get the default AI client (singleton)
1031
- * Creates one using environment variables on first call
1032
- */
1033
- declare function getAI(): AIClient;
1034
-
1035
- /**
1036
- * KV (Key-Value Store) Types
1037
- *
1038
- * Shared type definitions for KV operations.
1039
- * Used by both server-side client (server/kv.ts) and React hooks (react/hooks/use-kv.ts).
1040
- *
1041
- * Single Source of Truth — never duplicate these types.
1042
- */
1043
- /** TTL options for SET operations */
1044
- interface KvSetOptions {
1045
- /** Expire time in seconds */
1046
- ex?: number;
1047
- /** Expire time in milliseconds */
1048
- px?: number;
1049
- /** Unix timestamp (seconds) at which the key will expire */
1050
- exat?: number;
1051
- /** Unix timestamp (milliseconds) at which the key will expire */
1052
- pxat?: number;
1053
- /** Only set the key if it does not already exist */
1054
- nx?: boolean;
1055
- /** Only set the key if it already exists */
1056
- xx?: boolean;
1057
- }
1058
- /** Rate limit result */
1059
- interface KvRateLimitResult {
1060
- /** Whether the request is allowed (under rate limit) */
1061
- success: boolean;
1062
- /** The rate limit maximum */
1063
- limit: number;
1064
- /** Remaining requests in current window */
1065
- remaining: number;
1066
- /** Unix timestamp (ms) when the rate limit resets */
1067
- reset: number;
1068
- }
1069
- /** Sorted set member */
1070
- interface KvZMember {
1071
- /** Member identifier */
1072
- member: string;
1073
- /** Score for ranking */
1074
- score?: number;
1075
- }
1076
-
1077
- /**
1078
- * Server-side KV (Key-Value Store) Client
1079
- *
1080
- * Creates a client for key-value operations via Upstash Redis.
1081
- * Use this in Server Components, API routes, and server actions.
1082
- *
1083
- * @example
1084
- * ```ts
1085
- * import { createKv } from '@sylphx/sdk/server'
1086
- *
1087
- * const kv = createKv()
1088
- *
1089
- * // Basic operations
1090
- * await kv.set('user:123:profile', { name: 'John' }, { ex: 3600 })
1091
- * const profile = await kv.get('user:123:profile')
1092
- *
1093
- * // Atomic counter
1094
- * const views = await kv.incr('page:home:views')
1095
- *
1096
- * // Rate limiting
1097
- * const { success, remaining } = await kv.ratelimit('api:user123', {
1098
- * limit: 100,
1099
- * window: '1h',
1100
- * })
1101
- * ```
1102
- */
1103
-
1104
- interface KvClientOptions {
1105
- /** Secret key for authentication (default: SYLPHX_SECRET_KEY env var) */
1106
- secretKey?: string;
1107
- /** Platform URL (default: https://api.sylphx.com) */
1108
- platformUrl?: string;
1109
- }
1110
- interface KvClient {
1111
- /**
1112
- * Get a value by key
1113
- *
1114
- * @param key - Key name
1115
- * @returns The value, or null if not found
1116
- */
1117
- get<T = unknown>(key: string): Promise<{
1118
- value: T | null;
1119
- ttl: number | null;
1120
- }>;
1121
- /**
1122
- * Set a value
1123
- *
1124
- * @param key - Key name
1125
- * @param value - Any JSON-serializable value
1126
- * @param options - TTL and conditional options (ex, px, nx, xx)
1127
- * @returns Whether the SET was successful
1128
- */
1129
- set<T = unknown>(key: string, value: T, options?: KvSetOptions): Promise<boolean>;
1130
- /**
1131
- * Delete a key
1132
- *
1133
- * @param key - Key name
1134
- * @returns Number of keys deleted (0 or 1)
1135
- */
1136
- del(key: string): Promise<number>;
1137
- /**
1138
- * Check if a key exists
1139
- *
1140
- * @param key - Key name
1141
- * @returns Whether the key exists
1142
- */
1143
- exists(key: string): Promise<boolean>;
1144
- /**
1145
- * Get multiple values
1146
- *
1147
- * @param keys - Array of keys to get (max 100)
1148
- * @returns Map of key -> value (null if not found)
1149
- */
1150
- mget<T = unknown>(keys: string[]): Promise<Record<string, T | null>>;
1151
- /**
1152
- * Set multiple values
1153
- *
1154
- * @param entries - Array of { key, value } pairs (max 100)
1155
- * @param options - TTL options (ex or px only)
1156
- */
1157
- mset<T = unknown>(entries: Array<{
1158
- key: string;
1159
- value: T;
1160
- }>, options?: Pick<KvSetOptions, 'ex' | 'px'>): Promise<void>;
1161
- /**
1162
- * Increment a numeric value atomically
1163
- *
1164
- * Creates key with value 0 if it does not exist.
1165
- *
1166
- * @param key - Key name
1167
- * @param by - Amount to increment by (default: 1, use negative for decrement)
1168
- * @returns The value after increment
1169
- */
1170
- incr(key: string, by?: number): Promise<number>;
1171
- /**
1172
- * Set key expiration
1173
- *
1174
- * @param key - Key name
1175
- * @param seconds - TTL in seconds
1176
- * @returns Whether the TTL was set (false if key does not exist)
1177
- */
1178
- expire(key: string, seconds: number): Promise<boolean>;
1179
- /**
1180
- * Check rate limit using sliding window algorithm
1181
- *
1182
- * @param key - Rate limit identifier (e.g., "api:user123")
1183
- * @param options - Limit and window configuration
1184
- * @returns Rate limit status
1185
- *
1186
- * @example
1187
- * ```ts
1188
- * const { success, remaining, reset } = await kv.ratelimit('api:user123', {
1189
- * limit: 100,
1190
- * window: '1h',
1191
- * })
1192
- *
1193
- * if (!success) {
1194
- * throw new Error('Rate limit exceeded')
1195
- * }
1196
- * ```
1197
- */
1198
- ratelimit(key: string, options: {
1199
- limit: number;
1200
- window: string;
1201
- }): Promise<KvRateLimitResult>;
1202
- /**
1203
- * Set hash fields
1204
- *
1205
- * @param key - Hash key
1206
- * @param fields - Field-value pairs to set
1207
- * @returns Number of fields that were created (not updated)
1208
- */
1209
- hset<T = unknown>(key: string, fields: Record<string, T>): Promise<number>;
1210
- /**
1211
- * Get a hash field
1212
- *
1213
- * @param key - Hash key
1214
- * @param field - Field name
1215
- * @returns Field value, or null if not found
1216
- */
1217
- hget<T = unknown>(key: string, field: string): Promise<T | null>;
1218
- /**
1219
- * Get all hash fields
1220
- *
1221
- * @param key - Hash key
1222
- * @returns All field-value pairs, or null if key does not exist
1223
- */
1224
- hgetall<T = unknown>(key: string): Promise<Record<string, T> | null>;
1225
- /**
1226
- * Push values to the left of a list
1227
- *
1228
- * @param key - List key
1229
- * @param values - Values to push
1230
- * @returns Length of the list after push
1231
- */
1232
- lpush<T = unknown>(key: string, ...values: T[]): Promise<number>;
1233
- /**
1234
- * Get a range of elements from a list
1235
- *
1236
- * @param key - List key
1237
- * @param start - Start index (0-based, negative for from end)
1238
- * @param stop - Stop index (inclusive, -1 for end)
1239
- * @returns Values in the specified range
1240
- */
1241
- lrange<T = unknown>(key: string, start?: number, stop?: number): Promise<T[]>;
1242
- /**
1243
- * Add members with scores to a sorted set
1244
- *
1245
- * @param key - Sorted set key
1246
- * @param members - Score-member pairs
1247
- * @returns Number of members added (not updated)
1248
- */
1249
- zadd(key: string, ...members: Array<{
1250
- score: number;
1251
- member: string;
1252
- }>): Promise<number>;
1253
- /**
1254
- * Get members from a sorted set by rank range
1255
- *
1256
- * @param key - Sorted set key
1257
- * @param start - Start rank (0-based)
1258
- * @param stop - Stop rank (inclusive)
1259
- * @param options - Include scores and/or reverse order
1260
- * @returns Members in the specified range
1261
- */
1262
- zrange(key: string, start?: number, stop?: number, options?: {
1263
- withScores?: boolean;
1264
- rev?: boolean;
1265
- }): Promise<KvZMember[]>;
1266
- }
1267
- /**
1268
- * Create a server-side KV client
1269
- *
1270
- * Uses environment variables by default:
1271
- * - SYLPHX_SECRET_KEY: Your app's secret key (sk_dev_xxx, sk_stg_xxx, sk_prod_xxx)
1272
- */
1273
- declare function createKv(options?: KvClientOptions): KvClient;
1274
- /**
1275
- * Get the default KV client (singleton)
1276
- * Creates one using environment variables on first call
1277
- */
1278
- declare function getKv(): KvClient;
1279
-
1280
- /**
1281
- * Shared Realtime Types
1282
- *
1283
- * Single source of truth for types used by both:
1284
- * - Server: streams.ts (createStreams)
1285
- * - React: use-realtime.ts (useRealtime hook)
1286
- */
1287
- /** A message from a stream */
1288
- interface StreamMessage<T = unknown> {
1289
- /** Stream entry ID (e.g., "1234567890-0") */
1290
- id: string;
1291
- /** Event type */
1292
- event: string;
1293
- /** Channel the message was sent to */
1294
- channel: string;
1295
- /** Event data */
1296
- data: T;
1297
- /** Unix timestamp in milliseconds */
1298
- timestamp?: number;
1299
- }
1300
-
1301
- /**
1302
- * Server-side Streams Client
1303
- *
1304
- * Creates a client for real-time pub/sub via Redis Streams.
1305
- * Use this in Server Components, API routes, and server actions.
1306
- *
1307
- * @example
1308
- * ```ts
1309
- * import { createStreams } from '@sylphx/sdk/server'
1310
- *
1311
- * const streams = createStreams()
1312
- *
1313
- * // Emit an event
1314
- * const id = await streams.emit('chat:room-1', 'message', {
1315
- * text: 'Hello!',
1316
- * userId: '123',
1317
- * })
1318
- *
1319
- * // Get message history
1320
- * const messages = await streams.history('chat:room-1', { limit: 50 })
1321
- *
1322
- * // Using channel helper
1323
- * const room = streams.channel('chat:room-1')
1324
- * await room.emit('message', { text: 'Hello!' })
1325
- * const history = await room.history({ limit: 50 })
1326
- * ```
1327
- */
1328
-
1329
- interface StreamsClientOptions {
1330
- /** Secret key for authentication (default: SYLPHX_SECRET_KEY env var) */
1331
- secretKey?: string;
1332
- /** Platform URL (default: https://api.sylphx.com) */
1333
- platformUrl?: string;
1334
- }
1335
- /** Options for fetching stream history */
1336
- interface StreamHistoryOptions {
1337
- /** Start ID ("-" for oldest) */
1338
- start?: string;
1339
- /** End ID ("+" for newest) */
1340
- end?: string;
1341
- /** Maximum number of messages (max 1000) */
1342
- limit?: number;
1343
- }
1344
- /** Helper for a specific channel */
1345
- interface ChannelHelper {
1346
- /** Emit an event to this channel */
1347
- emit<T>(event: string, data: T): Promise<string>;
1348
- /** Get message history for this channel */
1349
- history<T>(options?: StreamHistoryOptions): Promise<StreamMessage<T>[]>;
1350
- }
1351
- interface StreamsClient {
1352
- /**
1353
- * Emit an event to a channel
1354
- *
1355
- * @param channel - Channel name (e.g., "chat:room-1")
1356
- * @param event - Event type (e.g., "message")
1357
- * @param data - Event data (any JSON-serializable value)
1358
- * @returns Stream entry ID
1359
- */
1360
- emit<T>(channel: string, event: string, data: T): Promise<string>;
1361
- /**
1362
- * Get message history for a channel
1363
- *
1364
- * @param channel - Channel name
1365
- * @param options - History options (start, end, limit)
1366
- * @returns Array of messages in chronological order
1367
- */
1368
- history<T>(channel: string, options?: StreamHistoryOptions): Promise<StreamMessage<T>[]>;
1369
- /**
1370
- * Get a channel helper for convenient operations
1371
- *
1372
- * @param channel - Channel name
1373
- * @returns Channel helper with emit() and history()
1374
- */
1375
- channel(name: string): ChannelHelper;
1376
- }
1377
- /**
1378
- * Create a server-side Streams client
1379
- *
1380
- * Uses environment variables by default:
1381
- *
1382
- * - SYLPHX_SECRET_KEY: Your app's secret key (sk_dev_xxx, sk_stg_xxx, sk_prod_xxx)
1383
- */
1384
- declare function createStreams(options?: StreamsClientOptions): StreamsClient;
1385
- /**
1386
- * Get the default Streams client (singleton)
1387
- * Creates one using environment variables on first call
1388
- */
1389
- declare function getStreams(): StreamsClient;
1390
-
1391
- /**
1392
- * Sylphx Server SDK
1393
- *
1394
- * Server-side operations using REST API for type safety.
1395
- * Uses connection URL (sylphx://sk_*@slug.sylphx.com) for authentication.
1396
- *
1397
- * @example
1398
- * ```typescript
1399
- * import { createServerClient, verifyWebhook } from '@sylphx/sdk/server'
1400
- *
1401
- * const client = createServerRestClient({
1402
- * secretKey: process.env.SYLPHX_SECRET_URL!,
1403
- * })
1404
- *
1405
- * // REST API calls
1406
- * const { data: plans } = await client.GET('/billing/plans')
1407
- * const { data: user } = await client.GET('/user/profile')
1408
- * ```
1409
- */
1410
-
1411
- interface ServerConfig {
1412
- /**
1413
- * Your Secret Key (keep secure, server-only)
1414
- *
1415
- * The secret key IS the app identity — no separate app ID needed.
1416
- * Use environment-specific keys for proper isolation:
1417
- * - Development: sk_dev_xxx (relaxed rate limits, test mode)
1418
- * - Staging: sk_stg_xxx (test mode, production-like settings)
1419
- * - Production: sk_prod_xxx (strict settings, live data)
1420
- */
1421
- secretKey: string;
1422
- /** Platform URL (defaults to https://sylphx.com) */
1423
- platformUrl?: string;
1424
- /** Optional cache TTL in seconds. Default: 60 */
1425
- revalidate?: number;
1426
- }
1427
- /**
1428
- * Create a Server REST Client for type-safe API calls.
1429
- *
1430
- * For the new connection URL API, use `createServerClient()` from '@sylphx/sdk'.
1431
- * This function creates an openapi-fetch REST client from a raw secret key.
1432
- *
1433
- * @example
1434
- * ```typescript
1435
- * const client = createServerRestClient({
1436
- * secretKey: 'sk_prod_...',
1437
- * })
1438
- *
1439
- * const { data: plans } = await client.GET('/billing/plans')
1440
- * ```
1441
- */
1442
- declare function createServerRestClient(config: ServerConfig): RestClient;
1443
- /**
1444
- * Create a Server REST Client with user context (access token)
1445
- */
1446
- declare function createAuthenticatedServerClient(config: ServerConfig, accessToken: string): RestClient;
1447
- /**
1448
- * Reset the JWKS cache.
1449
- *
1450
- * Use in tests to prevent state bleed between test cases.
1451
- * In production, the cache auto-expires after JWK_CACHE_TTL_MS (1 hour).
1452
- *
1453
- * @example
1454
- * ```typescript
1455
- * afterEach(() => resetJwksCache())
1456
- * ```
1457
- */
1458
- declare function resetJwksCache(): void;
1459
- /**
1460
- * Fetch JWKS from the platform
1461
- */
1462
- declare function getJwks(platformUrl?: string): Promise<JsonWebKey[]>;
1463
- /**
1464
- * Verify an access token from the platform
1465
- */
1466
- declare function verifyAccessToken(token: string, options: {
1467
- secretKey?: string;
1468
- platformUrl?: string;
1469
- }): Promise<AccessTokenPayload>;
1470
- interface WebhookPayload {
1471
- event: string;
1472
- data: unknown;
1473
- timestamp: number;
1474
- id: string;
1475
- }
1476
- interface WebhookVerifyResult {
1477
- valid: boolean;
1478
- payload?: WebhookPayload;
1479
- error?: string;
1480
- }
1481
- interface WebhookVerifyOptions {
1482
- /** Maximum age of webhook in milliseconds (default: 5 minutes) */
1483
- maxAge?: number;
1484
- /** Allow clock skew in milliseconds (default: 30 seconds) */
1485
- clockSkew?: number;
1486
- }
1487
- /**
1488
- * Verify webhook signature from Sylphx Platform
1489
- *
1490
- * The platform sends `X-Webhook-Signature` in `t={unix_seconds},v1={hmac_hex}`
1491
- * format. This function accepts either:
1492
- * - The raw header value (auto-parsed)
1493
- * - Pre-extracted `signature` + `timestamp` strings
1494
- *
1495
- * @example
1496
- * ```typescript
1497
- * import { verifyWebhook } from '@sylphx/sdk/server'
1498
- *
1499
- * export async function POST(request: Request) {
1500
- * const body = await request.text()
1501
- * const result = await verifyWebhook({
1502
- * payload: body,
1503
- * signatureHeader: request.headers.get('x-webhook-signature'),
1504
- * secret: process.env.SYLPHX_SECRET_KEY!,
1505
- * })
1506
- *
1507
- * if (!result.valid) {
1508
- * return new Response('Invalid signature', { status: 401 })
1509
- * }
1510
- *
1511
- * console.log('Received webhook:', result.payload)
1512
- * }
1513
- * ```
1514
- */
1515
- declare function verifyWebhook(options: {
1516
- payload: string;
1517
- /** Raw X-Webhook-Signature header value: "t={seconds},v1={hex}" */
1518
- signatureHeader?: string | null;
1519
- /** Pre-extracted HMAC hex (if parsing header yourself) */
1520
- signature?: string | null;
1521
- /** Pre-extracted timestamp in unix seconds (if parsing header yourself) */
1522
- timestamp?: string | null;
1523
- secret: string;
1524
- verifyOptions?: WebhookVerifyOptions;
1525
- }): Promise<WebhookVerifyResult>;
1526
- /**
1527
- * Create a webhook handler with automatic verification
1528
- *
1529
- * @example
1530
- * ```typescript
1531
- * import { createWebhookHandler } from '@sylphx/sdk/server'
1532
- *
1533
- * const handler = createWebhookHandler({
1534
- * secret: process.env.SYLPHX_SECRET_KEY!,
1535
- * handlers: {
1536
- * 'user.created': async (data) => {
1537
- * console.log('New user:', data)
1538
- * },
1539
- * 'subscription.updated': async (data) => {
1540
- * console.log('Subscription changed:', data)
1541
- * },
1542
- * },
1543
- * })
1544
- *
1545
- * export { handler as POST }
1546
- * ```
1547
- */
1548
- declare function createWebhookHandler(config: {
1549
- secret: string;
1550
- handlers: Record<string, (data: unknown) => Promise<void> | void>;
1551
- verifyOptions?: WebhookVerifyOptions;
1552
- }): (request: Request) => Promise<Response>;
1553
-
1554
- /** Common options for authenticated SDK fetch — secret key identifies the app */
1555
- interface AuthenticatedFetchOptions {
1556
- /** Secret key for authentication (sk_dev_xxx, sk_stg_xxx, sk_prod_xxx) */
1557
- secretKey: string;
1558
- /** Platform URL (defaults to https://sylphx.com) */
1559
- platformUrl?: string;
1560
- /** Optional cache TTL in seconds. Default: 60 */
1561
- revalidate?: number;
1562
- }
1563
- /**
1564
- * Options for public endpoints that only need an App ID (public key).
1565
- * The App ID IS the app identity — no separate key needed.
1566
- */
1567
- interface PublicFetchOptions {
1568
- /** App ID (app_dev_xxx, app_stg_xxx, app_prod_xxx) — identifies the app */
1569
- appId: string;
1570
- /** Platform URL (defaults to https://sylphx.com) */
1571
- platformUrl?: string;
1572
- /** Optional cache TTL in seconds. Default: 60 */
1573
- revalidate?: number;
1574
- }
1575
-
1576
- /** OAuth provider with display name */
1577
- interface OAuthProviderInfo {
1578
- id: OAuthProvider;
1579
- name: string;
1580
- }
1581
- /**
1582
- * Get enabled OAuth providers for an app (server-side)
1583
- *
1584
- * The App ID identifies the app via `X-App-Id` header.
1585
- *
1586
- * @example
1587
- * ```tsx
1588
- * const providers = await getOAuthProviders({
1589
- * appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
1590
- * })
1591
- * ```
1592
- */
1593
- declare function getOAuthProviders(options: PublicFetchOptions): Promise<OAuthProvider[]>;
1594
- /**
1595
- * Get enabled OAuth providers with full info (server-side)
1596
- */
1597
- declare function getOAuthProvidersWithInfo(options: PublicFetchOptions): Promise<OAuthProviderInfo[]>;
1598
-
1599
- /**
1600
- * Get subscription plans for an app (server-side)
1601
- *
1602
- * Note: Prefer using `getAppConfig()` which fetches all config in parallel.
1603
- * This function is used internally by `getAppConfig()`.
1604
- */
1605
- declare function getPlans(options: AuthenticatedFetchOptions): Promise<Plan[]>;
1606
-
1607
- /**
1608
- * Get consent types for an app (server-side)
1609
- *
1610
- * Note: Prefer using `getAppConfig()` which fetches all config in parallel.
1611
- * This function is used internally by `getAppConfig()`.
1612
- */
1613
- declare function getConsentTypes(options: AuthenticatedFetchOptions): Promise<ConsentType[]>;
1614
- /** Feature flag definition for SSR */
1615
- interface FeatureFlagDefinition {
1616
- key: string;
1617
- name: string;
1618
- description: string | null;
1619
- enabled: boolean;
1620
- rolloutPercentage: number;
1621
- targetPremiumOnly: boolean;
1622
- targetAdminOnly: boolean;
1623
- }
1624
- /**
1625
- * Get feature flag definitions for an app (server-side)
1626
- *
1627
- * Returns flag definitions. Evaluation (rollout, targeting) happens
1628
- * client-side using the LocalEvaluator with user context.
1629
- *
1630
- * @example
1631
- * ```tsx
1632
- * import { getFeatureFlags } from '@sylphx/sdk/server'
1633
- *
1634
- * export default async function RootLayout({ children }) {
1635
- * const flags = await getFeatureFlags({
1636
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1637
- * })
1638
- * return <FeatureFlagsProvider initialFlags={flags}>{children}</FeatureFlagsProvider>
1639
- * }
1640
- * ```
1641
- */
1642
- declare function getFeatureFlags(options: AuthenticatedFetchOptions): Promise<FeatureFlagDefinition[]>;
1643
- /** App metadata returned by /api/sdk/app */
1644
- interface AppMetadata {
1645
- id: string;
1646
- name: string;
1647
- slug: string;
1648
- }
1649
- /**
1650
- * Get app metadata (server-side)
1651
- *
1652
- * @internal Used by getAppConfig() - rarely called directly
1653
- */
1654
- declare function getAppMetadata(options: AuthenticatedFetchOptions): Promise<AppMetadata>;
1655
-
1656
- /**
1657
- * Options for getAppConfig()
1658
- */
1659
- interface GetAppConfigOptions {
1660
- /** Secret key for authenticated endpoints (plans, flags, consent types) */
1661
- secretKey: string;
1662
- /** App ID for public endpoints (OAuth providers) - identifies your app */
1663
- appId: string;
1664
- /** Platform URL (defaults to https://sylphx.com) */
1665
- platformUrl?: string;
1666
- /** Optional cache TTL in seconds. Default: 60 */
1667
- revalidate?: number;
1668
- }
1669
- /**
1670
- * Get complete app configuration for the SDK (server-side)
1671
- *
1672
- * This is the **recommended** way to initialize the Sylphx SDK. It fetches all
1673
- * configuration data in a single call from a Server Component, eliminating the
1674
- * need for client-side config fetching and the associated loading states.
1675
- *
1676
- * Returns:
1677
- * - plans: Subscription plans for billing
1678
- * - consentTypes: GDPR/CCPA consent configuration
1679
- * - oauthProviders: Enabled OAuth providers for auth
1680
- * - featureFlags: Feature flag definitions for client-side evaluation
1681
- * - app: App metadata (id, name, slug)
1682
- * - fetchedAt: ISO timestamp for cache debugging
1683
- *
1684
- * @example
1685
- * ```tsx
1686
- * // app/layout.tsx (Server Component)
1687
- * import { getAppConfig } from '@sylphx/sdk/server'
1688
- *
1689
- * export default async function RootLayout({ children }) {
1690
- * const config = await getAppConfig({
1691
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1692
- * appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
1693
- * })
1694
- *
1695
- * return (
1696
- * <html>
1697
- * <body>
1698
- * <SylphxProvider
1699
- * config={config}
1700
- * appId={process.env.NEXT_PUBLIC_SYLPHX_APP_ID!}
1701
- * >
1702
- * {children}
1703
- * </SylphxProvider>
1704
- * </body>
1705
- * </html>
1706
- * )
1707
- * }
1708
- * ```
1709
- */
1710
- declare function getAppConfig(options: GetAppConfigOptions): Promise<AppConfig>;
1711
- /** Referral leaderboard entry */
1712
- interface ReferralLeaderboardEntry {
1713
- rank: number;
1714
- userId: string;
1715
- /** Masked username for privacy */
1716
- name: string;
1717
- /** Number of successful referrals */
1718
- referrals: number;
1719
- isCurrentUser: boolean;
1720
- }
1721
- /** Referral leaderboard result */
1722
- interface ReferralLeaderboardResult {
1723
- entries: ReferralLeaderboardEntry[];
1724
- total: number;
1725
- period: 'all' | 'month' | 'week';
1726
- }
1727
- /**
1728
- * Get referral leaderboard for an app (server-side)
1729
- *
1730
- * @example
1731
- * ```tsx
1732
- * import { getReferralLeaderboard } from '@sylphx/sdk/server'
1733
- *
1734
- * export default async function ReferralsPage() {
1735
- * const leaderboard = await getReferralLeaderboard({
1736
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1737
- * limit: 10,
1738
- * period: 'month',
1739
- * })
1740
- * return <ReferralLeaderboard initialData={leaderboard} />
1741
- * }
1742
- * ```
1743
- */
1744
- declare function getReferralLeaderboard(options: AuthenticatedFetchOptions & {
1745
- limit?: number;
1746
- period?: 'all' | 'month' | 'week';
1747
- }): Promise<ReferralLeaderboardResult>;
1748
- /** Engagement leaderboard entry */
1749
- interface EngagementLeaderboardEntry {
1750
- rank: number;
1751
- userId: string;
1752
- name: string;
1753
- score: number;
1754
- isCurrentUser: boolean;
1755
- }
1756
- /** Engagement leaderboard result */
1757
- interface EngagementLeaderboardResult {
1758
- leaderboardId: string;
1759
- entries: EngagementLeaderboardEntry[];
1760
- period: string;
1761
- resetTime: string | null;
1762
- userEntry: EngagementLeaderboardEntry | null;
1763
- }
1764
- /**
1765
- * Get engagement leaderboard for an app (server-side)
1766
- *
1767
- * @example
1768
- * ```tsx
1769
- * import { getEngagementLeaderboard } from '@sylphx/sdk/server'
1770
- *
1771
- * export default async function LeaderboardPage() {
1772
- * const leaderboard = await getEngagementLeaderboard({
1773
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1774
- * leaderboardId: 'high-scores',
1775
- * })
1776
- * return <Leaderboard initialData={leaderboard} />
1777
- * }
1778
- * ```
1779
- */
1780
- declare function getEngagementLeaderboard(options: AuthenticatedFetchOptions & {
1781
- leaderboardId: string;
1782
- limit?: number;
1783
- }): Promise<EngagementLeaderboardResult>;
1784
- /** Database connection info returned by Platform */
1785
- interface DatabaseConnectionInfo {
1786
- connectionString: string;
1787
- databaseName: string;
1788
- roleName: string | null;
1789
- status: 'provisioning' | 'ready' | 'suspended' | 'failed' | 'deleted';
1790
- }
1791
- /** Database status info */
1792
- interface DatabaseStatusInfo {
1793
- status: 'provisioning' | 'ready' | 'suspended' | 'failed' | 'deleted' | 'not_provisioned';
1794
- region: string | null;
1795
- pgVersion: number | null;
1796
- databaseName: string | null;
1797
- }
1798
- /**
1799
- * Get database connection string from Platform (server-side)
1800
- *
1801
- * Use this when your app's database is provisioned by the Sylphx Platform.
1802
- * The connection string is securely stored on Platform and retrieved at startup.
1803
- *
1804
- * **Note:** This requires NEON_API_KEY and PLATFORM_ENCRYPTION_KEY to be
1805
- * configured on the Platform, and your app's database to be provisioned.
1806
- *
1807
- * @example
1808
- * ```typescript
1809
- * import { getDatabaseConnection } from '@sylphx/sdk/server'
1810
- *
1811
- * // In your database initialization
1812
- * const dbInfo = await getDatabaseConnection({
1813
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1814
- * })
1815
- *
1816
- * if (dbInfo) {
1817
- * const pool = new Pool({ connectionString: dbInfo.connectionString })
1818
- * }
1819
- * ```
1820
- */
1821
- declare function getDatabaseConnection(options: AuthenticatedFetchOptions): Promise<DatabaseConnectionInfo | null>;
1822
- /**
1823
- * Get database status from Platform (server-side)
1824
- *
1825
- * Use this to check if your app's database is provisioned and ready.
1826
- *
1827
- * @example
1828
- * ```typescript
1829
- * import { getDatabaseStatus } from '@sylphx/sdk/server'
1830
- *
1831
- * const status = await getDatabaseStatus({
1832
- * secretKey: process.env.SYLPHX_SECRET_KEY!,
1833
- * })
1834
- *
1835
- * if (status?.status === 'ready') {
1836
- * // Database is ready to use
1837
- * }
1838
- * ```
1839
- */
1840
- declare function getDatabaseStatus(options: AuthenticatedFetchOptions): Promise<DatabaseStatusInfo>;
1841
-
1842
- export { type AIClient, type AIClientOptions, type AppConfig, type AppMetadata, type ChannelHelper, type ChatCompletionChunk, type ChatCompletionOptions, type ChatCompletionResponse, type ChatCompletionStreamOptions, type ChatMessage, type ConsentType, type DatabaseConnectionInfo, type DatabaseStatusInfo, type EmbeddingOptions, type EmbeddingResponse, type EngagementLeaderboardEntry, type EngagementLeaderboardResult, type EnvironmentType, type FeatureFlagDefinition, type FunctionDeployOptions, type FunctionDeployResult, type FunctionInvokeOptions, type FunctionListOptions, type FunctionLogEntry, type FunctionLogsOptions, type FunctionRecord, type FunctionRuntime, type FunctionStatus, FunctionsClient, type GetAppConfigOptions, InvalidConnectionUrlError, type KeyType, type KeyValidationResult, type KvClient, type KvClientOptions, type KvRateLimitResult, type KvSetOptions, type KvZMember, type ModelInfo, type ModelsResponse, type OAuthProviderInfo, type Plan, type ReferralLeaderboardEntry, type ReferralLeaderboardResult, type RestClient, type RestClientConfig as ServerClientConfig, type ServerConfig, type StreamHistoryOptions, type StreamMessage, type StreamsClient, type StreamsClientOptions, type SylphxClientInput, type SylphxConfig, type WebhookPayload, type WebhookVerifyOptions, type WebhookVerifyResult, createAI, createAuthenticatedServerClient, createClient, createKv, createServerClient, createServerRestClient, createStreams, createWebhookHandler, decodeUserId, detectEnvironment, detectKeyType, encodeUserId, getAI, getAppConfig, getAppMetadata, getConsentTypes, getCookieNamespace, getDatabaseConnection, getDatabaseStatus, getEngagementLeaderboard, getFeatureFlags, getJwks, getKv, getOAuthProviders, getOAuthProvidersWithInfo, getPlans, getReferralLeaderboard, getStreams, isAppId, isDevelopmentKey, isDevelopmentRuntime, isProductionKey, isSecretKey, resetJwksCache, validateAndSanitizeAppId, validateAndSanitizeKey, validateAndSanitizeSecretKey, validateAppId, validateKey, validateSecretKey, verifyAccessToken, verifyWebhook };