@mercurjs/client 2.3.8-canary.4 → 2.3.9-canary.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.d.mts CHANGED
@@ -8,10 +8,92 @@ type InferOutput<TResponse> = TResponse extends MedusaResponse<infer Output> ? O
8
8
  type HttpMethod = "GET" | "POST" | "DELETE";
9
9
  type ChildKeys<T> = Exclude<keyof T, HttpMethod | "__esModule">;
10
10
 
11
+ /**
12
+ * Token storage and request authentication for the client.
13
+ *
14
+ * A token store keeps one credential under one key and survives reloads
15
+ * through `localStorage` when it exists, falling back to memory on the server
16
+ * or when storage is blocked. An auth scheme turns stores into the headers a
17
+ * Mercur API expects, resolved freshly on every request.
18
+ */
19
+ type TokenStorage = {
20
+ getItem(key: string): string | null;
21
+ setItem(key: string, value: string): void;
22
+ removeItem(key: string): void;
23
+ };
24
+ type TokenStore = {
25
+ readonly key: string;
26
+ get(): string | null;
27
+ set(token: string): void;
28
+ clear(): void;
29
+ /** Called with the new value after every `set` and `clear`, in this tab. */
30
+ subscribe(listener: (token: string | null) => void): () => void;
31
+ };
32
+ declare function createTokenStore(key: string, storage?: TokenStorage): TokenStore;
33
+ type MaybePromise<T> = T | Promise<T>;
34
+ type Credential = string | null | undefined;
35
+ /** One way of authenticating a request. Several can be combined in `ClientOptions.auth`. */
36
+ type AuthScheme = {
37
+ type: "bearer";
38
+ token: TokenStore | (() => MaybePromise<Credential>);
39
+ } | {
40
+ type: "api-key";
41
+ key: TokenStore | (() => MaybePromise<Credential>);
42
+ } | {
43
+ type: "publishable-key";
44
+ key: TokenStore | (() => MaybePromise<Credential>);
45
+ } | {
46
+ type: "header";
47
+ name: string;
48
+ value: TokenStore | (() => MaybePromise<Credential>);
49
+ };
50
+ type AuthOption = TokenStore | AuthScheme | AuthScheme[];
51
+ /** The session token of an admin or vendor panel user, as `Authorization: Bearer`. */
52
+ declare const bearerAuth: (token: TokenStore | (() => MaybePromise<Credential>)) => AuthScheme;
53
+ /** A secret API key (`sk_…`), sent the way Medusa expects it: HTTP Basic with the key as the user. */
54
+ declare const apiKeyAuth: (key: TokenStore | (() => MaybePromise<Credential>)) => AuthScheme;
55
+ /** A publishable API key (`pk_…`) for the Store API. */
56
+ declare const publishableKeyAuth: (key: TokenStore | (() => MaybePromise<Credential>)) => AuthScheme;
57
+ /** Any other credential header, e.g. the vendor panel's active seller. */
58
+ declare const headerAuth: (name: string, value: TokenStore | (() => MaybePromise<Credential>)) => AuthScheme;
59
+ declare function resolveAuthHeaders(auth: AuthOption | undefined): Promise<Record<string, string>>;
60
+ /** Clears every token store the auth option reads from; used after a 401. */
61
+ declare function clearAuthStores(auth: AuthOption | undefined): void;
62
+ /**
63
+ * The stores the admin and vendor panels keep their sessions in. A plugin's
64
+ * dashboard code reads the same session by using the same store, so it never
65
+ * has to know the storage key.
66
+ */
67
+ declare const ADMIN_TOKEN_KEY = "mercur_admin_auth_token";
68
+ declare const VENDOR_TOKEN_KEY = "mercur_vendor_auth_token";
69
+ declare const VENDOR_SELLER_KEY = "mercur_vendor_seller_id";
70
+ declare const adminTokenStore: TokenStore;
71
+ declare const vendorTokenStore: TokenStore;
72
+ declare const vendorSellerStore: TokenStore;
73
+ /** Auth for anything that talks to `/admin/*` as the signed-in panel user. */
74
+ declare const adminAuth: () => AuthScheme[];
75
+ /** Auth for `/vendor/*` as the signed-in seller member, scoped to the active seller. */
76
+ declare const vendorAuth: () => AuthScheme[];
77
+
11
78
  type ClientOptions = {
12
79
  baseUrl: string;
13
80
  fetchOptions?: RequestInit;
14
- headers?: () => Record<string, string>;
81
+ /**
82
+ * How requests authenticate: a token store (sent as a bearer token), one
83
+ * scheme, or several. Resolved on every request, so a token set after the
84
+ * client was created is picked up by the next call.
85
+ */
86
+ auth?: AuthOption;
87
+ /** Extra headers computed per request, on top of `auth`. */
88
+ headers?: () => Record<string, string> | Promise<Record<string, string>>;
89
+ /**
90
+ * Runs when the API answers 401. By default the bearer token stores in
91
+ * `auth` are cleared so the panel falls back to its login screen.
92
+ */
93
+ onUnauthorized?: (error: {
94
+ status: number;
95
+ message: string;
96
+ }) => void;
15
97
  };
