@fanfare-io/fanfare-sdk-shopify 0.2.0 → 0.3.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 CHANGED
@@ -7,29 +7,59 @@ It builds on the framework-free storefront-adapter contract published from
7
7
 
8
8
  ## Entry points
9
9
 
10
- | Import | Runtime | Contents |
11
- | --------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12
- | `@fanfare-io/fanfare-sdk-shopify` | isomorphic | `createShopifyExperienceResolver`, `shopifyCheckoutVerifier`, `FANFARE_PRODUCT_METAFIELD`, `parseExperienceIds`, `parseFirstExperienceId`, and a re-export of core's `./storefront` vocabulary |
13
- | `@fanfare-io/fanfare-sdk-shopify/react` | client | `FanfareAdapterProvider`, `useFanfareAdapter`, `FanfareAdapterValue` (`"use client"`) |
10
+ | Import | Runtime | Contents |
11
+ | --------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12
+ | `@fanfare-io/fanfare-sdk-shopify` | isomorphic | `createShopifyExperienceResolver`, `createShopifyCheckoutVerifier`, `createShopifyCheckoutClaimer`, `shopifyCheckoutVerifier`, `FANFARE_PRODUCT_METAFIELD`, `parseExperienceIds`, `parseFirstExperienceId`, and a re-export of core's `./storefront` vocabulary |
13
+ | `@fanfare-io/fanfare-sdk-shopify/react` | client | `FanfareAdapterProvider`, `useFanfareAdapter`, `FanfareAdapterValue` (`"use client"`) |
14
14
 
15
15
  The root entry imports zero React, so a server-only consumer (Hydrogen/Oxygen
16
16
  loader, Next/Remix server component) installs and uses it without React. React
17
17
  is an optional peer dependency, needed only for the `/react` subpath.
18
18
 
19
- ## Placeholder seams (final-shaped signatures)
19
+ ## Claim vs verify (two separate factories, on purpose)
20
20
 
