@callimacus/thamyr-core 4.12.0 → 4.13.1

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.
@@ -478,6 +478,21 @@ type WhisperPayload = {
478
478
  generated_at: number;
479
479
  ttl_ms: number;
480
480
  };
481
+ type ShopifyStorefrontSettings = {
482
+ /**
483
+ * The store’s primary domain: the host the Storefront API is called on.
484
+ */
485
+ domain: string | null;
486
+ storefrontToken: string | null;
487
+ sourceName: string | null;
488
+ /**
489
+ * Site locale → the buyer’s country (ISO 3166-1 alpha-2).
490
+ */
491
+ countries: {
492
+ [key: string]: string;
493
+ };
494
+ cartEnabled: boolean;
495
+ };
481
496
  type SkesisItemPage = {
482
497
  /**
483
498
  * The canonical page path, `/map/{locale}/{slug}/{id}`, as the sitemap lists it
@@ -819,6 +834,30 @@ type BlockPatchOutput = {
819
834
  */
820
835
  fire: Array<string>;
821
836
  };
837
+ type CartCommandOutput = {
838
+ storyId: string;
839
+ chapterId: string;
840
+ command: 'shopify-add';
841
+ /**
842
+ * The Shopify product variant to add.
843
+ */
844
+ variantId: string;
845
+ quantity: number;
846
+ item: {
847
+ /**
848
+ * The item the chat showed (`shopify:product:<gid>`).
849
+ */
850
+ id: string;
851
+ name: string;
852
+ /**
853
+ * The options that tell the variant apart, for the notice.
854
+ */
855
+ options: Array<{
856
+ name: string;
857
+ value: string;
858
+ }>;
859
+ };
860
+ };
822
861
  type BlockOutput = LegacyBlockOutput | CustomBlockOutput;