16
98
  type AddParamsToFn<Fn, TParams> = keyof TParams extends never ? Fn : Fn extends (input?: infer TInput) => infer TOutput ? (input: (TInput extends Record<string, any> ? Omit<TInput, 'fetchOptions'> : {}) & TParams & {
17
99
  fetchOptions?: RequestInit;
@@ -59,4 +141,4 @@ declare class ClientError extends Error {
59
141
  }
60
142
  declare function createClient(options: ClientOptions): any;
61
143
 
62
- export { ClientError, type InferClient, type InferClientInput, type InferClientOutput, createClient };
144
+ export { ADMIN_TOKEN_KEY, type AuthOption, type AuthScheme, ClientError, type InferClient, type InferClientInput, type InferClientOutput, type TokenStorage, type TokenStore, VENDOR_SELLER_KEY, VENDOR_TOKEN_KEY, adminAuth, adminTokenStore, apiKeyAuth, bearerAuth, clearAuthStores, createClient, createTokenStore, headerAuth, publishableKeyAuth, resolveAuthHeaders, vendorAuth, vendorSellerStore, vendorTokenStore };
package/dist/index.mjs CHANGED
@@ -75,6 +75,123 @@ function createRecursiveProxy(callback, path = []) {
75
75
  // src/utils.ts
76
76
  var kebabCase = (string) => string.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
77
77
 
78
+ // src/auth.ts
79
+ var memoryStorage = () => {
80
+ const map = /* @__PURE__ */ new Map();
81
+ return {
82
+ getItem: (key) => {
83
+ var _a;
84
+ return (_a = map.get(key)) != null ? _a : null;
85
+ },
86
+ setItem: (key, value) => void map.set(key, value),
87
+ removeItem: (key) => void map.delete(key)
88
+ };
89
+ };
90
+ var resolveStorage = (storage) => {
91
+ if (storage) return storage;
92
+ try {
93
+ if (typeof localStorage !== "undefined") {
94
+ localStorage.getItem("__mercur_probe__");
95
+ return localStorage;
96
+ }
97
+ } catch (e) {
98
+ }
99
+ return memoryStorage();
100
+ };
101
+ function createTokenStore(key, storage) {
102
+ const backing = resolveStorage(storage);
103
+ const listeners = /* @__PURE__ */ new Set();
104
+ const notify = (token) => listeners.forEach((l) => l(token));
105
+ return {
106
+ key,
107
+ get() {
108
+ try {
109
+ return backing.getItem(key);
110
+ } catch (e) {
111
+ return null;
112
+ }
113
+ },
114
+ set(token) {
115
+ try {
116
+ backing.setItem(key, token);
117
+ } catch (e) {
118
+ return;
119
+ }
120
+ notify(token);
121
+ },
122
+ clear() {
123
+ try {
124
+ backing.removeItem(key);
125
+ } catch (e) {
126
+ return;
127
+ }
128
+ notify(null);
129
+ },
130
+ subscribe(listener) {
131
+ listeners.add(listener);
132
+ return () => void listeners.delete(listener);
133
+ }
134
+ };
135
+ }
136
+ var read = (source) => typeof source === "function" ? source() : source.get();
137
+ var isTokenStore = (value) => typeof value === "object" && value !== null && "get" in value && "subscribe" in value;
138
+ var toBase64 = (value) => typeof btoa === "function" ? btoa(value) : Buffer.from(value, "utf8").toString("base64");
139
+ var bearerAuth = (token) => ({ type: "bearer", token });
140
+ var apiKeyAuth = (key) => ({ type: "api-key", key });
141
+ var publishableKeyAuth = (key) => ({ type: "publishable-key", key });
142
+ var headerAuth = (name, value) => ({ type: "header", name, value });
143
+ function resolveAuthHeaders(auth) {
144
+ return __async(this, null, function* () {
145
+ if (!auth) return {};
146
+ const schemes = isTokenStore(auth) ? [bearerAuth(auth)] : Array.isArray(auth) ? auth : [auth];
147
+ const headers = {};
148
+ for (const scheme of schemes) {
149
+ switch (scheme.type) {
150
+ case "bearer": {
151
+ const token = yield read(scheme.token);
152
+ if (token) headers.Authorization = `Bearer ${token}`;
153
+ break;
154
+ }
155
+ case "api-key": {
156
+ const key = yield read(scheme.key);
157
+ if (key) headers.Authorization = `Basic ${toBase64(`${key}:`)}`;
158
+ break;
159
+ }
160
+ case "publishable-key": {
161
+ const key = yield read(scheme.key);
162
+ if (key) headers["x-publishable-api-key"] = key;
163
+ break;
164
+ }
165
+ case "header": {
166
+ const value = yield read(scheme.value);
167
+ if (value) headers[scheme.name] = value;
168
+ break;
169
+ }
170
+ }
171
+ }
172
+ return headers;
173
+ });
174
+ }
175
+ function clearAuthStores(auth) {
176
+ if (!auth) return;
177
+ const schemes = isTokenStore(auth) ? [bearerAuth(auth)] : Array.isArray(auth) ? auth : [auth];
178
+ for (const scheme of schemes) {
179
+ const source = scheme.type === "bearer" ? scheme.token : scheme.type === "header" ? scheme.value : scheme.key;
180
+ if (isTokenStore(source) && scheme.type === "bearer") source.clear();
181
+ }
182
+ }
183
+ var ADMIN_TOKEN_KEY = "mercur_admin_auth_token";
184
+ var VENDOR_TOKEN_KEY = "mercur_vendor_auth_token";
185
+ var VENDOR_SELLER_KEY = "mercur_vendor_seller_id";
186
+ var adminTokenStore = createTokenStore(ADMIN_TOKEN_KEY);
187
+ var vendorTokenStore = createTokenStore(VENDOR_TOKEN_KEY);
188
+ var vendorSellerStore = createTokenStore(VENDOR_SELLER_KEY);
189
+ var adminAuth = () => [bearerAuth(adminTokenStore)];
190
+ var vendorAuth = () => [
191
+ bearerAuth(vendorTokenStore),
192
+ headerAuth("x-seller-id", vendorSellerStore)
193
+ ];
194
+
78
195
  // src/index.ts
79
196
  var ClientError = class extends Error {
80
197
  constructor(message, statusText, status, code) {
@@ -112,8 +229,8 @@ var toFormData = (payload) => {
112
229
  return formData;
113
230
  };
114
231
  function createClient(options) {
115
- const { baseUrl, fetchOptions: defaultFetchOptions, headers: resolveHeaders } = options;
116
- return createRecursiveProxy((path, args) => {
232
+ const { baseUrl, fetchOptions: defaultFetchOptions, headers: resolveHeaders, auth, onUnauthorized } = options;
233
+ return createRecursiveProxy((path, args) => __async(null, null, function* () {
117
234
  var _a;
118
235
  const action = path.pop();
119
236
  const input = (_a = args[0]) != null ? _a : {};
@@ -153,28 +270,51 @@ function createClient(options) {
153
270
  if (!isMultipart) {
154
271
  defaultHeaders["Content-Type"] = "application/json";
155
272
  }
156
- const headers = new Headers(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, defaultHeaders), defaultFetchOptions == null ? void 0 : defaultFetchOptions.headers), resolveHeaders == null ? void 0 : resolveHeaders()), inputFetchOptions == null ? void 0 : inputFetchOptions.headers));
273
+ const headers = new Headers(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, defaultHeaders), defaultFetchOptions == null ? void 0 : defaultFetchOptions.headers), yield resolveAuthHeaders(auth)), yield resolveHeaders == null ? void 0 : resolveHeaders()), inputFetchOptions == null ? void 0 : inputFetchOptions.headers));
157
274
  return fetch(url, __spreadProps(__spreadValues(__spreadValues({}, defaultFetchOptions), inputFetchOptions), {
158
275
  method,
159
276
  body,
160
277
  headers
161
278
  })).then((response) => __async(null, null, function* () {
162
- var _a2, _b2;
279
+ var _a2, _b2, _c;
163
280
  if (response.status >= 300) {
164
281
  const jsonError = yield response.json().catch(() => ({}));
282
+ if (response.status === 401) {
283
+ const message = (_a2 = jsonError.message) != null ? _a2 : response.statusText;
284
+ if (onUnauthorized) {
285
+ onUnauthorized({ status: 401, message });
286
+ } else {
287
+ clearAuthStores(auth);
288
+ }
289
+ }
165
290
  throw new ClientError(
166
- (_a2 = jsonError.message) != null ? _a2 : response.statusText,
291
+ (_b2 = jsonError.message) != null ? _b2 : response.statusText,
167
292
  response.statusText,
168
293
  response.status,
169
294
  typeof jsonError.code === "string" ? jsonError.code : void 0
170
295
  );
171
296
  }
172
- const isJsonRequest = (_b2 = headers.get("accept")) == null ? void 0 : _b2.includes("application/json");
297
+ const isJsonRequest = (_c = headers.get("accept")) == null ? void 0 : _c.includes("application/json");
173
298
  return isJsonRequest ? yield response.json() : response;
174
299
  }));
175
- });
300
+ }));
176
301
  }
