@occluze/tether 0.6.8 → 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.
Files changed (259) hide show
  1. package/README.md +11 -11
  2. package/dist/esm/astro.js +2 -2
  3. package/dist/esm/chunks/account-UIPWJQ3Y.js +1 -0
  4. package/dist/esm/chunks/account-pages-6ROA6Y4V.js +1 -0
  5. package/dist/esm/chunks/analytics-2V632IRV.js +1 -0
  6. package/dist/esm/chunks/chunk-4GCXM764.js +1 -0
  7. package/dist/esm/chunks/chunk-56FCLJJD.js +1 -0
  8. package/dist/esm/chunks/chunk-5Z5O3767.js +1 -0
  9. package/dist/esm/chunks/chunk-6OZJCAGZ.js +1 -0
  10. package/dist/esm/chunks/chunk-A5SYCVQM.js +1 -0
  11. package/dist/esm/chunks/chunk-BA4LQJJQ.js +1 -0
  12. package/dist/esm/chunks/chunk-BFTZR4OB.js +4 -0
  13. package/dist/esm/chunks/chunk-C5JYQCWH.js +1 -0
  14. package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
  15. package/dist/esm/chunks/chunk-MPDWM2NN.js +1 -0
  16. package/dist/esm/chunks/chunk-T44MUL4Y.js +1 -0
  17. package/dist/esm/chunks/chunk-YLUULATY.js +1 -0
  18. package/dist/esm/chunks/diagnose-3FEKRIEK.js +14 -0
  19. package/dist/esm/chunks/extras-6POFBNOP.js +1 -0
  20. package/dist/esm/chunks/markets-ui-FD6NI2OG.js +1 -0
  21. package/dist/esm/chunks/search-UJ73YBQ5.js +1 -0
  22. package/dist/esm/index.js +1 -1
  23. package/dist/esm/server.js +2 -2
  24. package/dist/presets/barba.esm.js +19 -23
  25. package/dist/tether.account-pages.js +320 -365
  26. package/dist/tether.account-pages.min.js +1 -1
  27. package/dist/tether.account.js +242 -491
  28. package/dist/tether.account.min.js +1 -1
  29. package/dist/tether.analytics.js +66 -88
  30. package/dist/tether.analytics.min.js +1 -1
  31. package/dist/tether.diagnose.js +36 -33
  32. package/dist/tether.diagnose.min.js +2 -2
  33. package/dist/tether.extras.js +28 -36
  34. package/dist/tether.extras.min.js +1 -1
  35. package/dist/tether.js +1270 -894
  36. package/dist/tether.markets.js +73 -90
  37. package/dist/tether.markets.min.js +1 -1
  38. package/dist/tether.min.js +0 -0
  39. package/dist/tether.min.js.map +4 -4
  40. package/dist/tether.search.js +44 -68
  41. package/dist/tether.search.min.js +1 -1
  42. package/dist/types/server/src/shared/account-orders.d.ts +44 -0
  43. package/dist/types/server/src/shared/account-routes.d.ts +153 -0
  44. package/dist/types/server/src/shared/api-versions.d.ts +13 -0
  45. package/dist/types/server/src/shared/error-message.d.ts +2 -0
  46. package/dist/types/server/src/shared/json.d.ts +25 -0
  47. package/dist/types/server/src/shared/market-codes.d.ts +16 -0
  48. package/dist/types/src/account/addresses-view.d.ts +5 -0
  49. package/dist/types/src/account/answers.d.ts +40 -0
  50. package/dist/types/src/account/markup.d.ts +19 -0
  51. package/dist/types/src/account/operations.d.ts +41 -0
  52. package/dist/types/src/account/order-lines.d.ts +13 -0
  53. package/dist/types/src/account/order-list.d.ts +12 -0
  54. package/dist/types/src/account/order-page.d.ts +6 -0
  55. package/dist/types/src/account/page-answers.d.ts +182 -0
  56. package/dist/types/src/account/page-events.d.ts +2 -0
  57. package/dist/types/src/account/server-client.d.ts +60 -0
  58. package/dist/types/src/account/subscriptions-view.d.ts +2 -0
  59. package/dist/types/src/account-api.d.ts +18 -0
  60. package/dist/types/src/account-marks.d.ts +15 -0
  61. package/dist/types/src/account-pages.d.ts +8 -0
  62. package/dist/types/src/account.d.ts +64 -0
  63. package/dist/types/src/amounts.d.ts +12 -0
  64. package/dist/types/src/analytics-shopify.d.ts +41 -0
  65. package/dist/types/src/analytics.d.ts +2 -0
  66. package/dist/types/src/api-version.d.ts +28 -0
  67. package/dist/types/src/astro.d.ts +42 -0
  68. package/dist/types/src/cart/cart-shape.d.ts +43 -0
  69. package/dist/types/src/cart/checkout.d.ts +16 -0
  70. package/dist/types/src/cart/drawer.d.ts +18 -0
  71. package/dist/types/src/cart/mutations.d.ts +79 -0
  72. package/dist/types/src/cart/render.d.ts +17 -0
  73. package/dist/types/src/cart/store.d.ts +182 -0
  74. package/dist/types/src/cart.d.ts +106 -0
  75. package/dist/types/src/client.d.ts +100 -0
  76. package/dist/types/src/collection.d.ts +25 -0
  77. package/dist/types/src/config-sections.d.ts +40 -0
  78. package/dist/types/src/config.d.ts +185 -0
  79. package/dist/types/src/controls/cart-controls.d.ts +2 -0
  80. package/dist/types/src/controls/discount.d.ts +2 -0
  81. package/dist/types/src/controls/extras.d.ts +5 -0
  82. package/dist/types/src/controls/navigation.d.ts +12 -0
  83. package/dist/types/src/diagnose-markup.d.ts +19 -0
  84. package/dist/types/src/diagnose.d.ts +60 -0
  85. package/dist/types/src/dom.d.ts +75 -0
  86. package/dist/types/src/extras.d.ts +21 -0
  87. package/dist/types/src/fragments.d.ts +158 -0
  88. package/dist/types/src/globals.d.ts +15 -0
  89. package/dist/types/src/images.d.ts +20 -0
  90. package/dist/types/src/index.d.ts +167 -0
  91. package/dist/types/src/initial.d.ts +56 -0
  92. package/dist/types/src/lazy.d.ts +28 -0
  93. package/dist/types/src/loader.d.ts +84 -0
  94. package/dist/types/src/market-codes.d.ts +6 -0
  95. package/dist/types/src/markets-ui.d.ts +32 -0
  96. package/dist/types/src/markets.d.ts +72 -0
  97. package/dist/types/src/markup.d.ts +131 -0
  98. package/dist/types/src/mounting.d.ts +46 -0
  99. package/dist/types/src/page-market.d.ts +27 -0
  100. package/dist/types/src/presets/barba.d.ts +56 -0
  101. package/dist/types/src/product-events.d.ts +2 -0
  102. package/dist/types/src/product-render.d.ts +17 -0
  103. package/dist/types/src/product-shape.d.ts +88 -0
  104. package/dist/types/src/product-state.d.ts +110 -0
  105. package/dist/types/src/product.d.ts +66 -0
  106. package/dist/types/src/repeaters.d.ts +16 -0
  107. package/dist/types/src/search.d.ts +33 -0
  108. package/dist/types/src/selling-plans.d.ts +64 -0
  109. package/dist/types/src/server.d.ts +88 -0
  110. package/dist/types/src/shopify-values.d.ts +46 -0
  111. package/dist/types/src/status.d.ts +33 -0
  112. package/dist/types/src/storage.d.ts +12 -0
  113. package/dist/types/src/store-html.d.ts +39 -0
  114. package/dist/types/src/storefront/catalog.d.ts +38 -0
  115. package/dist/types/src/storefront/json-ld.d.ts +20 -0
  116. package/dist/types/src/variant-render.d.ts +14 -0
  117. package/package.json +24 -23
  118. package/src/account/addresses-view.ts +58 -0
  119. package/src/account/answers.ts +79 -0
  120. package/src/account/markup.ts +36 -0
  121. package/src/account/operations.ts +145 -0
  122. package/src/account/{order-lines.js → order-lines.ts} +20 -41
  123. package/src/account/order-list.ts +107 -0
  124. package/src/account/order-page.ts +137 -0
  125. package/src/account/page-answers.ts +317 -0
  126. package/src/account/{page-events.js → page-events.ts} +55 -90
  127. package/src/account/server-client.ts +183 -0
  128. package/src/account/{subscriptions-view.js → subscriptions-view.ts} +25 -68
  129. package/src/account-api.ts +98 -0
  130. package/src/account-marks.ts +35 -0
  131. package/src/{account-pages.js → account-pages.ts} +29 -37
  132. package/src/account.ts +284 -0
  133. package/src/amounts.ts +64 -0
  134. package/src/{analytics-shopify.js → analytics-shopify.ts} +112 -107
  135. package/src/analytics.ts +315 -0
  136. package/src/{api-version.js → api-version.ts} +25 -31
  137. package/src/{astro.js → astro.ts} +110 -104
  138. package/src/cart/cart-shape.ts +25 -0
  139. package/src/cart/checkout.ts +101 -0
  140. package/src/cart/{drawer.js → drawer.ts} +18 -31
  141. package/src/cart/mutations.ts +237 -0
  142. package/src/cart/{render.js → render.ts} +68 -96
  143. package/src/cart/store.ts +279 -0
  144. package/src/{cart.js → cart.ts} +82 -128
  145. package/src/client.ts +391 -0
  146. package/src/{collection.js → collection.ts} +30 -38
  147. package/src/config-sections.ts +284 -0
  148. package/src/config.ts +480 -0
  149. package/src/controls/{cart-controls.js → cart-controls.ts} +22 -54
  150. package/src/controls/{discount.js → discount.ts} +13 -34
  151. package/src/controls/{extras.js → extras.ts} +15 -24
  152. package/src/controls/{navigation.js → navigation.ts} +24 -30
  153. package/src/{diagnose-markup.js → diagnose-markup.ts} +37 -72
  154. package/src/{diagnose.js → diagnose.ts} +95 -96
  155. package/src/dom.ts +268 -0
  156. package/src/{extras.js → extras.ts} +43 -52
  157. package/src/fragments.ts +261 -0
  158. package/src/globals.d.ts +15 -0
  159. package/src/{images.js → images.ts} +30 -43
  160. package/src/index.ts +415 -0
  161. package/src/initial.ts +101 -0
  162. package/src/lazy-iife.ts +197 -0
  163. package/src/lazy.ts +42 -0
  164. package/src/{loader.js → loader.ts} +149 -123
  165. package/src/market-codes.ts +6 -0
  166. package/src/{markets-ui.js → markets-ui.ts} +91 -68
  167. package/src/markets.ts +246 -0
  168. package/src/markup.ts +127 -0
  169. package/src/{mounting.js → mounting.ts} +88 -101
  170. package/src/{page-market.js → page-market.ts} +32 -38
  171. package/src/part-integrity.ts +7 -0
  172. package/src/parts/account-pages.ts +7 -0
  173. package/src/parts/account.ts +7 -0
  174. package/src/parts/analytics.ts +7 -0
  175. package/src/parts/diagnose.ts +7 -0
  176. package/src/parts/extras.ts +7 -0
  177. package/src/parts/markets.ts +7 -0
  178. package/src/parts/search.ts +7 -0
  179. package/src/presets/barba.ts +194 -0
  180. package/src/{product-events.js → product-events.ts} +33 -48
  181. package/src/{product-render.js → product-render.ts} +38 -77
  182. package/src/product-shape.ts +49 -0
  183. package/src/product-state.ts +318 -0
  184. package/src/{product.js → product.ts} +90 -126
  185. package/src/{repeaters.js → repeaters.ts} +40 -52
  186. package/src/{search.js → search.ts} +85 -103
  187. package/src/selling-plans.ts +145 -0
  188. package/src/server.ts +211 -0
  189. package/src/{shopify-values.js → shopify-values.ts} +24 -22
  190. package/src/{status.js → status.ts} +34 -54
  191. package/src/storage.ts +34 -0
  192. package/src/store-html.ts +359 -0
  193. package/src/storefront/catalog.ts +196 -0
  194. package/src/storefront/{json-ld.js → json-ld.ts} +45 -68
  195. package/src/{variant-render.js → variant-render.ts} +24 -50
  196. package/dist/esm/chunks/account-C3NP6HFB.js +0 -1
  197. package/dist/esm/chunks/account-pages-WLK374FO.js +0 -1
  198. package/dist/esm/chunks/analytics-G3SAWMNL.js +0 -1
  199. package/dist/esm/chunks/chunk-27PJJZGC.js +0 -1
  200. package/dist/esm/chunks/chunk-2FSXS55U.js +0 -1
  201. package/dist/esm/chunks/chunk-FCSWQDVV.js +0 -1
  202. package/dist/esm/chunks/chunk-FOMSMIAM.js +0 -1
  203. package/dist/esm/chunks/chunk-GZJTCLLW.js +0 -1
  204. package/dist/esm/chunks/chunk-HEQILZVR.js +0 -1
  205. package/dist/esm/chunks/chunk-JOOEXYF3.js +0 -4
  206. package/dist/esm/chunks/chunk-MPTQYLSM.js +0 -1
  207. package/dist/esm/chunks/chunk-NEHZ37TU.js +0 -3
  208. package/dist/esm/chunks/chunk-V74KGEJF.js +0 -1
  209. package/dist/esm/chunks/chunk-VT4CDUJJ.js +0 -1
  210. package/dist/esm/chunks/chunk-YJLWX2HK.js +0 -1
  211. package/dist/esm/chunks/diagnose-6VVGZNTY.js +0 -14
  212. package/dist/esm/chunks/extras-GJVRDFM3.js +0 -1
  213. package/dist/esm/chunks/markets-ui-UNB4PTA7.js +0 -1
  214. package/dist/esm/chunks/search-OU75RNQL.js +0 -1
  215. package/src/account/addresses-view.js +0 -92
  216. package/src/account/browser-session.js +0 -359
  217. package/src/account/markup.js +0 -48
  218. package/src/account/operations.js +0 -180
  219. package/src/account/order-list.js +0 -91
  220. package/src/account/order-page.js +0 -192
  221. package/src/account/server-client.js +0 -172
  222. package/src/account/shared.js +0 -111
  223. package/src/account/shopify-auth.js +0 -150
  224. package/src/account-api.js +0 -118
  225. package/src/account-links.js +0 -35
  226. package/src/account.js +0 -342
  227. package/src/amounts.js +0 -36
  228. package/src/analytics.js +0 -352
  229. package/src/cart/checkout.js +0 -97
  230. package/src/cart/mutations.js +0 -234
  231. package/src/cart/store.js +0 -326
  232. package/src/client.js +0 -267
  233. package/src/config.js +0 -349
  234. package/src/dom.js +0 -287
  235. package/src/fragments.js +0 -237
  236. package/src/index.js +0 -353
  237. package/src/initial.js +0 -87
  238. package/src/lazy-iife.js +0 -116
  239. package/src/lazy.js +0 -39
  240. package/src/market-codes.js +0 -35
  241. package/src/markets.js +0 -334
  242. package/src/markup.js +0 -64
  243. package/src/parts/account-pages.js +0 -6
  244. package/src/parts/account.js +0 -6
  245. package/src/parts/analytics.js +0 -6
  246. package/src/parts/diagnose.js +0 -6
  247. package/src/parts/extras.js +0 -6
  248. package/src/parts/markets.js +0 -6
  249. package/src/parts/search.js +0 -6
  250. package/src/presets/barba.js +0 -190
  251. package/src/product-state.js +0 -318
  252. package/src/selling-plans.js +0 -154
  253. package/src/server.js +0 -215
  254. package/src/store-html.js +0 -0
  255. package/src/storefront/catalog.js +0 -199
  256. package/types/astro.d.ts +0 -28
  257. package/types/barba.d.ts +0 -16
  258. package/types/index.d.ts +0 -577
  259. package/types/server.d.ts +0 -182
