@sylphx/sdk 0.0.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/dist/index.mjs ADDED
@@ -0,0 +1,3058 @@
1
+ // src/constants.ts
2
+ var DEFAULT_PLATFORM_URL = "https://sylphx.com";
3
+ var SDK_API_PATH = `/api/app/v1`;
4
+ var SDK_API_PATH_NEW = `/v1`;
5
+ var DEFAULT_SDK_API_HOST = "api.sylphx.com";
6
+ var SDK_VERSION = "0.1.0";
7
+ var SDK_PLATFORM = typeof window !== "undefined" ? "browser" : typeof process !== "undefined" && process.versions?.node ? "node" : "unknown";
8
+ var DEFAULT_TIMEOUT_MS = 3e4;
9
+ var SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60;
10
+ var SESSION_TOKEN_LIFETIME_MS = SESSION_TOKEN_LIFETIME_SECONDS * 1e3;
11
+ var REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
12
+ var FLAGS_CACHE_TTL_MS = 5 * 60 * 1e3;
13
+ var FLAGS_STALE_WHILE_REVALIDATE_MS = 60 * 1e3;
14
+ var MAX_RETRY_DELAY_MS = 3e4;
15
+ var BASE_RETRY_DELAY_MS = 1e3;
16
+ var ANALYTICS_SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
17
+ var WEBHOOK_MAX_AGE_MS = 5 * 60 * 1e3;
18
+ var WEBHOOK_CLOCK_SKEW_MS = 30 * 1e3;
19
+ var PKCE_CODE_TTL_MS = 10 * 60 * 1e3;
20
+ var JOBS_DLQ_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
21
+ var SESSION_REPLAY_MAX_DURATION_MS = 60 * 60 * 1e3;
22
+ var FLAGS_EXPOSURE_DEDUPE_WINDOW_MS = 60 * 60 * 1e3;
23
+ var CLICK_ID_EXPIRY_MS = 90 * 24 * 60 * 60 * 1e3;
24
+ var STALE_TIME_FREQUENT_MS = 60 * 1e3;
25
+ var STALE_TIME_MODERATE_MS = 2 * 60 * 1e3;
26
+ var STALE_TIME_STABLE_MS = 5 * 60 * 1e3;
27
+ var STALE_TIME_STATS_MS = 30 * 1e3;
28
+ var NEW_USER_THRESHOLD_MS = 60 * 1e3;
29
+ var STORAGE_MULTIPART_THRESHOLD_BYTES = 5 * 1024 * 1024;
30
+ var STORAGE_DEFAULT_MAX_SIZE_BYTES = 5 * 1024 * 1024;
31
+ var STORAGE_AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024;
32
+ var STORAGE_LARGE_MAX_SIZE_BYTES = 10 * 1024 * 1024;
33
+ var JWK_CACHE_TTL_MS = 60 * 60 * 1e3;
34
+ var CIRCUIT_BREAKER_FAILURE_THRESHOLD = 5;
35
+ var CIRCUIT_BREAKER_WINDOW_MS = 1e4;
36
+ var CIRCUIT_BREAKER_OPEN_DURATION_MS = 3e4;
37
+ var ETAG_CACHE_MAX_ENTRIES = 100;
38
+ var ETAG_CACHE_TTL_MS = 5 * 60 * 1e3;
39
+
40
+ // src/errors.ts
41
+ var ERROR_CODE_STATUS = {
42
+ BAD_REQUEST: 400,
43
+ UNAUTHORIZED: 401,
44
+ FORBIDDEN: 403,
45
+ NOT_FOUND: 404,
46
+ CONFLICT: 409,
47
+ PAYLOAD_TOO_LARGE: 413,
48
+ UNPROCESSABLE_ENTITY: 422,
49
+ TOO_MANY_REQUESTS: 429,
50
+ INTERNAL_SERVER_ERROR: 500,
51
+ NOT_IMPLEMENTED: 501,
52
+ BAD_GATEWAY: 502,
53
+ SERVICE_UNAVAILABLE: 503,
54
+ GATEWAY_TIMEOUT: 504,
55
+ NETWORK_ERROR: 0,
56
+ TIMEOUT: 0,
57
+ ABORTED: 0,
58
+ PARSE_ERROR: 0,
59
+ UNKNOWN: 0
60
+ };
61
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([
62
+ "NETWORK_ERROR",
63
+ "TIMEOUT",
64
+ "BAD_GATEWAY",
65
+ "SERVICE_UNAVAILABLE",
66
+ "GATEWAY_TIMEOUT",
67
+ "TOO_MANY_REQUESTS",
68
+ // With backoff
69
+ "INTERNAL_SERVER_ERROR"
70
+ // Sometimes transient
71
+ ]);
72
+ var SylphxError = class _SylphxError extends Error {
73
+ /** Error code for programmatic handling */
74
+ code;
75
+ /** HTTP status code */
76
+ status;
77
+ /** Additional context data */
78
+ data;
79
+ /** Whether this error is safe to retry */
80
+ isRetryable;
81
+ /** Retry-After value in seconds (for rate limiting) */
82
+ retryAfter;
83
+ /** Timestamp when error occurred */
84
+ timestamp;
85
+ constructor(message, options = {}) {
86
+ super(message, { cause: options.cause });
87
+ this.name = "SylphxError";
88
+ this.code = options.code ?? "UNKNOWN";
89
+ this.status = options.status ?? ERROR_CODE_STATUS[this.code];
90
+ this.data = options.data;
91
+ this.isRetryable = RETRYABLE_CODES.has(this.code);
92
+ this.retryAfter = options.retryAfter;
93
+ this.timestamp = /* @__PURE__ */ new Date();
94
+ if (Error.captureStackTrace) {
95
+ Error.captureStackTrace(this, _SylphxError);
96
+ }
97
+ }
98
+ /**
99
+ * Convert to JSON-serializable object
100
+ */
101
+ toJSON() {
102
+ return {
103
+ name: this.name,
104
+ message: this.message,
105
+ code: this.code,
106
+ status: this.status,
107
+ data: this.data,
108
+ isRetryable: this.isRetryable,
109
+ retryAfter: this.retryAfter,
110
+ timestamp: this.timestamp.toISOString()
111
+ };
112
+ }
113
+ };
114
+ var NetworkError = class extends SylphxError {
115
+ constructor(message = "Network request failed", options) {
116
+ super(message, { ...options, code: "NETWORK_ERROR" });
117
+ this.name = "NetworkError";
118
+ }
119
+ };
120
+ var TimeoutError = class extends SylphxError {
121
+ /** Timeout duration in milliseconds */
122
+ timeout;
123
+ constructor(timeout, options) {
124
+ super(`Request timed out after ${timeout}ms`, {
125
+ ...options,
126
+ code: "TIMEOUT"
127
+ });
128
+ this.name = "TimeoutError";
129
+ this.timeout = timeout;
130
+ }
131
+ };
132
+ var AuthenticationError = class extends SylphxError {
133
+ constructor(message = "Authentication required", options) {
134
+ super(message, { ...options, code: "UNAUTHORIZED" });
135
+ this.name = "AuthenticationError";
136
+ }
137
+ };
138
+ var AuthorizationError = class extends SylphxError {
139
+ constructor(message = "Permission denied", options) {
140
+ super(message, { ...options, code: "FORBIDDEN" });
141
+ this.name = "AuthorizationError";
142
+ }
143
+ };
144
+ var ValidationError = class extends SylphxError {
145
+ /** Field-specific errors */
146
+ fieldErrors;
147
+ constructor(message, options) {
148
+ super(message, { ...options, code: "UNPROCESSABLE_ENTITY" });
149
+ this.name = "ValidationError";
150
+ this.fieldErrors = options?.fieldErrors;
151
+ }
152
+ /**
153
+ * Get error message for a specific field
154
+ */
155
+ getFieldError(field) {
156
+ return this.fieldErrors?.[field]?.[0];
157
+ }
158
+ };
159
+ var RateLimitError = class extends SylphxError {
160
+ /** Maximum requests allowed in window */
161
+ limit;
162
+ /** Remaining requests in current window */
163
+ remaining;
164
+ /** Unix timestamp (seconds) when limit resets */
165
+ resetAt;
166
+ constructor(message = "Too many requests", options) {
167
+ super(message, { ...options, code: "TOO_MANY_REQUESTS" });
168
+ this.name = "RateLimitError";
169
+ this.limit = options?.limit;
170
+ this.remaining = options?.remaining;
171
+ this.resetAt = options?.resetAt;
172
+ }
173
+ /**
174
+ * Get Date when rate limit resets
175
+ */
176
+ getResetDate() {
177
+ return this.resetAt ? new Date(this.resetAt * 1e3) : void 0;
178
+ }
179
+ /**
180
+ * Get human-readable retry message
181
+ */
182
+ getRetryMessage() {
183
+ if (this.retryAfter) {
184
+ return `Please retry after ${this.retryAfter} seconds`;
185
+ }
186
+ if (this.resetAt) {
187
+ const seconds = Math.max(0, this.resetAt - Math.floor(Date.now() / 1e3));
188
+ return `Rate limit resets in ${seconds} seconds`;
189
+ }
190
+ return "Please wait before retrying";
191
+ }
192
+ };
193
+ var NotFoundError = class extends SylphxError {
194
+ /** Type of resource that wasn't found */
195
+ resourceType;
196
+ /** ID of the resource that wasn't found */
197
+ resourceId;
198
+ constructor(message = "Resource not found", options) {
199
+ super(message, { ...options, code: "NOT_FOUND" });
200
+ this.name = "NotFoundError";
201
+ this.resourceType = options?.resourceType;
202
+ this.resourceId = options?.resourceId;
203
+ }
204
+ };
205
+ function isSylphxError(error) {
206
+ return error instanceof SylphxError;
207
+ }
208
+ function isRetryableError(error) {
209
+ if (error instanceof SylphxError) {
210
+ return error.isRetryable;
211
+ }
212
+ if (error instanceof Error) {
213
+ const message = error.message.toLowerCase();
214
+ const name = error.name.toLowerCase();
215
+ if (name === "typeerror" && message.includes("fetch")) return true;
216
+ if (name === "networkerror") return true;
217
+ if (message.includes("timeout")) return true;
218
+ if (message.includes("timed out")) return true;
219
+ if (message.includes("econnrefused")) return true;
220
+ if (message.includes("econnreset")) return true;
221
+ if (message.includes("socket")) return true;
222
+ if (message.includes("502")) return true;
223
+ if (message.includes("503")) return true;
224
+ if (message.includes("504")) return true;
225
+ }
226
+ return false;
227
+ }
228
+ function getErrorMessage(error) {
229
+ if (error instanceof Error) {
230
+ return error.message;
231
+ }
232
+ if (typeof error === "string") {
233
+ return error;
234
+ }
235
+ return "An unknown error occurred";
236
+ }
237
+ function getErrorCode(error) {
238
+ if (error instanceof SylphxError) {
239
+ return error.code;
240
+ }
241
+ return "UNKNOWN";
242
+ }
243
+ function toSylphxError(error) {
244
+ if (error instanceof SylphxError) {
245
+ return error;
246
+ }
247
+ if (error instanceof Error) {
248
+ const message = error.message.toLowerCase();
249
+ const name = error.name.toLowerCase();
250
+ if (name === "typeerror" && message.includes("fetch")) {
251
+ return new NetworkError(error.message, { cause: error });
252
+ }
253
+ if (name === "aborterror" || message.includes("aborted")) {
254
+ return new SylphxError(error.message, { code: "ABORTED", cause: error });
255
+ }
256
+ if (message.includes("timeout")) {
257
+ return new TimeoutError(DEFAULT_TIMEOUT_MS, { cause: error });
258
+ }
259
+ if (message.includes("401") || message.includes("unauthorized")) {
260
+ return new AuthenticationError(error.message, { cause: error });
261
+ }
262
+ if (message.includes("403") || message.includes("forbidden")) {
263
+ return new AuthorizationError(error.message, { cause: error });
264
+ }
265
+ if (message.includes("404") || message.includes("not found")) {
266
+ return new NotFoundError(error.message, { cause: error });
267
+ }
268
+ if (message.includes("429") || message.includes("rate limit")) {
269
+ return new RateLimitError(error.message, { cause: error });
270
+ }
271
+ return new SylphxError(error.message, { cause: error });
272
+ }
273
+ return new SylphxError(getErrorMessage(error));
274
+ }
275
+ function exponentialBackoff(attempt, baseDelay = BASE_RETRY_DELAY_MS, maxDelay = MAX_RETRY_DELAY_MS) {
276
+ const exponentialDelay = baseDelay * Math.pow(2, attempt);
277
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
278
+ const jitter = cappedDelay * 0.25 * (Math.random() * 2 - 1);
279
+ return Math.round(cappedDelay + jitter);
280
+ }
281
+
282
+ // src/key-validation.ts
283
+ var APP_ID_PATTERN = /^app_(dev|stg|prod)_[a-z0-9_-]+$/;
284
+ var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod)_[a-z0-9_-]+$/;
285
+ var ENV_PREFIX_MAP = {
286
+ dev: "development",
287
+ stg: "staging",
288
+ prod: "production"
289
+ };
290
+ function detectKeyIssues(key) {
291
+ const issues = [];
292
+ if (key !== key.trim()) issues.push("whitespace");
293
+ if (key.includes("\n")) issues.push("newline");
294
+ if (key.includes("\r")) issues.push("carriage-return");
295
+ if (key.includes(" ")) issues.push("space");
296
+ if (key !== key.toLowerCase()) issues.push("uppercase-chars");
297
+ return issues;
298
+ }
299
+ function createSanitizationWarning(keyType, issues, envVarName) {
300
+ const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
301
+ return `[Sylphx] ${keyTypeName} contains ${issues.join(", ")}. This is commonly caused by Vercel CLI's 'env pull' command.
302
+
303
+ To fix permanently:
304
+ 1. Go to Vercel Dashboard \u2192 Your Project \u2192 Settings \u2192 Environment Variables
305
+ 2. Edit ${envVarName}
306
+ 3. Remove any trailing whitespace or newline characters
307
+ 4. Redeploy your application
308
+
309
+ The SDK will automatically sanitize the key, but fixing the source is recommended.`;
310
+ }
311
+ function createInvalidKeyError(keyType, key, envVarName) {
312
+ const prefix = keyType === "appId" ? "app" : "sk";
313
+ const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
314
+ const formatHint = `${prefix}_(dev|stg|prod)_[identifier]`;
315
+ const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
316
+ return `[Sylphx] Invalid ${keyTypeName} format.
317
+
318
+ Expected format: ${formatHint}
319
+ Received: "${maskedKey}"
320
+
321
+ Please check your ${envVarName} environment variable.
322
+ You can find your keys in the Sylphx Console \u2192 API Keys.
323
+
324
+ Common issues:
325
+ \u2022 Key has uppercase characters (must be lowercase)
326
+ \u2022 Key has wrong prefix (App ID: app_, Secret Key: sk_)
327
+ \u2022 Key has invalid environment (must be dev, stg, or prod)
328
+ \u2022 Key was copied with extra whitespace`;
329
+ }
330
+ function extractEnvironment(key) {
331
+ const match = key.match(/^(?:app|sk)_(dev|stg|prod)_/);
332
+ if (!match) return void 0;
333
+ return ENV_PREFIX_MAP[match[1]];
334
+ }
335
+ function validateKeyForType(key, keyType, pattern, envVarName) {
336
+ const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
337
+ if (!key) {
338
+ return {
339
+ valid: false,
340
+ sanitizedKey: "",
341
+ error: `[Sylphx] ${keyTypeName} is required. Set ${envVarName} in your environment variables.`,
342
+ issues: ["missing"]
343
+ };
344
+ }
345
+ const issues = detectKeyIssues(key);
346
+ if (pattern.test(key)) {
347
+ return {
348
+ valid: true,
349
+ sanitizedKey: key,
350
+ keyType,
351
+ environment: extractEnvironment(key),
352
+ issues: []
353
+ };
354
+ }
355
+ const sanitized = key.trim().toLowerCase();
356
+ if (pattern.test(sanitized)) {
357
+ return {
358
+ valid: true,
359
+ sanitizedKey: sanitized,
360
+ keyType,
361
+ environment: extractEnvironment(sanitized),
362
+ warning: createSanitizationWarning(keyType, issues, envVarName),
363
+ issues
364
+ };
365
+ }
366
+ return {
367
+ valid: false,
368
+ sanitizedKey: "",
369
+ error: createInvalidKeyError(keyType, key, envVarName),
370
+ issues: [...issues, "invalid-format"]
371
+ };
372
+ }
373
+ function validateAppId(key) {
374
+ return validateKeyForType(
375
+ key,
376
+ "appId",
377
+ APP_ID_PATTERN,
378
+ "NEXT_PUBLIC_SYLPHX_APP_ID"
379
+ );
380
+ }
381
+ function validateSecretKey(key) {
382
+ return validateKeyForType(
383
+ key,
384
+ "secret",
385
+ SECRET_KEY_PATTERN,
386
+ "SYLPHX_SECRET_KEY"
387
+ );
388
+ }
389
+ function validateAndSanitizeSecretKey(key) {
390
+ const result = validateSecretKey(key);
391
+ if (!result.valid) {
392
+ throw new Error(result.error);
393
+ }
394
+ if (result.warning) {
395
+ console.warn(result.warning);
396
+ }
397
+ return result.sanitizedKey;
398
+ }
399
+ function detectKeyType(key) {
400
+ const sanitized = key.trim().toLowerCase();
401
+ if (sanitized.startsWith("app_")) return "appId";
402
+ if (sanitized.startsWith("sk_")) return "secret";
403
+ return null;
404
+ }
405
+ function validateKey(key) {
406
+ const keyType = key ? detectKeyType(key) : null;
407
+ if (keyType === "appId") {
408
+ return validateAppId(key);
409
+ }
410
+ if (keyType === "secret") {
411
+ return validateSecretKey(key);
412
+ }
413
+ return {
414
+ valid: false,
415
+ sanitizedKey: "",
416
+ error: key ? `Invalid key format. Keys must start with 'app_' (App ID) or 'sk_' (Secret Key), followed by environment (dev/stg/prod) and identifier. Got: ${key.slice(0, 20)}...` : "API key is required but was not provided.",
417
+ issues: key ? ["invalid_format"] : ["missing"]
418
+ };
419
+ }
420
+
421
+ // src/config.ts
422
+ function httpStatusToErrorCode(status) {
423
+ switch (status) {
424
+ case 400:
425
+ return "BAD_REQUEST";
426
+ case 401:
427
+ return "UNAUTHORIZED";
428
+ case 403:
429
+ return "FORBIDDEN";
430
+ case 404:
431
+ return "NOT_FOUND";
432
+ case 409:
433
+ return "CONFLICT";
434
+ case 413:
435
+ return "PAYLOAD_TOO_LARGE";
436
+ case 422:
437
+ return "UNPROCESSABLE_ENTITY";
438
+ case 429:
439
+ return "TOO_MANY_REQUESTS";
440
+ case 500:
441
+ return "INTERNAL_SERVER_ERROR";
442
+ case 501:
443
+ return "NOT_IMPLEMENTED";
444
+ case 502:
445
+ return "BAD_GATEWAY";
446
+ case 503:
447
+ return "SERVICE_UNAVAILABLE";
448
+ case 504:
449
+ return "GATEWAY_TIMEOUT";
450
+ default:
451
+ return status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST";
452
+ }
453
+ }
454
+ function createConfig(input) {
455
+ let secretKey;
456
+ if (input.secretKey) {
457
+ const result = validateKey(input.secretKey);
458
+ if (!result.valid) {
459
+ throw new SylphxError(result.error || "Invalid API key", {
460
+ code: "BAD_REQUEST",
461
+ data: { issues: result.issues }
462
+ });
463
+ }
464
+ if (result.warning) {
465
+ console.warn(`[Sylphx] ${result.warning}`);
466
+ }
467
+ secretKey = result.sanitizedKey;
468
+ }
469
+ let platformUrl;
470
+ let apiBasePath;
471
+ if (input.platformUrl) {
472
+ platformUrl = input.platformUrl.trim();
473
+ apiBasePath = SDK_API_PATH;
474
+ } else if (input.ref) {
475
+ platformUrl = `https://${input.ref}.${DEFAULT_SDK_API_HOST}`;
476
+ apiBasePath = SDK_API_PATH_NEW;
477
+ } else {
478
+ platformUrl = DEFAULT_PLATFORM_URL;
479
+ apiBasePath = SDK_API_PATH;
480
+ }
481
+ return Object.freeze({
482
+ secretKey,
483
+ platformUrl,
484
+ ref: input.ref,
485
+ apiBasePath,
486
+ accessToken: input.accessToken
487
+ });
488
+ }
489
+ function withToken(config, accessToken) {
490
+ return Object.freeze({
491
+ ...config,
492
+ accessToken
493
+ // Preserve apiBasePath and ref from original config
494
+ });
495
+ }
496
+ function buildHeaders(config) {
497
+ const headers = {
498
+ "Content-Type": "application/json"
499
+ };
500
+ if (config.secretKey) {
501
+ headers["x-app-secret"] = config.secretKey;
502
+ }
503
+ if (config.accessToken) {
504
+ headers["Authorization"] = `Bearer ${config.accessToken}`;
505
+ }
506
+ return headers;
507
+ }
508
+ function buildApiUrl(config, path) {
509
+ const base = config.platformUrl.replace(/\/$/, "");
510
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
511
+ return `${base}${config.apiBasePath ?? SDK_API_PATH}${cleanPath}`;
512
+ }
513
+ async function callApi(config, path, options = {}) {
514
+ const {
515
+ method = "GET",
516
+ body,
517
+ query,
518
+ timeout = DEFAULT_TIMEOUT_MS,
519
+ signal,
520
+ idempotencyKey
521
+ } = options;
522
+ let url = buildApiUrl(config, path);
523
+ if (query) {
524
+ const params = new URLSearchParams();
525
+ for (const [key, value] of Object.entries(query)) {
526
+ if (value !== void 0) {
527
+ params.set(key, String(value));
528
+ }
529
+ }
530
+ const queryString = params.toString();
531
+ if (queryString) {
532
+ url += `?${queryString}`;
533
+ }
534
+ }
535
+ const controller = new AbortController();
536
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
537
+ const combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
538
+ const headers = buildHeaders(config);
539
+ if (idempotencyKey) {
540
+ headers["Idempotency-Key"] = idempotencyKey;
541
+ }
542
+ const fetchOptions = {
543
+ method,
544
+ headers,
545
+ signal: combinedSignal
546
+ };
547
+ if (body) {
548
+ fetchOptions.body = JSON.stringify(body);
549
+ }
550
+ let response;
551
+ try {
552
+ response = await fetch(url, fetchOptions);
553
+ } catch (error) {
554
+ clearTimeout(timeoutId);
555
+ if (error instanceof Error) {
556
+ if (error.name === "AbortError") {
557
+ if (controller.signal.aborted && !signal?.aborted) {
558
+ throw new TimeoutError(timeout);
559
+ }
560
+ throw new SylphxError("Request aborted", {
561
+ code: "ABORTED",
562
+ cause: error
563
+ });
564
+ }
565
+ throw new NetworkError(error.message, { cause: error });
566
+ }
567
+ throw new NetworkError("Network request failed");
568
+ } finally {
569
+ clearTimeout(timeoutId);
570
+ }
571
+ if (!response.ok) {
572
+ const errorBody = await response.text().catch(() => "");
573
+ let errorMessage = "Request failed";
574
+ let errorData;
575
+ if (errorBody) {
576
+ try {
577
+ const parsed = JSON.parse(errorBody);
578
+ errorMessage = parsed.error?.message ?? parsed.message ?? errorMessage;
579
+ errorData = parsed.error;
580
+ } catch {
581
+ errorMessage = response.statusText || errorMessage;
582
+ }
583
+ }
584
+ const errorCode = httpStatusToErrorCode(response.status);
585
+ const retryAfterHeader = response.headers.get("Retry-After");
586
+ const rateLimitLimit = response.headers.get("X-RateLimit-Limit");
587
+ const rateLimitRemaining = response.headers.get("X-RateLimit-Remaining");
588
+ const rateLimitReset = response.headers.get("X-RateLimit-Reset");
589
+ const retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : void 0;
590
+ if (response.status === 429) {
591
+ throw new RateLimitError(errorMessage || "Too many requests", {
592
+ status: response.status,
593
+ data: errorData,
594
+ retryAfter,
595
+ limit: rateLimitLimit ? Number.parseInt(rateLimitLimit, 10) : void 0,
596
+ remaining: rateLimitRemaining ? Number.parseInt(rateLimitRemaining, 10) : void 0,
597
+ resetAt: rateLimitReset ? Number.parseInt(rateLimitReset, 10) : void 0
598
+ });
599
+ }
600
+ throw new SylphxError(errorMessage, {
601
+ code: errorCode,
602
+ status: response.status,
603
+ data: errorData,
604
+ retryAfter
605
+ });
606
+ }
607
+ const text = await response.text();
608
+ if (!text) {
609
+ return {};
610
+ }
611
+ try {
612
+ return JSON.parse(text);
613
+ } catch (error) {
614
+ throw new SylphxError("Failed to parse response", {
615
+ code: "PARSE_ERROR",
616
+ cause: error instanceof Error ? error : void 0,
617
+ data: { body: text.slice(0, 200) }
618
+ // Include snippet for debugging
619
+ });
620
+ }
621
+ }
622
+
623
+ // src/debug.ts
624
+ var DEBUG_STORAGE_KEY = "sylphx_debug";
625
+ function isDebugEnabled() {
626
+ if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
627
+ try {
628
+ return localStorage.getItem(DEBUG_STORAGE_KEY) === "true";
629
+ } catch {
630
+ return false;
631
+ }
632
+ }
633
+ if (typeof process !== "undefined" && process.env) {
634
+ return process.env.SYLPHX_DEBUG === "true";
635
+ }
636
+ return false;
637
+ }
638
+ var debugModeCache = null;
639
+ function getDebugMode() {
640
+ if (debugModeCache === null) {
641
+ debugModeCache = isDebugEnabled();
642
+ }
643
+ return debugModeCache;
644
+ }
645
+ function resetDebugModeCache() {
646
+ debugModeCache = null;
647
+ }
648
+ function debugLog(category, message, data) {
649
+ if (!getDebugMode()) return;
650
+ const prefix = `[Sylphx ${category}]`;
651
+ if (data !== void 0) {
652
+ console.log(prefix, message, data);
653
+ } else {
654
+ console.log(prefix, message);
655
+ }
656
+ }
657
+ function debugWarn(category, message, data) {
658
+ if (!getDebugMode()) return;
659
+ const prefix = `[Sylphx ${category}]`;
660
+ if (data !== void 0) {
661
+ console.warn(prefix, message, data);
662
+ } else {
663
+ console.warn(prefix, message);
664
+ }
665
+ }
666
+ function debugError(category, message, error) {
667
+ if (!getDebugMode()) return;
668
+ const prefix = `[Sylphx ${category}]`;
669
+ if (error !== void 0) {
670
+ console.error(prefix, message, error);
671
+ } else {
672
+ console.error(prefix, message);
673
+ }
674
+ }
675
+ function debugTimer(category, operation) {
676
+ if (!getDebugMode()) {
677
+ return { end: () => {
678
+ } };
679
+ }
680
+ const start = performance.now();
681
+ return {
682
+ end() {
683
+ const duration = performance.now() - start;
684
+ debugLog(category, `${operation} completed`, {
685
+ durationMs: Math.round(duration)
686
+ });
687
+ }
688
+ };
689
+ }
690
+ function enableDebug() {
691
+ if (typeof localStorage === "undefined") {
692
+ console.warn(
693
+ "[Sylphx] Debug mode can only be enabled in browser environments"
694
+ );
695
+ return;
696
+ }
697
+ try {
698
+ localStorage.setItem(DEBUG_STORAGE_KEY, "true");
699
+ debugModeCache = true;
700
+ console.log(
701
+ "[Sylphx] Debug mode enabled. Refresh the page to see debug logs."
702
+ );
703
+ } catch (e) {
704
+ console.warn("[Sylphx] Failed to enable debug mode:", e);
705
+ }
706
+ }
707
+ function disableDebug() {
708
+ if (typeof localStorage === "undefined") {
709
+ console.warn(
710
+ "[Sylphx] Debug mode can only be disabled in browser environments"
711
+ );
712
+ return;
713
+ }
714
+ try {
715
+ localStorage.removeItem(DEBUG_STORAGE_KEY);
716
+ debugModeCache = false;
717
+ console.log("[Sylphx] Debug mode disabled.");
718
+ } catch (e) {
719
+ console.warn("[Sylphx] Failed to disable debug mode:", e);
720
+ }
721
+ }
722
+ function installGlobalDebugHelpers() {
723
+ if (typeof window === "undefined") return;
724
+ const w = window;
725
+ w.__sylphx = {
726
+ enableDebug,
727
+ disableDebug,
728
+ isDebugEnabled: getDebugMode
729
+ };
730
+ }
731
+
732
+ // src/rest-client.ts
733
+ import createClient from "openapi-fetch";
734
+ function createAuthMiddleware(config) {
735
+ return {
736
+ async onRequest({ request }) {
737
+ request.headers.set("X-SDK-Version", SDK_VERSION);
738
+ request.headers.set("X-SDK-Platform", SDK_PLATFORM);
739
+ if (config.secretKey) {
740
+ request.headers.set("x-app-secret", config.secretKey);
741
+ }
742
+ const token = config.getAccessToken?.();
743
+ if (token) {
744
+ request.headers.set("Authorization", `Bearer ${token}`);
745
+ }
746
+ return request;
747
+ }
748
+ };
749
+ }
750
+ function isRetryableStatus(status) {
751
+ return status >= 500 || status === 429;
752
+ }
753
+ var inFlightRequests = /* @__PURE__ */ new Map();
754
+ async function getRequestKey(request) {
755
+ const body = request.body ? await request.clone().text() : "";
756
+ return `${request.method}:${request.url}:${body}`;
757
+ }
758
+ function createDeduplicationMiddleware(config = {}) {
759
+ const { enabled = true, methods = ["GET"] } = config;
760
+ if (!enabled) {
761
+ return {
762
+ async onRequest({ request }) {
763
+ return request;
764
+ }
765
+ };
766
+ }
767
+ return {
768
+ async onRequest({ request }) {
769
+ if (!methods.includes(request.method)) {
770
+ return request;
771
+ }
772
+ const key = await getRequestKey(request);
773
+ const existing = inFlightRequests.get(key);
774
+ if (existing) {
775
+ const deduped = request.clone();
776
+ deduped._dedupKey = key;
777
+ return deduped;
778
+ }
779
+ request._dedupKey = key;
780
+ return request;
781
+ },
782
+ async onResponse({ request, response }) {
783
+ const key = request._dedupKey;
784
+ if (!key) return response;
785
+ const existing = inFlightRequests.get(key);
786
+ if (existing && inFlightRequests.get(key) !== void 0) {
787
+ const cachedResponse = await existing;
788
+ return cachedResponse.clone();
789
+ }
790
+ const responsePromise = Promise.resolve(response.clone());
791
+ inFlightRequests.set(key, responsePromise);
792
+ responsePromise.finally(() => {
793
+ setTimeout(() => inFlightRequests.delete(key), 100);
794
+ });
795
+ return response;
796
+ }
797
+ };
798
+ }
799
+ var CircuitBreakerOpenError = class extends Error {
800
+ remainingMs;
801
+ constructor(remainingMs) {
802
+ super(
803
+ `Circuit breaker is open. Retry after ${Math.ceil(remainingMs / 1e3)}s`
804
+ );
805
+ this.name = "CircuitBreakerOpenError";
806
+ this.remainingMs = remainingMs;
807
+ }
808
+ };
809
+ var circuitBreaker = null;
810
+ function getCircuitBreaker(config = {}) {
811
+ if (!circuitBreaker) {
812
+ circuitBreaker = {
813
+ state: "CLOSED",
814
+ failures: [],
815
+ openedAt: null,
816
+ config: {
817
+ enabled: config.enabled ?? true,
818
+ failureThreshold: config.failureThreshold ?? CIRCUIT_BREAKER_FAILURE_THRESHOLD,
819
+ windowMs: config.windowMs ?? CIRCUIT_BREAKER_WINDOW_MS,
820
+ openDurationMs: config.openDurationMs ?? CIRCUIT_BREAKER_OPEN_DURATION_MS,
821
+ isFailure: config.isFailure ?? ((status) => status >= 500 || status === 429)
822
+ }
823
+ };
824
+ }
825
+ return circuitBreaker;
826
+ }
827
+ function recordFailure(cb) {
828
+ const now = Date.now();
829
+ cb.failures = cb.failures.filter((t) => now - t < cb.config.windowMs);
830
+ cb.failures.push(now);
831
+ if (cb.failures.length >= cb.config.failureThreshold) {
832
+ cb.state = "OPEN";
833
+ cb.openedAt = now;
834
+ }
835
+ }
836
+ function recordSuccess(cb) {
837
+ if (cb.state === "HALF_OPEN") {
838
+ cb.state = "CLOSED";
839
+ cb.failures = [];
840
+ cb.openedAt = null;
841
+ }
842
+ }
843
+ function shouldAllowRequest(cb) {
844
+ const now = Date.now();
845
+ switch (cb.state) {
846
+ case "CLOSED":
847
+ return { allowed: true };
848
+ case "OPEN": {
849
+ const elapsed = now - (cb.openedAt ?? now);
850
+ if (elapsed >= cb.config.openDurationMs) {
851
+ cb.state = "HALF_OPEN";
852
+ return { allowed: true };
853
+ }
854
+ return {
855
+ allowed: false,
856
+ remainingMs: cb.config.openDurationMs - elapsed
857
+ };
858
+ }
859
+ case "HALF_OPEN":
860
+ return { allowed: true };
861
+ default:
862
+ return { allowed: true };
863
+ }
864
+ }
865
+ function createCircuitBreakerMiddleware(config) {
866
+ if (config === false) {
867
+ return {
868
+ async onRequest({ request }) {
869
+ return request;
870
+ }
871
+ };
872
+ }
873
+ const cb = getCircuitBreaker(config ?? {});
874
+ return {
875
+ async onRequest({ request }) {
876
+ if (!cb.config.enabled) {
877
+ return request;
878
+ }
879
+ const check = shouldAllowRequest(cb);
880
+ if (!check.allowed) {
881
+ throw new CircuitBreakerOpenError(check.remainingMs);
882
+ }
883
+ return request;
884
+ },
885
+ async onResponse({ response }) {
886
+ if (!cb.config.enabled) {
887
+ return response;
888
+ }
889
+ if (cb.config.isFailure(response.status)) {
890
+ recordFailure(cb);
891
+ } else {
892
+ recordSuccess(cb);
893
+ }
894
+ return response;
895
+ }
896
+ };
897
+ }
898
+ function resetCircuitBreaker() {
899
+ circuitBreaker = null;
900
+ }
901
+ function getCircuitBreakerState() {
902
+ if (!circuitBreaker) return null;
903
+ return {
904
+ state: circuitBreaker.state,
905
+ failures: circuitBreaker.failures.length,
906
+ openedAt: circuitBreaker.openedAt
907
+ };
908
+ }
909
+ var etagCache = /* @__PURE__ */ new Map();
910
+ function getETagCacheKey(request) {
911
+ return `${request.method}:${request.url}`;
912
+ }
913
+ function evictOldEntries(maxEntries, ttlMs) {
914
+ const now = Date.now();
915
+ for (const [key, entry] of etagCache) {
916
+ if (now - entry.timestamp > ttlMs) {
917
+ etagCache.delete(key);
918
+ }
919
+ }
920
+ if (etagCache.size > maxEntries) {
921
+ const entries = Array.from(etagCache.entries());
922
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
923
+ const toRemove = entries.slice(0, entries.length - maxEntries);
924
+ for (const [key] of toRemove) {
925
+ etagCache.delete(key);
926
+ }
927
+ }
928
+ }
929
+ function createETagMiddleware(config) {
930
+ if (config === false) {
931
+ return {
932
+ async onRequest({ request }) {
933
+ return request;
934
+ }
935
+ };
936
+ }
937
+ const {
938
+ enabled = true,
939
+ maxEntries = ETAG_CACHE_MAX_ENTRIES,
940
+ ttlMs = ETAG_CACHE_TTL_MS
941
+ } = config ?? {};
942
+ if (!enabled) {
943
+ return {
944
+ async onRequest({ request }) {
945
+ return request;
946
+ }
947
+ };
948
+ }
949
+ return {
950
+ async onRequest({ request }) {
951
+ if (request.method !== "GET") {
952
+ return request;
953
+ }
954
+ const cacheKey = getETagCacheKey(request);
955
+ const cached = etagCache.get(cacheKey);
956
+ if (cached) {
957
+ if (Date.now() - cached.timestamp > ttlMs) {
958
+ etagCache.delete(cacheKey);
959
+ } else {
960
+ request.headers.set("If-None-Match", cached.etag);
961
+ }
962
+ }
963
+ return request;
964
+ },
965
+ async onResponse({ request, response }) {
966
+ if (request.method !== "GET") {
967
+ return response;
968
+ }
969
+ const cacheKey = getETagCacheKey(request);
970
+ if (response.status === 304) {
971
+ const cached = etagCache.get(cacheKey);
972
+ if (cached) {
973
+ cached.timestamp = Date.now();
974
+ return new Response(cached.body, {
975
+ status: 200,
976
+ headers: response.headers
977
+ });
978
+ }
979
+ return response;
980
+ }
981
+ if (response.ok) {
982
+ const etag = response.headers.get("ETag");
983
+ if (etag) {
984
+ const cloned = response.clone();
985
+ const body = await cloned.text();
986
+ evictOldEntries(maxEntries, ttlMs);
987
+ etagCache.set(cacheKey, {
988
+ etag,
989
+ body,
990
+ timestamp: Date.now()
991
+ });
992
+ }
993
+ }
994
+ return response;
995
+ }
996
+ };
997
+ }
998
+ function createRetryMiddleware(retryConfig) {
999
+ if (retryConfig === false) {
1000
+ return {
1001
+ async onResponse({ response }) {
1002
+ return response;
1003
+ }
1004
+ };
1005
+ }
1006
+ const {
1007
+ maxRetries = 3,
1008
+ baseDelay = BASE_RETRY_DELAY_MS,
1009
+ maxDelay = MAX_RETRY_DELAY_MS,
1010
+ shouldRetry = isRetryableStatus,
1011
+ timeout = DEFAULT_TIMEOUT_MS
1012
+ } = retryConfig ?? {};
1013
+ let originalBody = null;
1014
+ return {
1015
+ async onRequest({ request }) {
1016
+ if (request.body) {
1017
+ originalBody = await request.clone().text();
1018
+ } else {
1019
+ originalBody = null;
1020
+ }
1021
+ if (!request.signal) {
1022
+ const controller = new AbortController();
1023
+ setTimeout(() => controller.abort(), timeout);
1024
+ return new Request(request.url, {
1025
+ method: request.method,
1026
+ headers: request.headers,
1027
+ body: originalBody,
1028
+ signal: controller.signal
1029
+ });
1030
+ }
1031
+ return request;
1032
+ },
1033
+ async onResponse({ response, request }) {
1034
+ let attempt = 0;
1035
+ let currentResponse = response;
1036
+ while (attempt < maxRetries && shouldRetry(currentResponse.status, attempt)) {
1037
+ const retryAfter = currentResponse.headers.get("Retry-After");
1038
+ const delay = retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : exponentialBackoff(attempt, baseDelay, maxDelay);
1039
+ await new Promise((resolve) => setTimeout(resolve, delay));
1040
+ attempt++;
1041
+ const controller = new AbortController();
1042
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1043
+ try {
1044
+ const retryRequest = new Request(request.url, {
1045
+ method: request.method,
1046
+ headers: request.headers,
1047
+ body: originalBody,
1048
+ signal: controller.signal
1049
+ });
1050
+ const newResponse = await fetch(retryRequest);
1051
+ clearTimeout(timeoutId);
1052
+ if (newResponse.ok || !shouldRetry(newResponse.status, attempt)) {
1053
+ return newResponse;
1054
+ }
1055
+ currentResponse = newResponse;
1056
+ } catch (error) {
1057
+ clearTimeout(timeoutId);
1058
+ if (attempt >= maxRetries) {
1059
+ throw error;
1060
+ }
1061
+ }
1062
+ }
1063
+ return currentResponse;
1064
+ }
1065
+ };
1066
+ }
1067
+ function validateClientConfig(config) {
1068
+ return {
1069
+ secretKey: validateAndSanitizeSecretKey(config.secretKey),
1070
+ baseUrl: (config.platformUrl || DEFAULT_PLATFORM_URL).trim()
1071
+ };
1072
+ }
1073
+ function createRestClient(config) {
1074
+ const { secretKey, baseUrl } = validateClientConfig(config);
1075
+ const client = createClient({
1076
+ baseUrl: `${baseUrl}${SDK_API_PATH}`,
1077
+ headers: {
1078
+ "Content-Type": "application/json",
1079
+ "x-app-secret": secretKey
1080
+ }
1081
+ });
1082
+ if (config.deduplication !== false) {
1083
+ client.use(createDeduplicationMiddleware(config.deduplication));
1084
+ }
1085
+ if (config.circuitBreaker !== false) {
1086
+ client.use(createCircuitBreakerMiddleware(config.circuitBreaker));
1087
+ }
1088
+ if (config.etag !== false) {
1089
+ client.use(createETagMiddleware(config.etag));
1090
+ }
1091
+ client.use(createRetryMiddleware(config.retry));
1092
+ return client;
1093
+ }
1094
+ function createDynamicRestClient(config) {
1095
+ const { secretKey, baseUrl } = validateClientConfig(config);
1096
+ const validatedConfig = {
1097
+ ...config,
1098
+ secretKey,
1099
+ platformUrl: baseUrl
1100
+ };
1101
+ const client = createClient({
1102
+ baseUrl: `${baseUrl}${SDK_API_PATH}`,
1103
+ headers: {
1104
+ "Content-Type": "application/json"
1105
+ }
1106
+ });
1107
+ if (config.deduplication !== false) {
1108
+ client.use(createDeduplicationMiddleware(config.deduplication));
1109
+ }
1110
+ client.use(createAuthMiddleware(validatedConfig));
1111
+ if (config.circuitBreaker !== false) {
1112
+ client.use(createCircuitBreakerMiddleware(config.circuitBreaker));
1113
+ }
1114
+ if (config.etag !== false) {
1115
+ client.use(createETagMiddleware(config.etag));
1116
+ }
1117
+ client.use(createRetryMiddleware(config.retry));
1118
+ return client;
1119
+ }
1120
+ function hasError(response) {
1121
+ return response.error !== void 0;
1122
+ }
1123
+ function getRestErrorMessage(error) {
1124
+ if (error && typeof error === "object" && "error" in error) {
1125
+ const err = error;
1126
+ return err.error?.message ?? "An unknown error occurred";
1127
+ }
1128
+ if (error instanceof Error) {
1129
+ return error.message;
1130
+ }
1131
+ return "An unknown error occurred";
1132
+ }
1133
+
1134
+ // src/auth.ts
1135
+ async function signIn(config, input) {
1136
+ return callApi(config, "/auth/login", {
1137
+ method: "POST",
1138
+ body: input
1139
+ });
1140
+ }
1141
+ async function signUp(config, input) {
1142
+ return callApi(config, "/auth/register", {
1143
+ method: "POST",
1144
+ body: input
1145
+ });
1146
+ }
1147
+ async function signOut(config) {
1148
+ await callApi(config, "/auth/logout", { method: "POST" });
1149
+ }
1150
+ async function refreshToken(config, token) {
1151
+ const response = await fetch(`${config.platformUrl}/api/v1/auth/token`, {
1152
+ method: "POST",
1153
+ headers: { "Content-Type": "application/json" },
1154
+ body: JSON.stringify({
1155
+ grant_type: "refresh_token",
1156
+ refresh_token: token,
1157
+ client_secret: config.secretKey
1158
+ })
1159
+ });
1160
+ if (!response.ok) {
1161
+ const error = await response.json().catch(() => ({ message: "Token refresh failed" }));
1162
+ throw new SylphxError(error.message ?? "Token refresh failed", {
1163
+ code: "UNAUTHORIZED"
1164
+ });
1165
+ }
1166
+ return response.json();
1167
+ }
1168
+ async function verifyEmail(config, token) {
1169
+ const response = await fetch(`${config.platformUrl}/api/v1/auth/verify-email`, {
1170
+ method: "POST",
1171
+ headers: buildHeaders(config),
1172
+ body: JSON.stringify({ token })
1173
+ });
1174
+ if (!response.ok) {
1175
+ const error = await response.json().catch(() => ({ message: "Email verification failed" }));
1176
+ throw new SylphxError(error.message ?? "Email verification failed", {
1177
+ code: "BAD_REQUEST"
1178
+ });
1179
+ }
1180
+ }
1181
+ async function forgotPassword(config, email) {
1182
+ await callApi(config, "/auth/forgot-password", {
1183
+ method: "POST",
1184
+ body: { email }
1185
+ });
1186
+ }
1187
+ async function resetPassword(config, input) {
1188
+ await callApi(config, "/auth/reset-password", {
1189
+ method: "POST",
1190
+ body: { token: input.token, newPassword: input.password }
1191
+ });
1192
+ }
1193
+ async function getSession(config) {
1194
+ if (!config.accessToken) {
1195
+ return { user: null };
1196
+ }
1197
+ try {
1198
+ const user = await callApi(config, "/auth/me");
1199
+ return { user };
1200
+ } catch {
1201
+ return { user: null };
1202
+ }
1203
+ }
1204
+ async function verifyTwoFactor(config, userId, code) {
1205
+ return callApi(config, "/auth/verify-2fa", {
1206
+ method: "POST",
1207
+ body: { userId, code }
1208
+ });
1209
+ }
1210
+ async function introspectToken(config, token, tokenTypeHint) {
1211
+ const response = await fetch(`${config.platformUrl}/api/v1/auth/introspect`, {
1212
+ method: "POST",
1213
+ headers: { "Content-Type": "application/json" },
1214
+ body: JSON.stringify({
1215
+ token,
1216
+ token_type_hint: tokenTypeHint,
1217
+ client_secret: config.secretKey
1218
+ })
1219
+ });
1220
+ if (!response.ok) {
1221
+ return { active: false };
1222
+ }
1223
+ return response.json();
1224
+ }
1225
+ async function revokeToken(config, token, options) {
1226
+ await fetch(`${config.platformUrl}/api/v1/auth/revoke`, {
1227
+ method: "POST",
1228
+ headers: { "Content-Type": "application/json" },
1229
+ body: JSON.stringify({
1230
+ token: options?.revokeAll ? void 0 : token,
1231
+ client_secret: config.secretKey,
1232
+ user_id: options?.userId,
1233
+ revoke_all: options?.revokeAll
1234
+ })
1235
+ });
1236
+ }
1237
+ async function revokeAllTokens(config, userId) {
1238
+ await revokeToken(config, "", { revokeAll: true, userId });
1239
+ }
1240
+
1241
+ // src/analytics.ts
1242
+ async function track(config, input) {
1243
+ await callApi(config, "/analytics/track", {
1244
+ method: "POST",
1245
+ body: {
1246
+ event: input.event,
1247
+ properties: input.properties ?? {},
1248
+ userId: input.userId,
1249
+ anonymousId: input.anonymousId,
1250
+ timestamp: input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1251
+ }
1252
+ });
1253
+ }
1254
+ async function page(config, input) {
1255
+ await callApi(config, "/analytics/page", {
1256
+ method: "POST",
1257
+ body: {
1258
+ name: input.name,
1259
+ properties: input.properties ?? {},
1260
+ userId: input.userId,
1261
+ anonymousId: input.anonymousId,
1262
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1263
+ }
1264
+ });
1265
+ }
1266
+ async function identify(config, input) {
1267
+ await callApi(config, "/analytics/identify", {
1268
+ method: "POST",
1269
+ body: {
1270
+ userId: input.userId,
1271
+ traits: input.traits ?? {},
1272
+ anonymousId: input.anonymousId
1273
+ }
1274
+ });
1275
+ }
1276
+ async function trackBatch(config, events) {
1277
+ await callApi(config, "/analytics/batch", {
1278
+ method: "POST",
1279
+ body: {
1280
+ events: events.map((e) => ({
1281
+ event: e.type === "track" ? e.event : e.type === "page" ? `$pageview` : "$identify",
1282
+ properties: {
1283
+ ...e.properties,
1284
+ ...e.type === "page" && e.name ? { name: e.name } : {},
1285
+ ...e.type === "identify" && e.traits ? { traits: e.traits } : {}
1286
+ },
1287
+ userId: e.userId,
1288
+ anonymousId: e.anonymousId,
1289
+ timestamp: e.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1290
+ }))
1291
+ }
1292
+ });
1293
+ }
1294
+ function generateAnonymousId() {
1295
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
1296
+ return crypto.randomUUID();
1297
+ }
1298
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1299
+ const r = Math.random() * 16 | 0;
1300
+ const v = c === "x" ? r : r & 3 | 8;
1301
+ return v.toString(16);
1302
+ });
1303
+ }
1304
+ function createTracker(config, defaultAnonymousId) {
1305
+ const anonymousId = defaultAnonymousId ?? generateAnonymousId();
1306
+ return {
1307
+ track: (event, properties, userId) => track(config, { event, properties, userId, anonymousId }),
1308
+ page: (name, properties, userId) => page(config, { name, properties, userId, anonymousId }),
1309
+ identify: (userId, traits) => identify(config, { userId, traits, anonymousId }),
1310
+ batch: (events) => trackBatch(
1311
+ config,
1312
+ events.map((e) => ({
1313
+ ...e,
1314
+ anonymousId: e.anonymousId ?? anonymousId
1315
+ }))
1316
+ ),
1317
+ /** Get the anonymous ID for this tracker */
1318
+ getAnonymousId: () => anonymousId
1319
+ };
1320
+ }
1321
+
1322
+ // src/ai.ts
1323
+ async function chat(config, input) {
1324
+ const response = await fetch(
1325
+ `${config.platformUrl}/api/v1/chat/completions`,
1326
+ {
1327
+ method: "POST",
1328
+ headers: {
1329
+ ...buildHeaders(config),
1330
+ Authorization: `Bearer ${config.secretKey}`
1331
+ },
1332
+ body: JSON.stringify({
1333
+ model: input.model,
1334
+ messages: input.messages,
1335
+ temperature: input.temperature,
1336
+ max_tokens: input.maxTokens,
1337
+ top_p: input.topP,
1338
+ frequency_penalty: input.frequencyPenalty,
1339
+ presence_penalty: input.presencePenalty,
1340
+ stop: input.stop,
1341
+ tools: input.tools,
1342
+ tool_choice: input.toolChoice
1343
+ })
1344
+ }
1345
+ );
1346
+ if (!response.ok) {
1347
+ const error = await response.json().catch(() => ({ error: { message: "Chat request failed" } }));
1348
+ throw new SylphxError(error?.error?.message ?? "Chat request failed", {
1349
+ code: "BAD_REQUEST"
1350
+ });
1351
+ }
1352
+ const data = await response.json();
1353
+ return {
1354
+ id: data.id,
1355
+ model: data.model,
1356
+ choices: data.choices.map((c) => ({
1357
+ index: c.index,
1358
+ message: {
1359
+ role: "assistant",
1360
+ content: c.message?.content,
1361
+ tool_calls: c.message?.tool_calls
1362
+ },
1363
+ finishReason: c.finish_reason
1364
+ })),
1365
+ usage: {
1366
+ promptTokens: data.usage.prompt_tokens,
1367
+ completionTokens: data.usage.completion_tokens,
1368
+ totalTokens: data.usage.total_tokens
1369
+ }
1370
+ };
1371
+ }
1372
+ function chatStream(config, input) {
1373
+ return {
1374
+ [Symbol.asyncIterator]: async function* () {
1375
+ const response = await fetch(
1376
+ `${config.platformUrl}/api/v1/chat/completions`,
1377
+ {
1378
+ method: "POST",
1379
+ headers: {
1380
+ ...buildHeaders(config),
1381
+ Authorization: `Bearer ${config.secretKey}`
1382
+ },
1383
+ body: JSON.stringify({
1384
+ model: input.model,
1385
+ messages: input.messages,
1386
+ temperature: input.temperature,
1387
+ max_tokens: input.maxTokens,
1388
+ top_p: input.topP,
1389
+ frequency_penalty: input.frequencyPenalty,
1390
+ presence_penalty: input.presencePenalty,
1391
+ stop: input.stop,
1392
+ tools: input.tools,
1393
+ tool_choice: input.toolChoice,
1394
+ stream: true
1395
+ })
1396
+ }
1397
+ );
1398
+ if (!response.ok) {
1399
+ const error = await response.json().catch(() => ({ error: { message: "Stream request failed" } }));
1400
+ throw new SylphxError(error?.error?.message ?? "Stream request failed", {
1401
+ code: "BAD_REQUEST"
1402
+ });
1403
+ }
1404
+ const reader = response.body?.getReader();
1405
+ if (!reader) {
1406
+ throw new SylphxError("No response body", {
1407
+ code: "INTERNAL_SERVER_ERROR"
1408
+ });
1409
+ }
1410
+ const decoder = new TextDecoder();
1411
+ let buffer = "";
1412
+ try {
1413
+ while (true) {
1414
+ const { done, value } = await reader.read();
1415
+ if (done) break;
1416
+ buffer += decoder.decode(value, { stream: true });
1417
+ const lines = buffer.split("\n");
1418
+ buffer = lines.pop() ?? "";
1419
+ for (const line of lines) {
1420
+ if (line.startsWith("data: ")) {
1421
+ const data = line.slice(6).trim();
1422
+ if (data === "[DONE]") return;
1423
+ try {
1424
+ const chunk = JSON.parse(data);
1425
+ yield {
1426
+ id: chunk.id ?? "",
1427
+ model: chunk.model ?? input.model,
1428
+ choices: (chunk.choices ?? []).map(
1429
+ (c) => ({
1430
+ index: typeof c.index === "number" ? c.index : 0,
1431
+ delta: {
1432
+ role: c.delta?.role,
1433
+ content: c.delta?.content,
1434
+ tool_calls: c.delta?.tool_calls
1435
+ },
1436
+ finishReason: c.finish_reason ?? null
1437
+ })
1438
+ )
1439
+ };
1440
+ } catch {
1441
+ }
1442
+ }
1443
+ }
1444
+ }
1445
+ } finally {
1446
+ reader.releaseLock();
1447
+ }
1448
+ }
1449
+ };
1450
+ }
1451
+ async function embed(config, input) {
1452
+ const response = await fetch(`${config.platformUrl}/api/v1/embeddings`, {
1453
+ method: "POST",
1454
+ headers: {
1455
+ ...buildHeaders(config),
1456
+ Authorization: `Bearer ${config.secretKey}`
1457
+ },
1458
+ body: JSON.stringify({
1459
+ model: input.model,
1460
+ input: input.input,
1461
+ dimensions: input.dimensions
1462
+ })
1463
+ });
1464
+ if (!response.ok) {
1465
+ const error = await response.json().catch(() => ({ error: { message: "Embedding request failed" } }));
1466
+ throw new SylphxError(error?.error?.message ?? "Embedding request failed", {
1467
+ code: "BAD_REQUEST"
1468
+ });
1469
+ }
1470
+ const data = await response.json();
1471
+ return {
1472
+ model: data.model,
1473
+ data: data.data,
1474
+ usage: {
1475
+ promptTokens: data.usage.prompt_tokens,
1476
+ totalTokens: data.usage.total_tokens
1477
+ }
1478
+ };
1479
+ }
1480
+ async function complete(config, model, prompt, options) {
1481
+ const response = await chat(config, {
1482
+ model,
1483
+ messages: [{ role: "user", content: prompt }],
1484
+ ...options
1485
+ });
1486
+ return response.choices[0]?.message.content ?? "";
1487
+ }
1488
+ async function streamToString(config, input) {
1489
+ let result = "";
1490
+ for await (const chunk of chatStream(config, input)) {
1491
+ result += chunk.choices[0]?.delta.content ?? "";
1492
+ }
1493
+ return result;
1494
+ }
1495
+
1496
+ // src/billing.ts
1497
+ async function getPlans(config) {
1498
+ return callApi(config, "/billing/plans");
1499
+ }
1500
+ async function getSubscription(config, userId) {
1501
+ return callApi(config, "/billing/subscription", {
1502
+ query: { userId }
1503
+ });
1504
+ }
1505
+ async function createCheckout(config, input) {
1506
+ return callApi(config, "/billing/checkout", {
1507
+ method: "POST",
1508
+ body: input
1509
+ });
1510
+ }
1511
+ async function createPortalSession(config, input) {
1512
+ return callApi(config, "/billing/portal", {
1513
+ method: "POST",
1514
+ body: input
1515
+ });
1516
+ }
1517
+ async function getBillingBalance(config) {
1518
+ return callApi(config, "/billing/balance");
1519
+ }
1520
+ async function getBillingUsage(config, options) {
1521
+ return callApi(config, "/billing/usage", {
1522
+ query: options?.month ? { month: options.month } : void 0
1523
+ });
1524
+ }
1525
+
1526
+ // src/storage.ts
1527
+ var UPLOAD_RETRY_CONFIG = {
1528
+ /** Maximum number of retry attempts (AWS S3 pattern) */
1529
+ maxRetries: 5,
1530
+ /** Base delay in milliseconds */
1531
+ baseDelayMs: BASE_RETRY_DELAY_MS,
1532
+ /** Maximum delay cap in milliseconds */
1533
+ maxDelayMs: MAX_RETRY_DELAY_MS,
1534
+ /** Jitter type: 'full' for full jitter (AWS recommended) */
1535
+ jitter: "full"
1536
+ };
1537
+ function calculateBackoffDelay(attempt) {
1538
+ const { baseDelayMs, maxDelayMs } = UPLOAD_RETRY_CONFIG;
1539
+ const exponentialDelay = baseDelayMs * 2 ** attempt;
1540
+ const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
1541
+ return Math.random() * cappedDelay;
1542
+ }
1543
+ async function sleep(ms, signal) {
1544
+ return new Promise((resolve, reject) => {
1545
+ if (signal?.aborted) {
1546
+ reject(new DOMException("Upload aborted", "AbortError"));
1547
+ return;
1548
+ }
1549
+ const timeoutId = setTimeout(resolve, ms);
1550
+ signal?.addEventListener(
1551
+ "abort",
1552
+ () => {
1553
+ clearTimeout(timeoutId);
1554
+ reject(new DOMException("Upload aborted", "AbortError"));
1555
+ },
1556
+ { once: true }
1557
+ );
1558
+ });
1559
+ }
1560
+ function isRetryableError3(error) {
1561
+ if (error instanceof DOMException && error.name === "AbortError") {
1562
+ return false;
1563
+ }
1564
+ if (error instanceof TypeError) {
1565
+ return true;
1566
+ }
1567
+ if (error instanceof Error && "status" in error) {
1568
+ const status = error.status;
1569
+ return status >= 500 || status === 429;
1570
+ }
1571
+ return false;
1572
+ }
1573
+ async function uploadFile(config, file, options) {
1574
+ const { signal } = options ?? {};
1575
+ if (signal?.aborted) {
1576
+ throw new DOMException("Upload aborted", "AbortError");
1577
+ }
1578
+ let tokenResponse = null;
1579
+ let lastError = null;
1580
+ for (let attempt = 0; attempt <= UPLOAD_RETRY_CONFIG.maxRetries; attempt++) {
1581
+ try {
1582
+ tokenResponse = await fetch(`${config.platformUrl}/api/v1/storage/upload`, {
1583
+ method: "POST",
1584
+ headers: buildHeaders(config),
1585
+ body: JSON.stringify({
1586
+ filename: file.name,
1587
+ contentType: file.type,
1588
+ size: file.size,
1589
+ path: options?.path,
1590
+ type: options?.type ?? "file",
1591
+ userId: options?.userId
1592
+ }),
1593
+ signal
1594
+ });
1595
+ if (tokenResponse.ok) {
1596
+ break;
1597
+ }
1598
+ if (tokenResponse.status >= 500 || tokenResponse.status === 429) {
1599
+ if (attempt < UPLOAD_RETRY_CONFIG.maxRetries) {
1600
+ const delay = calculateBackoffDelay(attempt);
1601
+ await sleep(delay, signal);
1602
+ continue;
1603
+ }
1604
+ }
1605
+ const error = await tokenResponse.json().catch(() => ({ message: "Failed to get upload token" }));
1606
+ throw new SylphxError(error.message ?? "Failed to get upload token", {
1607
+ code: "BAD_REQUEST"
1608
+ });
1609
+ } catch (error) {
1610
+ if (error instanceof DOMException && error.name === "AbortError") {
1611
+ throw error;
1612
+ }
1613
+ lastError = error instanceof Error ? error : new Error(String(error));
1614
+ if (isRetryableError3(error) && attempt < UPLOAD_RETRY_CONFIG.maxRetries) {
1615
+ const delay = calculateBackoffDelay(attempt);
1616
+ await sleep(delay, signal);
1617
+ continue;
1618
+ }
1619
+ throw lastError;
1620
+ }
1621
+ }
1622
+ if (!tokenResponse?.ok) {
1623
+ throw lastError ?? new SylphxError("Failed to get upload token after retries", {
1624
+ code: "BAD_REQUEST"
1625
+ });
1626
+ }
1627
+ const { uploadUrl, publicUrl } = await tokenResponse.json();
1628
+ return executeUploadWithRetry(file, uploadUrl, publicUrl, options);
1629
+ }
1630
+ async function executeUploadWithRetry(file, uploadUrl, publicUrl, options) {
1631
+ const { signal } = options ?? {};
1632
+ let lastError = null;
1633
+ for (let attempt = 0; attempt <= UPLOAD_RETRY_CONFIG.maxRetries; attempt++) {
1634
+ try {
1635
+ return await executeUpload(file, uploadUrl, publicUrl, options);
1636
+ } catch (error) {
1637
+ if (error instanceof DOMException && error.name === "AbortError") {
1638
+ throw error;
1639
+ }
1640
+ lastError = error instanceof Error ? error : new Error(String(error));
1641
+ if (isRetryableError3(error) && attempt < UPLOAD_RETRY_CONFIG.maxRetries) {
1642
+ const delay = calculateBackoffDelay(attempt);
1643
+ await sleep(delay, signal);
1644
+ continue;
1645
+ }
1646
+ throw lastError;
1647
+ }
1648
+ }
1649
+ throw lastError ?? new Error("Upload failed after retries");
1650
+ }
1651
+ function executeUpload(file, uploadUrl, publicUrl, options) {
1652
+ const { signal, onProgress } = options ?? {};
1653
+ return new Promise((resolve, reject) => {
1654
+ const xhr = new XMLHttpRequest();
1655
+ const handleAbort = () => {
1656
+ xhr.abort();
1657
+ reject(new DOMException("Upload aborted", "AbortError"));
1658
+ };
1659
+ if (signal?.aborted) {
1660
+ reject(new DOMException("Upload aborted", "AbortError"));
1661
+ return;
1662
+ }
1663
+ signal?.addEventListener("abort", handleAbort, { once: true });
1664
+ xhr.upload.addEventListener("progress", (event) => {
1665
+ if (event.lengthComputable && onProgress) {
1666
+ onProgress({
1667
+ loaded: event.loaded,
1668
+ total: event.total,
1669
+ progress: Math.round(event.loaded / event.total * 100)
1670
+ });
1671
+ }
1672
+ });
1673
+ xhr.addEventListener("load", () => {
1674
+ signal?.removeEventListener("abort", handleAbort);
1675
+ if (xhr.status >= 200 && xhr.status < 300) {
1676
+ resolve({
1677
+ url: publicUrl,
1678
+ pathname: options?.path ? `${options.path}/${file.name}` : file.name,
1679
+ contentType: file.type,
1680
+ size: file.size
1681
+ });
1682
+ } else {
1683
+ const error = new Error(`Upload failed with status ${xhr.status}`);
1684
+ error.status = xhr.status;
1685
+ reject(error);
1686
+ }
1687
+ });
1688
+ xhr.addEventListener("error", () => {
1689
+ signal?.removeEventListener("abort", handleAbort);
1690
+ reject(new TypeError("Network error during upload"));
1691
+ });
1692
+ xhr.addEventListener("abort", () => {
1693
+ signal?.removeEventListener("abort", handleAbort);
1694
+ reject(new DOMException("Upload aborted", "AbortError"));
1695
+ });
1696
+ xhr.open("PUT", uploadUrl);
1697
+ xhr.setRequestHeader("Content-Type", file.type);
1698
+ xhr.send(file);
1699
+ });
1700
+ }
1701
+ async function uploadAvatar(config, file, userId, options) {
1702
+ return uploadFile(config, file, {
1703
+ ...options,
1704
+ type: "avatar",
1705
+ userId
1706
+ });
1707
+ }
1708
+ async function deleteFile(config, fileId) {
1709
+ await callApi(config, `/storage/files/${fileId}`, { method: "DELETE" });
1710
+ }
1711
+ async function getFileUrl(config, fileId) {
1712
+ const data = await callApi(config, `/storage/files/${fileId}`, { method: "GET" });
1713
+ return data.url;
1714
+ }
1715
+ async function getFileInfo(config, fileId) {
1716
+ return callApi(config, `/storage/files/${fileId}`, {
1717
+ method: "GET"
1718
+ });
1719
+ }
1720
+ async function getSignedUrl(config, fileId, options) {
1721
+ return callApi(config, "/storage/signed-url", {
1722
+ method: "POST",
1723
+ body: {
1724
+ fileId,
1725
+ ...options
1726
+ }
1727
+ });
1728
+ }
1729
+
1730
+ // src/notifications.ts
1731
+ async function registerPush(config, subscription) {
1732
+ await callApi(config, "/notifications/register", {
1733
+ method: "POST",
1734
+ body: { subscription }
1735
+ });
1736
+ }
1737
+ async function unregisterPush(config, endpoint) {
1738
+ await callApi(config, "/notifications/unregister", {
1739
+ method: "POST",
1740
+ body: { endpoint }
1741
+ });
1742
+ }
1743
+ async function sendPush(config, userId, notification) {
1744
+ return callApi(config, "/notifications/send", {
1745
+ method: "POST",
1746
+ body: { userId, ...notification }
1747
+ });
1748
+ }
1749
+ async function getPushPreferences(config) {
1750
+ return callApi(config, "/notifications/preferences", { method: "GET" });
1751
+ }
1752
+ async function updatePushPreferences(config, preferences) {
1753
+ await callApi(config, "/notifications/preferences", {
1754
+ method: "PUT",
1755
+ body: preferences
1756
+ });
1757
+ }
1758
+
1759
+ // src/lib/notifications/service-worker.ts
1760
+ function initPushServiceWorker(config = {}) {
1761
+ const {
1762
+ defaultIcon,
1763
+ defaultBadge,
1764
+ onNotificationClick,
1765
+ onNotificationClose
1766
+ } = config;
1767
+ self.addEventListener("push", (event) => {
1768
+ if (!event.data) {
1769
+ console.warn("[Sylphx SW] Push event received without data");
1770
+ return;
1771
+ }
1772
+ let payload;
1773
+ try {
1774
+ payload = event.data.json();
1775
+ } catch {
1776
+ payload = {
1777
+ title: "Notification",
1778
+ body: event.data.text()
1779
+ };
1780
+ }
1781
+ const notificationOptions = {
1782
+ body: payload.body,
1783
+ icon: payload.icon || defaultIcon,
1784
+ badge: payload.badge || defaultBadge,
1785
+ image: payload.image,
1786
+ data: {
1787
+ ...payload.data,
1788
+ url: payload.url,
1789
+ _sylphxPayload: payload
1790
+ },
1791
+ tag: payload.tag,
1792
+ requireInteraction: payload.requireInteraction ?? false,
1793
+ vibrate: payload.vibrate,
1794
+ silent: payload.silent ?? false,
1795
+ actions: payload.actions
1796
+ };
1797
+ event.waitUntil(
1798
+ self.registration.showNotification(payload.title, notificationOptions)
1799
+ );
1800
+ });
1801
+ self.addEventListener("notificationclick", (event) => {
1802
+ event.notification.close();
1803
+ const data = event.notification.data;
1804
+ const payload = data?._sylphxPayload;
1805
+ const url = data?.url;
1806
+ if (onNotificationClick && payload) {
1807
+ onNotificationClick(payload);
1808
+ }
1809
+ if (event.action) {
1810
+ console.log("[Sylphx SW] Action clicked:", event.action);
1811
+ }
1812
+ if (url) {
1813
+ event.waitUntil(
1814
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
1815
+ for (const client of clientList) {
1816
+ if (client.url === url && "focus" in client) {
1817
+ return client.focus();
1818
+ }
1819
+ }
1820
+ return self.clients.openWindow(url);
1821
+ })
1822
+ );
1823
+ }
1824
+ });
1825
+ self.addEventListener("notificationclose", (event) => {
1826
+ const data = event.notification.data;
1827
+ const payload = data?._sylphxPayload;
1828
+ if (onNotificationClose && payload) {
1829
+ onNotificationClose(payload);
1830
+ }
1831
+ });
1832
+ self.addEventListener("activate", (event) => {
1833
+ event.waitUntil(
1834
+ // Claim all clients immediately
1835
+ self.clients.claim()
1836
+ );
1837
+ });
1838
+ console.log("[Sylphx SW] Push notification service worker initialized");
1839
+ }
1840
+ function createServiceWorkerScript(config = {}) {
1841
+ const { defaultIcon = "/icon-192.png", defaultBadge = "/badge-72.png" } = config;
1842
+ return `
1843
+ // Sylphx Push Notification Service Worker
1844
+ // Auto-generated - do not edit directly
1845
+
1846
+ const DEFAULT_ICON = '${defaultIcon}';
1847
+ const DEFAULT_BADGE = '${defaultBadge}';
1848
+
1849
+ self.addEventListener('push', (event) => {
1850
+ if (!event.data) return;
1851
+
1852
+ let payload;
1853
+ try {
1854
+ payload = event.data.json();
1855
+ } catch {
1856
+ payload = { title: 'Notification', body: event.data.text() };
1857
+ }
1858
+
1859
+ const options = {
1860
+ body: payload.body,
1861
+ icon: payload.icon || DEFAULT_ICON,
1862
+ badge: payload.badge || DEFAULT_BADGE,
1863
+ image: payload.image,
1864
+ data: { ...payload.data, url: payload.url },
1865
+ tag: payload.tag,
1866
+ requireInteraction: payload.requireInteraction || false,
1867
+ vibrate: payload.vibrate,
1868
+ silent: payload.silent || false,
1869
+ actions: payload.actions,
1870
+ };
1871
+
1872
+ event.waitUntil(
1873
+ self.registration.showNotification(payload.title, options)
1874
+ );
1875
+ });
1876
+
1877
+ self.addEventListener('notificationclick', (event) => {
1878
+ event.notification.close();
1879
+ const url = event.notification.data?.url;
1880
+
1881
+ if (url) {
1882
+ event.waitUntil(
1883
+ clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
1884
+ for (const client of clientList) {
1885
+ if (client.url === url && 'focus' in client) {
1886
+ return client.focus();
1887
+ }
1888
+ }
1889
+ if (clients.openWindow) {
1890
+ return clients.openWindow(url);
1891
+ }
1892
+ })
1893
+ );
1894
+ }
1895
+ });
1896
+
1897
+ self.addEventListener('activate', (event) => {
1898
+ event.waitUntil(clients.claim());
1899
+ });
1900
+
1901
+ console.log('[Sylphx SW] Push notification service worker active');
1902
+ `.trim();
1903
+ }
1904
+ async function registerPushServiceWorker(swPath = "/sw.js") {
1905
+ if (typeof window === "undefined") return null;
1906
+ if (!("serviceWorker" in navigator)) {
1907
+ console.warn("[Sylphx] Service workers not supported");
1908
+ return null;
1909
+ }
1910
+ try {
1911
+ const registration = await navigator.serviceWorker.register(swPath);
1912
+ console.log("[Sylphx] Service worker registered:", registration.scope);
1913
+ return registration;
1914
+ } catch (error) {
1915
+ console.error("[Sylphx] Service worker registration failed:", error);
1916
+ return null;
1917
+ }
1918
+ }
1919
+
1920
+ // src/jobs.ts
1921
+ async function scheduleJob(config, input) {
1922
+ return callApi(config, "/jobs/schedule", {
1923
+ method: "POST",
1924
+ body: input
1925
+ });
1926
+ }
1927
+ async function getJob(config, jobId) {
1928
+ return callApi(config, `/jobs/${jobId}`, { method: "GET" });
1929
+ }
1930
+ async function cancelJob(config, jobId) {
1931
+ const result = await callApi(
1932
+ config,
1933
+ `/jobs/${jobId}/cancel`,
1934
+ {
1935
+ method: "POST"
1936
+ }
1937
+ );
1938
+ return result.success;
1939
+ }
1940
+ async function listJobs(config, options) {
1941
+ return callApi(config, "/jobs", {
1942
+ method: "GET",
1943
+ query: options
1944
+ });
1945
+ }
1946
+ async function createCron(config, input) {
1947
+ return callApi(config, "/jobs/cron", {
1948
+ method: "POST",
1949
+ body: input
1950
+ });
1951
+ }
1952
+ async function pauseCron(config, scheduleId) {
1953
+ const result = await callApi(
1954
+ config,
1955
+ `/jobs/cron/${scheduleId}/pause`,
1956
+ {
1957
+ method: "POST"
1958
+ }
1959
+ );
1960
+ return result.success;
1961
+ }
1962
+ async function resumeCron(config, scheduleId) {
1963
+ const result = await callApi(
1964
+ config,
1965
+ `/jobs/cron/${scheduleId}/resume`,
1966
+ {
1967
+ method: "POST"
1968
+ }
1969
+ );
1970
+ return result.success;
1971
+ }
1972
+ async function deleteCron(config, scheduleId) {
1973
+ const result = await callApi(
1974
+ config,
1975
+ `/jobs/cron/${scheduleId}`,
1976
+ {
1977
+ method: "DELETE"
1978
+ }
1979
+ );
1980
+ return result.success;
1981
+ }
1982
+
1983
+ // src/flags.ts
1984
+ async function checkFlag(config, flagKey, context) {
1985
+ const response = await callApi(
1986
+ config,
1987
+ "/flags/evaluate",
1988
+ {
1989
+ method: "POST",
1990
+ body: {
1991
+ context: {
1992
+ userId: context?.userId,
1993
+ anonymousId: context?.anonymousId,
1994
+ properties: context?.properties
1995
+ },
1996
+ keys: [flagKey]
1997
+ }
1998
+ }
1999
+ );
2000
+ return response.data[flagKey] ?? {
2001
+ key: flagKey,
2002
+ enabled: false,
2003
+ reason: "flag_not_found"
2004
+ };
2005
+ }
2006
+ async function getFlags(config, flagKeys, context) {
2007
+ const response = await callApi(
2008
+ config,
2009
+ "/flags/evaluate",
2010
+ {
2011
+ method: "POST",
2012
+ body: {
2013
+ context: {
2014
+ userId: context?.userId,
2015
+ anonymousId: context?.anonymousId,
2016
+ properties: context?.properties
2017
+ },
2018
+ keys: flagKeys
2019
+ }
2020
+ }
2021
+ );
2022
+ return response.data;
2023
+ }
2024
+ async function getAllFlags(config, context) {
2025
+ const response = await callApi(
2026
+ config,
2027
+ "/flags/evaluate",
2028
+ {
2029
+ method: "POST",
2030
+ body: {
2031
+ context: {
2032
+ userId: context?.userId,
2033
+ anonymousId: context?.anonymousId,
2034
+ properties: context?.properties
2035
+ }
2036
+ // Omit keys to get all flags
2037
+ }
2038
+ }
2039
+ );
2040
+ return response.data;
2041
+ }
2042
+ async function isEnabled(config, flagKey, context) {
2043
+ const flag = await checkFlag(config, flagKey, context);
2044
+ return flag.enabled;
2045
+ }
2046
+ async function getVariant(config, flagKey, context) {
2047
+ const flag = await checkFlag(config, flagKey, context);
2048
+ return flag.variant;
2049
+ }
2050
+ async function getFlagPayload(config, flagKey, context) {
2051
+ const flag = await checkFlag(config, flagKey, context);
2052
+ return flag.payload;
2053
+ }
2054
+
2055
+ // src/webhooks.ts
2056
+ async function getWebhookConfig(config) {
2057
+ return callApi(config, "/webhooks/config", { method: "GET" });
2058
+ }
2059
+ async function updateWebhookConfig(config, data) {
2060
+ return callApi(config, "/webhooks/config", { method: "PUT", body: data });
2061
+ }
2062
+ async function getWebhookDeliveries(config, options) {
2063
+ return callApi(config, "/webhooks/deliveries", {
2064
+ method: "GET",
2065
+ query: options
2066
+ });
2067
+ }
2068
+ async function getWebhookDelivery(config, deliveryId) {
2069
+ return callApi(config, `/webhooks/deliveries/${deliveryId}`, {
2070
+ method: "GET"
2071
+ });
2072
+ }
2073
+ async function replayWebhookDelivery(config, deliveryId) {
2074
+ return callApi(config, `/webhooks/deliveries/${deliveryId}/replay`, {
2075
+ method: "POST"
2076
+ });
2077
+ }
2078
+ async function getWebhookStats(config, environmentId) {
2079
+ return callApi(config, "/webhooks/stats", {
2080
+ method: "GET",
2081
+ query: environmentId ? { environmentId } : void 0
2082
+ });
2083
+ }
2084
+
2085
+ // src/email.ts
2086
+ async function isEmailConfigured(config) {
2087
+ return callApi(config, "/email/configured", { method: "GET" });
2088
+ }
2089
+ async function sendEmail(config, options) {
2090
+ const { idempotencyKey, ...body } = options;
2091
+ return callApi(config, "/email/send", {
2092
+ method: "POST",
2093
+ body,
2094
+ idempotencyKey
2095
+ });
2096
+ }
2097
+ async function sendTemplatedEmail(config, options) {
2098
+ const { idempotencyKey, ...body } = options;
2099
+ return callApi(config, "/email/send-templated", {
2100
+ method: "POST",
2101
+ body,
2102
+ idempotencyKey
2103
+ });
2104
+ }
2105
+ async function sendEmailToUser(config, options) {
2106
+ const { idempotencyKey, ...body } = options;
2107
+ return callApi(config, "/email/send-to-user", {
2108
+ method: "POST",
2109
+ body,
2110
+ idempotencyKey
2111
+ });
2112
+ }
2113
+ async function scheduleEmail(config, options) {
2114
+ return callApi(config, "/email/schedule", { method: "POST", body: options });
2115
+ }
2116
+ async function listScheduledEmails(config, options) {
2117
+ return callApi(config, "/email/scheduled", {
2118
+ method: "GET",
2119
+ query: options
2120
+ });
2121
+ }
2122
+ async function getScheduledEmail(config, emailId) {
2123
+ return callApi(config, `/email/scheduled/${emailId}`, { method: "GET" });
2124
+ }
2125
+ async function cancelScheduledEmail(config, emailId) {
2126
+ return callApi(config, `/email/scheduled/${emailId}/cancel`, {
2127
+ method: "POST"
2128
+ });
2129
+ }
2130
+ async function rescheduleEmail(config, emailId, scheduledFor) {
2131
+ return callApi(config, `/email/scheduled/${emailId}/reschedule`, {
2132
+ method: "POST",
2133
+ body: { scheduledFor }
2134
+ });
2135
+ }
2136
+ async function getScheduledEmailStats(config) {
2137
+ return callApi(config, "/email/scheduled/stats", { method: "GET" });
2138
+ }
2139
+
2140
+ // src/consent.ts
2141
+ async function getConsentTypes(config) {
2142
+ return callApi(config, "/consent/types", { method: "GET" });
2143
+ }
2144
+ async function hasConsent(config, purposeSlug, input, defaults) {
2145
+ return callApi(config, "/consent/check", {
2146
+ method: "POST",
2147
+ body: { purposeSlug, ...input, defaults }
2148
+ });
2149
+ }
2150
+ async function getUserConsents(config, input) {
2151
+ return callApi(config, "/consent/user", {
2152
+ method: "GET",
2153
+ query: input
2154
+ });
2155
+ }
2156
+ async function setConsents(config, input) {
2157
+ return callApi(config, "/consent/set", { method: "POST", body: input });
2158
+ }
2159
+ async function acceptAllConsents(config, input) {
2160
+ return callApi(config, "/consent/accept-all", {
2161
+ method: "POST",
2162
+ body: input
2163
+ });
2164
+ }
2165
+ async function declineOptionalConsents(config, input) {
2166
+ return callApi(config, "/consent/decline-optional", {
2167
+ method: "POST",
2168
+ body: input
2169
+ });
2170
+ }
2171
+ async function linkAnonymousConsents(config, input) {
2172
+ return callApi(config, "/consent/link-anonymous", {
2173
+ method: "POST",
2174
+ body: input
2175
+ });
2176
+ }
2177
+ async function getConsentHistory(config, input) {
2178
+ return callApi(config, "/consent/history", {
2179
+ method: "GET",
2180
+ query: {
2181
+ userId: input.userId,
2182
+ anonymousId: input.anonymousId,
2183
+ limit: input.limit?.toString(),
2184
+ offset: input.offset?.toString()
2185
+ }
2186
+ });
2187
+ }
2188
+
2189
+ // src/referrals.ts
2190
+ async function getMyReferralCode(config, userId) {
2191
+ return callApi(config, "/referrals/code", {
2192
+ method: "GET",
2193
+ query: { userId }
2194
+ });
2195
+ }
2196
+ async function getReferralStats(config, userId) {
2197
+ return callApi(config, "/referrals/stats", {
2198
+ method: "GET",
2199
+ query: { userId }
2200
+ });
2201
+ }
2202
+ async function redeemReferralCode(config, input, defaults) {
2203
+ return callApi(config, "/referrals/redeem", {
2204
+ method: "POST",
2205
+ body: { ...input, defaults }
2206
+ });
2207
+ }
2208
+ async function getReferralLeaderboard(config, userId, options) {
2209
+ return callApi(config, "/referrals/leaderboard", {
2210
+ method: "GET",
2211
+ query: { userId, ...options }
2212
+ });
2213
+ }
2214
+ async function regenerateReferralCode(config, userId) {
2215
+ return callApi(config, "/referrals/code/regenerate", {
2216
+ method: "POST",
2217
+ body: { userId }
2218
+ });
2219
+ }
2220
+
2221
+ // src/lib/engagement/types.ts
2222
+ var ACHIEVEMENT_TIER_CONFIG = {
2223
+ bronze: { color: "#CD7F32", points: 10 },
2224
+ silver: { color: "#C0C0C0", points: 25 },
2225
+ gold: { color: "#FFD700", points: 50 },
2226
+ platinum: { color: "#00CED1", points: 100 },
2227
+ diamond: { color: "#B9F2FF", points: 200 }
2228
+ };
2229
+
2230
+ // src/engagement.ts
2231
+ async function getStreak(config, streakId, userId) {
2232
+ return callApi(config, "/engagement/streaks/get", {
2233
+ method: "GET",
2234
+ query: { streakId, userId }
2235
+ });
2236
+ }
2237
+ async function getAllStreaks(config, userId) {
2238
+ return callApi(config, "/engagement/streaks", {
2239
+ method: "GET",
2240
+ query: { userId }
2241
+ });
2242
+ }
2243
+ async function recordStreakActivity(config, input, userId, defaults) {
2244
+ const { idempotencyKey, ...inputBody } = input;
2245
+ return callApi(config, "/engagement/streaks/record", {
2246
+ method: "POST",
2247
+ body: { ...inputBody, userId, defaults },
2248
+ idempotencyKey
2249
+ });
2250
+ }
2251
+ async function recoverStreak(config, streakId, userId) {
2252
+ return callApi(config, "/engagement/streaks/recover", {
2253
+ method: "POST",
2254
+ body: { streakId, userId }
2255
+ });
2256
+ }
2257
+ async function getLeaderboard(config, leaderboardId, userId, options) {
2258
+ return callApi(config, "/engagement/leaderboards/get", {
2259
+ method: "GET",
2260
+ query: { leaderboardId, userId: userId ?? void 0, ...options }
2261
+ });
2262
+ }
2263
+ async function submitScore(config, input, userId, defaults) {
2264
+ return callApi(config, "/engagement/leaderboards/submit", {
2265
+ method: "POST",
2266
+ body: { ...input, userId, defaults }
2267
+ });
2268
+ }
2269
+ async function getUserLeaderboardRank(config, leaderboardId, userId) {
2270
+ return callApi(config, "/engagement/leaderboards/rank", {
2271
+ method: "GET",
2272
+ query: { leaderboardId, userId }
2273
+ });
2274
+ }
2275
+ async function getAchievements(config, userId) {
2276
+ return callApi(config, "/engagement/achievements", {
2277
+ method: "GET",
2278
+ query: { userId }
2279
+ });
2280
+ }
2281
+ async function getAchievement(config, achievementId, userId) {
2282
+ return callApi(config, "/engagement/achievements/get", {
2283
+ method: "GET",
2284
+ query: { achievementId, userId }
2285
+ });
2286
+ }
2287
+ async function unlockAchievement(config, achievementId, userId, defaults) {
2288
+ return callApi(config, "/engagement/achievements/unlock", {
2289
+ method: "POST",
2290
+ body: { achievementId, userId, defaults }
2291
+ });
2292
+ }
2293
+ async function incrementAchievementProgress(config, achievementId, amount, userId, defaults) {
2294
+ return callApi(config, "/engagement/achievements/progress", {
2295
+ method: "POST",
2296
+ body: { achievementId, amount, userId, defaults }
2297
+ });
2298
+ }
2299
+ async function getAchievementPoints(config, userId) {
2300
+ return callApi(config, "/engagement/achievements/points", {
2301
+ method: "GET",
2302
+ query: { userId }
2303
+ });
2304
+ }
2305
+
2306
+ // src/orgs.ts
2307
+ async function getOrganizations(config) {
2308
+ return callApi(config, "/orgs");
2309
+ }
2310
+ async function getOrganization(config, orgIdOrSlug) {
2311
+ return callApi(config, `/orgs/${orgIdOrSlug}`);
2312
+ }
2313
+ async function createOrganization(config, input) {
2314
+ return callApi(config, "/orgs", {
2315
+ method: "POST",
2316
+ body: input
2317
+ });
2318
+ }
2319
+ async function updateOrganization(config, orgIdOrSlug, input) {
2320
+ return callApi(
2321
+ config,
2322
+ `/orgs/${orgIdOrSlug}`,
2323
+ {
2324
+ method: "PUT",
2325
+ body: input
2326
+ }
2327
+ );
2328
+ }
2329
+ async function deleteOrganization(config, orgIdOrSlug) {
2330
+ return callApi(config, `/orgs/${orgIdOrSlug}`, {
2331
+ method: "DELETE"
2332
+ });
2333
+ }
2334
+ async function getOrganizationMembers(config, orgIdOrSlug) {
2335
+ return callApi(
2336
+ config,
2337
+ `/orgs/${orgIdOrSlug}/members`
2338
+ );
2339
+ }
2340
+ async function inviteOrganizationMember(config, orgIdOrSlug, input) {
2341
+ return callApi(
2342
+ config,
2343
+ `/orgs/${orgIdOrSlug}/members/invite`,
2344
+ {
2345
+ method: "POST",
2346
+ body: input
2347
+ }
2348
+ );
2349
+ }
2350
+ async function updateOrganizationMemberRole(config, orgIdOrSlug, memberId, role) {
2351
+ return callApi(
2352
+ config,
2353
+ `/orgs/${orgIdOrSlug}/members/${memberId}/role`,
2354
+ {
2355
+ method: "PUT",
2356
+ body: { role }
2357
+ }
2358
+ );
2359
+ }
2360
+ async function removeOrganizationMember(config, orgIdOrSlug, memberId) {
2361
+ return callApi(
2362
+ config,
2363
+ `/orgs/${orgIdOrSlug}/members/${memberId}`,
2364
+ {
2365
+ method: "DELETE"
2366
+ }
2367
+ );
2368
+ }
2369
+ async function leaveOrganization(config, orgIdOrSlug) {
2370
+ return callApi(config, `/orgs/${orgIdOrSlug}/leave`, {
2371
+ method: "POST"
2372
+ });
2373
+ }
2374
+ async function getOrganizationInvitations(config, orgIdOrSlug) {
2375
+ return callApi(
2376
+ config,
2377
+ `/orgs/${orgIdOrSlug}/invitations`
2378
+ );
2379
+ }
2380
+ async function acceptOrganizationInvitation(config, token) {
2381
+ return callApi(
2382
+ config,
2383
+ "/orgs/invitations/accept",
2384
+ {
2385
+ method: "POST",
2386
+ body: { token }
2387
+ }
2388
+ );
2389
+ }
2390
+ async function revokeOrganizationInvitation(config, orgIdOrSlug, invitationId) {
2391
+ return callApi(
2392
+ config,
2393
+ `/orgs/${orgIdOrSlug}/invitations/${invitationId}`,
2394
+ {
2395
+ method: "DELETE"
2396
+ }
2397
+ );
2398
+ }
2399
+ function hasRole(membership, minimumRole) {
2400
+ if (!membership) return false;
2401
+ const roleHierarchy = [
2402
+ "viewer",
2403
+ "analytics",
2404
+ "developer",
2405
+ "billing",
2406
+ "admin",
2407
+ "super_admin"
2408
+ ];
2409
+ const userRoleIndex = roleHierarchy.indexOf(membership.role);
2410
+ const requiredRoleIndex = roleHierarchy.indexOf(minimumRole);
2411
+ return userRoleIndex >= requiredRoleIndex;
2412
+ }
2413
+ function canManageMembers(membership) {
2414
+ return hasRole(membership, "admin");
2415
+ }
2416
+ function canManageSettings(membership) {
2417
+ return hasRole(membership, "admin");
2418
+ }
2419
+ function canDeleteOrganization(membership) {
2420
+ return hasRole(membership, "super_admin");
2421
+ }
2422
+
2423
+ // src/secrets.ts
2424
+ async function getSecret(config, input) {
2425
+ return callApi(config, "/secrets/get", {
2426
+ method: "POST",
2427
+ body: {
2428
+ key: input.key,
2429
+ environmentId: input.environmentId
2430
+ }
2431
+ });
2432
+ }
2433
+ async function getSecrets(config, input) {
2434
+ return callApi(config, "/secrets/getMany", {
2435
+ method: "POST",
2436
+ body: {
2437
+ keys: input.keys,
2438
+ environmentId: input.environmentId
2439
+ }
2440
+ });
2441
+ }
2442
+ async function listSecretKeys(config, input = {}) {
2443
+ return callApi(config, "/secrets/listKeys", {
2444
+ method: "GET",
2445
+ query: input.environmentId ? { environmentId: input.environmentId } : void 0
2446
+ });
2447
+ }
2448
+ async function hasSecret(config, key) {
2449
+ try {
2450
+ const keys = await listSecretKeys(config);
2451
+ return keys.some((k) => k.key === key);
2452
+ } catch {
2453
+ return false;
2454
+ }
2455
+ }
2456
+ async function getAllSecrets(config, environmentId) {
2457
+ const keys = await listSecretKeys(config, { environmentId });
2458
+ if (keys.length === 0) {
2459
+ return {};
2460
+ }
2461
+ return getSecrets(config, { keys: keys.map((k) => k.key), environmentId });
2462
+ }
2463
+
2464
+ // src/search.ts
2465
+ async function indexDocument(config, input) {
2466
+ return callApi(config, "/search/index", {
2467
+ method: "POST",
2468
+ body: {
2469
+ title: input.title,
2470
+ content: input.content,
2471
+ namespace: input.namespace ?? "default",
2472
+ externalId: input.externalId,
2473
+ url: input.url,
2474
+ metadata: input.metadata,
2475
+ category: input.category,
2476
+ type: input.type,
2477
+ tags: input.tags,
2478
+ language: input.language ?? "english",
2479
+ skipEmbedding: input.skipEmbedding ?? false,
2480
+ embeddingModel: input.embeddingModel ?? "openai/text-embedding-3-small"
2481
+ }
2482
+ });
2483
+ }
2484
+ async function batchIndex(config, input) {
2485
+ return callApi(config, "/search/batchIndex", {
2486
+ method: "POST",
2487
+ body: {
2488
+ documents: input.documents,
2489
+ namespace: input.namespace ?? "default",
2490
+ language: input.language ?? "english",
2491
+ skipEmbedding: input.skipEmbedding ?? false,
2492
+ embeddingModel: input.embeddingModel ?? "openai/text-embedding-3-small"
2493
+ }
2494
+ });
2495
+ }
2496
+ async function search(config, input) {
2497
+ return callApi(config, "/search/search", {
2498
+ method: "POST",
2499
+ body: {
2500
+ query: input.query,
2501
+ namespace: input.namespace ?? "default",
2502
+ searchType: input.searchType ?? "hybrid",
2503
+ limit: input.limit ?? 10,
2504
+ offset: input.offset ?? 0,
2505
+ minSimilarity: input.minSimilarity,
2506
+ typoTolerance: input.typoTolerance ?? true,
2507
+ language: input.language ?? "english",
2508
+ filters: input.filters,
2509
+ highlight: input.highlight ?? true,
2510
+ embeddingModel: input.embeddingModel ?? "openai/text-embedding-3-small",
2511
+ trackQuery: input.trackQuery ?? true,
2512
+ sessionId: input.sessionId,
2513
+ userId: input.userId
2514
+ }
2515
+ });
2516
+ }
2517
+ async function getFacets(config, input = {}) {
2518
+ return callApi(config, "/search/getFacets", {
2519
+ method: "POST",
2520
+ body: {
2521
+ namespace: input.namespace ?? "default",
2522
+ facets: input.facets ?? ["category", "type"],
2523
+ filters: input.filters
2524
+ }
2525
+ });
2526
+ }
2527
+ async function deleteDocument(config, input) {
2528
+ return callApi(config, "/search/delete", {
2529
+ method: "POST",
2530
+ body: {
2531
+ id: input.id,
2532
+ externalId: input.externalId,
2533
+ namespace: input.namespace ?? "default"
2534
+ }
2535
+ });
2536
+ }
2537
+ async function upsertDocument(config, input) {
2538
+ return callApi(config, "/search/upsert", {
2539
+ method: "POST",
2540
+ body: {
2541
+ title: input.title,
2542
+ content: input.content,
2543
+ namespace: input.namespace ?? "default",
2544
+ externalId: input.externalId,
2545
+ url: input.url,
2546
+ metadata: input.metadata,
2547
+ category: input.category,
2548
+ type: input.type,
2549
+ tags: input.tags,
2550
+ language: input.language ?? "english",
2551
+ skipEmbedding: input.skipEmbedding ?? false,
2552
+ embeddingModel: input.embeddingModel ?? "openai/text-embedding-3-small"
2553
+ }
2554
+ });
2555
+ }
2556
+ async function getSearchStats(config, namespace) {
2557
+ return callApi(config, "/search/getStats", {
2558
+ method: "POST",
2559
+ body: { namespace }
2560
+ });
2561
+ }
2562
+ async function trackClick(config, input) {
2563
+ return callApi(config, "/search/trackClick", {
2564
+ method: "POST",
2565
+ body: input
2566
+ });
2567
+ }
2568
+
2569
+ // src/database.ts
2570
+ async function getDatabaseConnectionString(config) {
2571
+ return callApi(
2572
+ config,
2573
+ "/sdk/database/connection-string",
2574
+ { method: "GET" }
2575
+ );
2576
+ }
2577
+ async function getDatabaseStatus(config) {
2578
+ return callApi(config, "/sdk/database/status", {
2579
+ method: "GET"
2580
+ });
2581
+ }
2582
+
2583
+ // src/kv.ts
2584
+ async function kvSet(config, request) {
2585
+ return callApi(config, "/sdk/kv/set", {
2586
+ method: "POST",
2587
+ body: request
2588
+ });
2589
+ }
2590
+ async function kvGet(config, key) {
2591
+ const result = await callApi(
2592
+ config,
2593
+ `/sdk/kv/get/${encodeURIComponent(key)}`,
2594
+ { method: "GET" }
2595
+ );
2596
+ return result.value;
2597
+ }
2598
+ async function kvDelete(config, key) {
2599
+ return callApi(
2600
+ config,
2601
+ `/sdk/kv/delete/${encodeURIComponent(key)}`,
2602
+ { method: "DELETE" }
2603
+ );
2604
+ }
2605
+ async function kvExists(config, key) {
2606
+ return callApi(
2607
+ config,
2608
+ `/sdk/kv/exists/${encodeURIComponent(key)}`,
2609
+ { method: "GET" }
2610
+ );
2611
+ }
2612
+ async function kvExpire(config, request) {
2613
+ return callApi(config, "/sdk/kv/expire", {
2614
+ method: "POST",
2615
+ body: request
2616
+ });
2617
+ }
2618
+ async function kvIncr(config, request) {
2619
+ return callApi(config, "/sdk/kv/incr", {
2620
+ method: "POST",
2621
+ body: request
2622
+ });
2623
+ }
2624
+ async function kvMset(config, request) {
2625
+ return callApi(config, "/sdk/kv/mset", {
2626
+ method: "POST",
2627
+ body: request
2628
+ });
2629
+ }
2630
+ async function kvMget(config, request) {
2631
+ const result = await callApi(
2632
+ config,
2633
+ "/sdk/kv/mget",
2634
+ { method: "POST", body: request }
2635
+ );
2636
+ return result.values;
2637
+ }
2638
+ async function kvHset(config, request) {
2639
+ return callApi(config, "/sdk/kv/hset", {
2640
+ method: "POST",
2641
+ body: request
2642
+ });
2643
+ }
2644
+ async function kvHget(config, request) {
2645
+ const result = await callApi(config, "/sdk/kv/hget", {
2646
+ method: "POST",
2647
+ body: request
2648
+ });
2649
+ return result.value;
2650
+ }
2651
+ async function kvHgetall(config, request) {
2652
+ const result = await callApi(config, "/sdk/kv/hgetall", {
2653
+ method: "POST",
2654
+ body: request
2655
+ });
2656
+ return result.value;
2657
+ }
2658
+ async function kvLpush(config, request) {
2659
+ return callApi(config, "/sdk/kv/lpush", {
2660
+ method: "POST",
2661
+ body: request
2662
+ });
2663
+ }
2664
+ async function kvLrange(config, request) {
2665
+ const result = await callApi(config, "/sdk/kv/lrange", {
2666
+ method: "POST",
2667
+ body: request
2668
+ });
2669
+ return result.items;
2670
+ }
2671
+ async function kvZadd(config, request) {
2672
+ return callApi(config, "/sdk/kv/zadd", {
2673
+ method: "POST",
2674
+ body: request
2675
+ });
2676
+ }
2677
+ async function kvZrange(config, request) {
2678
+ const result = await callApi(config, "/sdk/kv/zrange", {
2679
+ method: "POST",
2680
+ body: request
2681
+ });
2682
+ return result.members;
2683
+ }
2684
+ async function kvRateLimit(config, request) {
2685
+ return callApi(config, "/sdk/kv/ratelimit", {
2686
+ method: "POST",
2687
+ body: request
2688
+ });
2689
+ }
2690
+
2691
+ // src/realtime.ts
2692
+ async function realtimeEmit(config, request) {
2693
+ return callApi(config, "/sdk/realtime/emit", {
2694
+ method: "POST",
2695
+ body: request
2696
+ });
2697
+ }
2698
+ async function getRealtimeHistory(config, request) {
2699
+ const params = new URLSearchParams();
2700
+ params.set("channel", request.channel);
2701
+ if (request.limit !== void 0) params.set("limit", String(request.limit));
2702
+ if (request.after !== void 0) params.set("after", request.after);
2703
+ return callApi(
2704
+ config,
2705
+ `/sdk/realtime/history?${params.toString()}`,
2706
+ { method: "GET" }
2707
+ );
2708
+ }
2709
+
2710
+ // src/deploy.ts
2711
+ async function triggerDeploy(config, request) {
2712
+ return callApi(
2713
+ config,
2714
+ `/sdk/deploy/trigger/${encodeURIComponent(request.envId)}`,
2715
+ {
2716
+ method: "POST",
2717
+ body: request.forceRebuild !== void 0 ? { forceRebuild: request.forceRebuild } : void 0
2718
+ }
2719
+ );
2720
+ }
2721
+ async function getDeployStatus(config, envId) {
2722
+ return callApi(
2723
+ config,
2724
+ `/sdk/deploy/status/${encodeURIComponent(envId)}`,
2725
+ { method: "GET" }
2726
+ );
2727
+ }
2728
+ async function getDeployHistory(config, envId) {
2729
+ return callApi(
2730
+ config,
2731
+ `/sdk/deploy/history/${encodeURIComponent(envId)}`,
2732
+ { method: "GET" }
2733
+ );
2734
+ }
2735
+ async function rollbackDeploy(config, request) {
2736
+ return callApi(
2737
+ config,
2738
+ `/sdk/deploy/rollback/${encodeURIComponent(request.envId)}`,
2739
+ {
2740
+ method: "POST",
2741
+ body: { deploymentId: request.deploymentId }
2742
+ }
2743
+ );
2744
+ }
2745
+ async function getBuildLogHistory(config, envId) {
2746
+ return callApi(
2747
+ config,
2748
+ `/sdk/deploy/logs/${encodeURIComponent(envId)}/history`,
2749
+ { method: "GET" }
2750
+ );
2751
+ }
2752
+ async function listEnvVars(config, envId) {
2753
+ const result = await callApi(
2754
+ config,
2755
+ `/sdk/deploy/envvars/${encodeURIComponent(envId)}`,
2756
+ { method: "GET" }
2757
+ );
2758
+ return result.envVars;
2759
+ }
2760
+ async function setEnvVar(config, envId, request) {
2761
+ return callApi(
2762
+ config,
2763
+ `/sdk/deploy/envvars/${encodeURIComponent(envId)}`,
2764
+ {
2765
+ method: "POST",
2766
+ body: request
2767
+ }
2768
+ );
2769
+ }
2770
+ async function deleteEnvVar(config, envId, key) {
2771
+ return callApi(
2772
+ config,
2773
+ `/sdk/deploy/envvars/${encodeURIComponent(envId)}/${encodeURIComponent(key)}`,
2774
+ { method: "DELETE" }
2775
+ );
2776
+ }
2777
+ async function listCustomDomains(config, envId) {
2778
+ const result = await callApi(
2779
+ config,
2780
+ `/sdk/deploy/domains/${encodeURIComponent(envId)}`,
2781
+ { method: "GET" }
2782
+ );
2783
+ return result.domains;
2784
+ }
2785
+ async function addCustomDomain(config, envId, request) {
2786
+ return callApi(
2787
+ config,
2788
+ `/sdk/deploy/domains/${encodeURIComponent(envId)}`,
2789
+ {
2790
+ method: "POST",
2791
+ body: request
2792
+ }
2793
+ );
2794
+ }
2795
+ async function removeCustomDomain(config, envId, domain) {
2796
+ return callApi(
2797
+ config,
2798
+ `/sdk/deploy/domains/${encodeURIComponent(envId)}/${encodeURIComponent(domain)}`,
2799
+ { method: "DELETE" }
2800
+ );
2801
+ }
2802
+
2803
+ // src/monitoring.ts
2804
+ function errorToExceptionValue(error) {
2805
+ const frames = [];
2806
+ if (error.stack) {
2807
+ const lines = error.stack.split("\n").slice(1);
2808
+ for (const line of lines) {
2809
+ const v8Match = line.match(/^\s+at\s+(.+?)\s+\((.+):(\d+):(\d+)\)$/);
2810
+ if (v8Match) {
2811
+ frames.push({
2812
+ function: v8Match[1],
2813
+ filename: v8Match[2],
2814
+ lineno: Number(v8Match[3]),
2815
+ colno: Number(v8Match[4])
2816
+ });
2817
+ continue;
2818
+ }
2819
+ const v8AnonMatch = line.match(/^\s+at\s+(.+):(\d+):(\d+)$/);
2820
+ if (v8AnonMatch) {
2821
+ frames.push({
2822
+ filename: v8AnonMatch[1],
2823
+ lineno: Number(v8AnonMatch[2]),
2824
+ colno: Number(v8AnonMatch[3])
2825
+ });
2826
+ }
2827
+ }
2828
+ }
2829
+ return {
2830
+ type: error.name || "Error",
2831
+ value: error.message,
2832
+ stacktrace: frames.length > 0 ? { frames } : void 0
2833
+ };
2834
+ }
2835
+ async function captureException(config, error, options = {}) {
2836
+ const exceptionValue = errorToExceptionValue(error);
2837
+ const request = {
2838
+ ...options,
2839
+ exception: { values: [exceptionValue] }
2840
+ };
2841
+ return callApi(config, "/sdk/monitoring/exception", {
2842
+ method: "POST",
2843
+ body: request
2844
+ });
2845
+ }
2846
+ async function captureExceptionRaw(config, request) {
2847
+ return callApi(config, "/sdk/monitoring/exception", {
2848
+ method: "POST",
2849
+ body: request
2850
+ });
2851
+ }
2852
+ async function captureMessage(config, message, options = {}) {
2853
+ const request = { ...options, message };
2854
+ return callApi(config, "/sdk/monitoring/message", {
2855
+ method: "POST",
2856
+ body: request
2857
+ });
2858
+ }
2859
+ export {
2860
+ ACHIEVEMENT_TIER_CONFIG,
2861
+ AuthenticationError,
2862
+ AuthorizationError,
2863
+ CircuitBreakerOpenError,
2864
+ ERROR_CODE_STATUS,
2865
+ NetworkError,
2866
+ NotFoundError,
2867
+ RETRYABLE_CODES,
2868
+ RateLimitError,
2869
+ SylphxError,
2870
+ TimeoutError,
2871
+ ValidationError,
2872
+ acceptAllConsents,
2873
+ acceptOrganizationInvitation,
2874
+ addCustomDomain,
2875
+ batchIndex,
2876
+ canDeleteOrganization,
2877
+ canManageMembers,
2878
+ canManageSettings,
2879
+ cancelJob,
2880
+ cancelScheduledEmail,
2881
+ captureException,
2882
+ captureExceptionRaw,
2883
+ captureMessage,
2884
+ chat,
2885
+ chatStream,
2886
+ checkFlag,
2887
+ complete,
2888
+ createCheckout,
2889
+ createConfig,
2890
+ createCron,
2891
+ createDynamicRestClient,
2892
+ createOrganization,
2893
+ createPortalSession,
2894
+ createRestClient,
2895
+ createServiceWorkerScript,
2896
+ createTracker,
2897
+ debugError,
2898
+ debugLog,
2899
+ debugTimer,
2900
+ debugWarn,
2901
+ declineOptionalConsents,
2902
+ deleteCron,
2903
+ deleteDocument,
2904
+ deleteEnvVar,
2905
+ deleteFile,
2906
+ deleteOrganization,
2907
+ disableDebug,
2908
+ embed,
2909
+ enableDebug,
2910
+ exponentialBackoff,
2911
+ forgotPassword,
2912
+ generateAnonymousId,
2913
+ getAchievement,
2914
+ getAchievementPoints,
2915
+ getAchievements,
2916
+ getAllFlags,
2917
+ getAllSecrets,
2918
+ getAllStreaks,
2919
+ getBillingBalance,
2920
+ getBillingUsage,
2921
+ getBuildLogHistory,
2922
+ getCircuitBreakerState,
2923
+ getConsentHistory,
2924
+ getConsentTypes,
2925
+ getDatabaseConnectionString,
2926
+ getDatabaseStatus,
2927
+ getDebugMode,
2928
+ getDeployHistory,
2929
+ getDeployStatus,
2930
+ getErrorCode,
2931
+ getErrorMessage,
2932
+ getFacets,
2933
+ getFileInfo,
2934
+ getFileUrl,
2935
+ getFlagPayload,
2936
+ getFlags,
2937
+ getJob,
2938
+ getLeaderboard,
2939
+ getMyReferralCode,
2940
+ getOrganization,
2941
+ getOrganizationInvitations,
2942
+ getOrganizationMembers,
2943
+ getOrganizations,
2944
+ getPlans,
2945
+ getPushPreferences,
2946
+ getRealtimeHistory,
2947
+ getReferralLeaderboard,
2948
+ getReferralStats,
2949
+ getRestErrorMessage,
2950
+ getScheduledEmail,
2951
+ getScheduledEmailStats,
2952
+ getSearchStats,
2953
+ getSecret,
2954
+ getSecrets,
2955
+ getSession,
2956
+ getSignedUrl,
2957
+ getStreak,
2958
+ getSubscription,
2959
+ getUserConsents,
2960
+ getUserLeaderboardRank,
2961
+ getVariant,
2962
+ getWebhookConfig,
2963
+ getWebhookDeliveries,
2964
+ getWebhookDelivery,
2965
+ getWebhookStats,
2966
+ hasConsent,
2967
+ hasError,
2968
+ hasRole,
2969
+ hasSecret,
2970
+ identify,
2971
+ incrementAchievementProgress,
2972
+ indexDocument,
2973
+ initPushServiceWorker,
2974
+ installGlobalDebugHelpers,
2975
+ introspectToken,
2976
+ inviteOrganizationMember,
2977
+ isEmailConfigured,
2978
+ isEnabled,
2979
+ isRetryableError,
2980
+ isSylphxError,
2981
+ kvDelete,
2982
+ kvExists,
2983
+ kvExpire,
2984
+ kvGet,
2985
+ kvHget,
2986
+ kvHgetall,
2987
+ kvHset,
2988
+ kvIncr,
2989
+ kvLpush,
2990
+ kvLrange,
2991
+ kvMget,
2992
+ kvMset,
2993
+ kvRateLimit,
2994
+ kvSet,
2995
+ kvZadd,
2996
+ kvZrange,
2997
+ leaveOrganization,
2998
+ linkAnonymousConsents,
2999
+ listCustomDomains,
3000
+ listEnvVars,
3001
+ listJobs,
3002
+ listScheduledEmails,
3003
+ listSecretKeys,
3004
+ page,
3005
+ pauseCron,
3006
+ realtimeEmit,
3007
+ recordStreakActivity,
3008
+ recoverStreak,
3009
+ redeemReferralCode,
3010
+ refreshToken,
3011
+ regenerateReferralCode,
3012
+ registerPush,
3013
+ registerPushServiceWorker,
3014
+ removeCustomDomain,
3015
+ removeOrganizationMember,
3016
+ replayWebhookDelivery,
3017
+ rescheduleEmail,
3018
+ resetCircuitBreaker,
3019
+ resetDebugModeCache,
3020
+ resetPassword,
3021
+ resumeCron,
3022
+ revokeAllTokens,
3023
+ revokeOrganizationInvitation,
3024
+ revokeToken,
3025
+ rollbackDeploy,
3026
+ scheduleEmail,
3027
+ scheduleJob,
3028
+ search,
3029
+ sendEmail,
3030
+ sendEmailToUser,
3031
+ sendPush,
3032
+ sendTemplatedEmail,
3033
+ setConsents,
3034
+ setEnvVar,
3035
+ signIn,
3036
+ signOut,
3037
+ signUp,
3038
+ streamToString,
3039
+ submitScore,
3040
+ toSylphxError,
3041
+ track,
3042
+ trackBatch,
3043
+ trackClick,
3044
+ triggerDeploy,
3045
+ unlockAchievement,
3046
+ unregisterPush,
3047
+ updateOrganization,
3048
+ updateOrganizationMemberRole,
3049
+ updatePushPreferences,
3050
+ updateWebhookConfig,
3051
+ uploadAvatar,
3052
+ uploadFile,
3053
+ upsertDocument,
3054
+ verifyEmail,
3055
+ verifyTwoFactor,
3056
+ withToken
3057
+ };
3058
+ //# sourceMappingURL=index.mjs.map