177
302
  export {
303
+ ADMIN_TOKEN_KEY,
178
304
  ClientError,
179
- createClient
305
+ VENDOR_SELLER_KEY,
306
+ VENDOR_TOKEN_KEY,
307
+ adminAuth,
308
+ adminTokenStore,
309
+ apiKeyAuth,
310
+ bearerAuth,
311
+ clearAuthStores,
312
+ createClient,
313
+ createTokenStore,
314
+ headerAuth,
315
+ publishableKeyAuth,
316
+ resolveAuthHeaders,
317
+ vendorAuth,
318
+ vendorSellerStore,
319
+ vendorTokenStore
180
320
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/client",
3
- "version": "2.3.8-canary.4",
3
+ "version": "2.3.9-canary.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",
@@ -32,17 +32,19 @@
32
32
  "scripts": {
33
33
  "typecheck": "tsc --noEmit",
34
34
  "dev": "tsup --watch",
35
- "build": "tsup"
35
+ "build": "tsup",
36
+ "test": "vitest run"
36
37
  },
37
38
  "dependencies": {
38
39
  "@tanstack/react-query": "^5.64.2",
39
40
  "qs": "^6.12.1"
40
41
  },
41
42
  "devDependencies": {
42
- "@medusajs/framework": "2.21.2",
43
+ "@medusajs/framework": "2.22.0",
43
44
  "@types/qs": "^6.9.18",
44
45
  "tsup": "^8.5.0",
45
- "typescript": "5.9.3"
46
+ "typescript": "5.9.3",
47
+ "vitest": "^3.2.4"
46
48
  },
47
49
  "peerDependencies": {
48
50
  "typescript": ">=4.8.0"