@@ -0,0 +1,110 @@
1
+ import type { Money } from './shopify-values.js';
2
+ import type { Product, ProductVariant } from './fragments.js';
3
+ import type { PlanInGroup } from './selling-plans.js';
4
+ /** Option name → chosen value. */
5
+ export type Selection = Record<string, string>;
6
+ /**
7
+ * How the visitor wants to buy. Buying once keeps the plan offered next to the one-time price (the subscription
8
+ * binds `subscription-price` and `plan-name` show it, and subscribing switches to it); `null` for a product without
9
+ * plans.
10
+ */
11
+ export type Purchase = {
12
+ kind: 'one-time';
13
+ offeredPlanId: string | null;
14
+ } | {
15
+ kind: 'subscription';
16
+ planId: string;
17
+ };
18
+ /** How the line is bought: on a plan only when the visitor subscribes and the variant is on that plan. */
19
+ type LinePurchase = {
20
+ kind: 'one-time';
21
+ } | {
22
+ kind: 'subscription';
23
+ planId: string;
24
+ };
25
+ /** What the visitor chose, before it is checked against the product. */
26
+ interface ProductChoice {
27
+ selected: Selection;
28
+ purchase: Purchase;
29
+ quantity: number;
30
+ }
31
+ export type ShowCondition = 'in-stock' | 'out-of-stock' | 'backorder' | 'on-sale' | 'not-on-sale' | 'has-variants' | 'single-variant' | 'low-stock' | 'plenty-in-stock' | 'subscription-available' | 'subscription-unavailable' | 'subscription-only' | 'one-time-available' | 'subscription-selected' | 'one-time-selected' | 'has-savings';
32
+ export interface ProductView {
33
+ variant: ProductVariant;
34
+ /** The selection, matching `variant`. */
35
+ selected: Selection;
36
+ /** The choice, made valid for the product and `variant`. */
37
+ purchase: Purchase;
38
+ /** The purchase's plan, offered or chosen. */
39
+ plan: PlanInGroup | null;
40
+ /** How the line is bought. */
41
+ line: LinePurchase;
42
+ /** The variant is on at least one plan. */
43
+ subscriptionAvailable: boolean;
44
+ quantity: number;
45
+ /** What one item costs as bought. */
46
+ price: Money;
47
+ /** Only when on sale. */
48
+ compareAtPrice: Money | null;
49
+ /** What `plan` saves on the one-time price. */
50
+ savings: {
51
+ amount: Money | null;
52
+ percentage: number;
53
+ };
54
+ show: Record<ShowCondition, boolean>;
55
+ }
56
+ /** How the caller writes values as text. */
57
+ export interface Formatters {
58
+ money: (money: Money | null) => string;
59
+ percent: (percentage: number) => string;
60
+ }
61
+ /** What a bind writes; `null` and `undefined` clear it. */
62
+ type BindText = string | number | null | undefined;
63
+ export declare const selectionOf: (variant: ProductVariant) => Selection;
64
+ /**
65
+ * @param name An option name.
66
+ * @returns True when a variant for sale has this value and the other selected values.
67
+ */
68
+ export declare function isValueAvailable(product: Product, selected: Selection, name: string, value: string): boolean;
69
+ /** @returns The plan subscribed on, or offered while buying once. */
70
+ export declare const purchasePlanId: (purchase: Purchase) => string | null;
71
+ /** @returns On the plan when subscribing and the variant is on it, else once. */
72
+ export declare function linePurchase(purchase: Purchase, variant: ProductVariant | null): LinePurchase;
73
+ /**
74
+ * The purchase the visitor wants, made valid for the product and variant: one-time without plans, a subscription when
75
+ * the product requires one, and a plan the variant is on when it is on any (else a plan of the product, for the binds
76
+ * to offer).
77
+ *
78
+ * @param kind The way of buying the visitor asked for.
79
+ * @param preferredPlanId The plan to keep when it is the product's: the visitor's last one.
80
+ */
81
+ export declare function normalisePurchase(product: Product, variant: ProductVariant | null, kind: Purchase['kind'], preferredPlanId: string | null): Purchase;
82
+ interface InitialChoiceOptions {
83
+ /** A variant id or gid (`?variant=`); the first variant for sale without. */
84
+ variant?: string | null | undefined;
85
+ /** A plan to subscribe on (`?selling_plan=`). */
86
+ sellingPlan?: string | null | undefined;
87
+ /** The plan offered first, by id, name or option value. */
88
+ defaultPlan?: string | null | undefined;
89
+ /** `subscription` subscribes; anything else buys once. */
90
+ defaultPurchase?: string | null | undefined;
91
+ }
92
+ /** What a product starts with before the visitor chooses anything. */
93
+ export declare function initialChoice(product: Product, { variant, sellingPlan, defaultPlan, defaultPurchase }: InitialChoiceOptions): Omit<ProductChoice, 'quantity'>;
94
+ /**
95
+ * The view for the visitor's choice. A selection no variant has falls back to the first variant.
96
+ *
97
+ * @param lowStock `low-stock` at this many or fewer.
98
+ * @returns `null` for a product without variants.
99
+ */
100
+ export declare function productView(product: Product, choice: ProductChoice, lowStock: number): ProductView | null;
101
+ /** The `tether-bind` values that follow the variant and purchase. Thunks, so only the binds on the page are computed. */
102
+ export declare function variantBindTexts(view: ProductView, format: Formatters): Map<string, () => BindText>;
103
+ /**
104
+ * The `tether-plan-bind` values of one plan in a plan list, for the variant.
105
+ *
106
+ * @param planId The list item's `tether-plan-value`.
107
+ * @param plan The product's plan with that id.
108
+ */
109
+ export declare function planBindTexts(variant: ProductVariant, planId: string, plan: PlanInGroup | undefined, format: Formatters): Map<string, () => BindText>;
110
+ export {};
@@ -0,0 +1,66 @@
1
+ import type { Product, ProductVariant } from './fragments.js';
2
+ import type { ShopifyCart } from './cart/store.js';
3
+ import type { PlanInGroup } from './selling-plans.js';
4
+ import type { Purchase, Selection } from './product-state.js';
5
+ /** What a scope adds to the cart. */
6
+ interface CartLineInput {
7
+ merchandiseId: string;
8
+ quantity: number;
9
+ sellingPlanId?: string;
10
+ attributes?: {
11
+ key: string;
12
+ value: string;
13
+ }[];
14
+ }
15
+ export declare const scopes: WeakMap<Element, ProductScope>;
16
+ export declare function rememberOptionNames(products: readonly Pick<Product, 'handle' | 'options'>[]): void;
17
+ export declare class ProductScope {
18
+ readonly root: HTMLElement;
19
+ product: Product;
20
+ plans: PlanInGroup[];
21
+ selected: Selection;
22
+ /** How the visitor buys; `setPurchase` and `selectPlan` change it. */
23
+ purchase: Purchase;
24
+ variant: ProductVariant | null;
25
+ quantity: number;
26
+ constructor(root: HTMLElement, product: Product);
27
+ /** New data for the same product (a country switch). Keeps the visitor's choice. */
28
+ update(product: Product): void;
29
+ /** @returns Matches in this scope, including the root itself (a card link with tether-bind="link"). */
30
+ $(selector: string): HTMLElement[];
31
+ /**
32
+ * @param authored An option name in the markup.
33
+ * @returns The product's option of that name, or at that position in the build's language.
34
+ */
35
+ optName(authored: string | null): string;
36
+ get plan(): PlanInGroup | null;
37
+ /** True when the current variant can be bought on a plan. */
38
+ get subscriptionAvailable(): boolean;
39
+ /** True when the line is bought on a plan: subscribing, and the variant is on the plan. */
40
+ get isSubscription(): boolean;
41
+ /** @returns What would be added to the cart right now. */
42
+ getLine(): CartLineInput | null;
43
+ /** @returns `tether-line-attributes` and the filled `tether-line-attribute` fields. */
44
+ collectAttributes(): {
45
+ key: string;
46
+ value: string;
47
+ }[];
48
+ /**
49
+ * Selects an option value. A combination no variant has moves to the closest variant with the value: the most other
50
+ * choices kept, then one for sale.
51
+ */
52
+ select(name: string, value: string): void;
53
+ /** @param id A variant gid or numeric id. */
54
+ selectVariant(id: string): void;
55
+ setPurchase(kind: Purchase['kind']): void;
56
+ /** @param ref A plan id, numeric id, name or option value. */
57
+ selectPlan(ref: string | null): void;
58
+ setQuantity(quantity: number | string): void;
59
+ /** @returns The cart after adding; `null` when the variant can't be added. */
60
+ addToCart(): Promise<ShopifyCart | null>;
61
+ /** Learns the markup's option names when they are the product's, then writes the product. */
62
+ renderProduct(): void;
63
+ /** Derives the view of the current choice, keeps its variant, selection and purchase, and writes it. */
64
+ renderVariant(): void;
65
+ }
66
+ export {};
@@ -0,0 +1,16 @@
1
+ /** @returns True once the repeater's template has been captured (it may be empty after that). */
2
+ export declare const hasTemplate: (container: Element) => boolean;
3
+ /** Rebuilds the list on every render. Fine for images, tags, options. */
4
+ export declare function repeat<Item>(container: Element, items: readonly Item[], render: (node: HTMLElement, item: Item, index: number) => void): void;
5
+ interface KeyedHooks {
6
+ /** Runs on a node inserted for a new item. */
7
+ onEnter?: ((node: HTMLElement) => void) | null;
8
+ /** Runs on a node whose item is gone; `done()` removes it. */
9
+ onLeave?: ((node: HTMLElement, done: () => void) => void) | null;
10
+ }
11
+ /**
12
+ * Keyed repeater: keeps existing nodes, inserts new ones, removes old ones. Used for cart lines, so values can
13
+ * animate and lines can slide in and out.
14
+ */
15
+ export declare function repeatKeyed<Item>(container: Element, items: readonly Item[], keyOf: (item: Item, index: number) => string | number, render: (node: HTMLElement, item: Item, index: number, isNew: boolean) => void, { onEnter, onLeave }?: KeyedHooks): void;
16
+ export {};
@@ -0,0 +1,33 @@
1
+ import type { Money } from './shopify-values.js';
2
+ interface SearchImage {
3
+ url: string;
4
+ altText: string | null;
5
+ }
6
+ interface SearchProduct {
7
+ id: string;
8
+ handle: string;
9
+ title: string;
10
+ availableForSale: boolean;
11
+ featuredImage: SearchImage | null;
12
+ priceRange: {
13
+ minVariantPrice: Money;
14
+ };
15
+ }
16
+ interface SearchCollection {
17
+ handle: string;
18
+ title: string;
19
+ image: SearchImage | null;
20
+ }
21
+ interface SearchQuery {
22
+ text: string;
23
+ }
24
+ export interface SearchResults {
25
+ products: SearchProduct[];
26
+ collections: SearchCollection[];
27
+ queries: SearchQuery[];
28
+ }
29
+ /**
30
+ * Sets up every `[tether-search]` in `root` (and `root` itself) once.
31
+ */
32
+ export declare function mountSearch(root?: ParentNode & Partial<Pick<Element, 'matches'>>): void;
33
+ export {};
@@ -0,0 +1,64 @@
1
+ import type { Money } from './shopify-values.js';
2
+ import type { SellingPlan, SellingPlanGroup, SellingPlanAllocation } from './fragments.js';
3
+ /** A plan with the group it belongs to. */
4
+ export type PlanInGroup = SellingPlan & {
5
+ group: SellingPlanGroup;
6
+ };
7
+ export interface ProductWithPlans {
8
+ sellingPlanGroups?: {
9
+ nodes: SellingPlanGroup[];
10
+ } | null;
11
+ }
12
+ /** A variant as far as plans go. A cart line's variant has no allocations. */
13
+ export interface VariantWithPlans {
14
+ price: Money;
15
+ sellingPlanAllocations?: {
16
+ nodes: SellingPlanAllocation[];
17
+ } | null;
18
+ }
19
+ export type PlanAdjustment = {
20
+ type: 'percentage';
21
+ value: number;
22
+ } | {
23
+ type: 'amount';
24
+ value: Money;
25
+ } | {
26
+ type: 'price';
27
+ value: Money;
28
+ };
29
+ /**
30
+ * @param value A gid, a numeric id, or anything else.
31
+ * @param type The Shopify type, e.g. `ProductVariant`.
32
+ * @returns The gid of a numeric id; a gid or other value as written; `null` when empty.
33
+ */
34
+ export declare function toGid(value: unknown, type: string): string | null;
35
+ export declare const GID: {
36
+ product: (value: unknown) => string | null;
37
+ variant: (value: unknown) => string | null;
38
+ sellingPlan: (value: unknown) => string | null;
39
+ };
40
+ /** @returns All plans of the product, flattened. */
41
+ export declare function allPlans(product: ProductWithPlans | null | undefined): PlanInGroup[];
42
+ export declare function hasPlans(product: ProductWithPlans | null | undefined): boolean;
43
+ /** @returns The variant's allocation on the plan; `null` when the variant isn't in it. */
44
+ export declare function allocationFor(variant: VariantWithPlans | null | undefined, planId: string | null | undefined): SellingPlanAllocation | null;
45
+ /** @returns The plans this variant can be bought on. */
46
+ export declare function plansForVariant(product: ProductWithPlans | null | undefined, variant: VariantWithPlans | null | undefined): PlanInGroup[];
47
+ /** @returns The price per delivery on the plan; the one-time price when the variant isn't on it. */
48
+ export declare function planPrice(variant: VariantWithPlans | null | undefined, planId: string | null | undefined): Money | null;
49
+ /** @returns What the customer pays at checkout on the plan (prepaid plans charge more up front). */
50
+ export declare function planCheckoutCharge(variant: VariantWithPlans | null | undefined, planId: string | null | undefined): Money | null;
51
+ /** @returns What the plan saves on the one-time price; no amount and 0% when it saves nothing. */
52
+ export declare function planSavings(variant: VariantWithPlans | null | undefined, planId: string | null | undefined): {
53
+ amount: Money | null;
54
+ percentage: number;
55
+ };
56
+ /**
57
+ * @returns The plan's own discount, whatever the variant: a percentage, a fixed amount off or a fixed price.
58
+ */
59
+ export declare function planAdjustment(plan: Pick<SellingPlan, 'priceAdjustments'> | null | undefined): PlanAdjustment | null;
60
+ /**
61
+ * Finds a plan by id, numeric id, name or option value (case-insensitive), so Webflow editors can write
62
+ * `tether-default-plan="Every month"`.
63
+ */
64
+ export declare function findPlan(product: ProductWithPlans | null | undefined, ref: string | null | undefined): PlanInGroup | null;
@@ -0,0 +1,88 @@
1
+ import { withContext, TetherError } from './client.js';
2
+ import * as plans from './selling-plans.js';
3
+ import { imageUrl, formatPercent } from './dom.js';
4
+ import { initialState } from './initial.js';
5
+ import { STORE_ENV_NAMES, formatMoney, productKey } from './shopify-values.js';
6
+ import type { TetherConfig, TetherOptions } from './config.js';
7
+ import type { Product } from './fragments.js';
8
+ import type { Money } from './shopify-values.js';
9
+ import type { InitialOptions, InitialState } from './initial.js';
10
+ import type { JsonLdOptions } from './storefront/json-ld.js';
11
+ export { imageUrl, initialState, formatPercent, formatMoney, productKey };
12
+ export { TetherError, plans, withContext };
13
+ export type { StorefrontOptions, HydrateOptions };
14
+ export type { Money } from './shopify-values.js';
15
+ export type { Product as ShopifyProduct, ProductVariant as ShopifyVariant } from './fragments.js';
16
+ export type { ShopifyCart, ShopifyCartLine } from './cart/store.js';
17
+ export type { Collection as ShopifyCollection, ProductQuery } from './loader.js';
18
+ export type { InitialOptions, InitialState } from './initial.js';
19
+ export type { ImageSize } from './images.js';
20
+ export type { JsonLdOptions } from './storefront/json-ld.js';
21
+ /** What `createStorefront()` returns. */
22
+ export type Storefront = ReturnType<typeof createStorefront>;
23
+ /** The options only a storefront has. */
24
+ interface ServerOptions {
25
+ /** Replaces the global `fetch` (tests, caching). */
26
+ fetch?: typeof fetch;
27
+ /** Products per request for `allProducts()` (default 25). */
28
+ pageSize?: number;
29
+ /** The site's address, for product URLs in `jsonLd()` without `url`. */
30
+ siteUrl?: string;
31
+ }
32
+ /**
33
+ * The config options, or an `import.meta.env` spread into them: its `PUBLIC_SHOPIFY_*` names give the domain and
34
+ * token, its other keys are ignored.
35
+ */
36
+ type StorefrontOptions = TetherOptions & ServerOptions & StoreEnv;
37
+ /** The environment names that give the domain and the token. */
38
+ type StoreEnv = Partial<Record<(typeof STORE_ENV_NAMES)['domain' | 'token'][number], string>>;
39
+ type ServerConfig = TetherConfig & ServerOptions;
40
+ interface HydrateOptions {
41
+ card?: boolean;
42
+ images?: number;
43
+ }
44
+ /**
45
+ * @returns `productPath` `/products` → `/products/<handle>`; templates with `{handle}` and `{id}` work too. `null` when
46
+ * the path needs a handle or an id that is empty.
47
+ */
48
+ export declare function productUrl(handle: string, { productPath, id }?: {
49
+ productPath?: string;
50
+ id?: string;
51
+ }): string | null;
52
+ /**
53
+ * A storefront: the store's products and collections for the server, and the tags that hand them to the page.
54
+ *
55
+ * @throws {TetherError} `NOT_CONFIGURED` without a domain and a token.
56
+ */
57
+ export declare function createStorefront(options?: StorefrontOptions): {
58
+ hydrate: (products: Product | null | undefined | (Product | null | undefined)[], { card, images }?: HydrateOptions) => string;
59
+ formatMoney: (money: Money | null | undefined, locale?: string | undefined) => string;
60
+ formatPercent: (percentage: number, locale?: string | undefined) => string;
61
+ imageUrl: typeof imageUrl;
62
+ jsonLd: (product: Product | null | undefined, jsonLdOptions?: JsonLdOptions) => string;
63
+ jsonLdData: (product: Product | null | undefined, jsonLdOptions?: JsonLdOptions) => Record<string, unknown> | null;
64
+ initialState: {
65
+ (product: Product, initialOptions?: InitialOptions): InitialState;
66
+ (product: Product | null | undefined, initialOptions?: InitialOptions): InitialState | null;
67
+ };
68
+ productUrl: (handle: string, urlOptions?: {
69
+ productPath?: string;
70
+ id?: string;
71
+ }) => string | null;
72
+ plans: typeof plans;
73
+ getProduct: (raw: string | number | null | undefined) => Promise<Product | null>;
74
+ getProducts: (options?: import("./storefront/catalog.js").CatalogQuery) => Promise<import("./loader.js").ProductPage>;
75
+ allProducts: (options?: Omit<import("./storefront/catalog.js").CatalogQuery, "first" | "after">) => Promise<Product[]>;
76
+ getCollection: (handle: string, options?: Omit<import("./storefront/catalog.js").CatalogQuery, "collection">) => Promise<(import("./loader.js").Collection & {
77
+ products: Product[];
78
+ pageInfo: import("./loader.js").PageInfo;
79
+ }) | null>;
80
+ allCollections: () => Promise<import("./loader.js").Collection[]>;
81
+ getRecommendations: (product: string | number | {
82
+ id: string;
83
+ } | null | undefined, intent?: "RELATED" | "COMPLEMENTARY") => Promise<Product[]>;
84
+ config: ServerConfig;
85
+ query: <T>(query: string, variables?: Record<string, unknown>, requestOptions?: {
86
+ context?: boolean;
87
+ }) => Promise<T>;
88
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Conversions of Shopify values that the browser script and the server helpers (`src/server.ts`, `src/astro.ts`)
3
+ * share, so a server-rendered page and the script that takes it over agree. Pure: no config, no DOM.
4
+ */
5
+ /** A Storefront API `MoneyV2`. */
6
+ export interface Money {
7
+ amount: string;
8
+ currencyCode: string;
9
+ }
10
+ /** The `.env` names a store's domain and public Storefront token are read from, first match wins. */
11
+ export declare const STORE_ENV_NAMES: {
12
+ readonly domain: readonly ["PUBLIC_SHOPIFY_DOMAIN", "PUBLIC_SHOPIFY_STORE_DOMAIN", "SHOPIFY_DOMAIN"];
13
+ readonly token: readonly ["PUBLIC_SHOPIFY_STOREFRONT_TOKEN", "PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN", "SHOPIFY_STOREFRONT_TOKEN"];
14
+ };
15
+ /**
16
+ * True for an absolute https URL. Links and redirects that come from Shopify (checkout, status pages,
17
+ * tracking) are only used when this holds.
18
+ */
19
+ export declare function isHttpsUrl(url: unknown): url is string;
20
+ /**
21
+ * True for a token Shopify issues as a secret (`shpat_` Admin, `shpss_` shared secret, `shpca_` client credentials,
22
+ * `shppa_` private app, `shpua_` user). The public Storefront token has no such prefix. A secret token must never
23
+ * reach a page.
24
+ */
25
+ export declare function isSecretShopifyToken(token: string): boolean;
26
+ /**
27
+ * Money as text in `locale`, e.g. `{ amount: '30.0', currencyCode: 'EUR' }` in `nl-NL` → `€ 30,00`.
28
+ *
29
+ * @param locale An `Intl` locale; the runtime's own when absent.
30
+ * @returns `''` without money or with an amount that isn't a number.
31
+ */
32
+ export declare function formatMoney(money: Money | null | undefined, locale: string | undefined): string;
33
+ /**
34
+ * A product page's path. `productPath` is a folder (`/product` → `/product/<handle>`) or a template with `{handle}`
35
+ * and `{id}` (the numeric product id), e.g. `/product?handle={handle}`.
36
+ *
37
+ * @param id A product gid or numeric id.
38
+ * @returns `null` when the path needs a handle or an id the product lacks: no link beats a wrong one.
39
+ */
40
+ export declare function productUrl(productPath: string, handle: string | null | undefined, id?: string | null): string | null;
41
+ /**
42
+ * The key Tether loads a product by: a handle, or a product gid.
43
+ *
44
+ * @param raw A handle, a numeric id, a gid or a product URL (`…/products/<handle>`).
45
+ */
46
+ export declare function productKey(raw: string | number | null | undefined): string | null;
@@ -0,0 +1,33 @@
1
+ interface ProductKeySource {
2
+ /** The product to load; `null` when the attribute or the page URL has none. */
3
+ key: string | null;
4
+ /** The key comes from the page URL (`url:…`). */
5
+ fromUrl: boolean;
6
+ /** The attribute's value as written. */
7
+ raw: string;
8
+ }
9
+ type ScopeStatus = 'loading' | 'ready' | 'error' | 'not-found';
10
+ /**
11
+ * Reads a `tether-product` value:
12
+ * - `coffee-box`: a handle, numeric id, gid or product URL;
13
+ * - `url:handle`: `?handle=` from the page URL (any parameter: `url:p` reads `?p=`);
14
+ * - `url:path`: the last segment of the page path (`/shop/coffee-box` → `coffee-box`).
15
+ *
16
+ * @param name The attribute, without prefix.
17
+ */
18
+ export declare function resolveProductKey(el: Element, location?: Pick<Location, 'pathname' | 'search'>, name?: string): ProductKeySource;
19
+ /** The values of `tether-show` that follow the scope's state. */
20
+ export declare const STATUS_KEYS: readonly ScopeStatus[];
21
+ /** @returns True for a `TetherError` with code `NOT_FOUND`. */
22
+ export declare const isNotFound: (error: unknown) => boolean;
23
+ /** @returns The selector of every scope that owns its state elements; the nearest one wins. */
24
+ export declare const boundary: () => string;
25
+ /** @returns True when `el` belongs to the scope `root`, not to a scope nested in it. */
26
+ export declare const ownedBy: (el: Element, root: Element) => boolean;
27
+ /**
28
+ * Sets `tether-state` (loading | ready | error) and `tether-error` (not-found or an error code) on `root`, toggles
29
+ * `tether-show="loading|ready|error|not-found"` inside it, and disables its add-to-cart buttons when there's no
30
+ * product.
31
+ */
32
+ export declare function setScopeStatus(root: Element, status: ScopeStatus, error?: unknown): void;
33
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The browser's `localStorage` and `sessionStorage`, which may be blocked: accessing them throws in private mode, in
3
+ * sandboxed frames and where the visitor blocks site data, and writing throws when they are full. Tether then keeps
4
+ * nothing between pages: a read finds nothing, and a write or removal does nothing. Each call catches on its own, so a
5
+ * blocked area never stops a call on the other.
6
+ */
7
+ type StorageArea = 'localStorage' | 'sessionStorage';
8
+ /** @returns `null` when nothing is stored under `key`, or the storage is blocked. */
9
+ export declare function getStored(key: string, area?: StorageArea): string | null;
10
+ export declare function setStored(key: string, value: string, area?: StorageArea): void;
11
+ export declare function removeStored(key: string, area?: StorageArea): void;
12
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * HTML written in the Shopify admin (product and collection descriptions), made safe to insert into the site. Store
3
+ * staff and apps that can edit products are not trusted to act on the storefront, where customer sessions live. Only
4
+ * elements and attributes that show content are kept (an allow-list, so markup nobody reviewed is never let in):
5
+ * text formatting, lists, tables, images, links and video embeds look as written, inline `style` included, because
6
+ * the Shopify editor writes its formatting there. Script, page-wide `<style>` and forms (which could post the
7
+ * visitor's input anywhere) are removed, and embeds run sandboxed.
8
+ *
9
+ * A description stays where it is written, so it can't be laid over the page as a fake login (a phishing form on the
10
+ * shop's own origin). Removed for that: `id` (it would clobber `window.<id>` and the site's own ids); `class`, which
11
+ * would let a description borrow the site's own overlay or modal classes; and every style that takes content out of
12
+ * the flow (`position`, `inset`, `z-index`), moves it from where the flow put it (transforms, motion paths, negative
13
+ * margins, and negative indents and spacing beyond the small values that tighten text, `NEGATIVE_LIMITS`), copies it
14
+ * elsewhere (reflections) or sizes it to the window (viewport and container-query units).
15
+ * Outlines, shadows, filters, border-image and mask-border outsets, text decoration and text stroke paint beyond their
16
+ * element, so they are kept only while every number they give is a pixel length of a few dozen pixels at most
17
+ * (`MAX_PAINT_PX`): enough to set off an image or a callout, too little to cover the page.
18
+ * Styles that fetch anything go too.
19
+ *
20
+ * A frame shows whatever its origin serves, sized as the description likes, so a frame from a page store staff can
21
+ * write would be that fake login. Frames are kept only from the video players in `EMBED_ORIGINS` and the origins the
22
+ * site adds (`embedOrigins`), never from the site's own origin, where a sandbox can be lifted from inside. What an
23
+ * allowed frame shows is up to its host: the host's player, and whatever that player lets its users add (a call to
24
+ * action, an email gate). An origin a site adds is trusted the same way.
25
+ *
26
+ * Not prevented: content wider than the description (a table or embed on a narrow screen, or a box given a large
27
+ * width in pixels) overflows beside it, as in any HTML, and a capped shadow or outline still paints just past it. Tether
28
+ * doesn't clip the element it fills (`contain: paint`, `overflow`), because that would cut off those wide tables and
29
+ * embeds, stop margins collapsing through it and make it contain floats, changing how ordinary descriptions look. A
30
+ * site that wants the clip sets it on that element in its own CSS.
31
+ */
32
+ /**
33
+ * Store HTML as a fragment of content elements only.
34
+ *
35
+ * @param document The document the fragment is for.
36
+ * @param embedOrigins Origins iframes may load from besides the video players in `EMBED_ORIGINS`: the config's
37
+ * `embedOrigins`, already checked to be https origins.
38
+ */
39
+ export declare function storeHtmlFragment(html: string | null | undefined, document?: Document, embedOrigins?: readonly string[]): DocumentFragment;
@@ -0,0 +1,38 @@
1
+ import type { StorefrontConfig } from '../client.js';
2
+ import type { ProductFragmentConfig, Product } from '../fragments.js';
3
+ import type { Collection, PageInfo, ProductPage } from '../loader.js';
4
+ /** What the catalog needs of a storefront's config. */
5
+ type CatalogConfig = StorefrontConfig & ProductFragmentConfig;
6
+ /** A page of a collection's products, or of a search. */
7
+ export interface CatalogQuery {
8
+ /** A collection handle; without one, a search. */
9
+ collection?: string;
10
+ /** Storefront search syntax; `null` or `''` is every product. */
11
+ query?: string | null;
12
+ /** Default the storefront's `pageSize`. */
13
+ first?: number;
14
+ /** The cursor of the page before. */
15
+ after?: string | null;
16
+ /** `ProductCollectionSortKeys` for a collection, `ProductSortKeys` for a search. */
17
+ sortKey?: string;
18
+ reverse?: boolean;
19
+ /** `ProductFilter` values, for a collection. */
20
+ filters?: unknown[];
21
+ }
22
+ type CollectionWithProducts = Collection & {
23
+ products: Product[];
24
+ pageInfo: PageInfo;
25
+ };
26
+ type RecommendationIntent = 'RELATED' | 'COMPLEMENTARY';
27
+ /** @param pageSize Products per page when a query doesn't say. */
28
+ export declare function createCatalog(conf: CatalogConfig, pageSize: number): {
29
+ getProduct: (raw: string | number | null | undefined) => Promise<Product | null>;
30
+ getProducts: (options?: CatalogQuery) => Promise<ProductPage>;
31
+ allProducts: (options?: Omit<CatalogQuery, "first" | "after">) => Promise<Product[]>;
32
+ getCollection: (handle: string, options?: Omit<CatalogQuery, "collection">) => Promise<CollectionWithProducts | null>;
33
+ allCollections: () => Promise<Collection[]>;
34
+ getRecommendations: (product: string | number | {
35
+ id: string;
36
+ } | null | undefined, intent?: RecommendationIntent) => Promise<Product[]>;
37
+ };
38
+ export {};
@@ -0,0 +1,20 @@
1
+ import type { Product } from '../fragments.js';
2
+ export interface JsonLdOptions {
3
+ /** The product page's absolute URL; default from the storefront's `siteUrl`. */
4
+ url?: string;
5
+ /** Default the product's vendor. */
6
+ brand?: string;
7
+ }
8
+ /** Where the product pages are. */
9
+ interface JsonLdSite {
10
+ productPath: string;
11
+ siteUrl: string | undefined;
12
+ }
13
+ /**
14
+ * Text for a `<script>` tag: `<` is escaped so product text can never close the tag, and U+2028 / U+2029 so older
15
+ * JavaScript parsers read it.
16
+ */
17
+ export declare const scriptJson: (value: unknown) => string;
18
+ /** @returns The schema.org object, `null` without a product. */
19
+ export declare function jsonLdData(product: Product | null | undefined, options: JsonLdOptions, site: JsonLdSite): Record<string, unknown> | null;
20
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { ProductView } from './product-state.js';
2
+ import type { RenderScope } from './product-render.js';
3
+ /**
4
+ * The element of an add-to-cart button that holds its label, with the label as written kept in
5
+ * `data-tether-label`, so mode-dependent texts (`tether-subscribe-text`, `tether-soldout-text`) can be undone.
6
+ */
7
+ export declare function addButtonLabel(button: HTMLElement): {
8
+ label: HTMLElement;
9
+ original: string;
10
+ };
11
+ /**
12
+ * Writes what follows the variant and purchase.
13
+ */
14
+ export declare function writeVariant(scope: RenderScope, view: ProductView): void;