823
862
  type ChapterOutput = {
824
863
  id: string;
@@ -896,6 +935,10 @@ type CreateRoundContextOutput = {
896
935
  * Ask for the round's `outline`: the blocks it will show, in page order, sent before their contents are ready. Without it, no outline is sent.
897
936
  */
898
937
  outline?: boolean;
938
+ /**
939
+ * The client runs a Shopify cart in the shopper's browser: the chat may then answer an add to the bag with a `cart-command`. Without it, an add to the bag shows the item's details.
940
+ */
941
+ shopifyCart?: boolean;
899
942
  [key: string]: unknown;
900
943
  };
901
944
  type CustomBlockOutput = {
@@ -1273,6 +1316,9 @@ type ThamyrResponse$1 = {
1273
1316
  } | {
1274
1317
  type: 'block-patch';
1275
1318
  data: BlockPatchOutput;
1319
+ } | {
1320
+ type: 'cart-command';
1321
+ data: CartCommandOutput;
1276
1322
  };
1277
1323
  type UserInteraction$1 = {
1278
1324
  type: 'get-story';
@@ -2993,6 +3039,30 @@ type CartUpdateProductQuantityResponses = {
2993
3039
  */
2994
3040
  200: Basket;
2995
3041
  };
3042
+ type ShopifyGetStorefrontSettingsErrors = {
3043
+ /**
3044
+ * Validation error
3045
+ */
3046
+ 400: unknown;
3047
+ /**
3048
+ * Missing or invalid access token
3049
+ */
3050
+ 401: unknown;
3051
+ /**
3052
+ * The caller is not permitted to perform this operation
3053
+ */
3054
+ 403: PinaxForbidden;
3055
+ /**
3056
+ * The project has no store connected from Studio
3057
+ */
3058
+ 404: PublicError;
3059
+ };
3060
+ type ShopifyGetStorefrontSettingsResponses = {
3061
+ /**
3062
+ * Success
3063
+ */
3064
+ 200: ShopifyStorefrontSettings;
3065
+ };
2996
3066
  type SkesisSimilarErrors = {
2997
3067
  /**
2998
3068
  * Validation error
@@ -3263,6 +3333,14 @@ declare class Cart extends HeyApiClient {
3263
3333
  quantity: number;
3264
3334
  }, options?: Options<never, ThrowOnError>): RequestResult<CartUpdateProductQuantityResponses, CartUpdateProductQuantityErrors, ThrowOnError>;
3265
3335
  }
3336
+ declare class Shopify extends HeyApiClient {
3337
+ /**
3338
+ * Get the Shopify cart settings
3339
+ *
3340
+ * The store’s primary domain, its storefront token and channel source name (null until the store is a sales channel), the buyer’s country for each site locale, and whether the cart is on. The cart is off on the site’s preview and once the store uninstalled the app. Not found while the project has no store connected: the SDK then keeps its own config.
3341
+ */
3342
+ getStorefrontSettings<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>): RequestResult<ShopifyGetStorefrontSettingsResponses, ShopifyGetStorefrontSettingsErrors, ThrowOnError>;
3343
+ }
3266
3344
  declare class Skesis extends HeyApiClient {
3267
3345
  /**
3268
3346
  * Similar items
@@ -3310,6 +3388,8 @@ declare class CallimacusApi extends HeyApiClient {
3310
3388
  get analytics(): Analytics;
3311
3389
  private _cart?;
3312
3390
  get cart(): Cart;
3391
+ private _shopify?;
3392
+ get shopify(): Shopify;
3313
3393
  private _skesis?;
3314
3394
  get skesis(): Skesis;
3315
3395
  }
@@ -3372,6 +3452,13 @@ type ThamyrResponse = ThamyrResponse$1;
3372
3452
  declares in `patchOn` re-set its animation in place. Apply it with `applyBlockPatch`.
3373
3453
  */
3374
3454
  type BlockPatch = BlockPatchOutput;
3455
+ /**
3456
+ An add to the bag the chat asks the shopper's browser to make in its Shopify cart,
3457
+ at most once: it comes only on the socket whose round asked for it, only to a client
3458
+ that declared `context.shopifyCart`, and is never stored with the round, so a replay
3459
+ never sends it again. Run it with the Shopify cart (`@callimacus/thamyr-react` does).
3460
+ */
3461
+ type CartCommand = CartCommandOutput;
3375
3462
  /**
3376
3463
  What a Rive animation exposes to be set: the `properties` of a `rive` document section.
3377
3464
  */
@@ -3385,6 +3472,7 @@ declare const ThamyrResponseType: {
3385
3472
  readonly STORY_CREATED: "story-created";
3386
3473
  readonly STORY_SHARED: "story-shared";
3387
3474
  readonly BLOCK_PATCH: "block-patch";
3475
+ readonly CART_COMMAND: "cart-command";
3388
3476
  };
3389
3477
  type ThamyrResponseType = Values<typeof ThamyrResponseType>;
3390
3478
  /**
@@ -3501,6 +3589,10 @@ type InteractionContext = {
3501
3589
  The page title, carried in a delivered interaction's envelope.
3502
3590
  */
3503
3591
  title?: string;
3592
+ /**
3593
+ The client runs a Shopify cart (`useShopifyCart`): a round that declares it may get the chat's add to the bag as a `cart-command`.
3594
+ */
3595
+ shopifyCart?: boolean;
3504
3596
  };
3505
3597
  type UiObserverConfig = {
3506
3598
  enabled?: boolean;
@@ -4148,6 +4240,248 @@ declare function deliverResultOf(block: {
4148
4240
  data?: unknown;
4149
4241
  }): DeliverResult | undefined;
4150
4242
 
4243
+ /**
4244
+ The Shopify settings a site passes at init (`shopify` in the init config).
4245
+ */
4246
+ type ShopifyConfig = {
4247
+ /**
4248
+ The store's domain (its primary domain, or `brand.myshopify.com`): the cart calls the Storefront API there.
4249
+ */
4250
+ storeDomain: string;
4251
+ /**
4252
+ A public Storefront API token. Without one the cart uses Shopify's tokenless access.
4253
+ */
4254
+ storefrontToken?: string;
4255
+ /**
4256
+ The buyer's country (ISO 3166 alpha-2) when core names none for the site's locale.
4257
+ */
4258
+ country?: string;
4259
+ };
4260
+ /**
4261
+ What core's `GET /api/v3/shopify/storefront` answers once a store is connected.
4262
+ */
4263
+ type ShopifyStorefront = {
4264
+ domain: string | null;
4265
+ storefrontToken: string | null;
4266
+ sourceName: string | null;
4267
+ /**
4268
+ Site locale → the buyer's country, e.g. `{"it-IT": "IT", "en": "US"}`.
4269
+ */
4270
+ countries: Record<string, string>;
4271
+ /**
4272
+ Off on the site's preview, and once the store uninstalled the app.
4273
+ */
4274
+ cartEnabled: boolean;
4275
+ };
4276
+ /**
4277
+ The settings of the page: core's values where it has them, the init config's elsewhere.
4278
+ */
4279
+ type ShopifySettings = {
4280
+ cartEnabled: boolean;
4281
+ storeDomain?: string;
4282
+ /**
4283
+ What the bag is stored under in the browser: the config's domain when the init config names one, core's otherwise. It stays the same when the store gets connected or core fails to answer, so the bag does not empty.
4284
+ */
4285
+ cartKey?: string;
4286
+ storefrontToken?: string;
4287
+ sourceName?: string;
4288
+ countries: Record<string, string>;
4289
+ /**
4290
+ The config's country, for a locale `countries` does not name.
4291
+ */
4292
+ country?: string;
4293
+ };
4294
+ /**
4295
+ What one cart call runs with: the settings, with the buyer's country resolved for the site's current locale.
4296
+ */
4297
+ type ShopifyCartSettings = Omit<ShopifySettings, 'countries'>;
4298
+ declare function mergeShopifySettings(storefront: ShopifyStorefront | undefined, config?: ShopifyConfig): ShopifySettings;
4299
+ /**
4300
+ Reads core's Shopify settings for the site and merges them over the init
4301
+ config. Never rejects: a 404 means no store is connected yet, and any other
4302
+ failure (network, a 5xx, a malformed body) also leaves the config in charge —
4303
+ core is not in the cart path, so its outage must not take the cart down. Read
4304
+ it once per page load: the answer is `private, no-store`.
4305
+ @param client - The contract client every `/api/v3` call goes through (`createContractClient`).
4306
+ @param config - The init config's `shopify`.
4307
+ */
4308
+ declare function readShopifySettings(client: ContractClient, config?: ShopifyConfig): Promise<ShopifySettings>;
4309
+ /**
4310
+ The settings one cart call runs with: the buyer's country is the one core names for `locale`, else the config's.
4311
+ @param settings - What {@link readShopifySettings} resolved.
4312
+ @param locale - The site's current locale (`it-IT`, `en`).
4313
+ */
4314
+ declare function cartSettingsFor(settings: ShopifySettings, locale: string | undefined): ShopifyCartSettings;
4315
+
4316
+ /**
4317
+ The Storefront API version the cart is written against.
4318
+ */
4319
+ declare const STOREFRONT_API_VERSION = "2026-10";
4320
+ type ShopifyMoney = {
4321
+ amount: string;
4322
+ currencyCode: string;
4323
+ };
4324
+ type ShopifyCartLine = {
4325
+ id: string;
4326
+ quantity: number;
4327
+ merchandise: {
4328
+ id: string;
4329
+ title: string;
4330
+ product: {
4331
+ title: string;
4332
+ handle: string;
4333
+ };
4334
+ image: {
4335
+ url: string;
4336
+ altText: string | null;
4337
+ } | null;
4338
+ price: ShopifyMoney;
4339
+ selectedOptions: Array<{
4340
+ name: string;
4341
+ value: string;
4342
+ }>;
4343
+ };
4344
+ cost: {
4345
+ totalAmount: ShopifyMoney;
4346
+ };
4347
+ };
4348
+ /**
4349
+ Shopify's refusal of a cart change (`CartUserError`): sold out, not published to the channel, a quantity rule.
4350
+ `message` is written for the shopper.
4351
+ */
4352
+ type ShopifyUserError = {
4353
+ code: string | null;
4354
+ field: string[] | null;
4355
+ message: string;
4356
+ };
4357
+ type ShopifyCart = {
4358
+ id: string;
4359
+ checkoutUrl: string;
4360
+ totalQuantity: number;
4361
+ cost: {
4362
+ subtotalAmount: ShopifyMoney;
4363
+ totalAmount: ShopifyMoney;
4364
+ };
4365
+ lines: ShopifyCartLine[];
4366
+ };
4367
+ /**
4368
+ What Shopify did other than asked (`CartWarning`), e.g. `MERCHANDISE_NOT_ENOUGH_STOCK`: it added what it had.
4369
+ */
4370
+ type ShopifyCartWarning = {
4371
+ code: string;
4372
+ message: string;
4373
+ target: string;
4374
+ };
4375
+ /**
4376
+ - `cart_disabled`: the cart is off (the site's preview, or the store uninstalled the app).
4377
+ - `cart_unavailable`: no store is set up, or the store refuses the call (its storefront password, a revoked token).
4378
+ - `invalid_input`: not a product variant id, or not a whole quantity.
4379
+ - `refused`: Shopify said no; its reasons are in `userErrors`. A sold-out add is refused too, with Shopify's warning as the message.
4380
+ - `no_cart`: nothing to change or check out.
4381
+ - `throttled`: Shopify is rate limiting (HTTP 430 or 429).
4382
+ - `busy`: another tab held the cart creation for too long.
4383
+ - `network`: no answer, or none in time.
4384
+ - `http`: any other failed status. `graphql`: the API answered errors only.
4385
+ */
4386
+ type ShopifyCartErrorCode = 'cart_disabled' | 'cart_unavailable' | 'invalid_input' | 'refused' | 'no_cart' | 'throttled' | 'busy' | 'network' | 'http' | 'graphql';
4387
+ type ShopifyCartError = {
4388
+ code: ShopifyCartErrorCode;
4389
+ message: string;
4390
+ /**
4391
+ The HTTP status, when the store answered with one.
4392
+ */
4393
+ status?: number;
4394
+ userErrors?: ShopifyUserError[];
4395
+ };
4396
+ /**
4397
+ What a cart method resolves, never rejects with. `data` is the cart (`undefined`:
4398
+ none yet), or the checkout URL for `checkout`. On `error`, `data` is the cart as
4399
+ it stands when Shopify sent it along (a refused add leaves it unchanged).
4400
+ `warnings` is always there, empty when Shopify had nothing to say.
4401
+ */
4402
+ type ShopifyCartResult<T = ShopifyCart | undefined> = {
4403
+ data: T;
4404
+ error?: undefined;
4405
+ warnings: ShopifyCartWarning[];
4406
+ } | {
4407
+ data?: T;
4408
+ error: ShopifyCartError;
4409
+ warnings: ShopifyCartWarning[];
4410
+ };
4411
+ /**
4412
+ The slice of `localStorage` the cart uses.
4413
+ */
4414
+ type CartStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
4415
+ /**
4416
+ Runs `task` while no other tab runs one under the same `name`; rejects when the lock is not granted in time.
4417
+ */
4418
+ type CartLock = <T>(name: string, task: () => Promise<T>) => Promise<T>;
4419
+ type ShopifyCartClientOptions = {
4420
+ /**
4421
+ The store and the buyer, asked before every call: memoize what it fetches.
4422
+ */
4423
+ settings: () => Promise<ShopifyCartSettings>;
4424
+ /**
4425
+ Defaults to the global `fetch`.
4426
+ */
4427
+ fetch?: (input: string, init: RequestInit) => Promise<Response>;
4428
+ /**
4429
+ Defaults to `localStorage`, or memory where the browser refuses it.
4430
+ */
4431
+ storage?: CartStorage;
4432
+ /**
4433
+ Defaults to `navigator.locks`, or a lease kept in `storage` where there is none.
4434
+ */
4435
+ lock?: CartLock;
4436
+ /**
4437
+ Sends the shopper to checkout. Defaults to `location.assign`.
4438
+ */
4439
+ navigate?: (url: string) => void;
4440
+ };
4441
+ type ShopifyCartClient = {
4442
+ /**
4443
+ The cart as Shopify has it now; `data: undefined` when there is none yet, and the store is not asked then.
4444
+ */
4445
+ getCart: () => Promise<ShopifyCartResult>;
4446
+ /**
4447
+ Adds `quantity` (default 1) of a variant (`gid://shopify/ProductVariant/<id>`, or the bare number); creates the cart on the first add.
4448
+ */
4449
+ addItem: (variantId: string, quantity?: number) => Promise<ShopifyCartResult>;
4450
+ /**
4451
+ Removes a line (`ShopifyCartLine.id`).
4452
+ */
4453
+ removeItem: (lineId: string) => Promise<ShopifyCartResult>;
4454
+ /**
4455
+ Sets a line's quantity; 0 removes it, as in Shopify.
4456
+ */
4457
+ updateQuantity: (lineId: string, quantity: number) => Promise<ShopifyCartResult>;
4458
+ /**
4459
+ Sends the shopper to Shopify's checkout for the cart, and resolves its URL. An empty cart goes nowhere (`no_cart`).
4460
+ */
4461
+ checkout: () => Promise<ShopifyCartResult<string>>;
4462
+ };
4463
+ /**
4464
+ Whether `variantId` is a Shopify product variant gid (`gid://shopify/ProductVariant/123`).
4465
+ */
4466
+ declare const isVariantGid: (variantId: string) => boolean;
4467
+ /**
4468
+ The browser's storage, or one kept in memory where the browser refuses it. Pass the same one to every
4469
+ cart client of a page, so they share the cart id even where the browser keeps nothing.
4470
+ */
4471
+ declare function createPageStorage(): CartStorage;
4472
+ /**
4473
+ The Shopify cart in the shopper's browser, on the Storefront API (version
4474
+ {@link STOREFRONT_API_VERSION}) of the store the settings name, tokenless
4475
+ until a storefront token exists. Every method resolves a
4476
+ {@link ShopifyCartResult} and never rejects.
4477
+ @example
4478
+ ```ts
4479
+ const cart = createShopifyCart({settings: async () => ({cartEnabled: true, storeDomain: 'brand.com', country: 'IT'})});
4480
+ const {data, error, warnings} = await cart.addItem('gid://shopify/ProductVariant/123', 1);
4481
+ ```
4482
+ */
4483
+ declare function createShopifyCart(options: ShopifyCartClientOptions): ShopifyCartClient;
4484
+
4151
4485
  /**
4152
4486
  Logger utility for Thamyr SDK
4153
4487
  Provides configurable logging that can be enabled/disabled to avoid console spam
@@ -4238,5 +4572,5 @@ declare function transformImageUrl(cdnUrl: string, edits: ImageEdits): string;
4238
4572
  */
4239
4573
  declare function randomUuid(): string;
4240
4574
 
4241
- export { ACTION_TYPES, ADD_TO_CART_RESULT_BLOCK, CallimacusApi, CallimacusEvent, CallimacusService, ChapterStatus, ClientEvents, DELIVER_RESULT_BLOCK, GalleryMode, LANDING_VALUE_MAX_LENGTH, MAX_HANDSHAKE_RETRIES, SLInputEventType, ServerEvents, ThamyrHttpError, ThamyrResponseType, UserInteractionType, actionOf, addToCartResultOf, applyBlockPatch, blockKeyOf, callimacusService, classifyConnectError, createContractClient, deliverResultOf, extractTtsAudio, fieldsOf, interactionRound, isCustomBlock, isMasterProduct, isSetProduct, landingRound, logger, parseConnectRejection, parseMapPath, randomUuid, receptorsOf, resolveMapPage, shouldRetryHandshake, transformImageUrl };
4242
- export type { Action, ActionType, AddToCartResult, BCProduct, Basket, Block, BlockPatch, BoundingBox, CallimacusErrorMessage, Chapter, ClientInfo, ConnectRejection, ConnectionStatusCallback, ContractClient, CustomBlock, DeliverResult, DocumentIndexEntry, ErrorCallback, ErrorMessage, Gallery, GenericProduct, HomePage, Image, ImageEdits, ImageFormatEdit, ImageResizeEdit, InteractionContext, InteractionId, InteractionOptions, InteractionPayload, InteractionPayloadMap, LegacyBlock, LocalizedEPArtwork, LocalizedEPChapter, LogLevel, LoggerConfig, MapPage, MapPath, MasterProduct, MasterProductWithVariationGroup, OptionItem, ProductItem, ProductMatch, RecommendationsResponse, RiveManifest, RoundOptions, SLDocument, SLDocumentSection, SLInputEvent, ServerResponseCallback, SetProduct, SkesisItem, SkesisItemPage, SkesisRelatedResponse, SkesisSimilarResponse, Story, SuggestPayload, Suggestions, ThamyrInteractionMap, ThamyrResponse, Topic, TopicContentsPage, TopicSummary, TtsAudio, UiElement, UiObserverConfig, UiSnapshot, UserInteraction, Variant, VariationAttribute, VariationGroup, Video, WhisperPayload };
4575
+ export { ACTION_TYPES, ADD_TO_CART_RESULT_BLOCK, CallimacusApi, CallimacusEvent, CallimacusService, ChapterStatus, ClientEvents, DELIVER_RESULT_BLOCK, GalleryMode, LANDING_VALUE_MAX_LENGTH, MAX_HANDSHAKE_RETRIES, SLInputEventType, STOREFRONT_API_VERSION, ServerEvents, ThamyrHttpError, ThamyrResponseType, UserInteractionType, actionOf, addToCartResultOf, applyBlockPatch, blockKeyOf, callimacusService, cartSettingsFor, classifyConnectError, createContractClient, createPageStorage, createShopifyCart, deliverResultOf, extractTtsAudio, fieldsOf, interactionRound, isCustomBlock, isMasterProduct, isSetProduct, isVariantGid, landingRound, logger, mergeShopifySettings, parseConnectRejection, parseMapPath, randomUuid, readShopifySettings, receptorsOf, resolveMapPage, shouldRetryHandshake, transformImageUrl };
4576
+ export type { Action, ActionType, AddToCartResult, BCProduct, Basket, Block, BlockPatch, BoundingBox, CallimacusErrorMessage, CartCommand, CartLock, CartStorage, Chapter, ClientInfo, ConnectRejection, ConnectionStatusCallback, ContractClient, CustomBlock, DeliverResult, DocumentIndexEntry, ErrorCallback, ErrorMessage, Gallery, GenericProduct, HomePage, Image, ImageEdits, ImageFormatEdit, ImageResizeEdit, InteractionContext, InteractionId, InteractionOptions, InteractionPayload, InteractionPayloadMap, LegacyBlock, LocalizedEPArtwork, LocalizedEPChapter, LogLevel, LoggerConfig, MapPage, MapPath, MasterProduct, MasterProductWithVariationGroup, OptionItem, ProductItem, ProductMatch, RecommendationsResponse, RiveManifest, RoundOptions, SLDocument, SLDocumentSection, SLInputEvent, ServerResponseCallback, SetProduct, ShopifyCart, ShopifyCartClient, ShopifyCartClientOptions, ShopifyCartError, ShopifyCartErrorCode, ShopifyCartLine, ShopifyCartResult, ShopifyCartSettings, ShopifyCartWarning, ShopifyConfig, ShopifyMoney, ShopifySettings, ShopifyStorefront, ShopifyUserError, SkesisItem, SkesisItemPage, SkesisRelatedResponse, SkesisSimilarResponse, Story, SuggestPayload, Suggestions, ThamyrInteractionMap, ThamyrResponse, Topic, TopicContentsPage, TopicSummary, TtsAudio, UiElement, UiObserverConfig, UiSnapshot, UserInteraction, Variant, VariationAttribute, VariationGroup, Video, WhisperPayload };