@sylphx/sdk 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4783,7 +4783,7 @@ var init_webapi = __esm({
4783
4783
  // src/connection-url.ts
4784
4784
  var SYLPHX_PROTOCOL = "sylphx:";
4785
4785
  var DEFAULT_VERSION = "v1";
4786
- var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}$/;
4786
+ var CREDENTIAL_REGEX = /^(pk|sk)_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32,64}$/;
4787
4787
  var VERSION_REGEX = /^v[0-9]+$/;
4788
4788
  var SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
4789
4789
  var InvalidConnectionUrlError = class _InvalidConnectionUrlError extends Error {
@@ -4800,7 +4800,7 @@ function fail(reason) {
4800
4800
  function parseCredential(raw) {
4801
4801
  const match = CREDENTIAL_REGEX.exec(raw);
4802
4802
  if (!match) {
4803
- fail(`credential must match (pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}, got "${raw}"`);
4803
+ fail(`credential must match (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}, got "${raw}"`);
4804
4804
  }
4805
4805
  return {
4806
4806
  credentialType: match[1],
@@ -4879,7 +4879,7 @@ init_constants();
4879
4879
  init_errors();
4880
4880
  var LEGACY_EMBEDDED_REF_PATTERN = /^(pk|sk)_(dev|stg|prod|prev)_[a-z0-9]{12}_[a-f0-9]+$/;
4881
4881
  var LEGACY_APP_KEY_PATTERN = /^app_(dev|stg|prod|prev)_/;
4882
- var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
4882
+ var MIGRATION_MESSAGE = "API key format has changed. Use a sylphx:// connection URL instead.\n\nNew format: sylphx://pk_prod_{ref?}_{hex}@your-slug.api.sylphx.com\n\nGenerate new credentials from the Sylphx Console \u2192 Your App \u2192 Environments.\nSee https://docs.sylphx.com/migration for details.";
4883
4883
  function rejectLegacyKeyFormat(input) {
4884
4884
  const trimmed = input.trim().toLowerCase();
4885
4885
  if (LEGACY_APP_KEY_PATTERN.test(trimmed)) {
@@ -5012,7 +5012,7 @@ function createConfigFromComponents(input) {
5012
5012
  });
5013
5013
  }
5014
5014
  throw new SylphxError(
5015
- `[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)_[a-f0-9]{32,64}. Got: "${trimmedCred.slice(0, 30)}..."`,
5015
+ `[Sylphx] Invalid credential format. Expected (pk|sk)_(dev|stg|prod|prev)(_{ref})?_{hex}. Got: "${trimmedCred.slice(0, 30)}..."`,
5016
5016
  { code: "BAD_REQUEST" }
5017
5017
  );
5018
5018
  }
@@ -5291,11 +5291,12 @@ init_constants();
5291
5291
  init_errors();
5292
5292
 
5293
5293
  // src/key-validation.ts
5294
- var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod)_[a-z0-9_-]+$/;
5294
+ var SECRET_KEY_PATTERN = /^sk_(dev|stg|prod|prev)_[a-z0-9_-]+$/;
5295
5295
  var ENV_PREFIX_MAP = {
5296
5296
  dev: "development",
5297
5297
  stg: "staging",
5298
- prod: "production"
5298
+ prod: "production",
5299
+ prev: "preview"
5299
5300
  };
5300
5301
  function detectKeyIssues(key) {
5301
5302
  const issues = [];
@@ -5320,7 +5321,7 @@ The SDK will automatically sanitize the key, but fixing the source is recommende
5320
5321
  }
5321
5322
  function createInvalidKeyError(keyType, key, envVarName) {
5322
5323
  const maskedKey = key.length > 20 ? `${key.slice(0, 20)}...` : key;
5323
- const formatHint = keyType === "appId" ? "pk_(dev|stg|prod)_{ref}_{hex} or app_(dev|stg|prod)_[id]" : "sk_(dev|stg|prod)_{ref}_{hex}";
5324
+ const formatHint = keyType === "appId" ? "pk_(dev|stg|prod|prev)_{ref}_{hex} or app_(dev|stg|prod|prev)_[id]" : "sk_(dev|stg|prod|prev)_{ref}_{hex}";
5324
5325
  const keyTypeName = keyType === "appId" ? "App ID" : "Secret Key";
5325
5326
  return `[Sylphx] Invalid ${keyTypeName} format.
5326
5327
 
@@ -5333,7 +5334,7 @@ You can find your keys in the Sylphx Console \u2192 API Keys.
5333
5334
  Common issues:
5334
5335
  \u2022 Key has uppercase characters (must be lowercase)
5335
5336
  \u2022 Key has wrong prefix (App ID: pk_ or app_, Secret Key: sk_)
5336
- \u2022 Key has invalid environment (must be dev, stg, or prod)
5337
+ \u2022 Key has invalid environment (must be dev, stg, prod, or prev)
5337
5338
  \u2022 Key was copied with extra whitespace`;
5338
5339
  }
5339
5340
  function extractEnvironment(key) {
@@ -5380,7 +5381,7 @@ function validateKeyForType(key, keyType, pattern, envVarName) {
5380
5381
  };
5381
5382
  }
5382
5383
  function validateSecretKey(key) {
5383
- return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "SYLPHX_SECRET_KEY");
5384
+ return validateKeyForType(key, "secret", SECRET_KEY_PATTERN, "secret credential");
5384
5385
  }
5385
5386
  function validateAndSanitizeSecretKey(key) {
5386
5387
  const result = validateSecretKey(key);
@@ -5402,7 +5403,14 @@ async function runPipeline(middlewares, initial) {
5402
5403
  if (next) request = next;
5403
5404
  }
5404
5405
  }
5405
- let response = await fetch(request);
5406
+ const fetchWithMiddleware = middlewares.reduceRight(
5407
+ (next, mw) => mw.onFetch ? async (request2) => {
5408
+ const response2 = await mw.onFetch?.({ request: request2, next });
5409
+ return response2 ?? next(request2);
5410
+ } : next,
5411
+ (request2) => fetch(request2)
5412
+ );
5413
+ let response = await fetchWithMiddleware(request);
5406
5414
  for (const mw of middlewares) {
5407
5415
  if (mw.onResponse) {
5408
5416
  const next = await mw.onResponse({ request, response });
@@ -5421,6 +5429,11 @@ function buildUrl(baseUrl, path, params) {
5421
5429
  ).toString();
5422
5430
  return `${url}?${search2}`;
5423
5431
  }
5432
+ function cloneRequestWithHeaders(request, updateHeaders) {
5433
+ const headers = new Headers(request.headers);
5434
+ updateHeaders(headers);
5435
+ return new Request(request, { headers });
5436
+ }
5424
5437
  function interpolatePath(path, pathParams) {
5425
5438
  if (!pathParams) return path;
5426
5439
  return path.replace(/\{(\w+)\}/g, (_match, key) => {
@@ -5483,66 +5496,51 @@ function buildClient(baseUrl, baseHeaders) {
5483
5496
  function createAuthMiddleware(config) {
5484
5497
  return {
5485
5498
  async onRequest({ request }) {
5486
- request.headers.set("X-SDK-Version", SDK_VERSION);
5487
- request.headers.set("X-SDK-Platform", SDK_PLATFORM);
5488
- if (config.secretKey) {
5489
- request.headers.set("x-app-secret", config.secretKey);
5490
- }
5491
- const token = config.getAccessToken?.();
5492
- if (token) {
5493
- request.headers.set("Authorization", `Bearer ${token}`);
5494
- }
5495
- return request;
5499
+ return cloneRequestWithHeaders(request, (headers) => {
5500
+ headers.set("X-SDK-Version", SDK_VERSION);
5501
+ headers.set("X-SDK-Platform", SDK_PLATFORM);
5502
+ if (config.secretKey) {
5503
+ headers.set("x-app-secret", config.secretKey);
5504
+ }
5505
+ const token = config.getAccessToken?.();
5506
+ if (token) {
5507
+ headers.set("Authorization", `Bearer ${token}`);
5508
+ }
5509
+ });
5496
5510
  }
5497
5511
  };
5498
5512
  }
5499
5513
  function isRetryableStatus(status) {
5500
5514
  return status >= 500 || status === 429;
5501
5515
  }
5502
- var inFlightRequests = /* @__PURE__ */ new Map();
5503
5516
  async function getRequestKey(request) {
5504
5517
  const body = request.body ? await request.clone().text() : "";
5505
5518
  return `${request.method}:${request.url}:${body}`;
5506
5519
  }
5507
5520
  function createDeduplicationMiddleware(config = {}) {
5508
5521
  const { enabled = true, methods = ["GET"] } = config;
5509
- if (!enabled) {
5510
- return {
5511
- async onRequest({ request }) {
5512
- return request;
5513
- }
5514
- };
5515
- }
5522
+ if (!enabled) return {};
5523
+ const inFlightRequests = /* @__PURE__ */ new Map();
5516
5524
  return {
5517
- async onRequest({ request }) {
5518
- if (!methods.includes(request.method)) {
5519
- return request;
5525
+ async onFetch({ request, next }) {
5526
+ const method = request.method.toUpperCase();
5527
+ if (!methods.includes(method)) {
5528
+ return next(request);
5520
5529
  }
5521
5530
  const key = await getRequestKey(request);
5522
5531
  const existing = inFlightRequests.get(key);
5523
5532
  if (existing) {
5524
- const deduped = request.clone();
5525
- deduped._dedupKey = key;
5526
- return deduped;
5527
- }
5528
- ;
5529
- request._dedupKey = key;
5530
- return request;
5531
- },
5532
- async onResponse({ request, response }) {
5533
- const key = request._dedupKey;
5534
- if (!key) return response;
5535
- const existing = inFlightRequests.get(key);
5536
- if (existing && inFlightRequests.get(key) !== void 0) {
5537
5533
  const cachedResponse = await existing;
5538
5534
  return cachedResponse.clone();
5539
5535
  }
5540
- const responsePromise = Promise.resolve(response.clone());
5536
+ const responsePromise = next(request).then((response) => response.clone());
5541
5537
  inFlightRequests.set(key, responsePromise);
5542
- responsePromise.finally(() => {
5543
- setTimeout(() => inFlightRequests.delete(key), 100);
5544
- });
5545
- return response;
5538
+ try {
5539
+ const response = await responsePromise;
5540
+ return response.clone();
5541
+ } finally {
5542
+ inFlightRequests.delete(key);
5543
+ }
5546
5544
  }
5547
5545
  };
5548
5546
  }
@@ -5708,7 +5706,9 @@ function createETagMiddleware(config) {
5708
5706
  if (Date.now() - cached.timestamp > ttlMs) {
5709
5707
  etagCache.delete(cacheKey);
5710
5708
  } else {
5711
- request.headers.set("If-None-Match", cached.etag);
5709
+ return cloneRequestWithHeaders(request, (headers) => {
5710
+ headers.set("If-None-Match", cached.etag);
5711
+ });
5712
5712
  }
5713
5713
  }
5714
5714
  return request;
@@ -6000,11 +6000,28 @@ async function inviteUser(config, input) {
6000
6000
  body: input
6001
6001
  });
6002
6002
  }
6003
- async function switchOrg(config, orgId) {
6004
- return callApi(config, "/auth/switch-org", {
6003
+ function normalizeOrgScopedTokenResponse(data) {
6004
+ const accessToken = data.accessToken ?? data.access_token;
6005
+ if (!accessToken) {
6006
+ throw new Error("Invalid org-scoped token response: missing access token");
6007
+ }
6008
+ return {
6009
+ token: accessToken,
6010
+ accessToken,
6011
+ expiresIn: data.expiresIn ?? data.expires_in,
6012
+ tokenType: data.tokenType ?? data.token_type,
6013
+ user: data.user
6014
+ };
6015
+ }
6016
+ async function getOrgScopedToken(config, orgId) {
6017
+ const data = await callApi(config, "/auth/switch-org", {
6005
6018
  method: "POST",
6006
6019
  body: { orgId }
6007
6020
  });
6021
+ return normalizeOrgScopedTokenResponse(data);
6022
+ }
6023
+ async function switchOrg(config, orgId) {
6024
+ return getOrgScopedToken(config, orgId);
6008
6025
  }
6009
6026
  var device = {
6010
6027
  /**
@@ -8002,6 +8019,9 @@ async function getBillingUsage(config, options) {
8002
8019
  });
8003
8020
  }
8004
8021
 
8022
+ // src/storage.ts
8023
+ init_errors();
8024
+
8005
8025
  // src/lib/retry.ts
8006
8026
  init_constants();
8007
8027
  var DEFAULT_RETRY_CONFIG = {
@@ -8124,7 +8144,6 @@ var PATHS = {
8124
8144
  versions: (id) => `/storage/files/${encodeURIComponent(String(id))}/versions`,
8125
8145
  versionRestore: (id, vid) => `/storage/files/${encodeURIComponent(String(id))}/versions/${encodeURIComponent(String(vid))}:restore`
8126
8146
  };
8127
- var isBrowser = () => typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
8128
8147
  var hasXhr = () => typeof globalThis.XMLHttpRequest !== "undefined";
8129
8148
  var hasLocalStorage = () => {
8130
8149
  try {
@@ -8136,16 +8155,14 @@ var hasLocalStorage = () => {
8136
8155
  };
8137
8156
  async function computeSha256Hex(blob) {
8138
8157
  const subtle = globalThis.crypto?.subtle;
8139
- if (subtle?.digest) {
8140
- const buf2 = await blob.arrayBuffer();
8141
- const digest2 = await subtle.digest("SHA-256", buf2);
8142
- return bytesToHex(new Uint8Array(digest2));
8158
+ if (!subtle?.digest) {
8159
+ throw new SylphxError("Web Crypto SHA-256 support is required for storage uploads", {
8160
+ code: "NOT_IMPLEMENTED"
8161
+ });
8143
8162
  }
8144
- const nodeCrypto = await import("crypto");
8145
- const hash = nodeCrypto.createHash("sha256");
8146
- const buf = Buffer.from(await blob.arrayBuffer());
8147
- hash.update(buf);
8148
- return hash.digest("hex");
8163
+ const buf = await blob.arrayBuffer();
8164
+ const digest2 = await subtle.digest("SHA-256", buf);
8165
+ return bytesToHex(new Uint8Array(digest2));
8149
8166
  }
8150
8167
  function bytesToHex(b) {
8151
8168
  let s = "";
@@ -8162,10 +8179,6 @@ function persistResume(rec) {
8162
8179
  localStorage.setItem(resumeKey(rec.uploadId), JSON.stringify(rec));
8163
8180
  } catch {
8164
8181
  }
8165
- return;
8166
- }
8167
- if (!isBrowser()) {
8168
- void persistResumeNode(rec);
8169
8182
  }
8170
8183
  }
8171
8184
  function clearResume(uploadId) {
@@ -8174,41 +8187,6 @@ function clearResume(uploadId) {
8174
8187
  localStorage.removeItem(resumeKey(uploadId));
8175
8188
  } catch {
8176
8189
  }
8177
- return;
8178
- }
8179
- if (!isBrowser()) void clearResumeNode(uploadId);
8180
- }
8181
- async function persistResumeNode(rec) {
8182
- try {
8183
- const fs = await import("fs/promises");
8184
- const path = await import("path");
8185
- const os = await import("os");
8186
- const dir = path.join(os.homedir(), ".sylphx");
8187
- await fs.mkdir(dir, { recursive: true });
8188
- const file = path.join(dir, "uploads.json");
8189
- let store = {};
8190
- try {
8191
- const text = await fs.readFile(file, "utf8");
8192
- store = JSON.parse(text);
8193
- } catch {
8194
- store = {};
8195
- }
8196
- store[rec.uploadId] = rec;
8197
- await fs.writeFile(file, JSON.stringify(store), "utf8");
8198
- } catch {
8199
- }
8200
- }
8201
- async function clearResumeNode(uploadId) {
8202
- try {
8203
- const fs = await import("fs/promises");
8204
- const path = await import("path");
8205
- const os = await import("os");
8206
- const file = path.join(os.homedir(), ".sylphx", "uploads.json");
8207
- const text = await fs.readFile(file, "utf8");
8208
- const store = JSON.parse(text);
8209
- delete store[uploadId];
8210
- await fs.writeFile(file, JSON.stringify(store), "utf8");
8211
- } catch {
8212
8190
  }
8213
8191
  }
8214
8192
  function putBlob(url, body, headers, signal, onProgress) {
@@ -8925,7 +8903,7 @@ function createTasksHandler(taskDefs, options = {}) {
8925
8903
  { status: 400 }
8926
8904
  );
8927
8905
  }
8928
- const signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ?? process.env.SYLPHX_SECRET_KEY ?? "";
8906
+ const signingSecret = options.signingSecret ?? process.env.SYLPHX_SIGNING_SECRET ?? "";
8929
8907
  if (signingSecret) {
8930
8908
  const signature = req.headers.get("x-sylphx-signature") ?? "";
8931
8909
  if (!signature) {
@@ -11032,6 +11010,7 @@ export {
11032
11010
  getMemberPermissions,
11033
11011
  getMyReferralCode,
11034
11012
  getOidcDiscoveryDocument,
11013
+ getOrgScopedToken,
11035
11014
  getOrganization,
11036
11015
  getOrganizationInvitations,
11037
11016
  getOrganizationMembers,