@fanfare-io/fanfare-sdk-shopify 0.7.0 → 0.7.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/cart-adapter.d.ts +1 -1
- package/dist/checkout-types.d.ts +3 -2
- package/dist/checkout-types.js +1 -1
- package/dist/claimer.d.ts +6 -0
- package/dist/claimer.js +1 -1
- package/dist/react/use-fanfare-checkout.js +1 -1
- package/dist/react/use-fanfare-claim.d.ts +6 -4
- package/dist/react/use-fanfare-claim.js +1 -1
- package/package.json +3 -3
package/dist/cart-adapter.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface FanfareCartAdapter<TCart> {
|
|
|
15
15
|
createCart(): Promise<TCart>;
|
|
16
16
|
/** Add one line to the cart AFTER the gate token is written; returns the updated cart. */
|
|
17
17
|
addLine(cart: TCart, line: GatedLine): Promise<TCart>;
|
|
18
|
-
/** Extract the claim-target cart id and
|
|
18
|
+
/** Extract the claim-target cart id and checkout URL. `gatedCheckout` needs both before claiming. */
|
|
19
19
|
readCart(cart: TCart): {
|
|
20
20
|
id: string;
|
|
21
21
|
checkoutUrl?: string;
|
package/dist/checkout-types.d.ts
CHANGED
|
@@ -56,13 +56,14 @@ export declare function gatedLineToRequestedPurchase(line: GatedLine): ShopifyRe
|
|
|
56
56
|
* add via `retry()`, never re-claim (that double-reserves).
|
|
57
57
|
* - `reservation_expired` — the reservation lapsed mid-add; a fresh `gatedCheckout` is required.
|
|
58
58
|
* - `cart_id_mismatch` — the cart `addLine` returned is not the one claimed (adapter bug).
|
|
59
|
-
* - `checkout_url_missing` —
|
|
59
|
+
* - `checkout_url_missing` — the cart exposed no checkout URL when the claim/redirect needed it (adapter bug).
|
|
60
|
+
* - `checkout_url_mismatch` — the cart checkout URL changed after the claim was signed (adapter bug).
|
|
60
61
|
* - `busy` — a `gatedCheckout` is already in flight; disable the buy button on `pending`.
|
|
61
62
|
*
|
|
62
63
|
* Only `add_line_failed` carries a `retry()`; branch on `retryable` (or the presence
|
|
63
64
|
* of `retry`), never on `code` alone, to decide whether to retry.
|
|
64
65
|
*/
|
|
65
|
-
export type FanfareCheckoutCode = "cart_create_failed" | "no_claim_context" | "cart_not_materialized" | "claim_failed" | "add_line_failed" | "reservation_expired" | "cart_id_mismatch" | "checkout_url_missing" | "busy";
|
|
66
|
+
export type FanfareCheckoutCode = "cart_create_failed" | "no_claim_context" | "cart_not_materialized" | "claim_failed" | "add_line_failed" | "reservation_expired" | "cart_id_mismatch" | "checkout_url_missing" | "checkout_url_mismatch" | "busy";
|
|
66
67
|
/** Which phase the failure came from, for coarse UI/metric bucketing. */
|
|
67
68
|
export type FanfareCheckoutFailureKind = "create_cart" | "claim" | "add_line" | "busy";
|
|
68
69
|
/**
|
package/dist/checkout-types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function t(t,r){const
|
|
1
|
+
function t(t,r){const i="Product"===r?"ProductVariant":"Product";if(t.startsWith(`gid://shopify/${i}/`))throw new Error(`Expected a Shopify ${r} id but received a ${i} GID: ${t}`);return/^[1-9]\d*$/.test(t)?`gid://shopify/${r}/${t}`:t}function r(r){return t(r,"Product")}function i(r){return t(r,"ProductVariant")}function n(t){return{productId:t.shopifyProductId,variantId:t.merchandiseId,quantity:t.quantity}}export{n as gatedLineToRequestedPurchase,r as productGid,i as variantGid};
|
package/dist/claimer.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export interface ShopifyClaimContext {
|
|
|
17
17
|
* that build a claimer directly may instead pass an Ajax cart token incl.
|
|
18
18
|
* `?key=`; the proxy/cart bridge resolves either form to a cart GID.) */
|
|
19
19
|
cartId: string;
|
|
20
|
+
/**
|
|
21
|
+
* The Shopify cart checkout URL. When present, the SDK signs the relayed DPoP
|
|
22
|
+
* proof over this URL so the app proxy can ask the admission authority to
|
|
23
|
+
* verify possession for the eventual checkout target.
|
|
24
|
+
*/
|
|
25
|
+
checkoutUrl?: string;
|
|
20
26
|
/**
|
|
21
27
|
* Optional intended-purchase payload. The cart id names WHERE the gate token is
|
|
22
28
|
* written; this names WHAT is being bought, so the admission authority can apply
|
package/dist/claimer.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
import{buildAdmissionProofHeaders as t}from"@fanfare-io/fanfare-sdk-core/handoff";function e(e){if(!e.proxyBaseUrl||""===e.proxyBaseUrl.trim())throw new Error("createShopifyCheckoutClaimer: proxyBaseUrl is required");const r=`${e.proxyBaseUrl.replace(/\/+$/,"")}/${(e.endpoint??"admissions/claim").replace(/^\/+/,"")}`,a=e.fetchImpl??globalThis.fetch;return{async claim(){try{const o=e.getClaimContext?await e.getClaimContext():null;if(null==o)return{ok:!1,reason:"no_claim_context"};const n=o.checkoutUrl?.trim(),i=""===n?void 0:n;let c=null;if(void 0!==i)try{c=await t({url:i,method:"POST"})}catch{c=null}const l=await a(r,{method:"POST",headers:{"Content-Type":"application/json",...c??{}},body:JSON.stringify({credential:o.credential,distributionId:o.distributionId,cartId:o.cartId,...void 0===i?{}:{checkoutUrl:i},...void 0===o.requestedPurchase?{}:{requestedPurchase:o.requestedPurchase}}),signal:AbortSignal.timeout(e.timeoutMs??1e4)});if(!l.ok){const t=await async function(t){if(409===t.status)try{const e=await t.json();if("cart_not_materialized"===e?.error)return"cart_not_materialized"}catch{}return`proxy_${t.status}`}(l);return{ok:!1,reason:t}}const s=await async function(t){try{const e=await t.json();return"string"==typeof e?.expDate&&e.expDate.length>0?e.expDate:null}catch{return null}}(l);return null===s?{ok:!1,reason:"claim_malformed"}:{ok:!0,expDate:s}}catch{return{ok:!1,reason:"claim_unreachable"}}}}}export{e as createShopifyCheckoutClaimer};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{useState as e,useRef as r,useCallback as t}from"react";import{useFanfareClaim as a}from"./use-fanfare-claim.js";function c(c){const{claim:
|
|
2
|
+
import{useState as e,useRef as r,useCallback as t}from"react";import{canonicalizeAdmissionProofUrl as c}from"@fanfare-io/fanfare-sdk-core/handoff";import{useFanfareClaim as a}from"./use-fanfare-claim.js";function n(e){try{return c(e)}catch{return null}}function o(c){const{claim:o}=a(),[i,l]=e(!1),d=r(c);d.current=c;const u=r(null),k=t(e=>{if(null!==u.current){const e={ok:!1,code:"busy",retryable:!0,kind:"busy"};return Promise.resolve(e)}const r=(async()=>{l(!0);try{return await e()}finally{u.current=null,l(!1)}})();return u.current=r,r},[]),s=t(async(e,r,t,c,a,o)=>{const i=d.current;if(function(e){const r=Date.parse(e);return!Number.isNaN(r)&&Date.now()>r}(o))return{ok:!1,code:"reservation_expired",retryable:!1,kind:"add_line",cart:e,expDate:o};let l=e;for(const[n,d]of r.slice(t).entries()){const e=t+n;try{l=await i.addLine(l,d)}catch{const t=l;return{ok:!1,code:"add_line_failed",retryable:!0,kind:"add_line",failedLineIndex:e,cart:t,expDate:o,retry:()=>k(()=>s(t,r,e,c,a,o))}}}const u=i.readCart(l);return u.id!==c?{ok:!1,code:"cart_id_mismatch",retryable:!1,kind:"add_line",cart:l,expDate:o}:void 0===u.checkoutUrl||""===u.checkoutUrl?{ok:!1,code:"checkout_url_missing",retryable:!1,kind:"add_line",cart:l,expDate:o}:n(u.checkoutUrl)!==n(a)?{ok:!1,code:"checkout_url_mismatch",retryable:!1,kind:"add_line",cart:l,expDate:o}:{ok:!0,cart:l,checkoutUrl:u.checkoutUrl,expDate:o}},[k]);return{gatedCheckout:t(e=>{const{gatedLine:r,ungatedLines:t}=e,c=void 0===t?[r]:[r,...t];return k(async()=>{const e=d.current;let t;try{t=await e.createCart()}catch{return{ok:!1,code:"cart_create_failed",retryable:!0,kind:"create_cart"}}const a=e.readCart(t),i=a.id;if(void 0===a.checkoutUrl||""===a.checkoutUrl||!n(a.checkoutUrl))return{ok:!1,code:"checkout_url_missing",retryable:!1,kind:"claim",cart:t};const l=await o({cartId:i,checkoutUrl:a.checkoutUrl,purchase:r});return l.ok?s(t,c,0,i,a.checkoutUrl,l.expDate):"no_claim_context"===(u=l.reason)?{ok:!1,code:"no_claim_context",retryable:!1,kind:"claim"}:"cart_not_materialized"===u?{ok:!1,code:"cart_not_materialized",retryable:!0,kind:"claim"}:{ok:!1,code:"claim_failed",retryable:!0,kind:"claim"};var u})},[o,k,s]),claim:o,pending:i}}export{o as useFanfareCheckout};
|
|
@@ -17,10 +17,11 @@ export interface UseFanfareClaimResult {
|
|
|
17
17
|
* `no_claim_context` means not-yet-ready). Never rejects — every outcome
|
|
18
18
|
* resolves to a `ClaimResult`.
|
|
19
19
|
*
|
|
20
|
-
* Pass `{ cartId, purchase }` to forward
|
|
21
|
-
* gate, or a bare `cartId` string for a
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Pass `{ cartId, checkoutUrl, purchase }` to forward the cart checkout URL and
|
|
21
|
+
* product/variant/quantity context to the gate, or a bare `cartId` string for a
|
|
22
|
+
* product-less legacy drop (the body omits the purchase and DPoP checkout URL).
|
|
23
|
+
* `useFanfareCheckout().gatedCheckout` is the recommended path that owns the
|
|
24
|
+
* create→claim→add-line ordering for you.
|
|
24
25
|
*/
|
|
25
26
|
claim(input: string | FanfareClaimInput): Promise<ClaimResult>;
|
|
26
27
|
/** `"idle"` → `"claiming"` → `"claimed"` | `"error"`. */
|
|
@@ -33,6 +34,7 @@ export interface UseFanfareClaimResult {
|
|
|
33
34
|
*/
|
|
34
35
|
export interface FanfareClaimInput {
|
|
35
36
|
cartId: string;
|
|
37
|
+
checkoutUrl?: string;
|
|
36
38
|
purchase: GatedLine;
|
|
37
39
|
}
|
|
38
40
|
export declare function useFanfareClaim(): UseFanfareClaimResult;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{useState as r,useRef as t,useMemo as e,useCallback as o}from"react";import{gatedLineToRequestedPurchase as n}from"../checkout-types.js";import{createShopifyCheckoutClaimer as i}from"../claimer.js";import{useShopifyAdapter as c}from"./shopify-provider.js";function s(){const{config:s,setActiveCartId:
|
|
2
|
+
import{useState as r,useRef as t,useMemo as e,useCallback as o}from"react";import{gatedLineToRequestedPurchase as n}from"../checkout-types.js";import{createShopifyCheckoutClaimer as i}from"../claimer.js";import{useShopifyAdapter as c}from"./shopify-provider.js";function s(){const{config:s,setActiveCartId:u,getCheckoutContext:a}=c(),[l,m]=r("idle"),p=t(a);p.current=a;const d=t(void 0),f=t(void 0),v=s.shopify.proxyBaseUrl,y=e(()=>void 0===v||""===v.trim()?null:i({proxyBaseUrl:v,endpoint:"admissions/reserve",getClaimContext:()=>{const r=p.current();if(null===r)return null;const t=d.current,e=f.current;return{...r,...void 0===e?{}:{checkoutUrl:e},...void 0===t?{}:{requestedPurchase:t}}}}),[v]);return{claim:o(async r=>{if(null===y)return m("error"),{ok:!1,reason:"no_claim_context"};const t="string"==typeof r?r:r.cartId;d.current="string"==typeof r?void 0:n(r.purchase),f.current="string"==typeof r?void 0:r.checkoutUrl,u(t),m("claiming");const e=await y.claim();return m(e.ok?"claimed":"error"),e},[y,u]),state:l}}export{s as useFanfareClaim};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanfare-io/fanfare-sdk-shopify",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Shopify storefront adapter for Fanfare SDK: product→experience resolver, checkout verifier, and experienceIds metafield primitives",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": "^18.0.0 || ^19.1.1",
|
|
41
41
|
"react-dom": "^18.0.0 || ^19.1.1",
|
|
42
|
-
"@fanfare-io/fanfare-sdk-core": "0.7.
|
|
42
|
+
"@fanfare-io/fanfare-sdk-core": "0.7.2"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"react": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"vite-plugin-dts": "^4.5.4",
|
|
65
65
|
"vite-tsconfig-paths": "^5.1.4",
|
|
66
66
|
"vitest": "^3.2.4",
|
|
67
|
-
"@fanfare-io/fanfare-sdk-core": "0.7.
|
|
67
|
+
"@fanfare-io/fanfare-sdk-core": "0.7.2"
|
|
68
68
|
},
|
|
69
69
|
"sideEffects": false,
|
|
70
70
|
"keywords": [
|