@cronvello/shop-sdk 0.1.0 → 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/README.md +34 -0
- package/dist/browser.cjs +100 -2
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +99 -3
- package/dist/browser.js.map +1 -1
- package/dist/{error-C-cJ6OgX.d.cts → error-C5_qRXMp.d.cts} +54 -1
- package/dist/{error-BFzxyQkW.d.ts → error-Do0kyiZ6.d.ts} +54 -1
- package/dist/index.cjs +100 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +99 -3
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +100 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +99 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -53,6 +53,59 @@ interface ShopClient {
|
|
|
53
53
|
/** Stable production endpoint used when no override or resolver is supplied. */
|
|
54
54
|
declare const SHOP_API_URL = "https://node-shop-production-86b7.up.railway.app";
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* `match` — the deployment reports exactly the contract compiled into this SDK.
|
|
58
|
+
* `drift` — it reports a different one. The types here are a lie about that
|
|
59
|
+
* deployment; requests can fail on shapes the compiler called fine.
|
|
60
|
+
* `unknown` — nobody could be asked (offline, 503, or a node-shop old enough not
|
|
61
|
+
* to serve the endpoint yet). Explicitly NOT the same as `match`.
|
|
62
|
+
*/
|
|
63
|
+
type ShopContractStatus = "match" | "drift" | "unknown";
|
|
64
|
+
type ShopDiagnosis = {
|
|
65
|
+
/**
|
|
66
|
+
* True only when the contract was actually VERIFIED to match. `unknown` is
|
|
67
|
+
* false on purpose: a green light that means "could not check" is exactly the
|
|
68
|
+
* failure this whole mechanism exists to remove.
|
|
69
|
+
*/
|
|
70
|
+
ok: boolean;
|
|
71
|
+
baseUrl: string | null;
|
|
72
|
+
sdkVersion: string;
|
|
73
|
+
reachable: boolean;
|
|
74
|
+
contract: {
|
|
75
|
+
status: ShopContractStatus;
|
|
76
|
+
sdk: string;
|
|
77
|
+
live: string | null;
|
|
78
|
+
files: number | null;
|
|
79
|
+
};
|
|
80
|
+
/** Plain sentences, safe to log or show. Empty when `ok` is true. */
|
|
81
|
+
problems: string[];
|
|
82
|
+
checkedAt: string;
|
|
83
|
+
};
|
|
84
|
+
type ShopDiagnoseOptions = {
|
|
85
|
+
/** Preferred: reuses the client's own base URL, including a dynamic resolver. */
|
|
86
|
+
client?: Pick<ShopClient, "baseUrl">;
|
|
87
|
+
baseUrl?: BaseUrlProvider;
|
|
88
|
+
fetch?: typeof globalThis.fetch;
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Ask a running node-shop which contract it speaks and compare it to the one
|
|
94
|
+
* this SDK was built against.
|
|
95
|
+
*
|
|
96
|
+
* Read-only, unauthenticated, and cheap — meant to sit behind a consumer's own
|
|
97
|
+
* health endpoint so the answer is a fact anyone can pull, rather than something
|
|
98
|
+
* a person has to remember to check after a release.
|
|
99
|
+
*
|
|
100
|
+
* Never throws for an operational reason: an unreachable shop is a RESULT
|
|
101
|
+
* (`reachable: false`), not an exception, so a health route cannot accidentally
|
|
102
|
+
* turn a diagnosis into a 500. Only a genuinely unusable base URL throws
|
|
103
|
+
* (`ShopConfigurationError`), because that is a wiring mistake, not a state.
|
|
104
|
+
*/
|
|
105
|
+
declare function diagnoseShop(options?: ShopDiagnoseOptions): Promise<ShopDiagnosis>;
|
|
106
|
+
|
|
107
|
+
declare const CONTRACT_SHA256 = "335eed391a2786a4f7f010b056d2a6a5b49d9e1244960a15516a5d77b93693b5";
|
|
108
|
+
|
|
56
109
|
type ShopErrorDetails = {
|
|
57
110
|
status: number;
|
|
58
111
|
method: string;
|
|
@@ -75,4 +128,4 @@ declare class ShopConfigurationError extends Error {
|
|
|
75
128
|
constructor(message: string);
|
|
76
129
|
}
|
|
77
130
|
|
|
78
|
-
export { type BaseUrlProvider as B, type HeaderProvider as H, type MaybePromise as M, type ShopClientOptions as S, type ShopClient as a, SHOP_API_URL as b, ShopApiError as c, type ShopAuthType as d, ShopConfigurationError as e, type
|
|
131
|
+
export { type BaseUrlProvider as B, CONTRACT_SHA256 as C, type HeaderProvider as H, type MaybePromise as M, type ShopClientOptions as S, type ShopClient as a, SHOP_API_URL as b, ShopApiError as c, type ShopAuthType as d, ShopConfigurationError as e, type ShopContractStatus as f, type ShopDiagnoseOptions as g, type ShopDiagnosis as h, type ShopRequestContext as i, type ShopRequestOptions as j, type ShopRoute as k, type ShopRouteAuth as l, type ShopRouteBody as m, type ShopRouteInput as n, type ShopRouteKey as o, type ShopRouteMethod as p, type ShopRouteParams as q, type ShopRouteQuery as r, type ShopRouteResponse as s, type ShopRouteResponseData as t, diagnoseShop as u };
|
|
@@ -53,6 +53,59 @@ interface ShopClient {
|
|
|
53
53
|
/** Stable production endpoint used when no override or resolver is supplied. */
|
|
54
54
|
declare const SHOP_API_URL = "https://node-shop-production-86b7.up.railway.app";
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* `match` — the deployment reports exactly the contract compiled into this SDK.
|
|
58
|
+
* `drift` — it reports a different one. The types here are a lie about that
|
|
59
|
+
* deployment; requests can fail on shapes the compiler called fine.
|
|
60
|
+
* `unknown` — nobody could be asked (offline, 503, or a node-shop old enough not
|
|
61
|
+
* to serve the endpoint yet). Explicitly NOT the same as `match`.
|
|
62
|
+
*/
|
|
63
|
+
type ShopContractStatus = "match" | "drift" | "unknown";
|
|
64
|
+
type ShopDiagnosis = {
|
|
65
|
+
/**
|
|
66
|
+
* True only when the contract was actually VERIFIED to match. `unknown` is
|
|
67
|
+
* false on purpose: a green light that means "could not check" is exactly the
|
|
68
|
+
* failure this whole mechanism exists to remove.
|
|
69
|
+
*/
|
|
70
|
+
ok: boolean;
|
|
71
|
+
baseUrl: string | null;
|
|
72
|
+
sdkVersion: string;
|
|
73
|
+
reachable: boolean;
|
|
74
|
+
contract: {
|
|
75
|
+
status: ShopContractStatus;
|
|
76
|
+
sdk: string;
|
|
77
|
+
live: string | null;
|
|
78
|
+
files: number | null;
|
|
79
|
+
};
|
|
80
|
+
/** Plain sentences, safe to log or show. Empty when `ok` is true. */
|
|
81
|
+
problems: string[];
|
|
82
|
+
checkedAt: string;
|
|
83
|
+
};
|
|
84
|
+
type ShopDiagnoseOptions = {
|
|
85
|
+
/** Preferred: reuses the client's own base URL, including a dynamic resolver. */
|
|
86
|
+
client?: Pick<ShopClient, "baseUrl">;
|
|
87
|
+
baseUrl?: BaseUrlProvider;
|
|
88
|
+
fetch?: typeof globalThis.fetch;
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Ask a running node-shop which contract it speaks and compare it to the one
|
|
94
|
+
* this SDK was built against.
|
|
95
|
+
*
|
|
96
|
+
* Read-only, unauthenticated, and cheap — meant to sit behind a consumer's own
|
|
97
|
+
* health endpoint so the answer is a fact anyone can pull, rather than something
|
|
98
|
+
* a person has to remember to check after a release.
|
|
99
|
+
*
|
|
100
|
+
* Never throws for an operational reason: an unreachable shop is a RESULT
|
|
101
|
+
* (`reachable: false`), not an exception, so a health route cannot accidentally
|
|
102
|
+
* turn a diagnosis into a 500. Only a genuinely unusable base URL throws
|
|
103
|
+
* (`ShopConfigurationError`), because that is a wiring mistake, not a state.
|
|
104
|
+
*/
|
|
105
|
+
declare function diagnoseShop(options?: ShopDiagnoseOptions): Promise<ShopDiagnosis>;
|
|
106
|
+
|
|
107
|
+
declare const CONTRACT_SHA256 = "335eed391a2786a4f7f010b056d2a6a5b49d9e1244960a15516a5d77b93693b5";
|
|
108
|
+
|
|
56
109
|
type ShopErrorDetails = {
|
|
57
110
|
status: number;
|
|
58
111
|
method: string;
|
|
@@ -75,4 +128,4 @@ declare class ShopConfigurationError extends Error {
|
|
|
75
128
|
constructor(message: string);
|
|
76
129
|
}
|
|
77
130
|
|
|
78
|
-
export { type BaseUrlProvider as B, type HeaderProvider as H, type MaybePromise as M, type ShopClientOptions as S, type ShopClient as a, SHOP_API_URL as b, ShopApiError as c, type ShopAuthType as d, ShopConfigurationError as e, type
|
|
131
|
+
export { type BaseUrlProvider as B, CONTRACT_SHA256 as C, type HeaderProvider as H, type MaybePromise as M, type ShopClientOptions as S, type ShopClient as a, SHOP_API_URL as b, ShopApiError as c, type ShopAuthType as d, ShopConfigurationError as e, type ShopContractStatus as f, type ShopDiagnoseOptions as g, type ShopDiagnosis as h, type ShopRequestContext as i, type ShopRequestOptions as j, type ShopRoute as k, type ShopRouteAuth as l, type ShopRouteBody as m, type ShopRouteInput as n, type ShopRouteKey as o, type ShopRouteMethod as p, type ShopRouteParams as q, type ShopRouteQuery as r, type ShopRouteResponse as s, type ShopRouteResponseData as t, diagnoseShop as u };
|
package/dist/index.cjs
CHANGED
|
@@ -5621,7 +5621,7 @@ function createShopClient(options = {}) {
|
|
|
5621
5621
|
const defaultTimeoutMs = options.timeoutMs ?? 3e4;
|
|
5622
5622
|
const defaultRetries = options.retries ?? 2;
|
|
5623
5623
|
const retryDelayMs = options.retryDelayMs ?? 250;
|
|
5624
|
-
async function
|
|
5624
|
+
async function resolveBaseUrl2() {
|
|
5625
5625
|
const value = typeof baseUrl === "function" ? await baseUrl() : baseUrl;
|
|
5626
5626
|
return normalizeBaseUrl(value);
|
|
5627
5627
|
}
|
|
@@ -5643,7 +5643,7 @@ function createShopClient(options = {}) {
|
|
|
5643
5643
|
const body = hasBody ? JSON.stringify(values.body) : void 0;
|
|
5644
5644
|
const retries = Math.max(0, requestOptions.retries ?? defaultRetries);
|
|
5645
5645
|
const canRetry = SAFE_METHODS.has(route.method) || requestOptions.retryUnsafe === true;
|
|
5646
|
-
const resolvedBaseUrl = await
|
|
5646
|
+
const resolvedBaseUrl = await resolveBaseUrl2();
|
|
5647
5647
|
for (let attempt = 0; ; attempt += 1) {
|
|
5648
5648
|
const timeout = createRequestSignal(requestOptions.signal, requestOptions.timeoutMs ?? defaultTimeoutMs);
|
|
5649
5649
|
try {
|
|
@@ -5720,6 +5720,103 @@ function createShopClient(options = {}) {
|
|
|
5720
5720
|
return { baseUrl, request, requestText, requestRaw };
|
|
5721
5721
|
}
|
|
5722
5722
|
|
|
5723
|
+
// src/contract-hash.ts
|
|
5724
|
+
var CONTRACT_SHA256 = "335eed391a2786a4f7f010b056d2a6a5b49d9e1244960a15516a5d77b93693b5";
|
|
5725
|
+
|
|
5726
|
+
// src/client/diagnose.ts
|
|
5727
|
+
var SDK_VERSION = "0.2.0" ;
|
|
5728
|
+
async function resolveBaseUrl(options) {
|
|
5729
|
+
const provider = options.client?.baseUrl ?? options.baseUrl ?? SHOP_API_URL;
|
|
5730
|
+
const value = typeof provider === "function" ? await provider() : provider;
|
|
5731
|
+
return normalizeBaseUrl(value);
|
|
5732
|
+
}
|
|
5733
|
+
async function diagnoseShop(options = {}) {
|
|
5734
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5735
|
+
const baseUrl = await resolveBaseUrl(options);
|
|
5736
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
5737
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
5738
|
+
const base = {
|
|
5739
|
+
ok: false,
|
|
5740
|
+
baseUrl,
|
|
5741
|
+
sdkVersion: SDK_VERSION,
|
|
5742
|
+
reachable: false,
|
|
5743
|
+
contract: { status: "unknown", sdk: CONTRACT_SHA256, live: null, files: null },
|
|
5744
|
+
problems: [],
|
|
5745
|
+
checkedAt
|
|
5746
|
+
};
|
|
5747
|
+
if (typeof doFetch !== "function") {
|
|
5748
|
+
return { ...base, problems: ["No fetch implementation available; pass `fetch` explicitly."] };
|
|
5749
|
+
}
|
|
5750
|
+
const controller = new AbortController();
|
|
5751
|
+
const onAbort = () => controller.abort(options.signal?.reason);
|
|
5752
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
5753
|
+
if (options.signal?.aborted) controller.abort(options.signal.reason);
|
|
5754
|
+
const timer = setTimeout(() => controller.abort(new Error(`Contract probe timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
5755
|
+
let response;
|
|
5756
|
+
try {
|
|
5757
|
+
response = await doFetch(`${baseUrl}/health/contract`, {
|
|
5758
|
+
method: "GET",
|
|
5759
|
+
headers: { accept: "application/json" },
|
|
5760
|
+
signal: controller.signal
|
|
5761
|
+
});
|
|
5762
|
+
} catch (error) {
|
|
5763
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5764
|
+
return { ...base, problems: [`node-shop at ${baseUrl} could not be reached: ${detail}`] };
|
|
5765
|
+
} finally {
|
|
5766
|
+
clearTimeout(timer);
|
|
5767
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
5768
|
+
}
|
|
5769
|
+
if (response.status === 404) {
|
|
5770
|
+
return {
|
|
5771
|
+
...base,
|
|
5772
|
+
reachable: true,
|
|
5773
|
+
problems: [
|
|
5774
|
+
`node-shop at ${baseUrl} does not serve GET /health/contract, so the contract cannot be verified. Deploy a node-shop that includes it.`
|
|
5775
|
+
]
|
|
5776
|
+
};
|
|
5777
|
+
}
|
|
5778
|
+
if (!response.ok) {
|
|
5779
|
+
return {
|
|
5780
|
+
...base,
|
|
5781
|
+
reachable: true,
|
|
5782
|
+
problems: [`node-shop at ${baseUrl} answered ${response.status} for GET /health/contract; contract not verified.`]
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
let body;
|
|
5786
|
+
try {
|
|
5787
|
+
body = await response.json();
|
|
5788
|
+
} catch {
|
|
5789
|
+
return { ...base, reachable: true, problems: [`node-shop at ${baseUrl} returned a non-JSON contract fingerprint.`] };
|
|
5790
|
+
}
|
|
5791
|
+
const live = typeof body.contractSha256 === "string" && body.contractSha256 ? body.contractSha256 : null;
|
|
5792
|
+
const files = typeof body.files === "number" ? body.files : null;
|
|
5793
|
+
if (!live) {
|
|
5794
|
+
return {
|
|
5795
|
+
...base,
|
|
5796
|
+
reachable: true,
|
|
5797
|
+
contract: { ...base.contract, files },
|
|
5798
|
+
problems: [`node-shop at ${baseUrl} could not compute its own contract fingerprint; contract not verified.`]
|
|
5799
|
+
};
|
|
5800
|
+
}
|
|
5801
|
+
if (live !== CONTRACT_SHA256) {
|
|
5802
|
+
return {
|
|
5803
|
+
...base,
|
|
5804
|
+
reachable: true,
|
|
5805
|
+
contract: { status: "drift", sdk: CONTRACT_SHA256, live, files },
|
|
5806
|
+
problems: [
|
|
5807
|
+
`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.`
|
|
5808
|
+
]
|
|
5809
|
+
};
|
|
5810
|
+
}
|
|
5811
|
+
return {
|
|
5812
|
+
...base,
|
|
5813
|
+
ok: true,
|
|
5814
|
+
reachable: true,
|
|
5815
|
+
contract: { status: "match", sdk: CONTRACT_SHA256, live, files }
|
|
5816
|
+
};
|
|
5817
|
+
}
|
|
5818
|
+
|
|
5819
|
+
exports.CONTRACT_SHA256 = CONTRACT_SHA256;
|
|
5723
5820
|
exports.SHOP_API_URL = SHOP_API_URL;
|
|
5724
5821
|
exports.ShopApiError = ShopApiError;
|
|
5725
5822
|
exports.ShopConfigurationError = ShopConfigurationError;
|
|
@@ -5774,5 +5871,6 @@ exports.apiRoutes_users = apiRoutes_users;
|
|
|
5774
5871
|
exports.apiRoutes_variant_entitlements = apiRoutes_variant_entitlements;
|
|
5775
5872
|
exports.apiRoutes_webhooks = apiRoutes_webhooks;
|
|
5776
5873
|
exports.createShopClient = createShopClient;
|
|
5874
|
+
exports.diagnoseShop = diagnoseShop;
|
|
5777
5875
|
//# sourceMappingURL=index.cjs.map
|
|
5778
5876
|
//# sourceMappingURL=index.cjs.map
|