@cronvello/shop-sdk 0.1.0 → 0.2.2

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.cjs CHANGED
@@ -847,7 +847,7 @@ var apiRoutes_b2b = {
847
847
  meta: {
848
848
  tags: ["b2b"],
849
849
  summary: "Create B2B dynamic checkout session",
850
- description: "Creates a Stripe checkout session with dynamic price_data for trusted external apps. Supports mixed carts (one-time + recurring). Auth: Bearer BACKEND_TO_BACKEND_API_KEY.",
850
+ description: "Creates a Stripe checkout session with dynamic price_data for trusted external apps. Supports mixed carts (one-time + recurring). Auth: Bearer BACKEND_TO_BACKEND_API_KEY or App-Scoped API Key.",
851
851
  bodyContentType: "application/json",
852
852
  validated: { "params": false, "query": false, "body": true }
853
853
  },
@@ -2059,6 +2059,19 @@ var apiRoutes_external_apps = {
2059
2059
  },
2060
2060
  types: null
2061
2061
  },
2062
+ "external_apps_service_rotate_key": {
2063
+ method: "POST",
2064
+ path: "/external-apps/service/rotate-key/:appId",
2065
+ auth: { "type": "x_api_key_https" },
2066
+ meta: {
2067
+ tags: ["external-apps"],
2068
+ summary: "Rotate external app api key (B2B zero-downtime)",
2069
+ description: "Rotates the API key of an external app. The previous key remains valid during the specified gracePeriodHours (default: 2h) to prevent downtime during deployments. Authenticated via backend-to-backend API key.",
2070
+ bodyContentType: "application/json",
2071
+ validated: { "params": true, "query": true, "body": true }
2072
+ },
2073
+ types: null
2074
+ },
2062
2075
  "external_apps_service_delete": {
2063
2076
  method: "DELETE",
2064
2077
  path: "/external-apps/service/delete/:appId",
@@ -2078,7 +2091,7 @@ var apiRoutes_external_apps = {
2078
2091
  meta: {
2079
2092
  tags: ["external-apps"],
2080
2093
  summary: "Get external app registration status (B2B)",
2081
- description: "Returns registration status for an app identified by its string appId. Used by AMP to discover existing connections."
2094
+ description: "Returns registration status for an app identified by its string appId. Used by AMP to discover existing connections. Includes authMethod and, for OAuth apps, the oauth client_id we will present to that app plus a truncated SHA-256 fingerprint of the stored client secret, so the other side can prove a rotation actually landed here. Never the secret itself. Also reports the entitlement paths we have stored, because registration is an upsert: a caller that only wants to hand us new credentials must send these back unchanged, or it silently repoints where we fetch entitlements from."
2082
2095
  },
2083
2096
  types: null
2084
2097
  },
@@ -5621,7 +5634,7 @@ function createShopClient(options = {}) {
5621
5634
  const defaultTimeoutMs = options.timeoutMs ?? 3e4;
5622
5635
  const defaultRetries = options.retries ?? 2;
5623
5636
  const retryDelayMs = options.retryDelayMs ?? 250;
5624
- async function resolveBaseUrl() {
5637
+ async function resolveBaseUrl2() {
5625
5638
  const value = typeof baseUrl === "function" ? await baseUrl() : baseUrl;
5626
5639
  return normalizeBaseUrl(value);
5627
5640
  }
@@ -5643,7 +5656,7 @@ function createShopClient(options = {}) {
5643
5656
  const body = hasBody ? JSON.stringify(values.body) : void 0;
5644
5657
  const retries = Math.max(0, requestOptions.retries ?? defaultRetries);
5645
5658
  const canRetry = SAFE_METHODS.has(route.method) || requestOptions.retryUnsafe === true;
5646
- const resolvedBaseUrl = await resolveBaseUrl();
5659
+ const resolvedBaseUrl = await resolveBaseUrl2();
5647
5660
  for (let attempt = 0; ; attempt += 1) {
5648
5661
  const timeout = createRequestSignal(requestOptions.signal, requestOptions.timeoutMs ?? defaultTimeoutMs);
5649
5662
  try {
@@ -5720,6 +5733,103 @@ function createShopClient(options = {}) {
5720
5733
  return { baseUrl, request, requestText, requestRaw };
5721
5734
  }
5722
5735
 
5736
+ // src/contract-hash.ts
5737
+ var CONTRACT_SHA256 = "05f34929e71fb2fcb3a4c04707facca22ee46f8960a4cc47ce0a3f1a6df36056";
5738
+
5739
+ // src/client/diagnose.ts
5740
+ var SDK_VERSION = "0.2.2" ;
5741
+ async function resolveBaseUrl(options) {
5742
+ const provider = options.client?.baseUrl ?? options.baseUrl ?? SHOP_API_URL;
5743
+ const value = typeof provider === "function" ? await provider() : provider;
5744
+ return normalizeBaseUrl(value);
5745
+ }
5746
+ async function diagnoseShop(options = {}) {
5747
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
5748
+ const baseUrl = await resolveBaseUrl(options);
5749
+ const doFetch = options.fetch ?? globalThis.fetch;
5750
+ const timeoutMs = options.timeoutMs ?? 5e3;
5751
+ const base = {
5752
+ ok: false,
5753
+ baseUrl,
5754
+ sdkVersion: SDK_VERSION,
5755
+ reachable: false,
5756
+ contract: { status: "unknown", sdk: CONTRACT_SHA256, live: null, files: null },
5757
+ problems: [],
5758
+ checkedAt
5759
+ };
5760
+ if (typeof doFetch !== "function") {
5761
+ return { ...base, problems: ["No fetch implementation available; pass `fetch` explicitly."] };
5762
+ }
5763
+ const controller = new AbortController();
5764
+ const onAbort = () => controller.abort(options.signal?.reason);
5765
+ options.signal?.addEventListener("abort", onAbort, { once: true });
5766
+ if (options.signal?.aborted) controller.abort(options.signal.reason);
5767
+ const timer = setTimeout(() => controller.abort(new Error(`Contract probe timed out after ${timeoutMs}ms`)), timeoutMs);
5768
+ let response;
5769
+ try {
5770
+ response = await doFetch(`${baseUrl}/health/contract`, {
5771
+ method: "GET",
5772
+ headers: { accept: "application/json" },
5773
+ signal: controller.signal
5774
+ });
5775
+ } catch (error) {
5776
+ const detail = error instanceof Error ? error.message : String(error);
5777
+ return { ...base, problems: [`node-shop at ${baseUrl} could not be reached: ${detail}`] };
5778
+ } finally {
5779
+ clearTimeout(timer);
5780
+ options.signal?.removeEventListener("abort", onAbort);
5781
+ }
5782
+ if (response.status === 404) {
5783
+ return {
5784
+ ...base,
5785
+ reachable: true,
5786
+ problems: [
5787
+ `node-shop at ${baseUrl} does not serve GET /health/contract, so the contract cannot be verified. Deploy a node-shop that includes it.`
5788
+ ]
5789
+ };
5790
+ }
5791
+ if (!response.ok) {
5792
+ return {
5793
+ ...base,
5794
+ reachable: true,
5795
+ problems: [`node-shop at ${baseUrl} answered ${response.status} for GET /health/contract; contract not verified.`]
5796
+ };
5797
+ }
5798
+ let body;
5799
+ try {
5800
+ body = await response.json();
5801
+ } catch {
5802
+ return { ...base, reachable: true, problems: [`node-shop at ${baseUrl} returned a non-JSON contract fingerprint.`] };
5803
+ }
5804
+ const live = typeof body.contractSha256 === "string" && body.contractSha256 ? body.contractSha256 : null;
5805
+ const files = typeof body.files === "number" ? body.files : null;
5806
+ if (!live) {
5807
+ return {
5808
+ ...base,
5809
+ reachable: true,
5810
+ contract: { ...base.contract, files },
5811
+ problems: [`node-shop at ${baseUrl} could not compute its own contract fingerprint; contract not verified.`]
5812
+ };
5813
+ }
5814
+ if (live !== CONTRACT_SHA256) {
5815
+ return {
5816
+ ...base,
5817
+ reachable: true,
5818
+ contract: { status: "drift", sdk: CONTRACT_SHA256, live, files },
5819
+ problems: [
5820
+ `Contract drift: node-shop at ${baseUrl} speaks ${live.slice(0, 12)}, @cronvello/shop-sdk ${SDK_VERSION} was built against ${CONTRACT_SHA256.slice(0, 12)}. Pin a newer SDK version, or deploy the node-shop the SDK was built from.`
5821
+ ]
5822
+ };
5823
+ }
5824
+ return {
5825
+ ...base,
5826
+ ok: true,
5827
+ reachable: true,
5828
+ contract: { status: "match", sdk: CONTRACT_SHA256, live, files }
5829
+ };
5830
+ }
5831
+
5832
+ exports.CONTRACT_SHA256 = CONTRACT_SHA256;
5723
5833
  exports.SHOP_API_URL = SHOP_API_URL;
5724
5834
  exports.ShopApiError = ShopApiError;
5725
5835
  exports.ShopConfigurationError = ShopConfigurationError;
@@ -5774,5 +5884,6 @@ exports.apiRoutes_users = apiRoutes_users;
5774
5884
  exports.apiRoutes_variant_entitlements = apiRoutes_variant_entitlements;
5775
5885
  exports.apiRoutes_webhooks = apiRoutes_webhooks;
5776
5886
  exports.createShopClient = createShopClient;
5887
+ exports.diagnoseShop = diagnoseShop;
5777
5888
  //# sourceMappingURL=index.cjs.map
5778
5889
  //# sourceMappingURL=index.cjs.map