@asteby/storefront-react 0.5.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -420,6 +420,97 @@ type JsonLdProps = {
420
420
  /** Renders `<script type="application/ld+json">` safely (works in SSR). */
421
421
  declare function JsonLd({ data, nonce }: JsonLdProps): react.JSX.Element | null;
422
422
 
423
+ /** Types OPS ships built in; they cannot be redefined through the API. */
424
+ declare const OPS_BUILTIN_SECTION_TYPES: readonly ["hero", "product_grid", "collection_list", "rich_text", "faq", "image_with_text"];
425
+ declare const OPS_PROP_KINDS: readonly ["text", "textarea", "richtext", "image", "link", "number", "select", "boolean", "collection", "collection_list", "product", "product_list", "list"];
426
+ type OpsPropKind = (typeof OPS_PROP_KINDS)[number];
427
+ type OpsSectionProp = {
428
+ key: string;
429
+ kind: OpsPropKind;
430
+ label: string;
431
+ required?: boolean;
432
+ default?: unknown;
433
+ options?: {
434
+ value: string;
435
+ label: string;
436
+ }[];
437
+ min?: number;
438
+ max?: number;
439
+ fields?: OpsSectionProp[];
440
+ };
441
+ type OpsSectionType = {
442
+ type: string;
443
+ label: string;
444
+ description?: string;
445
+ category?: string;
446
+ schema: {
447
+ props: OpsSectionProp[];
448
+ };
449
+ max_per_page?: number;
450
+ };
451
+ type SectionTypeIssue = {
452
+ /** Dotted path, e.g. `promo.schema.props[1].fields[0].key`. */
453
+ path: string;
454
+ message: string;
455
+ };
456
+ type SectionTypeWarning = {
457
+ type: string;
458
+ /** Prop key when the warning is about a single prop. */
459
+ prop?: string;
460
+ message: string;
461
+ };
462
+ declare class SectionTypesValidationError extends Error {
463
+ readonly issues: SectionTypeIssue[];
464
+ constructor(issues: SectionTypeIssue[]);
465
+ }
466
+ type ToOpsOptions = {
467
+ /** Skip OPS built-in types (default true). */
468
+ excludeBuiltin?: boolean;
469
+ /**
470
+ * Prop keys OPS already knows for each built-in type (e.g. `{ hero: ['title','subtitle'] }`).
471
+ * With it, only the extra local props are warned about; without it, every
472
+ * locally-defined built-in type gets one generic warning.
473
+ */
474
+ builtinProps?: Record<string, readonly string[]>;
475
+ };
476
+ type ToOpsResult = {
477
+ types: OpsSectionType[];
478
+ warnings: SectionTypeWarning[];
479
+ };
480
+ /** Local mirror of the OPS validation, so mistakes surface before the request. */
481
+ declare function validateOpsSectionTypes(types: readonly OpsSectionType[]): SectionTypeIssue[];
482
+ type DefinitionsSource = {
483
+ definitions(): SectionDefinition<any, any>[];
484
+ } | readonly SectionDefinition<any, any>[];
485
+ /**
486
+ * Converts the registry (or a list of definitions) to OPS section types.
487
+ * Throws `SectionTypesValidationError` (with `issues`) when something would
488
+ * be rejected by OPS.
489
+ */
490
+ declare function sectionTypesToOps(source: DefinitionsSource, options?: ToOpsOptions): ToOpsResult;
491
+ type SectionTypesTarget = {
492
+ request<T>(spec: {
493
+ method: 'PUT';
494
+ path: string;
495
+ body?: unknown;
496
+ }): Promise<T>;
497
+ } | {
498
+ baseUrl: string;
499
+ token: string;
500
+ fetch?: typeof fetch;
501
+ };
502
+ type SyncResult = {
503
+ created: number;
504
+ updated: number;
505
+ warnings: SectionTypeWarning[];
506
+ };
507
+ /**
508
+ * Validates locally and then `PUT`s the section types to OPS. Needs an `sk_`
509
+ * token with `content:write`. `target` is a `createStorefrontClient()` client or
510
+ * `{ baseUrl, token, fetch? }`. Returns `{ created, updated, warnings }`.
511
+ */
512
+ declare function syncSectionTypes(target: SectionTypesTarget, source: DefinitionsSource, options?: ToOpsOptions): Promise<SyncResult>;
513
+
423
514
  type SectionPropKind = 'text' | 'textarea' | 'richtext' | 'number' | 'boolean' | 'url' | 'image' | 'color' | 'select' | 'collection' | 'product' | 'products' | 'list';
424
515
  type SectionPropSchema = {
425
516
  label: string;
@@ -465,6 +556,10 @@ type SectionDefinition<P = Record<string, unknown>, C = any> = {
465
556
  type: string;
466
557
  label?: string;
467
558
  description?: string;
559
+ /** OPS editor grouping (only used when exporting to OPS). */
560
+ category?: string;
561
+ /** Max instances per page (only used when exporting to OPS). */
562
+ maxPerPage?: number;
468
563
  schema: SectionSchema;
469
564
  component: ComponentType<SectionComponentProps<P, C>>;
470
565
  };
@@ -485,6 +580,8 @@ declare function defineSection<const S extends SectionSchema, C = any>(definitio
485
580
  type: string;
486
581
  label?: string;
487
582
  description?: string;
583
+ category?: string;
584
+ maxPerPage?: number;
488
585
  schema: S;
489
586
  component: ComponentType<SectionComponentProps<InferSectionProps<S>, C>>;
490
587
  }): SectionDefinition<InferSectionProps<S>, C>;
@@ -513,6 +610,12 @@ declare class SectionRegistry {
513
610
  definitions(): SectionDefinition<any, any>[];
514
611
  /** JSON Schemas of every registered section, keyed by type. */
515
612
  toJSONSchema(): Record<string, Record<string, unknown>>;
613
+ /**
614
+ * Section types in the OPS `storefront_content` format, ready for
615
+ * `PUT /storefront/content/section-types`. Throws `SectionTypesValidationError`
616
+ * on anything OPS would reject; `warnings` flags built-in types.
617
+ */
618
+ toOpsSectionTypes(options?: ToOpsOptions): ToOpsResult;
516
619
  }
517
620
  declare function createSectionRegistry(definitions?: readonly SectionDefinition<any, any>[]): SectionRegistry;
518
621
  type SectionsProps<C = unknown> = {
@@ -773,4 +876,4 @@ type NearestLocationFormProps<L extends ShopBranch = ShopBranch> = {
773
876
  */
774
877
  declare function NearestLocationForm<L extends ShopBranch = ShopBranch>({ locations, action, name, defaultValue, onSubmitPostalCode, onLocate, labels, children, ...rest }: NearestLocationFormProps<L>): react.JSX.Element | null;
775
878
 
776
- export { AddToCartButton, type AddToCartButtonProps, AvailabilityBadge, type AvailabilityBadgeProps, type AvailabilityInput, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartLineProblem, type CartLineProblemProps, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, RestockNotifyForm, type RestockNotifyFormProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, SrOnly, StockConflictAlert, type StockConflictAlertProps, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantOptionState, type VariantOptionStateProps, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };
879
+ export { AddToCartButton, type AddToCartButtonProps, AvailabilityBadge, type AvailabilityBadgeProps, type AvailabilityInput, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartLineProblem, type CartLineProblemProps, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OPS_BUILTIN_SECTION_TYPES, OPS_PROP_KINDS, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type OpsPropKind, type OpsSectionProp, type OpsSectionType, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, RestockNotifyForm, type RestockNotifyFormProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, type SectionTypeIssue, type SectionTypeWarning, type SectionTypesTarget, SectionTypesValidationError, Sections, type SectionsProps, type SeoMetaInput, SrOnly, StockConflictAlert, type StockConflictAlertProps, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type SyncResult, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type ToOpsOptions, type ToOpsResult, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantOptionState, type VariantOptionStateProps, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, sectionTypesToOps, seoMeta, serializeCartCookie, serializeJsonLd, syncSectionTypes, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, validateOpsSectionTypes, writeCartCookie };
package/dist/index.d.ts CHANGED
@@ -420,6 +420,97 @@ type JsonLdProps = {
420
420
  /** Renders `<script type="application/ld+json">` safely (works in SSR). */
421
421
  declare function JsonLd({ data, nonce }: JsonLdProps): react.JSX.Element | null;
422
422
 
423
+ /** Types OPS ships built in; they cannot be redefined through the API. */
424
+ declare const OPS_BUILTIN_SECTION_TYPES: readonly ["hero", "product_grid", "collection_list", "rich_text", "faq", "image_with_text"];
425
+ declare const OPS_PROP_KINDS: readonly ["text", "textarea", "richtext", "image", "link", "number", "select", "boolean", "collection", "collection_list", "product", "product_list", "list"];
426
+ type OpsPropKind = (typeof OPS_PROP_KINDS)[number];
427
+ type OpsSectionProp = {
428
+ key: string;
429
+ kind: OpsPropKind;
430
+ label: string;
431
+ required?: boolean;
432
+ default?: unknown;
433
+ options?: {
434
+ value: string;
435
+ label: string;
436
+ }[];
437
+ min?: number;
438
+ max?: number;
439
+ fields?: OpsSectionProp[];
440
+ };
441
+ type OpsSectionType = {
442
+ type: string;
443
+ label: string;
444
+ description?: string;
445
+ category?: string;
446
+ schema: {
447
+ props: OpsSectionProp[];
448
+ };
449
+ max_per_page?: number;
450
+ };
451
+ type SectionTypeIssue = {
452
+ /** Dotted path, e.g. `promo.schema.props[1].fields[0].key`. */
453
+ path: string;
454
+ message: string;
455
+ };
456
+ type SectionTypeWarning = {
457
+ type: string;
458
+ /** Prop key when the warning is about a single prop. */
459
+ prop?: string;
460
+ message: string;
461
+ };
462
+ declare class SectionTypesValidationError extends Error {
463
+ readonly issues: SectionTypeIssue[];
464
+ constructor(issues: SectionTypeIssue[]);
465
+ }
466
+ type ToOpsOptions = {
467
+ /** Skip OPS built-in types (default true). */
468
+ excludeBuiltin?: boolean;
469
+ /**
470
+ * Prop keys OPS already knows for each built-in type (e.g. `{ hero: ['title','subtitle'] }`).
471
+ * With it, only the extra local props are warned about; without it, every
472
+ * locally-defined built-in type gets one generic warning.
473
+ */
474
+ builtinProps?: Record<string, readonly string[]>;
475
+ };
476
+ type ToOpsResult = {
477
+ types: OpsSectionType[];
478
+ warnings: SectionTypeWarning[];
479
+ };
480
+ /** Local mirror of the OPS validation, so mistakes surface before the request. */
481
+ declare function validateOpsSectionTypes(types: readonly OpsSectionType[]): SectionTypeIssue[];
482
+ type DefinitionsSource = {
483
+ definitions(): SectionDefinition<any, any>[];
484
+ } | readonly SectionDefinition<any, any>[];
485
+ /**
486
+ * Converts the registry (or a list of definitions) to OPS section types.
487
+ * Throws `SectionTypesValidationError` (with `issues`) when something would
488
+ * be rejected by OPS.
489
+ */
490
+ declare function sectionTypesToOps(source: DefinitionsSource, options?: ToOpsOptions): ToOpsResult;
491
+ type SectionTypesTarget = {
492
+ request<T>(spec: {
493
+ method: 'PUT';
494
+ path: string;
495
+ body?: unknown;
496
+ }): Promise<T>;
497
+ } | {
498
+ baseUrl: string;
499
+ token: string;
500
+ fetch?: typeof fetch;
501
+ };
502
+ type SyncResult = {
503
+ created: number;
504
+ updated: number;
505
+ warnings: SectionTypeWarning[];
506
+ };
507
+ /**
508
+ * Validates locally and then `PUT`s the section types to OPS. Needs an `sk_`
509
+ * token with `content:write`. `target` is a `createStorefrontClient()` client or
510
+ * `{ baseUrl, token, fetch? }`. Returns `{ created, updated, warnings }`.
511
+ */
512
+ declare function syncSectionTypes(target: SectionTypesTarget, source: DefinitionsSource, options?: ToOpsOptions): Promise<SyncResult>;
513
+
423
514
  type SectionPropKind = 'text' | 'textarea' | 'richtext' | 'number' | 'boolean' | 'url' | 'image' | 'color' | 'select' | 'collection' | 'product' | 'products' | 'list';
424
515
  type SectionPropSchema = {
425
516
  label: string;
@@ -465,6 +556,10 @@ type SectionDefinition<P = Record<string, unknown>, C = any> = {
465
556
  type: string;
466
557
  label?: string;
467
558
  description?: string;
559
+ /** OPS editor grouping (only used when exporting to OPS). */
560
+ category?: string;
561
+ /** Max instances per page (only used when exporting to OPS). */
562
+ maxPerPage?: number;
468
563
  schema: SectionSchema;
469
564
  component: ComponentType<SectionComponentProps<P, C>>;
470
565
  };
@@ -485,6 +580,8 @@ declare function defineSection<const S extends SectionSchema, C = any>(definitio
485
580
  type: string;
486
581
  label?: string;
487
582
  description?: string;
583
+ category?: string;
584
+ maxPerPage?: number;
488
585
  schema: S;
489
586
  component: ComponentType<SectionComponentProps<InferSectionProps<S>, C>>;
490
587
  }): SectionDefinition<InferSectionProps<S>, C>;
@@ -513,6 +610,12 @@ declare class SectionRegistry {
513
610
  definitions(): SectionDefinition<any, any>[];
514
611
  /** JSON Schemas of every registered section, keyed by type. */
515
612
  toJSONSchema(): Record<string, Record<string, unknown>>;
613
+ /**
614
+ * Section types in the OPS `storefront_content` format, ready for
615
+ * `PUT /storefront/content/section-types`. Throws `SectionTypesValidationError`
616
+ * on anything OPS would reject; `warnings` flags built-in types.
617
+ */
618
+ toOpsSectionTypes(options?: ToOpsOptions): ToOpsResult;
516
619
  }
517
620
  declare function createSectionRegistry(definitions?: readonly SectionDefinition<any, any>[]): SectionRegistry;
518
621
  type SectionsProps<C = unknown> = {
@@ -773,4 +876,4 @@ type NearestLocationFormProps<L extends ShopBranch = ShopBranch> = {
773
876
  */
774
877
  declare function NearestLocationForm<L extends ShopBranch = ShopBranch>({ locations, action, name, defaultValue, onSubmitPostalCode, onLocate, labels, children, ...rest }: NearestLocationFormProps<L>): react.JSX.Element | null;
775
878
 
776
- export { AddToCartButton, type AddToCartButtonProps, AvailabilityBadge, type AvailabilityBadgeProps, type AvailabilityInput, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartLineProblem, type CartLineProblemProps, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, RestockNotifyForm, type RestockNotifyFormProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, SrOnly, StockConflictAlert, type StockConflictAlertProps, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantOptionState, type VariantOptionStateProps, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };
879
+ export { AddToCartButton, type AddToCartButtonProps, AvailabilityBadge, type AvailabilityBadgeProps, type AvailabilityInput, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartLineProblem, type CartLineProblemProps, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OPS_BUILTIN_SECTION_TYPES, OPS_PROP_KINDS, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type OpsPropKind, type OpsSectionProp, type OpsSectionType, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, RestockNotifyForm, type RestockNotifyFormProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, type SectionTypeIssue, type SectionTypeWarning, type SectionTypesTarget, SectionTypesValidationError, Sections, type SectionsProps, type SeoMetaInput, SrOnly, StockConflictAlert, type StockConflictAlertProps, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type SyncResult, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type ToOpsOptions, type ToOpsResult, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantOptionState, type VariantOptionStateProps, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, sectionTypesToOps, seoMeta, serializeCartCookie, serializeJsonLd, syncSectionTypes, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, validateOpsSectionTypes, writeCartCookie };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ export { ThemeStyle, isSafeCssValue, themeFontsHref, themeToCss, themeToCssVaria
3
3
  import { useOptionalStorefront } from './chunk-DQBL2DFJ.js';
4
4
  export { StorefrontProvider, useOptionalStorefront, useStorefront, useStorefrontClient } from './chunk-DQBL2DFJ.js';
5
5
  import { createContext, useRef, useState, useCallback, useEffect, useMemo, useContext, Fragment as Fragment$1, useId } from 'react';
6
- import { resolveAvailability, productAvailability, availabilityView, availabilityMessages, formatAvailabilityMessage, cartLineProblemView, isStorefrontError, schemaAvailability, formatMoney } from '@asteby/storefront';
6
+ import { resolveAvailability, productAvailability, availabilityView, availabilityMessages, formatAvailabilityMessage, cartLineProblemView, isStorefrontError, schemaAvailability, StorefrontError, formatMoney } from '@asteby/storefront';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
8
  import { toSchemaOpeningHours, isValidPoint, rankByDistance, openStatus, directionsUrl } from '@asteby/storefront/geo';
9
9
 
@@ -857,6 +857,156 @@ function JsonLd({ data, nonce }) {
857
857
  if (!data || Array.isArray(data) && data.length === 0) return null;
858
858
  return /* @__PURE__ */ jsx("script", { type: "application/ld+json", nonce, dangerouslySetInnerHTML: { __html: serializeJsonLd(data) } });
859
859
  }
860
+ var OPS_BUILTIN_SECTION_TYPES = ["hero", "product_grid", "collection_list", "rich_text", "faq", "image_with_text"];
861
+ var OPS_PROP_KINDS = ["text", "textarea", "richtext", "image", "link", "number", "select", "boolean", "collection", "collection_list", "product", "product_list", "list"];
862
+ var SectionTypesValidationError = class extends Error {
863
+ issues;
864
+ constructor(issues) {
865
+ super(`Invalid section types for OPS:
866
+ ${issues.map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`);
867
+ this.name = "SectionTypesValidationError";
868
+ this.issues = issues;
869
+ }
870
+ };
871
+ var SNAKE = /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/;
872
+ var KIND_MAP = {
873
+ text: "text",
874
+ textarea: "textarea",
875
+ richtext: "richtext",
876
+ url: "text",
877
+ color: "text",
878
+ image: "image",
879
+ number: "number",
880
+ boolean: "boolean",
881
+ select: "select",
882
+ collection: "collection",
883
+ product: "product",
884
+ products: "product_list",
885
+ list: "list"
886
+ };
887
+ var LIST_KINDS = /* @__PURE__ */ new Set(["list"]);
888
+ function convertProps(schema, path, issues) {
889
+ const out = [];
890
+ for (const [key, spec] of Object.entries(schema ?? {})) {
891
+ const p = `${path}.${key}`;
892
+ const kind = KIND_MAP[spec.kind];
893
+ if (!kind) {
894
+ issues.push({ path: `${p}.kind`, message: `unknown kind "${String(spec.kind)}"` });
895
+ continue;
896
+ }
897
+ const prop = { key, kind, label: spec.label };
898
+ if (spec.required) prop.required = true;
899
+ if ("default" in spec && spec.default !== void 0) prop.default = spec.default;
900
+ if (spec.options) prop.options = spec.options.map((o) => ({ value: o.value, label: o.label }));
901
+ if (spec.min !== void 0) prop.min = spec.min;
902
+ if (spec.max !== void 0) prop.max = spec.max;
903
+ if (LIST_KINDS.has(kind)) prop.fields = convertProps(spec.items, `${p}.fields`, issues);
904
+ out.push(prop);
905
+ }
906
+ return out;
907
+ }
908
+ function validateOpsSectionTypes(types) {
909
+ const issues = [];
910
+ const seen = /* @__PURE__ */ new Set();
911
+ const walk = (props, path) => {
912
+ if (!Array.isArray(props)) {
913
+ issues.push({ path, message: "must be a list" });
914
+ return;
915
+ }
916
+ const keys = /* @__PURE__ */ new Set();
917
+ props.forEach((prop, i) => {
918
+ const p = `${path}[${i}]`;
919
+ if (typeof prop.key !== "string" || !SNAKE.test(prop.key)) issues.push({ path: `${p}.key`, message: `"${String(prop.key)}" must be snake_case (a-z, 0-9, _)` });
920
+ else if (keys.has(prop.key)) issues.push({ path: `${p}.key`, message: `duplicate key "${prop.key}"` });
921
+ else keys.add(prop.key);
922
+ if (typeof prop.label !== "string" || !prop.label.trim()) issues.push({ path: `${p}.label`, message: "label is required" });
923
+ if (!OPS_PROP_KINDS.includes(prop.kind)) {
924
+ issues.push({ path: `${p}.kind`, message: `unknown kind "${String(prop.kind)}"` });
925
+ return;
926
+ }
927
+ if (prop.kind === "list") {
928
+ if (!prop.fields || prop.fields.length === 0) issues.push({ path: `${p}.fields`, message: 'list requires a non-empty "fields"' });
929
+ else walk(prop.fields, `${p}.fields`);
930
+ }
931
+ if (prop.kind === "select" && (!prop.options || prop.options.length === 0)) issues.push({ path: `${p}.options`, message: 'select requires non-empty "options"' });
932
+ if (prop.min !== void 0 && prop.max !== void 0 && prop.min > prop.max) issues.push({ path: `${p}.max`, message: "max must be >= min" });
933
+ });
934
+ };
935
+ types.forEach((t, i) => {
936
+ const p = t.type ? t.type : `[${i}]`;
937
+ if (!t.type || !t.type.trim()) issues.push({ path: `${p}.type`, message: "type is required" });
938
+ else if (seen.has(t.type)) issues.push({ path: `${p}.type`, message: `duplicate type "${t.type}"` });
939
+ else seen.add(t.type);
940
+ if (!t.label || !t.label.trim()) issues.push({ path: `${p}.label`, message: "label is required" });
941
+ if (t.max_per_page !== void 0 && (!Number.isInteger(t.max_per_page) || t.max_per_page < 1)) issues.push({ path: `${p}.max_per_page`, message: "must be a positive integer" });
942
+ walk(t.schema?.props, `${p}.schema.props`);
943
+ });
944
+ return issues;
945
+ }
946
+ function sectionTypesToOps(source, options = {}) {
947
+ const excludeBuiltin = options.excludeBuiltin ?? true;
948
+ const defs = Array.isArray(source) ? source : source.definitions();
949
+ const builtin = new Set(OPS_BUILTIN_SECTION_TYPES);
950
+ const issues = [];
951
+ const warnings = [];
952
+ const types = [];
953
+ for (const def of defs) {
954
+ if (excludeBuiltin && builtin.has(def.type)) {
955
+ const known = options.builtinProps?.[def.type];
956
+ if (known) {
957
+ for (const name of Object.keys(def.schema ?? {})) {
958
+ if (!known.includes(name)) warnings.push({ type: def.type, prop: name, message: `prop "${name}" of built-in type "${def.type}" does not exist in the OPS schema; built-in types cannot be redefined, so it will not be editable in OPS` });
959
+ }
960
+ } else {
961
+ warnings.push({ type: def.type, message: `"${def.type}" is a built-in OPS type: not synced, and its local props cannot be verified against the OPS schema` });
962
+ }
963
+ continue;
964
+ }
965
+ const entryIssues = [];
966
+ const props = convertProps(def.schema, `${def.type}.schema.props`, entryIssues);
967
+ issues.push(...entryIssues);
968
+ const out = { type: def.type, label: def.label ?? def.type, schema: { props } };
969
+ if (def.description) out.description = def.description;
970
+ if (def.category) out.category = def.category;
971
+ if (def.maxPerPage !== void 0) out.max_per_page = def.maxPerPage;
972
+ types.push(out);
973
+ }
974
+ issues.push(...validateOpsSectionTypes(types));
975
+ if (issues.length) throw new SectionTypesValidationError(issues);
976
+ return { types, warnings };
977
+ }
978
+ var SYNC_PATH = "/storefront/content/section-types";
979
+ var count = (v) => Array.isArray(v) ? v.length : typeof v === "number" ? v : 0;
980
+ async function syncSectionTypes(target, source, options = {}) {
981
+ const { types, warnings } = sectionTypesToOps(source, options);
982
+ let raw;
983
+ if ("request" in target && typeof target.request === "function") {
984
+ raw = await target.request({ method: "PUT", path: SYNC_PATH, body: { section_types: types } });
985
+ } else {
986
+ const t = target;
987
+ if (!t.token.startsWith("sk_")) throw new TypeError("syncSectionTypes: an sk_ token with content:write is required");
988
+ const f = t.fetch ?? globalThis.fetch.bind(globalThis);
989
+ const url = t.baseUrl.replace(/\/+$/, "") + SYNC_PATH;
990
+ const res = await f(url, {
991
+ method: "PUT",
992
+ headers: { Authorization: `Bearer ${t.token}`, "Content-Type": "application/json", Accept: "application/json" },
993
+ body: JSON.stringify({ section_types: types })
994
+ });
995
+ const text = await res.text();
996
+ let body;
997
+ try {
998
+ body = text ? JSON.parse(text) : void 0;
999
+ } catch {
1000
+ body = void 0;
1001
+ }
1002
+ if (!res.ok) {
1003
+ throw new StorefrontError({ status: res.status, code: body?.code ?? body?.error?.code ?? "unknown", message: body?.message ?? body?.error?.message ?? `Section types sync failed (${res.status})`, url });
1004
+ }
1005
+ raw = body;
1006
+ }
1007
+ const data = raw && typeof raw === "object" && "data" in raw ? raw.data : raw;
1008
+ return { created: count(data?.created), updated: count(data?.updated), warnings };
1009
+ }
860
1010
  function defineSection(definition) {
861
1011
  return definition;
862
1012
  }
@@ -880,6 +1030,9 @@ function resolveSectionProps(schema, raw, path = "") {
880
1030
  else if (spec.required) errors.push({ prop: key, message: "is required" });
881
1031
  continue;
882
1032
  }
1033
+ if (spec.kind === "image" && typeof value === "object" && value !== null && typeof value.url === "string") {
1034
+ value = value.url;
1035
+ }
883
1036
  if (STRING_KINDS.has(spec.kind)) {
884
1037
  if (typeof value !== "string") {
885
1038
  fail2("must be a string");
@@ -1003,6 +1156,14 @@ var SectionRegistry = class {
1003
1156
  toJSONSchema() {
1004
1157
  return Object.fromEntries(this.definitions().map((d) => [d.type, sectionJsonSchema(d)]));
1005
1158
  }
1159
+ /**
1160
+ * Section types in the OPS `storefront_content` format, ready for
1161
+ * `PUT /storefront/content/section-types`. Throws `SectionTypesValidationError`
1162
+ * on anything OPS would reject; `warnings` flags built-in types.
1163
+ */
1164
+ toOpsSectionTypes(options) {
1165
+ return sectionTypesToOps(this, options);
1166
+ }
1006
1167
  };
1007
1168
  function createSectionRegistry(definitions = []) {
1008
1169
  return new SectionRegistry(definitions);
@@ -1377,6 +1538,6 @@ function NearestLocationForm({
1377
1538
  );
1378
1539
  }
1379
1540
 
1380
- export { AddToCartButton, AvailabilityBadge, CART_COOKIE_NAME, CartLineProblem, CartProvider, DirectionsLink, JsonLd, LocationList, LocationPicker, NearestLocationForm, OpenStatus, PickupOption, RestockNotifyForm, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, SectionRegistry, Sections, SrOnly, StockConflictAlert, VariantOptionState, VariantSelector, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, toMerchandise, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useProductOptions, useSelectedLocation, writeCartCookie };
1541
+ export { AddToCartButton, AvailabilityBadge, CART_COOKIE_NAME, CartLineProblem, CartProvider, DirectionsLink, JsonLd, LocationList, LocationPicker, NearestLocationForm, OPS_BUILTIN_SECTION_TYPES, OPS_PROP_KINDS, OpenStatus, PickupOption, RestockNotifyForm, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, SectionRegistry, SectionTypesValidationError, Sections, SrOnly, StockConflictAlert, VariantOptionState, VariantSelector, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, sectionTypesToOps, seoMeta, serializeCartCookie, serializeJsonLd, syncSectionTypes, toMerchandise, useAvailability, useCart, useGeolocation, useNearestLocations, useOptionalCart, useProductOptions, useSelectedLocation, validateOpsSectionTypes, writeCartCookie };
1381
1542
  //# sourceMappingURL=index.js.map
1382
1543
  //# sourceMappingURL=index.js.map