@sylphx/sdk 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -232,7 +232,7 @@ module.exports = __toCommonJS(index_exports);
232
232
 
233
233
  // src/constants.ts
234
234
  var DEFAULT_PLATFORM_URL = "https://sylphx.com";
235
- var SDK_API_PATH = `/api/app/v1`;
235
+ var SDK_API_PATH = `/api/v1`;
236
236
  var SDK_API_PATH_NEW = `/v1`;
237
237
  var DEFAULT_SDK_API_HOST = "api.sylphx.com";
238
238
  var SDK_VERSION = "0.1.0";
@@ -683,6 +683,7 @@ function httpStatusToErrorCode(status) {
683
683
  return status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST";
684
684
  }
685
685
  }
686
+ var REF_PATTERN = /^[a-z0-9]{16}$/;
686
687
  function createConfig(input) {
687
688
  let secretKey;
688
689
  if (input.secretKey) {
@@ -698,13 +699,22 @@ function createConfig(input) {
698
699
  }
699
700
  secretKey = result.sanitizedKey;
700
701
  }
702
+ if (input.ref !== void 0) {
703
+ const trimmedRef = input.ref.trim();
704
+ if (!REF_PATTERN.test(trimmedRef)) {
705
+ throw new SylphxError(
706
+ `[Sylphx] Invalid project ref format: "${input.ref}". Expected a 16-character lowercase alphanumeric string (e.g. "abc123def456ghij"). Get your ref from Platform Console \u2192 Projects \u2192 Your Project \u2192 Overview.`,
707
+ { code: "BAD_REQUEST" }
708
+ );
709
+ }
710
+ }
701
711
  let platformUrl;
702
712
  let apiBasePath;
703
713
  if (input.platformUrl) {
704
714
  platformUrl = input.platformUrl.trim();
705
715
  apiBasePath = SDK_API_PATH;
706
716
  } else if (input.ref) {
707
- platformUrl = `https://${input.ref}.${DEFAULT_SDK_API_HOST}`;
717
+ platformUrl = `https://${input.ref.trim()}.${DEFAULT_SDK_API_HOST}`;
708
718
  apiBasePath = SDK_API_PATH_NEW;
709
719
  } else {
710
720
  platformUrl = DEFAULT_PLATFORM_URL;
@@ -1008,6 +1018,7 @@ function createDeduplicationMiddleware(config = {}) {
1008
1018
  deduped._dedupKey = key;
1009
1019
  return deduped;
1010
1020
  }
1021
+ ;
1011
1022
  request._dedupKey = key;
1012
1023
  return request;
1013
1024
  },
@@ -1031,30 +1042,24 @@ function createDeduplicationMiddleware(config = {}) {
1031
1042
  var CircuitBreakerOpenError = class extends Error {
1032
1043
  remainingMs;
1033
1044
  constructor(remainingMs) {
1034
- super(
1035
- `Circuit breaker is open. Retry after ${Math.ceil(remainingMs / 1e3)}s`
1036
- );
1045
+ super(`Circuit breaker is open. Retry after ${Math.ceil(remainingMs / 1e3)}s`);
1037
1046
  this.name = "CircuitBreakerOpenError";
1038
1047
  this.remainingMs = remainingMs;
1039
1048
  }
1040
1049
  };
1041
- var circuitBreaker = null;
1042
- function getCircuitBreaker(config = {}) {
1043
- if (!circuitBreaker) {
1044
- circuitBreaker = {
1045
- state: "CLOSED",
1046
- failures: [],
1047
- openedAt: null,
1048
- config: {
1049
- enabled: config.enabled ?? true,
1050
- failureThreshold: config.failureThreshold ?? CIRCUIT_BREAKER_FAILURE_THRESHOLD,
1051
- windowMs: config.windowMs ?? CIRCUIT_BREAKER_WINDOW_MS,
1052
- openDurationMs: config.openDurationMs ?? CIRCUIT_BREAKER_OPEN_DURATION_MS,
1053
- isFailure: config.isFailure ?? ((status) => status >= 500 || status === 429)
1054
- }
1055
- };
1056
- }
1057
- return circuitBreaker;
1050
+ function createCircuitBreakerInstance(config = {}) {
1051
+ return {
1052
+ state: "CLOSED",
1053
+ failures: [],
1054
+ openedAt: null,
1055
+ config: {
1056
+ enabled: config.enabled ?? true,
1057
+ failureThreshold: config.failureThreshold ?? CIRCUIT_BREAKER_FAILURE_THRESHOLD,
1058
+ windowMs: config.windowMs ?? CIRCUIT_BREAKER_WINDOW_MS,
1059
+ openDurationMs: config.openDurationMs ?? CIRCUIT_BREAKER_OPEN_DURATION_MS,
1060
+ isFailure: config.isFailure ?? ((status) => status >= 500 || status === 429)
1061
+ }
1062
+ };
1058
1063
  }
1059
1064
  function recordFailure(cb) {
1060
1065
  const now = Date.now();
@@ -1102,7 +1107,8 @@ function createCircuitBreakerMiddleware(config) {
1102
1107
  }
1103
1108
  };
1104
1109
  }
1105
- const cb = getCircuitBreaker(config ?? {});
1110
+ const cb = createCircuitBreakerInstance(config ?? {});
1111
+ _lastCircuitBreaker = cb;
1106
1112
  return {
1107
1113
  async onRequest({ request }) {
1108
1114
  if (!cb.config.enabled) {
@@ -1127,15 +1133,21 @@ function createCircuitBreakerMiddleware(config) {
1127
1133
  }
1128
1134
  };
1129
1135
  }
1136
+ var _lastCircuitBreaker = null;
1130
1137
  function resetCircuitBreaker() {
1131
- circuitBreaker = null;
1138
+ if (_lastCircuitBreaker) {
1139
+ _lastCircuitBreaker.state = "CLOSED";
1140
+ _lastCircuitBreaker.failures = [];
1141
+ _lastCircuitBreaker.openedAt = null;
1142
+ }
1143
+ _lastCircuitBreaker = null;
1132
1144
  }
1133
1145
  function getCircuitBreakerState() {
1134
- if (!circuitBreaker) return null;
1146
+ if (!_lastCircuitBreaker) return null;
1135
1147
  return {
1136
- state: circuitBreaker.state,
1137
- failures: circuitBreaker.failures.length,
1138
- openedAt: circuitBreaker.openedAt
1148
+ state: _lastCircuitBreaker.state,
1149
+ failures: _lastCircuitBreaker.failures.length,
1150
+ openedAt: _lastCircuitBreaker.openedAt
1139
1151
  };
1140
1152
  }
1141
1153
  var etagCache = /* @__PURE__ */ new Map();
@@ -1227,6 +1239,7 @@ function createETagMiddleware(config) {
1227
1239
  }
1228
1240
  };
1229
1241
  }
1242
+ var retryBodyMap = /* @__PURE__ */ new WeakMap();
1230
1243
  function createRetryMiddleware(retryConfig) {
1231
1244
  if (retryConfig === false) {
1232
1245
  return {
@@ -1242,27 +1255,22 @@ function createRetryMiddleware(retryConfig) {
1242
1255
  shouldRetry = isRetryableStatus,
1243
1256
  timeout = DEFAULT_TIMEOUT_MS
1244
1257
  } = retryConfig ?? {};
1245
- let originalBody = null;
1246
1258
  return {
1247
1259
  async onRequest({ request }) {
1248
- if (request.body) {
1249
- originalBody = await request.clone().text();
1250
- } else {
1251
- originalBody = null;
1252
- }
1253
- if (!request.signal) {
1254
- const controller = new AbortController();
1255
- setTimeout(() => controller.abort(), timeout);
1256
- return new Request(request.url, {
1257
- method: request.method,
1258
- headers: request.headers,
1259
- body: originalBody,
1260
- signal: controller.signal
1261
- });
1262
- }
1263
- return request;
1260
+ const body = request.body ? await request.clone().text() : null;
1261
+ const controller = new AbortController();
1262
+ setTimeout(() => controller.abort(), timeout);
1263
+ const newRequest = new Request(request.url, {
1264
+ method: request.method,
1265
+ headers: request.headers,
1266
+ body,
1267
+ signal: controller.signal
1268
+ });
1269
+ retryBodyMap.set(newRequest, body);
1270
+ return newRequest;
1264
1271
  },
1265
1272
  async onResponse({ response, request }) {
1273
+ const originalBody = retryBodyMap.get(request) ?? null;
1266
1274
  let attempt = 0;
1267
1275
  let currentResponse = response;
1268
1276
  while (attempt < maxRetries && shouldRetry(currentResponse.status, attempt)) {
@@ -1282,16 +1290,19 @@ function createRetryMiddleware(retryConfig) {
1282
1290
  const newResponse = await fetch(retryRequest);
1283
1291
  clearTimeout(timeoutId);
1284
1292
  if (newResponse.ok || !shouldRetry(newResponse.status, attempt)) {
1293
+ retryBodyMap.delete(request);
1285
1294
  return newResponse;
1286
1295
  }
1287
1296
  currentResponse = newResponse;
1288
1297
  } catch (error) {
1289
1298
  clearTimeout(timeoutId);
1290
1299
  if (attempt >= maxRetries) {
1300
+ retryBodyMap.delete(request);
1291
1301
  throw error;
1292
1302
  }
1293
1303
  }
1294
1304
  }
1305
+ retryBodyMap.delete(request);
1295
1306
  return currentResponse;
1296
1307
  }
1297
1308
  };
@@ -1380,35 +1391,20 @@ async function signOut(config) {
1380
1391
  await callApi(config, "/auth/logout", { method: "POST" });
1381
1392
  }
1382
1393
  async function refreshToken(config, token) {
1383
- const response = await fetch(`${config.platformUrl}/api/v1/auth/token`, {
1394
+ return callApi(config, "/auth/token", {
1384
1395
  method: "POST",
1385
- headers: { "Content-Type": "application/json" },
1386
- body: JSON.stringify({
1396
+ body: {
1387
1397
  grant_type: "refresh_token",
1388
1398
  refresh_token: token,
1389
1399
  client_secret: config.secretKey
1390
- })
1400
+ }
1391
1401
  });
1392
- if (!response.ok) {
1393
- const error = await response.json().catch(() => ({ message: "Token refresh failed" }));
1394
- throw new SylphxError(error.message ?? "Token refresh failed", {
1395
- code: "UNAUTHORIZED"
1396
- });
1397
- }
1398
- return response.json();
1399
1402
  }
1400
1403
  async function verifyEmail(config, token) {
1401
- const response = await fetch(`${config.platformUrl}/api/v1/auth/verify-email`, {
1404
+ await callApi(config, "/auth/verify-email", {
1402
1405
  method: "POST",
1403
- headers: buildHeaders(config),
1404
- body: JSON.stringify({ token })
1406
+ body: { token }
1405
1407
  });
1406
- if (!response.ok) {
1407
- const error = await response.json().catch(() => ({ message: "Email verification failed" }));
1408
- throw new SylphxError(error.message ?? "Email verification failed", {
1409
- code: "BAD_REQUEST"
1410
- });
1411
- }
1412
1408
  }
1413
1409
  async function forgotPassword(config, email) {
1414
1410
  await callApi(config, "/auth/forgot-password", {