@shop-on/customer-runtime 0.5.0 → 0.6.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.
@@ -0,0 +1,21 @@
1
+ //#region src/internal-api.d.ts
2
+ export declare class InternalApiError extends Error {
3
+ readonly status: number;
4
+ readonly bodySnippet: string;
5
+ constructor(path: string, status: number, bodySnippet: string);
6
+ }
7
+ /**
8
+ * POST a JSON body to `/api/internal/customer/<path>`. Throws
9
+ * `InternalApiError` on non-2xx so callers can decide how to map status →
10
+ * domain outcome (e.g. token mint maps 404 → CredentialsUnavailableError).
11
+ */
12
+ export declare function internalPost<TResponse>(path: string, body: unknown, { signal }?: {
13
+ signal?: AbortSignal;
14
+ }): Promise<TResponse>;
15
+ /**
16
+ * DELETE `/api/internal/customer/<path>`. Used by the handoff consume path —
17
+ * the endpoint always returns 200 (with `{ ok: false, reason }` on miss), so
18
+ * a non-2xx here is a real error, not a domain miss.
19
+ */
20
+ export declare function internalDelete<TResponse>(path: string): Promise<TResponse>;
21
+ //#endregion
package/dist/relay.d.ts CHANGED
@@ -5,4 +5,10 @@
5
5
  * a request or without a valid IP. Spread these last into outgoing headers.
6
6
  */
7
7
  export declare function relayHeaders(): Record<string, string>;
8
+ /**
9
+ * `X-Client-IP` from `CF-Connecting-IP` plus `X-Relay-Auth`, for the developers
10
+ * and platform Workers' api calls. Empty without SPN_STAFF_RELAY_SECRET, outside
11
+ * a request, or without a valid IP: with no secret Rails would ignore the IP.
12
+ */
13
+ export declare function staffRelayHeaders(): Record<string, string>;
8
14
  //#endregion
package/dist/relay.js CHANGED
@@ -20,5 +20,19 @@ function relayHeaders() {
20
20
  "X-Relay-Auth": secret
21
21
  } : { "X-Buyer-IP": ip };
22
22
  }
23
+ /**
24
+ * `X-Client-IP` from `CF-Connecting-IP` plus `X-Relay-Auth`, for the developers
25
+ * and platform Workers' api calls. Empty without SPN_STAFF_RELAY_SECRET, outside
26
+ * a request, or without a valid IP: with no secret Rails would ignore the IP.
27
+ */
28
+ function staffRelayHeaders() {
29
+ const secret = process.env.SPN_STAFF_RELAY_SECRET;
30
+ const ip = currentRequest()?.headers.get("cf-connecting-ip");
31
+ if (!secret || !ip || !isIP(ip)) return {};
32
+ return {
33
+ "X-Client-IP": ip,
34
+ "X-Relay-Auth": secret
35
+ };
36
+ }
23
37
  //#endregion
24
- export { relayHeaders };
38
+ export { relayHeaders, staffRelayHeaders };
@@ -29,6 +29,14 @@ export interface ShopBoundCookieConfig {
29
29
  path: "/";
30
30
  };
31
31
  }
32
+ /**
33
+ * Storefronts on `<handle>.adorly.mn` run merchant script, and `adorly.mn` is
34
+ * not a public suffix, so any storefront can set a same-named cookie for the
35
+ * parent domain and fix a buyer (or a staff app's user) into the attacker's
36
+ * session. Only the host itself can set a `__Host-` cookie. The prefix
37
+ * requires `Secure`, so plain-http dev keeps the bare name.
38
+ */
39
+ export declare function hostOnlyCookieName(base: string, secure?: boolean): string;
32
40
  export declare function createShopBoundCookieConfig(opts: {
33
41
  name: string;
34
42
  password: string;
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Storefronts on `<handle>.adorly.mn` run merchant script, and `adorly.mn` is
4
4
  * not a public suffix, so any storefront can set a same-named cookie for the
5
- * parent domain and fix a buyer into the attacker's session. Only the host
6
- * itself can set a `__Host-` cookie. The prefix requires `Secure`, so
7
- * plain-http dev keeps the bare name.
5
+ * parent domain and fix a buyer (or a staff app's user) into the attacker's
6
+ * session. Only the host itself can set a `__Host-` cookie. The prefix
7
+ * requires `Secure`, so plain-http dev keeps the bare name.
8
8
  */
9
- function hostOnlyCookieName(base, secure) {
9
+ function hostOnlyCookieName(base, secure = process.env.NODE_ENV === "production") {
10
10
  return secure ? `__Host-${base}` : base;
11
11
  }
12
12
  function createShopBoundCookieConfig(opts) {
@@ -36,4 +36,4 @@ function validateShopBinding(payload, expectedShopHandle) {
36
36
  return payload;
37
37
  }
38
38
  //#endregion
39
- export { createShopBoundCookieConfig, validateShopBinding };
39
+ export { createShopBoundCookieConfig, hostOnlyCookieName, validateShopBinding };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shop-on/customer-runtime",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "SEE LICENSE IN dist/LICENSE.md",
5
5
  "files": [
6
6
  "dist"
@@ -11,6 +11,7 @@
11
11
  "./graphql": "./dist/graphql.js",
12
12
  "./handoff": "./dist/handoff.js",
13
13
  "./handoff-state": "./dist/handoff-state.js",
14
+ "./internal-api": "./dist/internal-api.js",
14
15
  "./lease": "./dist/lease.js",
15
16
  "./oauth": "./dist/oauth.js",
16
17
  "./relay": "./dist/relay.js",