@snowcone-app/sdk 0.3.2 → 0.3.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#268](https://github.com/snowcone-app/snowcone-monorepo/pull/268) [`dd90eba`](https://github.com/snowcone-app/snowcone-monorepo/commit/dd90eba8bbfbbaef82379cb997af84e21f7cad8f) Thanks [@kevinsproles](https://github.com/kevinsproles)! - fix(realtime): fail fast when a color/variant product is missing `variantId`
8
+
9
+ `RenderSession` now rejects a product that has a real variant axis (its
10
+ `options.combinations` expose 2+ distinct `variantId`s) but no valid `variantId`,
11
+ surfacing an immediate, actionable error at config/`connect()` time instead of
12
+ hanging silently for ~30s until the watchdog. Pass the catalog product's
13
+ `options` on `RenderProduct` to opt in. Single-variant / no-option products are
14
+ unaffected and still render with no `variantId`. The watchdog remains as the
15
+ backstop. New exports: `assertVariantSelected`, `RenderProductOptions`,
16
+ `RenderProductCombination`.
17
+
3
18
  ## 0.3.2
4
19
 
5
20
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -65,6 +65,7 @@ __export(index_exports, {
65
65
  UniversalContextProvider: () => UniversalContextProvider,
66
66
  VueAdapter: () => VueAdapter,
67
67
  adapterRegistry: () => adapterRegistry,
68
+ assertVariantSelected: () => assertVariantSelected,
68
69
  autoRegister: () => autoRegister,
69
70
  buildMockupUrl: () => buildMockupUrl2,
70
71
  componentRegistry: () => componentRegistry,
@@ -5523,6 +5524,30 @@ async function fetchRealtimeGrant(grantUrl, shop, fetchImpl) {
5523
5524
  }
5524
5525
 
5525
5526
  // src/realtime/session.ts
5527
+ function variantIdsOf(product) {
5528
+ const combos = product.options?.combinations;
5529
+ if (!combos?.length) return [];
5530
+ const ids = /* @__PURE__ */ new Set();
5531
+ for (const c of combos) {
5532
+ if (typeof c.variantId === "string" && c.variantId !== "") ids.add(c.variantId);
5533
+ }
5534
+ return [...ids];
5535
+ }
5536
+ function assertVariantSelected(product) {
5537
+ const variantIds = variantIdsOf(product);
5538
+ if (variantIds.length < 2) return;
5539
+ const fix = `pass product.variantId from options.combinations[].variantId (valid ids: ${variantIds.join(", ")})`;
5540
+ if (!product.variantId) {
5541
+ throw new Error(
5542
+ `RenderSession: product ${product.productId} requires a variantId \u2014 ${fix}`
5543
+ );
5544
+ }
5545
+ if (!variantIds.includes(product.variantId)) {
5546
+ throw new Error(
5547
+ `RenderSession: product ${product.productId} has an invalid variantId "${product.variantId}" \u2014 ${fix}`
5548
+ );
5549
+ }
5550
+ }
5526
5551
  var DEFAULT_RENDER_TIMEOUT_MS = 3e4;
5527
5552
  var RenderSession = class {
5528
5553
  svc;
@@ -5538,6 +5563,7 @@ var RenderSession = class {
5538
5563
  if (!opts.getToken && !opts.grantUrl) {
5539
5564
  throw new Error("RenderSession: provide `grantUrl` or `getToken` to authorize the session");
5540
5565
  }
5566
+ if (opts.product) assertVariantSelected(opts.product);
5541
5567
  this.opts = opts;
5542
5568
  this.product = opts.product ?? null;
5543
5569
  this.renderTimeoutMs = opts.renderTimeoutMs ?? DEFAULT_RENDER_TIMEOUT_MS;
@@ -5557,6 +5583,7 @@ var RenderSession = class {
5557
5583
  }
5558
5584
  /** Set or update the product. Sends config immediately if already connected. */
5559
5585
  setProduct(product) {
5586
+ assertVariantSelected(product);
5560
5587
  this.product = product;
5561
5588
  this.svc.sendConfig(this.toConfig(product));
5562
5589
  }
@@ -5572,6 +5599,13 @@ var RenderSession = class {
5572
5599
  );
5573
5600
  }
5574
5601
  const product = this.product;
5602
+ try {
5603
+ assertVariantSelected(product);
5604
+ } catch (err) {
5605
+ const message = err instanceof Error ? err.message : String(err);
5606
+ this.errorCb?.(message);
5607
+ return Promise.reject(err instanceof Error ? err : new Error(message));
5608
+ }
5575
5609
  this.ready = new Promise((resolve, reject) => {
5576
5610
  let settled = false;
5577
5611
  this.svc.setCallbacks({
@@ -5831,6 +5865,7 @@ function createClient(config) {
5831
5865
  UniversalContextProvider,
5832
5866
  VueAdapter,
5833
5867
  adapterRegistry,
5868
+ assertVariantSelected,
5834
5869
  autoRegister,
5835
5870
  buildMockupUrl,
5836
5871
  componentRegistry,
package/dist/index.d.cts CHANGED
@@ -2604,6 +2604,29 @@ interface RenderState {
2604
2604
  elements?: Array<Record<string, unknown>>;
2605
2605
  [key: string]: unknown;
2606
2606
  }
2607
+ /**
2608
+ * A single variant combination from the catalog product's `options.combinations`.
2609
+ * Each combination pins one value per variant-affecting attribute plus the
2610
+ * `variantId` (gvid) that selection resolves to. Pass `options` on
2611
+ * {@link RenderProduct} so the session can FAIL FAST when a required `variantId`
2612
+ * is missing instead of silently hanging until the watchdog.
2613
+ */
2614
+ interface RenderProductCombination {
2615
+ /** The variant (gvid) this combination resolves to. */
2616
+ variantId?: string;
2617
+ /** Attribute selections (e.g. `{ Frame: 'Walnut', Size: '5x7' }`). */
2618
+ [attribute: string]: string | number | undefined;
2619
+ }
2620
+ /**
2621
+ * The catalog product's `options` block (structurally typed). Only
2622
+ * `combinations` is consumed by the session — to detect whether the product has
2623
+ * a real variant axis (multiple distinct `variantId`s) so a missing/invalid
2624
+ * {@link RenderProduct.variantId} can be rejected up front.
2625
+ */
2626
+ interface RenderProductOptions {
2627
+ combinations?: RenderProductCombination[];
2628
+ [key: string]: unknown;
2629
+ }
2607
2630
  /** Product the session renders against (Snowcone catalog ids). */
2608
2631
  interface RenderProduct {
2609
2632
  productId: string;
@@ -2618,13 +2641,34 @@ interface RenderProduct {
2618
2641
  * Optional variant (gvid). Omit if the product has no variant axis. Required
2619
2642
  * for products with a color option — it auto-fills the color placement; without
2620
2643
  * it the render waits for a color blob that never comes.
2644
+ *
2645
+ * If you also pass {@link RenderProduct.options}, the session validates this
2646
+ * up front: a product whose `options.combinations` exposes more than one
2647
+ * variant REQUIRES a `variantId` (and it must be one of the combinations'),
2648
+ * else the session throws immediately rather than hanging until the watchdog.
2621
2649
  */
2622
2650
  variantId?: string;
2651
+ /**
2652
+ * The catalog product's `options` block (its `options.combinations[].variantId`
2653
+ * are the valid variant ids). Optional, but passing it lets the session detect
2654
+ * a missing/invalid required `variantId` and FAIL FAST — turning the classic
2655
+ * 30s silent hang into an immediate, actionable error. You can pass the whole
2656
+ * catalog product's `options` object verbatim.
2657
+ */
2658
+ options?: RenderProductOptions;
2623
2659
  /** Render width in px (default 1000). */
2624
2660
  width?: number;
2625
2661
  /** Per-placement settings (tiling, scale mode). */
2626
2662
  placementSettings?: Record<string, PlacementSettings>;
2627
2663
  }
2664
+ /**
2665
+ * Throws immediately if the product has a variant axis (its
2666
+ * `options.combinations` expose 2+ distinct `variantId`s) but no valid
2667
+ * `variantId` was supplied. This is the fail-fast guard for the otherwise-silent
2668
+ * 30s render hang on color/variant products. Products with no `options` (or a
2669
+ * single variant) are left untouched — they render fine with no `variantId`.
2670
+ */
2671
+ declare function assertVariantSelected(product: RenderProduct): void;
2628
2672
  interface RenderSessionOptions {
2629
2673
  /** Publishable shop id (= shop.id, like Stripe pk_). */
2630
2674
  shop: string;
@@ -2819,4 +2863,4 @@ declare function getProduct(idOrSlug: string, config?: Partial<SdkConfig>): Prom
2819
2863
  */
2820
2864
  declare function createClient(config: SdkConfig): SnowconeClient;
2821
2865
 
2822
- export { AbstractTemplateRenderer, type AddToCartOptions, Animations, type ArtSelectorContext, type ArtSelectorDescriptor, type ArtSelectorOptions, type ArtworkData, type AspectRatio, Attributes, type BaseDescriptor, type BlendConfig, type BuildMockupUrlConfig, type CartDetail, type CatalogListResponse, type CatalogProduct$2 as CatalogProduct, ClassNames, type ColorDesignElement, ColorPickerUtils, Combination, CommonProps, ComponentContext, type ComponentDefinition, ComponentDescriptor, ComponentEventManager, ComponentFactory, ComponentLifecycle, ComponentLifecycleHooks, type ComponentMetadata, ComponentProps, ComponentRegistry, ComponentState, type ComponentTemplate, ComponentTemplates, type ContainerDescriptor, ContextBridge, type ContextComparator, type ContextConsumer, ContextInjector, type ContextProviderConfig, type ContextSubscriber, ContextSynchronizer, type CreateDesignStateInput, type CreatedDesignState, DEFAULT_ARTWORK_URL, DEFAULT_ASPECT_RATIO, DEFAULT_COLOR, DEFAULT_GRANT_BASE, DEFAULT_MOCKUP_BASE, DEFAULT_PLACEMENT_DIMENSIONS, type Descriptor, type Design, type DesignElement, type DesignGenerationContext, type Effects, Elements, type ErrorContext, type ErrorHandler, ErrorManager, type EventDefinition, EventDelegator, EventEmitter, EventHandler, type EventListener, type EventPayload, type Fill, Focus, FrameworkAdapter, FrameworkUtilities, type GetMockupUrlOptions, type ImageAlignment, type ImageDesignElement, type LifecycleHook, LifecycleManager, type LifecyclePhase, type LifecycleTransition, LitAdapter, type MaskOverride, type MockupGenerationOptions, MockupResult, type MockupService, type MockupServiceConfig, OptionAttribute, type OptionChoice, type OptionChoiceRenderData, type OptionRenderData, OptionSelection, type Placement, type PlacementDesign, PlacementSettings, type ProductArtAlignmentContext, type ProductArtAlignmentDescriptor, type ProductArtAlignmentOptions, type ProductContext, type ProductContextEvents, ProductContextManager, type ProductData, type ProductFetcher, type ProductImageDescriptor, type ProductImageOptions, ProductLoader, type ProductMockupData, type ProductOptionChoice, type ProductOptionData, type ProductOptionsDescriptor, type ProductOptionsOptions, type ProductPlacement, type ProductPriceDescriptor, type ProductPriceOptions, ProductProps, type ProductSpec, type ProductTitleDescriptor, type ProductTitleOptions, type ProductVariant, type PropDefinition, type PropSchema, type PropType, PropertyManager, REALTIME_WS_URL, type RateLimitState, type RealtimeGrant, RealtimeMockupService, type RegularArtwork, type RenderProduct, RenderSession, type RenderSessionOptions, type RenderState, type SdkConfig, type SeamlessPattern, type SignedUrlResponse, type SnowconeClient, StandardComponents, StandardEvents, StateManager, type StateManagerOptions, type StateMiddleware, type StateSelector, type StateSubscriber, type StateUpdater, Styles, SvelteAdapter, SwatchUtils, type TagName, TemplateBuilder, type TemplateNode, type TemplateNodeType, type TemplateRenderer, type TextDescriptor, type TileCount, UniversalContextProvider, type UserSelection, type ValidationError, type ValidationResult, VueAdapter, autoRegister, buildMockupUrl, componentRegistry, createAddToCartEvent, createAddToCartHandler, createCartDetail, createClient, createContextProvider, createDesignForPlacements, createDesignState, createErrorHandler, createEventManager, createLifecycle, createLitComponent, createProductContext, createProductLoader, createPropertyManager, createStateHook, createStateStore, createSvelteComponent, createUniversalProvider, createVueComponent, describeArtSelector, describeProductArtAlignment, describeProductImage, describeProductOptions, describeProductPrice, describeProductTitle, extractProductId, fetchRealtimeGrant, filterImagePlacements, findClosestSnapPoint, findVariantForSelection, formatPrice, formatValidationErrors, getDefaultVariantId, getEffectiveAlignment, getIncompleteSelectionMessage, getMissingSelections, getMockupUrl, getOptionRenderType, getProduct, getSelectionDisplayText, getSnapPoints, getVariant, handleOptionChange, isSelectionComplete, isValidAlignment, isValidTileCount, listProducts, mintRealtimeGrant, mockupUrl, normalizeChoice, prepareOptionRenderData, registerStandardComponents, resolveMockupId, resolveVariantId, retryOperation, simulateCartOperation, toCombinations, toOptionAttributes, useFrameworkAdapter as useVueAdapter, validateAlignment, validateDesignElement, validateEffects, validateImageUrl, validateMockupOptions, validateProductSelection, validateProductSpec, validateRequiredOptions, validateTileCount, withContext, withErrorHandling, withSyncErrorHandling };
2866
+ export { AbstractTemplateRenderer, type AddToCartOptions, Animations, type ArtSelectorContext, type ArtSelectorDescriptor, type ArtSelectorOptions, type ArtworkData, type AspectRatio, Attributes, type BaseDescriptor, type BlendConfig, type BuildMockupUrlConfig, type CartDetail, type CatalogListResponse, type CatalogProduct$2 as CatalogProduct, ClassNames, type ColorDesignElement, ColorPickerUtils, Combination, CommonProps, ComponentContext, type ComponentDefinition, ComponentDescriptor, ComponentEventManager, ComponentFactory, ComponentLifecycle, ComponentLifecycleHooks, type ComponentMetadata, ComponentProps, ComponentRegistry, ComponentState, type ComponentTemplate, ComponentTemplates, type ContainerDescriptor, ContextBridge, type ContextComparator, type ContextConsumer, ContextInjector, type ContextProviderConfig, type ContextSubscriber, ContextSynchronizer, type CreateDesignStateInput, type CreatedDesignState, DEFAULT_ARTWORK_URL, DEFAULT_ASPECT_RATIO, DEFAULT_COLOR, DEFAULT_GRANT_BASE, DEFAULT_MOCKUP_BASE, DEFAULT_PLACEMENT_DIMENSIONS, type Descriptor, type Design, type DesignElement, type DesignGenerationContext, type Effects, Elements, type ErrorContext, type ErrorHandler, ErrorManager, type EventDefinition, EventDelegator, EventEmitter, EventHandler, type EventListener, type EventPayload, type Fill, Focus, FrameworkAdapter, FrameworkUtilities, type GetMockupUrlOptions, type ImageAlignment, type ImageDesignElement, type LifecycleHook, LifecycleManager, type LifecyclePhase, type LifecycleTransition, LitAdapter, type MaskOverride, type MockupGenerationOptions, MockupResult, type MockupService, type MockupServiceConfig, OptionAttribute, type OptionChoice, type OptionChoiceRenderData, type OptionRenderData, OptionSelection, type Placement, type PlacementDesign, PlacementSettings, type ProductArtAlignmentContext, type ProductArtAlignmentDescriptor, type ProductArtAlignmentOptions, type ProductContext, type ProductContextEvents, ProductContextManager, type ProductData, type ProductFetcher, type ProductImageDescriptor, type ProductImageOptions, ProductLoader, type ProductMockupData, type ProductOptionChoice, type ProductOptionData, type ProductOptionsDescriptor, type ProductOptionsOptions, type ProductPlacement, type ProductPriceDescriptor, type ProductPriceOptions, ProductProps, type ProductSpec, type ProductTitleDescriptor, type ProductTitleOptions, type ProductVariant, type PropDefinition, type PropSchema, type PropType, PropertyManager, REALTIME_WS_URL, type RateLimitState, type RealtimeGrant, RealtimeMockupService, type RegularArtwork, type RenderProduct, type RenderProductCombination, type RenderProductOptions, RenderSession, type RenderSessionOptions, type RenderState, type SdkConfig, type SeamlessPattern, type SignedUrlResponse, type SnowconeClient, StandardComponents, StandardEvents, StateManager, type StateManagerOptions, type StateMiddleware, type StateSelector, type StateSubscriber, type StateUpdater, Styles, SvelteAdapter, SwatchUtils, type TagName, TemplateBuilder, type TemplateNode, type TemplateNodeType, type TemplateRenderer, type TextDescriptor, type TileCount, UniversalContextProvider, type UserSelection, type ValidationError, type ValidationResult, VueAdapter, assertVariantSelected, autoRegister, buildMockupUrl, componentRegistry, createAddToCartEvent, createAddToCartHandler, createCartDetail, createClient, createContextProvider, createDesignForPlacements, createDesignState, createErrorHandler, createEventManager, createLifecycle, createLitComponent, createProductContext, createProductLoader, createPropertyManager, createStateHook, createStateStore, createSvelteComponent, createUniversalProvider, createVueComponent, describeArtSelector, describeProductArtAlignment, describeProductImage, describeProductOptions, describeProductPrice, describeProductTitle, extractProductId, fetchRealtimeGrant, filterImagePlacements, findClosestSnapPoint, findVariantForSelection, formatPrice, formatValidationErrors, getDefaultVariantId, getEffectiveAlignment, getIncompleteSelectionMessage, getMissingSelections, getMockupUrl, getOptionRenderType, getProduct, getSelectionDisplayText, getSnapPoints, getVariant, handleOptionChange, isSelectionComplete, isValidAlignment, isValidTileCount, listProducts, mintRealtimeGrant, mockupUrl, normalizeChoice, prepareOptionRenderData, registerStandardComponents, resolveMockupId, resolveVariantId, retryOperation, simulateCartOperation, toCombinations, toOptionAttributes, useFrameworkAdapter as useVueAdapter, validateAlignment, validateDesignElement, validateEffects, validateImageUrl, validateMockupOptions, validateProductSelection, validateProductSpec, validateRequiredOptions, validateTileCount, withContext, withErrorHandling, withSyncErrorHandling };
package/dist/index.d.ts CHANGED
@@ -2604,6 +2604,29 @@ interface RenderState {
2604
2604
  elements?: Array<Record<string, unknown>>;
2605
2605
  [key: string]: unknown;
2606
2606
  }
2607
+ /**
2608
+ * A single variant combination from the catalog product's `options.combinations`.
2609
+ * Each combination pins one value per variant-affecting attribute plus the
2610
+ * `variantId` (gvid) that selection resolves to. Pass `options` on
2611
+ * {@link RenderProduct} so the session can FAIL FAST when a required `variantId`
2612
+ * is missing instead of silently hanging until the watchdog.
2613
+ */
2614
+ interface RenderProductCombination {
2615
+ /** The variant (gvid) this combination resolves to. */
2616
+ variantId?: string;
2617
+ /** Attribute selections (e.g. `{ Frame: 'Walnut', Size: '5x7' }`). */
2618
+ [attribute: string]: string | number | undefined;
2619
+ }
2620
+ /**
2621
+ * The catalog product's `options` block (structurally typed). Only
2622
+ * `combinations` is consumed by the session — to detect whether the product has
2623
+ * a real variant axis (multiple distinct `variantId`s) so a missing/invalid
2624
+ * {@link RenderProduct.variantId} can be rejected up front.
2625
+ */
2626
+ interface RenderProductOptions {
2627
+ combinations?: RenderProductCombination[];
2628
+ [key: string]: unknown;
2629
+ }
2607
2630
  /** Product the session renders against (Snowcone catalog ids). */
2608
2631
  interface RenderProduct {
2609
2632
  productId: string;
@@ -2618,13 +2641,34 @@ interface RenderProduct {
2618
2641
  * Optional variant (gvid). Omit if the product has no variant axis. Required
2619
2642
  * for products with a color option — it auto-fills the color placement; without
2620
2643
  * it the render waits for a color blob that never comes.
2644
+ *
2645
+ * If you also pass {@link RenderProduct.options}, the session validates this
2646
+ * up front: a product whose `options.combinations` exposes more than one
2647
+ * variant REQUIRES a `variantId` (and it must be one of the combinations'),
2648
+ * else the session throws immediately rather than hanging until the watchdog.
2621
2649
  */
2622
2650
  variantId?: string;
2651
+ /**
2652
+ * The catalog product's `options` block (its `options.combinations[].variantId`
2653
+ * are the valid variant ids). Optional, but passing it lets the session detect
2654
+ * a missing/invalid required `variantId` and FAIL FAST — turning the classic
2655
+ * 30s silent hang into an immediate, actionable error. You can pass the whole
2656
+ * catalog product's `options` object verbatim.
2657
+ */
2658
+ options?: RenderProductOptions;
2623
2659
  /** Render width in px (default 1000). */
2624
2660
  width?: number;
2625
2661
  /** Per-placement settings (tiling, scale mode). */
2626
2662
  placementSettings?: Record<string, PlacementSettings>;
2627
2663
  }
2664
+ /**
2665
+ * Throws immediately if the product has a variant axis (its
2666
+ * `options.combinations` expose 2+ distinct `variantId`s) but no valid
2667
+ * `variantId` was supplied. This is the fail-fast guard for the otherwise-silent
2668
+ * 30s render hang on color/variant products. Products with no `options` (or a
2669
+ * single variant) are left untouched — they render fine with no `variantId`.
2670
+ */
2671
+ declare function assertVariantSelected(product: RenderProduct): void;
2628
2672
  interface RenderSessionOptions {
2629
2673
  /** Publishable shop id (= shop.id, like Stripe pk_). */
2630
2674
  shop: string;
@@ -2819,4 +2863,4 @@ declare function getProduct(idOrSlug: string, config?: Partial<SdkConfig>): Prom
2819
2863
  */
2820
2864
  declare function createClient(config: SdkConfig): SnowconeClient;
2821
2865
 
2822
- export { AbstractTemplateRenderer, type AddToCartOptions, Animations, type ArtSelectorContext, type ArtSelectorDescriptor, type ArtSelectorOptions, type ArtworkData, type AspectRatio, Attributes, type BaseDescriptor, type BlendConfig, type BuildMockupUrlConfig, type CartDetail, type CatalogListResponse, type CatalogProduct$2 as CatalogProduct, ClassNames, type ColorDesignElement, ColorPickerUtils, Combination, CommonProps, ComponentContext, type ComponentDefinition, ComponentDescriptor, ComponentEventManager, ComponentFactory, ComponentLifecycle, ComponentLifecycleHooks, type ComponentMetadata, ComponentProps, ComponentRegistry, ComponentState, type ComponentTemplate, ComponentTemplates, type ContainerDescriptor, ContextBridge, type ContextComparator, type ContextConsumer, ContextInjector, type ContextProviderConfig, type ContextSubscriber, ContextSynchronizer, type CreateDesignStateInput, type CreatedDesignState, DEFAULT_ARTWORK_URL, DEFAULT_ASPECT_RATIO, DEFAULT_COLOR, DEFAULT_GRANT_BASE, DEFAULT_MOCKUP_BASE, DEFAULT_PLACEMENT_DIMENSIONS, type Descriptor, type Design, type DesignElement, type DesignGenerationContext, type Effects, Elements, type ErrorContext, type ErrorHandler, ErrorManager, type EventDefinition, EventDelegator, EventEmitter, EventHandler, type EventListener, type EventPayload, type Fill, Focus, FrameworkAdapter, FrameworkUtilities, type GetMockupUrlOptions, type ImageAlignment, type ImageDesignElement, type LifecycleHook, LifecycleManager, type LifecyclePhase, type LifecycleTransition, LitAdapter, type MaskOverride, type MockupGenerationOptions, MockupResult, type MockupService, type MockupServiceConfig, OptionAttribute, type OptionChoice, type OptionChoiceRenderData, type OptionRenderData, OptionSelection, type Placement, type PlacementDesign, PlacementSettings, type ProductArtAlignmentContext, type ProductArtAlignmentDescriptor, type ProductArtAlignmentOptions, type ProductContext, type ProductContextEvents, ProductContextManager, type ProductData, type ProductFetcher, type ProductImageDescriptor, type ProductImageOptions, ProductLoader, type ProductMockupData, type ProductOptionChoice, type ProductOptionData, type ProductOptionsDescriptor, type ProductOptionsOptions, type ProductPlacement, type ProductPriceDescriptor, type ProductPriceOptions, ProductProps, type ProductSpec, type ProductTitleDescriptor, type ProductTitleOptions, type ProductVariant, type PropDefinition, type PropSchema, type PropType, PropertyManager, REALTIME_WS_URL, type RateLimitState, type RealtimeGrant, RealtimeMockupService, type RegularArtwork, type RenderProduct, RenderSession, type RenderSessionOptions, type RenderState, type SdkConfig, type SeamlessPattern, type SignedUrlResponse, type SnowconeClient, StandardComponents, StandardEvents, StateManager, type StateManagerOptions, type StateMiddleware, type StateSelector, type StateSubscriber, type StateUpdater, Styles, SvelteAdapter, SwatchUtils, type TagName, TemplateBuilder, type TemplateNode, type TemplateNodeType, type TemplateRenderer, type TextDescriptor, type TileCount, UniversalContextProvider, type UserSelection, type ValidationError, type ValidationResult, VueAdapter, autoRegister, buildMockupUrl, componentRegistry, createAddToCartEvent, createAddToCartHandler, createCartDetail, createClient, createContextProvider, createDesignForPlacements, createDesignState, createErrorHandler, createEventManager, createLifecycle, createLitComponent, createProductContext, createProductLoader, createPropertyManager, createStateHook, createStateStore, createSvelteComponent, createUniversalProvider, createVueComponent, describeArtSelector, describeProductArtAlignment, describeProductImage, describeProductOptions, describeProductPrice, describeProductTitle, extractProductId, fetchRealtimeGrant, filterImagePlacements, findClosestSnapPoint, findVariantForSelection, formatPrice, formatValidationErrors, getDefaultVariantId, getEffectiveAlignment, getIncompleteSelectionMessage, getMissingSelections, getMockupUrl, getOptionRenderType, getProduct, getSelectionDisplayText, getSnapPoints, getVariant, handleOptionChange, isSelectionComplete, isValidAlignment, isValidTileCount, listProducts, mintRealtimeGrant, mockupUrl, normalizeChoice, prepareOptionRenderData, registerStandardComponents, resolveMockupId, resolveVariantId, retryOperation, simulateCartOperation, toCombinations, toOptionAttributes, useFrameworkAdapter as useVueAdapter, validateAlignment, validateDesignElement, validateEffects, validateImageUrl, validateMockupOptions, validateProductSelection, validateProductSpec, validateRequiredOptions, validateTileCount, withContext, withErrorHandling, withSyncErrorHandling };
2866
+ export { AbstractTemplateRenderer, type AddToCartOptions, Animations, type ArtSelectorContext, type ArtSelectorDescriptor, type ArtSelectorOptions, type ArtworkData, type AspectRatio, Attributes, type BaseDescriptor, type BlendConfig, type BuildMockupUrlConfig, type CartDetail, type CatalogListResponse, type CatalogProduct$2 as CatalogProduct, ClassNames, type ColorDesignElement, ColorPickerUtils, Combination, CommonProps, ComponentContext, type ComponentDefinition, ComponentDescriptor, ComponentEventManager, ComponentFactory, ComponentLifecycle, ComponentLifecycleHooks, type ComponentMetadata, ComponentProps, ComponentRegistry, ComponentState, type ComponentTemplate, ComponentTemplates, type ContainerDescriptor, ContextBridge, type ContextComparator, type ContextConsumer, ContextInjector, type ContextProviderConfig, type ContextSubscriber, ContextSynchronizer, type CreateDesignStateInput, type CreatedDesignState, DEFAULT_ARTWORK_URL, DEFAULT_ASPECT_RATIO, DEFAULT_COLOR, DEFAULT_GRANT_BASE, DEFAULT_MOCKUP_BASE, DEFAULT_PLACEMENT_DIMENSIONS, type Descriptor, type Design, type DesignElement, type DesignGenerationContext, type Effects, Elements, type ErrorContext, type ErrorHandler, ErrorManager, type EventDefinition, EventDelegator, EventEmitter, EventHandler, type EventListener, type EventPayload, type Fill, Focus, FrameworkAdapter, FrameworkUtilities, type GetMockupUrlOptions, type ImageAlignment, type ImageDesignElement, type LifecycleHook, LifecycleManager, type LifecyclePhase, type LifecycleTransition, LitAdapter, type MaskOverride, type MockupGenerationOptions, MockupResult, type MockupService, type MockupServiceConfig, OptionAttribute, type OptionChoice, type OptionChoiceRenderData, type OptionRenderData, OptionSelection, type Placement, type PlacementDesign, PlacementSettings, type ProductArtAlignmentContext, type ProductArtAlignmentDescriptor, type ProductArtAlignmentOptions, type ProductContext, type ProductContextEvents, ProductContextManager, type ProductData, type ProductFetcher, type ProductImageDescriptor, type ProductImageOptions, ProductLoader, type ProductMockupData, type ProductOptionChoice, type ProductOptionData, type ProductOptionsDescriptor, type ProductOptionsOptions, type ProductPlacement, type ProductPriceDescriptor, type ProductPriceOptions, ProductProps, type ProductSpec, type ProductTitleDescriptor, type ProductTitleOptions, type ProductVariant, type PropDefinition, type PropSchema, type PropType, PropertyManager, REALTIME_WS_URL, type RateLimitState, type RealtimeGrant, RealtimeMockupService, type RegularArtwork, type RenderProduct, type RenderProductCombination, type RenderProductOptions, RenderSession, type RenderSessionOptions, type RenderState, type SdkConfig, type SeamlessPattern, type SignedUrlResponse, type SnowconeClient, StandardComponents, StandardEvents, StateManager, type StateManagerOptions, type StateMiddleware, type StateSelector, type StateSubscriber, type StateUpdater, Styles, SvelteAdapter, SwatchUtils, type TagName, TemplateBuilder, type TemplateNode, type TemplateNodeType, type TemplateRenderer, type TextDescriptor, type TileCount, UniversalContextProvider, type UserSelection, type ValidationError, type ValidationResult, VueAdapter, assertVariantSelected, autoRegister, buildMockupUrl, componentRegistry, createAddToCartEvent, createAddToCartHandler, createCartDetail, createClient, createContextProvider, createDesignForPlacements, createDesignState, createErrorHandler, createEventManager, createLifecycle, createLitComponent, createProductContext, createProductLoader, createPropertyManager, createStateHook, createStateStore, createSvelteComponent, createUniversalProvider, createVueComponent, describeArtSelector, describeProductArtAlignment, describeProductImage, describeProductOptions, describeProductPrice, describeProductTitle, extractProductId, fetchRealtimeGrant, filterImagePlacements, findClosestSnapPoint, findVariantForSelection, formatPrice, formatValidationErrors, getDefaultVariantId, getEffectiveAlignment, getIncompleteSelectionMessage, getMissingSelections, getMockupUrl, getOptionRenderType, getProduct, getSelectionDisplayText, getSnapPoints, getVariant, handleOptionChange, isSelectionComplete, isValidAlignment, isValidTileCount, listProducts, mintRealtimeGrant, mockupUrl, normalizeChoice, prepareOptionRenderData, registerStandardComponents, resolveMockupId, resolveVariantId, retryOperation, simulateCartOperation, toCombinations, toOptionAttributes, useFrameworkAdapter as useVueAdapter, validateAlignment, validateDesignElement, validateEffects, validateImageUrl, validateMockupOptions, validateProductSelection, validateProductSpec, validateRequiredOptions, validateTileCount, withContext, withErrorHandling, withSyncErrorHandling };
package/dist/index.js CHANGED
@@ -4690,6 +4690,30 @@ async function fetchRealtimeGrant(grantUrl, shop, fetchImpl) {
4690
4690
  }
4691
4691
 
4692
4692
  // src/realtime/session.ts
4693
+ function variantIdsOf(product) {
4694
+ const combos = product.options?.combinations;
4695
+ if (!combos?.length) return [];
4696
+ const ids = /* @__PURE__ */ new Set();
4697
+ for (const c of combos) {
4698
+ if (typeof c.variantId === "string" && c.variantId !== "") ids.add(c.variantId);
4699
+ }
4700
+ return [...ids];
4701
+ }
4702
+ function assertVariantSelected(product) {
4703
+ const variantIds = variantIdsOf(product);
4704
+ if (variantIds.length < 2) return;
4705
+ const fix = `pass product.variantId from options.combinations[].variantId (valid ids: ${variantIds.join(", ")})`;
4706
+ if (!product.variantId) {
4707
+ throw new Error(
4708
+ `RenderSession: product ${product.productId} requires a variantId \u2014 ${fix}`
4709
+ );
4710
+ }
4711
+ if (!variantIds.includes(product.variantId)) {
4712
+ throw new Error(
4713
+ `RenderSession: product ${product.productId} has an invalid variantId "${product.variantId}" \u2014 ${fix}`
4714
+ );
4715
+ }
4716
+ }
4693
4717
  var DEFAULT_RENDER_TIMEOUT_MS = 3e4;
4694
4718
  var RenderSession = class {
4695
4719
  svc;
@@ -4705,6 +4729,7 @@ var RenderSession = class {
4705
4729
  if (!opts.getToken && !opts.grantUrl) {
4706
4730
  throw new Error("RenderSession: provide `grantUrl` or `getToken` to authorize the session");
4707
4731
  }
4732
+ if (opts.product) assertVariantSelected(opts.product);
4708
4733
  this.opts = opts;
4709
4734
  this.product = opts.product ?? null;
4710
4735
  this.renderTimeoutMs = opts.renderTimeoutMs ?? DEFAULT_RENDER_TIMEOUT_MS;
@@ -4724,6 +4749,7 @@ var RenderSession = class {
4724
4749
  }
4725
4750
  /** Set or update the product. Sends config immediately if already connected. */
4726
4751
  setProduct(product) {
4752
+ assertVariantSelected(product);
4727
4753
  this.product = product;
4728
4754
  this.svc.sendConfig(this.toConfig(product));
4729
4755
  }
@@ -4739,6 +4765,13 @@ var RenderSession = class {
4739
4765
  );
4740
4766
  }
4741
4767
  const product = this.product;
4768
+ try {
4769
+ assertVariantSelected(product);
4770
+ } catch (err) {
4771
+ const message = err instanceof Error ? err.message : String(err);
4772
+ this.errorCb?.(message);
4773
+ return Promise.reject(err instanceof Error ? err : new Error(message));
4774
+ }
4742
4775
  this.ready = new Promise((resolve, reject) => {
4743
4776
  let settled = false;
4744
4777
  this.svc.setCallbacks({
@@ -4997,6 +5030,7 @@ export {
4997
5030
  UniversalContextProvider,
4998
5031
  VueAdapter,
4999
5032
  adapterRegistry,
5033
+ assertVariantSelected,
5000
5034
  autoRegister,
5001
5035
  buildMockupUrl2 as buildMockupUrl,
5002
5036
  componentRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snowcone-app/sdk",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Snowcone SDK for product mockups and print-on-demand",
5
5
  "keywords": [
6
6
  "merch",