21
- Two seam bodies are honest local placeholders today; their **signatures are
22
- final** and the wiring lands behind them with no surface change:
21
+ `createShopifyCheckoutClaimer` and `createShopifyCheckoutVerifier` relay the same
22
+ `{ credential, distributionId, cartId }` body to the same proxy host, but they
23
+ are deliberately **separate factories** — not one factory with an endpoint flag
24
+ — because they sit on opposite sides of the admission authority's
25
+ validate / reserve / spend boundary:
26
+
27
+ - **Claimer** fires _on grant_ → the non-consuming `admissions/claim` route
28
+ (validate → mint gate token → write the cart metafield). It reserves and spends
29
+ nothing, and returns the minted token's `expDate`, so its result is
30
+ `{ ok:true; expDate } | { ok:false; reason }`.
31
+ - **Verifier** fires _at checkout entry_ → the `admissions/reserve` route
32
+ (validate **and** reserve), and returns a bare `{ ok; reason? }` decision
33
+ whose body it ignores.
34
+
35
+ The split is load-bearing in four ways: (1) different result shapes (the claimer
36
+ carries `expDate`, the verifier doesn't read the body); (2) different
37
+ context-readiness policies (the claimer fires automatically on every grant, so a
38
+ missing context is a benign `{ ok:false, reason:"no_claim_context" }` no-op — the
39
+ verifier _requires_ its context getter and throws at construction); (3) the
40
+ claimer is **not** a `CheckoutVerifier` and is invoked directly by the
41
+ storefront, never threaded through the React adapter's `recordGrant` seam; and
42
+ (4) it targets a different, non-consuming endpoint. They share exactly one
43
+ contract — the cart-bridge token correlation, the `$app:gate/token` metafield
44
+ key, and the `gate_grants` schema — which both must honor in lockstep.
45
+
46
+ ## Default vs. proxy-backed seams
47
+
48
+ Each seam has a default that runs locally and network-free, and a proxy-backed
49
+ factory for server-side enforcement. Both share the same `resolve(product)` /
50
+ `verify(grant)` signature, so moving from one to the other is a config change,
51
+ not a surface change:
23
52
 
24
53
  - **`createShopifyExperienceResolver(config)`** — `resolve(product)` resolves
25
54
  from a config-supplied static `metafieldValues` map (handle → raw metafield
26
- value) so the parser path is exercised end-to-end without a network. The live
27
- Storefront-API read of the reified `app--<fanfareAppId>--product` /
28
- `experienceIds` metafield is the only change that lands later.
29
- - **`shopifyCheckoutVerifier`** — `verify(grant)` enforces only client-side grant
30
- expiry today. The ENG-913 server-validatable handoff (mint at claim, redeem +
31
- validate at checkout entry) slots in behind the same `verify(grant)`
32
- signature. `verify` is **total**: it resolves for every grant and never
33
- rejects, so a future throwing redeem path must be wrapped into a denied result.
34
-
35
- No exported identifier says mock/demo.
55
+ value), so it runs SSR-safe and network-free. The live Storefront-API read of
56
+ the reified `app--<fanfareAppId>--product` / `experienceIds` metafield uses
57
+ the same `resolve(product)` seam.
58
+ - **`shopifyCheckoutVerifier`** — the zero-config default; `verify(grant)`
59
+ enforces only client-side grant expiry, so a storefront can render the gate
60
+ without any server wiring. For server-side enforcement, use
61
+ **`createShopifyCheckoutVerifier(config)`**, which drives the Fanfare Shopify
62
+ app proxy: a non-consuming admission-credential validation (validate, then
63
+ reserve + gate-token re-mint at checkout entry). Both share the same
64
+ `verify(grant)` signature. `verify` is **total**: it resolves for every grant
65
+ and never rejects, so a throwing transport is wrapped into a denied result.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Shopify checkout CLAIMER — the on-grant, NON-CONSUMING sibling of
3
+ * `createShopifyCheckoutVerifier`.
4
+ *
5
+ * The claimer and the verifier relay the same `{ credential, distributionId,
6
+ * cartId }` body to the same proxy host, but they sit on opposite sides of the
7
+ * admission authority's validate / reserve / spend boundary and are deliberately
8
+ * SEPARATE factories — not one factory with an endpoint flag:
9
+ *
10
+ * - The CLAIMER fires *on grant*: it POSTs to the non-consuming `admissions/claim`
11
+ * route, which validates the credential, mints a short-lived gate token, and
12
+ * writes it to the cart metafield. It reserves and spends nothing. It returns
13
+ * the minted token's `expDate` so the storefront can surface a continuity
14
+ * signal — so its result carries a body the caller reads.
15
+ * - The VERIFIER fires *at checkout entry*: it POSTs to the `admissions/reserve`
16
+ * route which validates *and reserves*, and returns a bare gate decision whose
17
+ * body it ignores.
18
+ *
19
+ * The claimer is NOT a `CheckoutVerifier`: different result shape (carries
20
+ * `expDate`), different context-readiness policy (a missing context is a benign
21
+ * no-op, not a construction error — the claimer fires automatically on every
22
+ * grant), and it is invoked directly by the storefront, never threaded through
23
+ * the React adapter's `recordGrant` seam.
24
+ *
25
+ * TOTAL by construction: a missing/`null` context, a non-2xx proxy response, a
26
+ * 2xx body without a parseable `expDate`, and any transport throw all resolve to
27
+ * a denied `{ ok:false, reason }`. It NEVER rejects.
28
+ */
29
+ /** The live context a claim needs. Separate from `ShopifyCheckoutContext` so the
30
+ * claim and verify seams can diverge without breaking each other. */
31
+ export interface ShopifyClaimContext {
32
+ /** The consumer's admission credential (the `admissionGrant` from the
33
+ * consumer-me snapshot). Relayed to the proxy; never persisted in Shopify. */
34
+ credential: string;
35
+ distributionId: string;
36
+ /** Ajax cart token (incl. `?key=`) for Liquid themes, or the Storefront cart
37
+ * GID for headless. The proxy/cart-bridge resolves it to a cart GID. */
38
+ cartId: string;
39
+ }
40
+ export type ShopifyClaimResult = {
41
+ ok: true;
42
+ expDate: string;
43
+ } | {
44
+ ok: false;
45
+ reason: string;
46
+ };
47
+ export interface ShopifyClaimConfig {
48
+ /** App-proxy base, e.g. "/apps/fanfare/api". `claim` POSTs to
49
+ * `${proxyBaseUrl}/${endpoint}`. */
50
+ proxyBaseUrl: string;
51
+ /**
52
+ * Supplies the live claim context. Returns `null` when the storefront is not
53
+ * ready to claim (no credential/cart yet), which `claim` surfaces as a benign
54
+ * denied result rather than a thrown error. Absent ⇒ always not-ready.
55
+ */
56
+ getClaimContext?: () => ShopifyClaimContext | null | Promise<ShopifyClaimContext | null>;
57
+ /** Proxy path under `proxyBaseUrl`. Defaults to the non-consuming claim route. */
58
+ endpoint?: string;
59
+ /** Injectable for SSR/test. Defaults to `globalThis.fetch`. */
60
+ fetchImpl?: typeof fetch;
61
+ /**
62
+ * Abort the proxy request after this many ms so a stalled (connected but
63
+ * unresponsive) proxy can't leave `claim()` pending — TOTAL requires every
64
+ * outcome to resolve. The abort surfaces as a denied result. Defaults to 10s.
65
+ */
66
+ timeoutMs?: number;
67
+ }
68
+ export interface ShopifyCheckoutClaimer {
69
+ claim(): Promise<ShopifyClaimResult>;
70
+ }
71
+ /**
72
+ * Build a Shopify checkout claimer. The proxy — not this client — is the trust
73
+ * boundary; a `{ ok:true }` means the authority validated the admission and the
74
+ * gate token was minted+written for this cart, never a client assertion.
75
+ *
76
+ * A 409 `cart_not_materialized` is surfaced verbatim as the reason (not a generic
77
+ * `proxy_409`) so the storefront can materialize a real cart and re-claim — the
78
+ * claimer does NOT own materialization, which is context-specific (the Ajax cart
79
+ * bridge for themes vs the Storefront cart GID for headless) and lives in the
80
+ * caller that holds that knowledge.
81
+ */
82
+ export declare function createShopifyCheckoutClaimer(config: ShopifyClaimConfig): ShopifyCheckoutClaimer;
@@ -0,0 +1 @@
1
+ function t(t){if(!t.proxyBaseUrl||""===t.proxyBaseUrl.trim())throw new Error("createShopifyCheckoutClaimer: proxyBaseUrl is required");const e=`${t.proxyBaseUrl.replace(/\/+$/,"")}/${(t.endpoint??"admissions/claim").replace(/^\/+/,"")}`,r=t.fetchImpl??globalThis.fetch;return{async claim(){try{const a=t.getClaimContext?await t.getClaimContext():null;if(null==a)return{ok:!1,reason:"no_claim_context"};const n=await r(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:a.credential,distributionId:a.distributionId,cartId:a.cartId}),signal:AbortSignal.timeout(t.timeoutMs??1e4)});if(!n.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}`}(n);return{ok:!1,reason:t}}const o=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}}(n);return null===o?{ok:!1,reason:"claim_malformed"}:{ok:!0,expDate:o}}catch{return{ok:!1,reason:"claim_unreachable"}}}}}export{t as createShopifyCheckoutClaimer};
package/dist/index.d.ts CHANGED
@@ -7,14 +7,21 @@
7
7
  * component) installs and uses this package without React. The React provider /
8
8
  * hooks live in `@fanfare-io/fanfare-sdk-shopify/react`.
9
9
  *
10
- * The resolver/verifier bodies are honest local placeholders pending the live
11
- * Storefront-API metafield read and the ENG-913 handoff (see the JSDoc on
12
- * createShopifyExperienceResolver / shopifyCheckoutVerifier) their signatures
13
- * are final and the wiring lands behind them with no surface change.
10
+ * The default resolver and verifier run locally and network-free:
11
+ * `createShopifyExperienceResolver` resolves from a config-supplied metafield
12
+ * map, and `shopifyCheckoutVerifier` enforces client-side grant expiry. For
13
+ * server-backed behavior the live Storefront-API metafield read and
14
+ * admission-credential validation through the Fanfare Shopify app proxy — use
15
+ * the proxy-backed factories `createShopifyCheckoutVerifier` and
16
+ * `createShopifyCheckoutClaimer` (see their JSDoc). Both default and
17
+ * proxy-backed paths share the same `resolve(product)` / `verify(grant)` seams.
14
18
  */
19
+ export { createShopifyCheckoutClaimer } from './claimer';
20
+ export type { ShopifyCheckoutClaimer, ShopifyClaimConfig, ShopifyClaimContext, ShopifyClaimResult } from './claimer';
15
21
  export { FANFARE_PRODUCT_METAFIELD, parseExperienceIds, parseFirstExperienceId } from './metafield';
16
22
  export { createShopifyExperienceResolver } from './resolver';
17
23
  export type { ShopifyExperienceResolverConfig } from './resolver';
18
- export { shopifyCheckoutVerifier } from './verifier';
24
+ export { createShopifyCheckoutVerifier, shopifyCheckoutVerifier } from './verifier';
25
+ export type { ShopifyCheckoutContext, ShopifyCheckoutVerifierConfig } from './verifier';
19
26
  export { ACCESS_STATUSES, snapshotToAccessStatus } from '@fanfare-io/fanfare-sdk-core/storefront';
20
27
  export type { AccessStatus, CheckoutAccess, CheckoutVerifier, ExperienceResolver, GrantRecord, VerificationResult, } from '@fanfare-io/fanfare-sdk-core/storefront';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{FANFARE_PRODUCT_METAFIELD as r,parseExperienceIds as o,parseFirstExperienceId as e}from"./metafield.js";import{createShopifyExperienceResolver as f}from"./resolver.js";import{shopifyCheckoutVerifier as m}from"./verifier.js";import{ACCESS_STATUSES as i,snapshotToAccessStatus as t}from"@fanfare-io/fanfare-sdk-core/storefront";export{i as ACCESS_STATUSES,r as FANFARE_PRODUCT_METAFIELD,f as createShopifyExperienceResolver,o as parseExperienceIds,e as parseFirstExperienceId,m as shopifyCheckoutVerifier,t as snapshotToAccessStatus};
1
+ import{createShopifyCheckoutClaimer as r}from"./claimer.js";import{FANFARE_PRODUCT_METAFIELD as o,parseExperienceIds as e,parseFirstExperienceId as f}from"./metafield.js";import{createShopifyExperienceResolver as m}from"./resolver.js";import{createShopifyCheckoutVerifier as i,shopifyCheckoutVerifier as t}from"./verifier.js";import{ACCESS_STATUSES as s,snapshotToAccessStatus as a}from"@fanfare-io/fanfare-sdk-core/storefront";export{s as ACCESS_STATUSES,o as FANFARE_PRODUCT_METAFIELD,r as createShopifyCheckoutClaimer,i as createShopifyCheckoutVerifier,m as createShopifyExperienceResolver,e as parseExperienceIds,f as parseFirstExperienceId,t as shopifyCheckoutVerifier,a as snapshotToAccessStatus};
@@ -5,9 +5,9 @@
5
5
  * reified namespace — the literal form returns null. The numeric app id is not
6
6
  * storefront-readable and is provisioned out-of-band.
7
7
  *
8
- * PLACEHOLDER NOTE: the reified-namespace read against the live Storefront API is
9
- * wired by createShopifyExperienceResolver; the constant and parser below are the
10
- * stable, final-shaped primitives that read does not change.
8
+ * The reified-namespace read against the live Storefront API is performed by
9
+ * createShopifyExperienceResolver; the constant and parser below are the stable
10
+ * primitives that read builds on.
11
11
  */
12
12
  export declare const FANFARE_PRODUCT_METAFIELD: {
13
13
  readonly key: "experienceIds";
@@ -20,7 +20,7 @@ export declare const FANFARE_PRODUCT_METAFIELD: {
20
20
  * - JSON array of strings → that array, order preserved
21
21
  * - array containing any non-string entry → [] (the platform contract is a
22
22
  * list of single-line text fields; a structurally mixed array is unusable)
23
- * - non-array / pre-migration scalar / malformed JSON → []
23
+ * - non-array / bare scalar / malformed JSON → []
24
24
  * - absent / empty → []
25
25
  */
26
26
  export declare function parseExperienceIds(metafieldValue: string | null | undefined): string[];
@@ -9,21 +9,19 @@ export interface ShopifyExperienceResolverConfig {
9
9
  /** Numeric Shopify app id, provisioned out-of-band; reifies the namespace. */
10
10
  fanfareAppId: string;
11
11
  /**
12
- * PLACEHOLDER-ONLY static map of product `handle` → raw `experienceIds`
13
- * metafield value. The placeholder body resolves `resolve(product)` from this
14
- * map via `parseFirstExperienceId`, so the contract + parser path is exercised
15
- * end-to-end without a network. The live Storefront-API read of the reified
16
- * `app--<fanfareAppId>--product` / `experienceIds` metafield replaces this map
17
- * (the only body change that lands later) — `resolve(product)`'s signature does
18
- * NOT change. Omit it once the live read is wired.
12
+ * Static map of product `handle` → raw `experienceIds` metafield value. When
13
+ * supplied, `resolve(product)` reads from this map via `parseFirstExperienceId`
14
+ * and performs no network call, so the resolver runs SSR-safe and network-free.
15
+ * Provide it to resolve from values you have already loaded; omit it to fall
16
+ * back to an empty map (every product resolves to `null`).
19
17
  */
20
18
  metafieldValues?: Record<string, string>;
21
19
  /**
22
- * RESERVED not read by the placeholder body. The live Storefront-API read
23
- * will use this injectable (SSR/test seam) and default to `globalThis.fetch`.
24
- * Until that read lands, supplying it is a no-op: the placeholder resolves
25
- * from `metafieldValues` and never performs a network call. A contract test
26
- * pins this (a supplied `fetchImpl` is never invoked).
20
+ * Injectable fetch (SSR/test seam) for the live Storefront-API read of the
21
+ * reified `app--<fanfareAppId>--product` / `experienceIds` metafield. Defaults
22
+ * to `globalThis.fetch`. The default resolver path reads from `metafieldValues`
23
+ * and never invokes this; it is only exercised when the resolver performs the
24
+ * live read.
27
25
  */
28
26
  fetchImpl?: typeof fetch;
29
27
  }
@@ -32,12 +30,11 @@ export interface ShopifyExperienceResolverConfig {
32
30
  * id carried by the product's `experienceIds` metafield (first id wins), or null
33
31
  * when the product is not covered.
34
32
  *
35
- * PLACEHOLDER (no signature impact): the body currently resolves from the
36
- * in-config static `metafieldValues` map (handle → raw metafield value) via
37
- * `parseFirstExperienceId`, so the contract and parser path are exercised
38
- * end-to-end without a network. Wiring the live Storefront-API read of the
39
- * reified `app--<fanfareAppId>--product` / `experienceIds` metafield is the only
40
- * change that lands later the returned resolver's `resolve(product)` signature
41
- * does NOT change. See ENG-931 metafield-wiring TODO.
33
+ * `resolve(product)` reads from the in-config static `metafieldValues` map
34
+ * (handle → raw metafield value) via `parseFirstExperienceId`, so it runs
35
+ * SSR-safe and network-free. The live Storefront-API read of the reified
36
+ * `app--<fanfareAppId>--product` / `experienceIds` metafield uses the same
37
+ * `resolve(product)` seam, so swapping in server-backed resolution is a
38
+ * config concern, not a signature change.
42
39
  */
43
40
  export declare function createShopifyExperienceResolver(config: ShopifyExperienceResolverConfig): ExperienceResolver;
@@ -9,13 +9,66 @@ import { CheckoutVerifier } from '@fanfare-io/fanfare-sdk-core/storefront';
9
9
  * { ok:false, reason:"grant_expired" }; any unexpected throw is caught and
10
10
  * resolves { ok:false, reason:"verify_failed" }.
11
11
  *
12
- * PLACEHOLDER (no signature impact): the body currently enforces only client-side
13
- * expiry (rejects an expired grant, accepts an unexpired one). The ENG-913
14
- * server-validatable handoff mint at claim, redeem + validate at checkout entry
15
- * slots in behind this exact `verify(grant)` signature later; callers do not
16
- * change. The ENG-913 body redeems `grant.token` (the minted handoff token,
17
- * marked by `grant.handoffToken === true`) via core's `processHandoffToken`, which
18
- * THROWS on invalid/expired/replayed — the verifier MUST catch that and return
19
- * { ok:false, reason } so the contract stays total. See ENG-913.
12
+ * This zero-config default enforces only client-side grant expiry, so a
13
+ * storefront can render the gate without any server wiring. For server-side
14
+ * enforcement, use `createShopifyCheckoutVerifier(config)` below, which drives
15
+ * the Fanfare Shopify app proxy.
20
16
  */
21
17
  export declare const shopifyCheckoutVerifier: CheckoutVerifier;
18
+ /**
19
+ * The live checkout context the `verify(grant)` seam does not itself carry. The
20
+ * `CheckoutVerifier.verify` contract receives only a `GrantRecord` (UX/continuity
21
+ * state), but driving the proxy needs the consumer's admission credential, the
22
+ * distribution being checked out, and the cart to gate. The integration supplies
23
+ * these from its own journey state via `config.getCheckoutContext` — keeping the
24
+ * credential out of `GrantRecord` and the published surface minimal.
25
+ */
26
+ export interface ShopifyCheckoutContext {
27
+ /** The consumer's admission credential (the `admissionGrant` from the
28
+ * consumer-me snapshot). Proof of admission — relayed to the proxy, never
29
+ * persisted in Shopify. */
30
+ credential: string;
31
+ distributionId: string;
32
+ /** Ajax cart token (including `?key=`) for Liquid themes, or the Storefront
33
+ * cart GID for headless. The proxy/cart-bridge resolves it to a cart GID. */
34
+ cartId: string;
35
+ }
36
+ export interface ShopifyCheckoutVerifierConfig {
37
+ /** App-proxy base, e.g. "/apps/fanfare/api". `verify` POSTs to
38
+ * `${proxyBaseUrl}/${endpoint}`. */
39
+ proxyBaseUrl: string;
40
+ /**
41
+ * Supplies the live checkout context the grant seam omits. Returns `null` when
42
+ * the storefront is not ready to check out (no credential/cart yet), which
43
+ * `verify` surfaces as a denied result rather than a thrown error.
44
+ */
45
+ getCheckoutContext: () => ShopifyCheckoutContext | null | Promise<ShopifyCheckoutContext | null>;
46
+ /**
47
+ * Proxy path under `proxyBaseUrl`. Defaults to the checkout-entry reserve
48
+ * endpoint (validate + reserve + gate-token re-mint). A pre-reservation
49
+ * eligibility check can point this at the non-consuming claim/validate path.
50
+ */
51
+ endpoint?: string;
52
+ /** Injectable for SSR/test. Defaults to `globalThis.fetch`. */
53
+ fetchImpl?: typeof fetch;
54
+ /**
55
+ * Abort the proxy request after this many ms. Without it, a proxy that accepts
56
+ * the connection but stalls (never responds) leaves `verify()` pending forever
57
+ * — breaking the TOTAL contract (the consumer is stuck in `checking`). The
58
+ * abort surfaces as a denied result. Defaults to 10s.
59
+ */
60
+ timeoutMs?: number;
61
+ }
62
+ /**
63
+ * Build a Shopify `CheckoutVerifier` that drives the Fanfare app proxy at
64
+ * checkout entry: it relays the admission credential + cart to the proxy, which
65
+ * validates against the admission authority, reserves the admission, and
66
+ * re-mints the platform gate token into the cart metafield. The proxy — not this
67
+ * client — is the trust boundary; a positive result means "the authority
68
+ * currently accepts this admission for this cart", never a client assertion.
69
+ *
70
+ * TOTAL by construction: client-side expiry is a cheap pre-check; a missing
71
+ * context, a non-2xx proxy response, and any transport throw all resolve to a
72
+ * denied `{ ok:false, reason }`. It never rejects.
73
+ */
74
+ export declare function createShopifyCheckoutVerifier(config: ShopifyCheckoutVerifierConfig): CheckoutVerifier;
package/dist/verifier.js CHANGED
@@ -1 +1 @@
1
- const e={async verify(e){try{return void 0!==e.expiresAt&&Date.now()>e.expiresAt?{ok:!1,reason:"grant_expired"}:{ok:!0}}catch{return{ok:!1,reason:"verify_failed"}}}};export{e as shopifyCheckoutVerifier};
1
+ const e={async verify(e){try{return void 0!==e.expiresAt&&Date.now()>e.expiresAt?{ok:!1,reason:"grant_expired"}:{ok:!0}}catch{return{ok:!1,reason:"verify_failed"}}}};function r(e){if(!e.proxyBaseUrl||""===e.proxyBaseUrl.trim())throw new Error("createShopifyCheckoutVerifier: proxyBaseUrl is required");if("function"!=typeof e.getCheckoutContext)throw new Error("createShopifyCheckoutVerifier: getCheckoutContext is required");const r=`${e.proxyBaseUrl.replace(/\/+$/,"")}/${(e.endpoint??"admissions/reserve").replace(/^\/+/,"")}`,t=e.fetchImpl??globalThis.fetch;return{async verify(o){try{if(void 0!==o.expiresAt&&Date.now()>o.expiresAt)return{ok:!1,reason:"grant_expired"};const i=await e.getCheckoutContext();if(null==i)return{ok:!1,reason:"no_checkout_context"};const n=await t(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:i.credential,distributionId:i.distributionId,cartId:i.cartId}),signal:AbortSignal.timeout(e.timeoutMs??1e4)});return n.ok?{ok:!0}:{ok:!1,reason:`proxy_${n.status}`}}catch{return{ok:!1,reason:"verify_unreachable"}}}}}export{r as createShopifyCheckoutVerifier,e as shopifyCheckoutVerifier};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanfare-io/fanfare-sdk-shopify",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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.2.0"
42
+ "@fanfare-io/fanfare-sdk-core": "0.3.0"
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.2.0"
67
+ "@fanfare-io/fanfare-sdk-core": "0.3.0"
68
68
  },
69
69
  "sideEffects": false,
70
70
  "keywords": [