@sylphx/sdk 0.10.0 → 0.10.1
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.
- package/README.md +13 -12
- package/dist/index.d.ts +43 -23
- package/dist/index.mjs +81 -102
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.ts +19 -6
- package/dist/nextjs/index.mjs +127 -22
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.ts +20 -12
- package/dist/react/index.mjs +199 -153
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.ts +41 -34
- package/dist/server/index.mjs +81 -66
- package/dist/server/index.mjs.map +1 -1
- package/dist/web-analytics.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { SdkBillingPlan, SdkConsentType } from '@sylphx/contract';
|
|
|
15
15
|
* import { createRestClient } from '@sylphx/sdk'
|
|
16
16
|
*
|
|
17
17
|
* const client = createRestClient({
|
|
18
|
-
* secretKey: process.env.
|
|
18
|
+
* secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,
|
|
19
19
|
* })
|
|
20
20
|
*
|
|
21
21
|
* const { data: user, error } = await client.GET('/auth/me')
|
|
@@ -130,6 +130,10 @@ interface Middleware {
|
|
|
130
130
|
onRequest?: (ctx: {
|
|
131
131
|
request: Request;
|
|
132
132
|
}) => Promise<Request | undefined> | Request | undefined;
|
|
133
|
+
onFetch?: (ctx: {
|
|
134
|
+
request: Request;
|
|
135
|
+
next: (request: Request) => Promise<Response>;
|
|
136
|
+
}) => Promise<Response | undefined> | Response | undefined;
|
|
133
137
|
onResponse?: (ctx: {
|
|
134
138
|
request: Request;
|
|
135
139
|
response: Response;
|
|
@@ -192,7 +196,7 @@ declare class InvalidConnectionUrlError extends Error {
|
|
|
192
196
|
* import { createClient } from '@sylphx/sdk'
|
|
193
197
|
*
|
|
194
198
|
* const sylphx = createClient(process.env.SYLPHX_URL!)
|
|
195
|
-
* // Parses: sylphx://pk_prod_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
199
|
+
* // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
196
200
|
* ```
|
|
197
201
|
*/
|
|
198
202
|
|
|
@@ -264,7 +268,7 @@ interface SylphxClientInput {
|
|
|
264
268
|
* @example Connection URL (recommended)
|
|
265
269
|
* ```typescript
|
|
266
270
|
* const sylphx = createClient(process.env.NEXT_PUBLIC_SYLPHX_URL!)
|
|
267
|
-
* // Parses: sylphx://pk_prod_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
271
|
+
* // Parses: sylphx://pk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
268
272
|
* ```
|
|
269
273
|
*
|
|
270
274
|
* @example Explicit components
|
|
@@ -285,7 +289,7 @@ declare function createClient(input: string | SylphxClientInput): SylphxConfig;
|
|
|
285
289
|
* @example Connection URL (recommended)
|
|
286
290
|
* ```typescript
|
|
287
291
|
* const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
|
|
288
|
-
* // Parses: sylphx://sk_prod_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
292
|
+
* // Parses: sylphx://sk_prod_{ref?}_{hex}@bold-river-a1b2c3.api.sylphx.com
|
|
289
293
|
* ```
|
|
290
294
|
*
|
|
291
295
|
* @example Explicit components
|
|
@@ -469,17 +473,17 @@ interface AccessTokenPayload {
|
|
|
469
473
|
* 5. Single Source of Truth - All key logic in one place
|
|
470
474
|
*
|
|
471
475
|
* Key Formats (ADR-021):
|
|
472
|
-
* - Publishable key: pk_(dev|stg|prod)_{ref}_{32hex} — client-safe (new)
|
|
473
|
-
* - Secret Key: sk_(dev|stg|prod)_{ref}_{64hex} — server-side only
|
|
476
|
+
* - Publishable key: pk_(dev|stg|prod|prev)_{ref}_{32hex} — client-safe (new)
|
|
477
|
+
* - Secret Key: sk_(dev|stg|prod|prev)_{ref}_{64hex} — server-side only
|
|
474
478
|
*
|
|
475
479
|
* Legacy Key Formats (backward-compat):
|
|
476
|
-
* - App ID (old): app_(dev|stg|prod)_[identifier] — Public identifier
|
|
480
|
+
* - App ID (old): app_(dev|stg|prod|prev)_[identifier] — Public identifier
|
|
477
481
|
*
|
|
478
482
|
* Special Internal Formats (NOT rotated):
|
|
479
483
|
* - Console bootstrap: app_prod_platform_{slug} / sk_prod_platform_{slug}
|
|
480
484
|
*/
|
|
481
485
|
/** Environment type derived from key prefix */
|
|
482
|
-
type EnvironmentType = 'development' | 'staging' | 'production';
|
|
486
|
+
type EnvironmentType = 'development' | 'staging' | 'production' | 'preview';
|
|
483
487
|
/** Key type - publicKey (pk_*), appId (legacy app_*), or secret (sk_*) */
|
|
484
488
|
type KeyType = 'publicKey' | 'appId' | 'secret';
|
|
485
489
|
/** Validation result with clear error information */
|
|
@@ -529,7 +533,7 @@ declare function validateAndSanitizeAppId(key: string | undefined | null): strin
|
|
|
529
533
|
*
|
|
530
534
|
* @example
|
|
531
535
|
* ```typescript
|
|
532
|
-
* const result = validateSecretKey(
|
|
536
|
+
* const result = validateSecretKey('sk_prod_...')
|
|
533
537
|
* if (!result.valid) {
|
|
534
538
|
* throw new Error(result.error)
|
|
535
539
|
* }
|
|
@@ -601,7 +605,7 @@ declare function isSecretKey(key: string): boolean;
|
|
|
601
605
|
*
|
|
602
606
|
* @example
|
|
603
607
|
* ```typescript
|
|
604
|
-
* const result = validateKey(
|
|
608
|
+
* const result = validateKey('sk_prod_...')
|
|
605
609
|
* if (!result.valid) {
|
|
606
610
|
* throw new Error(result.error)
|
|
607
611
|
* }
|
|
@@ -671,17 +675,11 @@ type OAuthProvider = OAuthProviderId | (string & {});
|
|
|
671
675
|
* }
|
|
672
676
|
* ```
|
|
673
677
|
*
|
|
674
|
-
* ```bash
|
|
675
|
-
* # Deploy via CLI
|
|
676
|
-
* sylphx functions deploy hello-world
|
|
677
|
-
* # → https://my-project.sylphx.app/functions/hello-world
|
|
678
|
-
* ```
|
|
679
|
-
*
|
|
680
678
|
* ```typescript
|
|
681
|
-
* //
|
|
679
|
+
* // Deploy programmatically from trusted server-side tooling
|
|
682
680
|
* import { createConfig, FunctionsClient } from '@sylphx/sdk'
|
|
683
681
|
*
|
|
684
|
-
* const config = createConfig({ secretKey: process.env.
|
|
682
|
+
* const config = createConfig({ secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey! })
|
|
685
683
|
*
|
|
686
684
|
* await FunctionsClient.deploy(config, {
|
|
687
685
|
* name: 'hello-world',
|
|
@@ -723,7 +721,8 @@ interface FunctionDeployOptions {
|
|
|
723
721
|
* Must export a default handler function:
|
|
724
722
|
* export default async function(req: Request): Promise<Response> { ... }
|
|
725
723
|
*
|
|
726
|
-
* For
|
|
724
|
+
* For larger bundles, use a production-backed deployment workflow that
|
|
725
|
+
* uploads source as an artifact instead of embedding it in JSON.
|
|
727
726
|
*/
|
|
728
727
|
code: string;
|
|
729
728
|
/** Human-readable display name */
|
|
@@ -942,7 +941,9 @@ declare function decodeUserId(prefixedId: string): string | null;
|
|
|
942
941
|
* ```
|
|
943
942
|
*/
|
|
944
943
|
interface AIClientOptions {
|
|
945
|
-
/**
|
|
944
|
+
/** Server connection URL. Defaults to SYLPHX_SECRET_URL. */
|
|
945
|
+
secretUrl?: string;
|
|
946
|
+
/** @deprecated Use secretUrl. */
|
|
946
947
|
secretKey?: string;
|
|
947
948
|
/** Platform URL (default: https://api.sylphx.com) */
|
|
948
949
|
platformUrl?: string;
|
|
@@ -1047,7 +1048,7 @@ interface AIClient {
|
|
|
1047
1048
|
*
|
|
1048
1049
|
* Uses environment variables by default:
|
|
1049
1050
|
*
|
|
1050
|
-
* -
|
|
1051
|
+
* - SYLPHX_SECRET_URL: Your app's server connection URL
|
|
1051
1052
|
*/
|
|
1052
1053
|
declare function createAI(options?: AIClientOptions): AIClient;
|
|
1053
1054
|
/**
|
|
@@ -1126,7 +1127,9 @@ interface KvZMember {
|
|
|
1126
1127
|
*/
|
|
1127
1128
|
|
|
1128
1129
|
interface KvClientOptions {
|
|
1129
|
-
/**
|
|
1130
|
+
/** Server connection URL. Defaults to SYLPHX_SECRET_URL. */
|
|
1131
|
+
secretUrl?: string;
|
|
1132
|
+
/** @deprecated Use secretUrl. */
|
|
1130
1133
|
secretKey?: string;
|
|
1131
1134
|
/** Platform URL (default: https://api.sylphx.com) */
|
|
1132
1135
|
platformUrl?: string;
|
|
@@ -1292,7 +1295,7 @@ interface KvClient {
|
|
|
1292
1295
|
* Create a server-side KV client
|
|
1293
1296
|
*
|
|
1294
1297
|
* Uses environment variables by default:
|
|
1295
|
-
* -
|
|
1298
|
+
* - SYLPHX_SECRET_URL: Your app's server connection URL
|
|
1296
1299
|
*/
|
|
1297
1300
|
declare function createKv(options?: KvClientOptions): KvClient;
|
|
1298
1301
|
/**
|
|
@@ -1351,7 +1354,9 @@ interface StreamMessage<T = unknown> {
|
|
|
1351
1354
|
*/
|
|
1352
1355
|
|
|
1353
1356
|
interface StreamsClientOptions {
|
|
1354
|
-
/**
|
|
1357
|
+
/** Server connection URL. Defaults to SYLPHX_SECRET_URL. */
|
|
1358
|
+
secretUrl?: string;
|
|
1359
|
+
/** @deprecated Use secretUrl. */
|
|
1355
1360
|
secretKey?: string;
|
|
1356
1361
|
/** Platform URL (default: https://api.sylphx.com) */
|
|
1357
1362
|
platformUrl?: string;
|
|
@@ -1403,7 +1408,7 @@ interface StreamsClient {
|
|
|
1403
1408
|
*
|
|
1404
1409
|
* Uses environment variables by default:
|
|
1405
1410
|
*
|
|
1406
|
-
* -
|
|
1411
|
+
* - SYLPHX_SECRET_URL: Your app's server connection URL
|
|
1407
1412
|
*/
|
|
1408
1413
|
declare function createStreams(options?: StreamsClientOptions): StreamsClient;
|
|
1409
1414
|
/**
|
|
@@ -1422,8 +1427,10 @@ declare function getStreams(): StreamsClient;
|
|
|
1422
1427
|
* ```typescript
|
|
1423
1428
|
* import { createServerClient, verifyWebhook } from '@sylphx/sdk/server'
|
|
1424
1429
|
*
|
|
1430
|
+
* const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
|
|
1425
1431
|
* const client = createServerRestClient({
|
|
1426
|
-
* secretKey:
|
|
1432
|
+
* secretKey: sylphx.secretKey!,
|
|
1433
|
+
* platformUrl: sylphx.baseUrl.replace(/\/v1$/, ''),
|
|
1427
1434
|
* })
|
|
1428
1435
|
*
|
|
1429
1436
|
* // REST API calls
|
|
@@ -1525,7 +1532,7 @@ interface WebhookVerifyOptions {
|
|
|
1525
1532
|
* const result = await verifyWebhook({
|
|
1526
1533
|
* payload: body,
|
|
1527
1534
|
* signatureHeader: request.headers.get('x-webhook-signature'),
|
|
1528
|
-
* secret: process.env.
|
|
1535
|
+
* secret: process.env.SYLPHX_WEBHOOK_SECRET!,
|
|
1529
1536
|
* })
|
|
1530
1537
|
*
|
|
1531
1538
|
* if (!result.valid) {
|
|
@@ -1555,7 +1562,7 @@ declare function verifyWebhook(options: {
|
|
|
1555
1562
|
* import { createWebhookHandler } from '@sylphx/sdk/server'
|
|
1556
1563
|
*
|
|
1557
1564
|
* const handler = createWebhookHandler({
|
|
1558
|
-
* secret: process.env.
|
|
1565
|
+
* secret: process.env.SYLPHX_WEBHOOK_SECRET!,
|
|
1559
1566
|
* handlers: {
|
|
1560
1567
|
* 'user.created': async (data) => {
|
|
1561
1568
|
* console.log('New user:', data)
|
|
@@ -1656,7 +1663,7 @@ interface FeatureFlagDefinition {
|
|
|
1656
1663
|
* import { getFeatureFlags } from '@sylphx/sdk/server'
|
|
1657
1664
|
*
|
|
1658
1665
|
* export default async function RootLayout({ children }) {
|
|
1659
|
-
* const sylphx = createServerClient(process.env.
|
|
1666
|
+
* const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
|
|
1660
1667
|
* const flags = await getFeatureFlags({
|
|
1661
1668
|
* secretKey: sylphx.secretKey!,
|
|
1662
1669
|
* platformUrl: sylphx.baseUrl.replace(/\/v[0-9]+$/, ''),
|
|
@@ -1713,7 +1720,7 @@ interface GetAppConfigOptions {
|
|
|
1713
1720
|
* import { getAppConfig } from '@sylphx/sdk/server'
|
|
1714
1721
|
*
|
|
1715
1722
|
* export default async function RootLayout({ children }) {
|
|
1716
|
-
* const sylphx = createServerClient(process.env.
|
|
1723
|
+
* const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
|
|
1717
1724
|
* const config = await getAppConfig({
|
|
1718
1725
|
* secretKey: sylphx.secretKey!,
|
|
1719
1726
|
* appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
|
|
@@ -1761,7 +1768,7 @@ interface ReferralLeaderboardResult {
|
|
|
1761
1768
|
*
|
|
1762
1769
|
* export default async function ReferralsPage() {
|
|
1763
1770
|
* const leaderboard = await getReferralLeaderboard({
|
|
1764
|
-
* secretKey: process.env.
|
|
1771
|
+
* secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,
|
|
1765
1772
|
* limit: 10,
|
|
1766
1773
|
* period: 'month',
|
|
1767
1774
|
* })
|
|
@@ -1798,7 +1805,7 @@ interface EngagementLeaderboardResult {
|
|
|
1798
1805
|
*
|
|
1799
1806
|
* export default async function LeaderboardPage() {
|
|
1800
1807
|
* const leaderboard = await getEngagementLeaderboard({
|
|
1801
|
-
* secretKey: process.env.
|
|
1808
|
+
* secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,
|
|
1802
1809
|
* leaderboardId: 'high-scores',
|
|
1803
1810
|
* })
|
|
1804
1811
|
* return <Leaderboard initialData={leaderboard} />
|
|
@@ -1838,7 +1845,7 @@ interface DatabaseStatusInfo {
|
|
|
1838
1845
|
*
|
|
1839
1846
|
* // In your database initialization
|
|
1840
1847
|
* const dbInfo = await getDatabaseConnection({
|
|
1841
|
-
* secretKey: process.env.
|
|
1848
|
+
* secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,
|
|
1842
1849
|
* })
|
|
1843
1850
|
*
|
|
1844
1851
|
* if (dbInfo) {
|
|
@@ -1857,7 +1864,7 @@ declare function getDatabaseConnection(options: AuthenticatedFetchOptions): Prom
|
|
|
1857
1864
|
* import { getDatabaseStatus } from '@sylphx/sdk/server'
|
|
1858
1865
|
*
|
|
1859
1866
|
* const status = await getDatabaseStatus({
|
|
1860
|
-
* secretKey: process.env.
|
|
1867
|
+
* secretKey: createServerClient(process.env.SYLPHX_SECRET_URL!).secretKey!,
|
|
1861
1868
|
* })
|
|
1862
1869
|
*
|
|
1863
1870
|
* if (status?.status === 'ready') {
|
package/dist/server/index.mjs
CHANGED
|
@@ -1360,13 +1360,14 @@ function exponentialBackoff(attempt, baseDelay = BASE_RETRY_DELAY_MS, maxDelay =
|
|
|
1360
1360
|
}
|
|
1361
1361
|
|
|
1362
1362
|
// src/key-validation.ts
|
|
1363
|
-
var PUBLIC_KEY_PATTERN = /^pk_(dev|stg|prod)_[a-z0-9]{12}_[a-f0-9]{32}$/;
|
|
1363
|
+
var PUBLIC_KEY_PATTERN = /^pk_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32}$/;
|
|
1364
1364
|
var APP_ID_PATTERN = /^(app|pk)_(dev|stg|prod|prev)_[a-z0-9_-]+$/;
|
|
1365
|
-
var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod)_[a-z0-9_-]+$/;
|
|
1365
|
+
var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod|prev)_[a-z0-9_-]+$/;
|
|
1366
1366
|
var ENV_PREFIX_MAP = {
|
|
1367
1367
|
dev: "development",
|
|
1368
1368
|
stg: "staging",
|
|
1369
|
-
prod: "production"
|
|
1369
|
+
prod: "production",
|
|
1370
|
+
prev: "preview"
|
|
1370
1371
|
};
|
|
1371
1372
|
function detectKeyIssues(key) {
|
|
1372
1373
|
const issues = [];
|
|
@@ -1391,7 +1392,7 @@ The SDK will automatically sanitize the key, but fixing the source is recommende
|
|
|
1391
1392
|
}
|
|
1392
1393
|
function createInvalidKeyError(keyType, key, envVarName) {
|
|
1393
1394
|
const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
|
|
1394
|
-
const formatHint = keyType === "appId" ? "pk_(dev|stg|prod)_{ref}_{hex} or app_(dev|stg|prod)_[id]" : "sk_(dev|stg|prod)_{ref}_{hex}";
|
|
1395
|
+
const formatHint = keyType === "appId" ? "pk_(dev|stg|prod|prev)_{ref}_{hex} or app_(dev|stg|prod|prev)_[id]" : "sk_(dev|stg|prod|prev)_{ref}_{hex}";
|
|
1395
1396
|
const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
|
|
1396
1397
|
return `[Sylphx] Invalid ${keyTypeName} format.
|
|
1397
1398
|
|
|
@@ -1404,7 +1405,7 @@ You can find your keys in the Sylphx Console \u2192 API Keys.
|
|
|
1404
1405
|
Common issues:
|
|
1405
1406
|
\u2022 Key has uppercase characters (must be lowercase)
|
|
1406
1407
|
\u2022 Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)
|
|
1407
|
-
\u2022 Key has invalid environment (must be dev, stg, or
|
|
1408
|
+
\u2022 Key has invalid environment (must be dev, stg, prod, or prev)
|
|
1408
1409
|
\u2022 Key was copied with extra whitespace`;
|
|
1409
1410
|
}
|
|
1410
1411
|
function extractEnvironment(key) {
|
|
@@ -1451,7 +1452,7 @@ function validateKeyForType(key, keyType, pattern, envVarName) {
|
|
|
1451
1452
|
};
|
|
1452
1453
|
}
|
|
1453
1454
|
function validatePublicKey(key) {
|
|
1454
|
-
return validateKeyForType(key, "publicKey", PUBLIC_KEY_PATTERN, "
|
|
1455
|
+
return validateKeyForType(key, "publicKey", PUBLIC_KEY_PATTERN, "publishable credential");
|
|
1455
1456
|
}
|
|
1456
1457
|
function validateAppId(key) {
|
|
1457
1458
|
return validateKeyForType(key, "appId", APP_ID_PATTERN, "NEXT_PUBLIC_SYLPHX_APP_ID");
|
|
@@ -1467,7 +1468,7 @@ function validateAndSanitizeAppId(key) {
|
|
|
1467
1468
|
return result.sanitizedKey;
|
|
1468
1469
|
}
|
|
1469
1470
|
function validateSecretKey(key) {
|
|
1470
|
-
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "
|
|
1471
|
+
return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "secret credential");
|
|
1471
1472
|
}
|
|
1472
1473
|
function validateAndSanitizeSecretKey(key) {
|
|
1473
1474
|
const result = validateSecretKey(key);
|
|
@@ -1508,7 +1509,7 @@ function isProductionKey(key) {
|
|
|
1508
1509
|
}
|
|
1509
1510
|
function getCookieNamespace(secretKey) {
|
|
1510
1511
|
const env = detectEnvironment(secretKey);
|
|
1511
|
-
const shortEnv = env === "development" ? "dev" : env === "staging" ? "stg" : "prod";
|
|
1512
|
+
const shortEnv = env === "development" ? "dev" : env === "staging" ? "stg" : env === "preview" ? "prev" : "prod";
|
|
1512
1513
|
return `sylphx_${shortEnv}`;
|
|
1513
1514
|
}
|
|
1514
1515
|
function detectKeyType(key) {
|
|
@@ -1538,7 +1539,7 @@ function validateKey(key) {
|
|
|
1538
1539
|
return {
|
|
1539
1540
|
valid: false,
|
|
1540
1541
|
sanitizedKey: "",
|
|
1541
|
-
error: key ? `Invalid key format. Keys must start with 'pk_' (publishable), 'app_' (legacy), or 'sk_' (secret), followed by environment (dev/stg/prod). Got: ${key.slice(0, 20)}...` : "API key is required but was not provided.",
|
|
1542
|
+
error: key ? `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)}...` : "API key is required but was not provided.",
|
|
1542
1543
|
issues: key ? ["invalid_format"] : ["missing"]
|
|
1543
1544
|
};
|
|
1544
1545
|
}
|
|
@@ -1571,7 +1572,14 @@ async function runPipeline(middlewares, initial) {
|
|
|
1571
1572
|
if (next) request = next;
|
|
1572
1573
|
}
|
|
1573
1574
|
}
|
|
1574
|
-
|
|
1575
|
+
const fetchWithMiddleware = middlewares.reduceRight(
|
|
1576
|
+
(next, mw) => mw.onFetch ? async (request2) => {
|
|
1577
|
+
const response2 = await mw.onFetch?.({ request: request2, next });
|
|
1578
|
+
return response2 ?? next(request2);
|
|
1579
|
+
} : next,
|
|
1580
|
+
(request2) => fetch(request2)
|
|
1581
|
+
);
|
|
1582
|
+
let response = await fetchWithMiddleware(request);
|
|
1575
1583
|
for (const mw of middlewares) {
|
|
1576
1584
|
if (mw.onResponse) {
|
|
1577
1585
|
const next = await mw.onResponse({ request, response });
|
|
@@ -1590,6 +1598,11 @@ function buildUrl(baseUrl, path, params) {
|
|
|
1590
1598
|
).toString();
|
|
1591
1599
|
return `${url}?${search}`;
|
|
1592
1600
|
}
|
|
1601
|
+
function cloneRequestWithHeaders(request, updateHeaders) {
|
|
1602
|
+
const headers = new Headers(request.headers);
|
|
1603
|
+
updateHeaders(headers);
|
|
1604
|
+
return new Request(request, { headers });
|
|
1605
|
+
}
|
|
1593
1606
|
function interpolatePath(path, pathParams) {
|
|
1594
1607
|
if (!pathParams) return path;
|
|
1595
1608
|
return path.replace(/\{(\w+)\}/g, (_match, key) => {
|
|
@@ -1652,66 +1665,51 @@ function buildClient(baseUrl, baseHeaders) {
|
|
|
1652
1665
|
function createAuthMiddleware(config) {
|
|
1653
1666
|
return {
|
|
1654
1667
|
async onRequest({ request }) {
|
|
1655
|
-
request
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1668
|
+
return cloneRequestWithHeaders(request, (headers) => {
|
|
1669
|
+
headers.set("X-SDK-Version", SDK_VERSION);
|
|
1670
|
+
headers.set("X-SDK-Platform", SDK_PLATFORM);
|
|
1671
|
+
if (config.secretKey) {
|
|
1672
|
+
headers.set("x-app-secret", config.secretKey);
|
|
1673
|
+
}
|
|
1674
|
+
const token = config.getAccessToken?.();
|
|
1675
|
+
if (token) {
|
|
1676
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1665
1679
|
}
|
|
1666
1680
|
};
|
|
1667
1681
|
}
|
|
1668
1682
|
function isRetryableStatus(status) {
|
|
1669
1683
|
return status >= 500 || status === 429;
|
|
1670
1684
|
}
|
|
1671
|
-
var inFlightRequests = /* @__PURE__ */ new Map();
|
|
1672
1685
|
async function getRequestKey(request) {
|
|
1673
1686
|
const body = request.body ? await request.clone().text() : "";
|
|
1674
1687
|
return `${request.method}:${request.url}:${body}`;
|
|
1675
1688
|
}
|
|
1676
1689
|
function createDeduplicationMiddleware(config = {}) {
|
|
1677
1690
|
const { enabled = true, methods = ["GET"] } = config;
|
|
1678
|
-
if (!enabled) {
|
|
1679
|
-
|
|
1680
|
-
async onRequest({ request }) {
|
|
1681
|
-
return request;
|
|
1682
|
-
}
|
|
1683
|
-
};
|
|
1684
|
-
}
|
|
1691
|
+
if (!enabled) return {};
|
|
1692
|
+
const inFlightRequests = /* @__PURE__ */ new Map();
|
|
1685
1693
|
return {
|
|
1686
|
-
async
|
|
1687
|
-
|
|
1688
|
-
|
|
1694
|
+
async onFetch({ request, next }) {
|
|
1695
|
+
const method = request.method.toUpperCase();
|
|
1696
|
+
if (!methods.includes(method)) {
|
|
1697
|
+
return next(request);
|
|
1689
1698
|
}
|
|
1690
1699
|
const key = await getRequestKey(request);
|
|
1691
1700
|
const existing = inFlightRequests.get(key);
|
|
1692
1701
|
if (existing) {
|
|
1693
|
-
const deduped = request.clone();
|
|
1694
|
-
deduped._dedupKey = key;
|
|
1695
|
-
return deduped;
|
|
1696
|
-
}
|
|
1697
|
-
;
|
|
1698
|
-
request._dedupKey = key;
|
|
1699
|
-
return request;
|
|
1700
|
-
},
|
|
1701
|
-
async onResponse({ request, response }) {
|
|
1702
|
-
const key = request._dedupKey;
|
|
1703
|
-
if (!key) return response;
|
|
1704
|
-
const existing = inFlightRequests.get(key);
|
|
1705
|
-
if (existing && inFlightRequests.get(key) !== void 0) {
|
|
1706
1702
|
const cachedResponse = await existing;
|
|
1707
1703
|
return cachedResponse.clone();
|
|
1708
1704
|
}
|
|
1709
|
-
const responsePromise =
|
|
1705
|
+
const responsePromise = next(request).then((response) => response.clone());
|
|
1710
1706
|
inFlightRequests.set(key, responsePromise);
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1707
|
+
try {
|
|
1708
|
+
const response = await responsePromise;
|
|
1709
|
+
return response.clone();
|
|
1710
|
+
} finally {
|
|
1711
|
+
inFlightRequests.delete(key);
|
|
1712
|
+
}
|
|
1715
1713
|
}
|
|
1716
1714
|
};
|
|
1717
1715
|
}
|
|
@@ -1861,7 +1859,9 @@ function createETagMiddleware(config) {
|
|
|
1861
1859
|
if (Date.now() - cached.timestamp > ttlMs) {
|
|
1862
1860
|
etagCache.delete(cacheKey);
|
|
1863
1861
|
} else {
|
|
1864
|
-
request
|
|
1862
|
+
return cloneRequestWithHeaders(request, (headers) => {
|
|
1863
|
+
headers.set("If-None-Match", cached.etag);
|
|
1864
|
+
});
|
|
1865
1865
|
}
|
|
1866
1866
|
}
|
|
1867
1867
|
return request;
|
|
@@ -2018,7 +2018,7 @@ function createDynamicRestClient(config) {
|
|
|
2018
2018
|
// src/connection-url.ts
|
|
2019
2019
|
var SYLPHX_PROTOCOL = "sylphx:";
|
|
2020
2020
|
var DEFAULT_VERSION = "v1";
|
|
2021
|
-
var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}$/;
|
|
2021
|
+
var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32,64}$/;
|
|
2022
2022
|
var VERSION_REGEX = /^v[0-9]+$/;
|
|
2023
2023
|
var SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
2024
2024
|
var InvalidConnectionUrlError = class _InvalidConnectionUrlError extends Error {
|
|
@@ -2035,7 +2035,7 @@ function fail(reason) {
|
|
|
2035
2035
|
function parseCredential(raw) {
|
|
2036
2036
|
const match = CREDENTIAL_REGEX.exec(raw);
|
|
2037
2037
|
if (!match) {
|
|
2038
|
-
fail(`credential must match (pk|sk)_(dev|stg|prod|prev)_
|
|
2038
|
+
fail(`credential must match (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}, got "${raw}"`);
|
|
2039
2039
|
}
|
|
2040
2040
|
return {
|
|
2041
2041
|
credentialType: match[1],
|
|
@@ -2112,7 +2112,7 @@ function parseConnectionUrl(url) {
|
|
|
2112
2112
|
// src/config.ts
|
|
2113
2113
|
var LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/;
|
|
2114
2114
|
var LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/;
|
|
2115
|
-
var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
|
|
2115
|
+
var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{ref?}_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
|
|
2116
2116
|
function rejectLegacyKeyFormat(input) {
|
|
2117
2117
|
const trimmed = input.trim().toLowerCase();
|
|
2118
2118
|
if (LEGACY_APP_KEY_PATTERN.test(trimmed)) {
|
|
@@ -2245,7 +2245,7 @@ function createConfigFromComponents(input) {
|
|
|
2245
2245
|
});
|
|
2246
2246
|
}
|
|
2247
2247
|
throw new SylphxError(
|
|
2248
|
-
`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)_
|
|
2248
|
+
`[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}. Got: "${trimmedCred.slice(0, 30)}..."`,
|
|
2249
2249
|
{ code: "BAD_REQUEST" }
|
|
2250
2250
|
);
|
|
2251
2251
|
}
|
|
@@ -2571,14 +2571,32 @@ function decodeUserId(prefixedId) {
|
|
|
2571
2571
|
return null;
|
|
2572
2572
|
}
|
|
2573
2573
|
|
|
2574
|
+
// src/server/secret-url.ts
|
|
2575
|
+
function resolveServerConnection(options = {}) {
|
|
2576
|
+
const configuredSecret = resolveSecretUrl(options.secretUrl ?? options.secretKey);
|
|
2577
|
+
if (!configuredSecret) {
|
|
2578
|
+
throw new Error("SYLPHX_SECRET_URL is required for server-side SDK calls");
|
|
2579
|
+
}
|
|
2580
|
+
if (configuredSecret.trim().startsWith("sylphx://")) {
|
|
2581
|
+
const config = createServerClient(configuredSecret);
|
|
2582
|
+
return {
|
|
2583
|
+
secretKey: config.credential,
|
|
2584
|
+
baseUrl: options.platformUrl?.trim() || config.baseUrl.replace(/\/v1\/?$/, "")
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
return {
|
|
2588
|
+
secretKey: validateAndSanitizeSecretKey(configuredSecret),
|
|
2589
|
+
baseUrl: options.platformUrl?.trim() || `https://${DEFAULT_SDK_API_HOST}`
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2574
2593
|
// src/server/ai.ts
|
|
2575
2594
|
function createAI(options = {}) {
|
|
2576
2595
|
const baseURL = (options.platformUrl || `https://${DEFAULT_SDK_API_HOST}`).trim();
|
|
2577
|
-
const
|
|
2578
|
-
const apiKey = validateAndSanitizeSecretKey(rawApiKey);
|
|
2596
|
+
const { secretKey } = resolveServerConnection(options);
|
|
2579
2597
|
const headers = {
|
|
2580
2598
|
"Content-Type": "application/json",
|
|
2581
|
-
Authorization: `Bearer ${
|
|
2599
|
+
Authorization: `Bearer ${secretKey}`
|
|
2582
2600
|
};
|
|
2583
2601
|
async function chat(opts) {
|
|
2584
2602
|
const response = await fetch(`${baseURL}/api/v1/chat/completions`, {
|
|
@@ -2666,8 +2684,7 @@ function getAI() {
|
|
|
2666
2684
|
|
|
2667
2685
|
// src/server/kv.ts
|
|
2668
2686
|
function createKv(options = {}) {
|
|
2669
|
-
const
|
|
2670
|
-
const secretKey = validateAndSanitizeSecretKey(resolveSecretUrl(options.secretKey));
|
|
2687
|
+
const { baseUrl, secretKey } = resolveServerConnection(options);
|
|
2671
2688
|
const headers = {
|
|
2672
2689
|
"Content-Type": "application/json",
|
|
2673
2690
|
"x-app-secret": secretKey,
|
|
@@ -2678,7 +2695,7 @@ function createKv(options = {}) {
|
|
|
2678
2695
|
let lastError;
|
|
2679
2696
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
2680
2697
|
try {
|
|
2681
|
-
const response = await fetch(`${
|
|
2698
|
+
const response = await fetch(`${baseUrl}${SDK_API_PATH}/kv${path}`, {
|
|
2682
2699
|
method,
|
|
2683
2700
|
headers,
|
|
2684
2701
|
body: body ? JSON.stringify(body) : void 0
|
|
@@ -2855,15 +2872,13 @@ function getKv() {
|
|
|
2855
2872
|
|
|
2856
2873
|
// src/server/streams.ts
|
|
2857
2874
|
function createStreams(options = {}) {
|
|
2858
|
-
const
|
|
2859
|
-
const rawApiKey = options.secretKey || process.env.SYLPHX_SECRET_KEY;
|
|
2860
|
-
const apiKey = validateAndSanitizeSecretKey(rawApiKey);
|
|
2875
|
+
const { baseUrl, secretKey } = resolveServerConnection(options);
|
|
2861
2876
|
const headers = {
|
|
2862
2877
|
"Content-Type": "application/json",
|
|
2863
|
-
"x-app-secret":
|
|
2878
|
+
"x-app-secret": secretKey
|
|
2864
2879
|
};
|
|
2865
2880
|
async function emit(channel, event, data) {
|
|
2866
|
-
const response = await fetch(`${
|
|
2881
|
+
const response = await fetch(`${baseUrl}${SDK_API_PATH}/realtime/emit`, {
|
|
2867
2882
|
method: "POST",
|
|
2868
2883
|
headers,
|
|
2869
2884
|
body: JSON.stringify({ channel, event, data })
|
|
@@ -2876,7 +2891,7 @@ function createStreams(options = {}) {
|
|
|
2876
2891
|
return result.id;
|
|
2877
2892
|
}
|
|
2878
2893
|
async function history(channel, options2) {
|
|
2879
|
-
const response = await fetch(`${
|
|
2894
|
+
const response = await fetch(`${baseUrl}${SDK_API_PATH}/realtime/history`, {
|
|
2880
2895
|
method: "POST",
|
|
2881
2896
|
headers,
|
|
2882
2897
|
body: JSON.stringify({
|