@replohq/sdk 0.1.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 (280) hide show
  1. package/LICENSE +11 -0
  2. package/_vendor/replo-utils/lib/crypto.d.ts +30 -0
  3. package/_vendor/replo-utils/lib/errors.d.ts +83 -0
  4. package/_vendor/replo-utils/lib/json.d.ts +6 -0
  5. package/_vendor/replo-utils/lib/json.mjs +43 -0
  6. package/_vendor/replo-utils/lib/misc.d.ts +145 -0
  7. package/_vendor/replo-utils/lib/object.d.ts +31 -0
  8. package/_vendor/schemas/errors.d.ts +459 -0
  9. package/_vendor/schemas/generated/consent.d.ts +55 -0
  10. package/_vendor/schemas/generated/errors.d.ts +61 -0
  11. package/_vendor/schemas/loaderKeys.d.ts +1 -0
  12. package/_vendor/schemas/loaderKeys.mjs +27 -0
  13. package/_vendor/schemas/money.d.ts +60 -0
  14. package/_vendor/schemas/money.mjs +2051 -0
  15. package/analytics/analytics-provider.d.ts +16 -0
  16. package/analytics/analytics-provider.js +113 -0
  17. package/analytics/analytics-provider.js.map +7 -0
  18. package/analytics/analytics-sink.d.ts +26 -0
  19. package/analytics/analytics-sink.js +1 -0
  20. package/analytics/analytics-sink.js.map +7 -0
  21. package/analytics/get-analytics-sinks.d.ts +27 -0
  22. package/analytics/get-analytics-sinks.js +52 -0
  23. package/analytics/get-analytics-sinks.js.map +7 -0
  24. package/analytics/hooks/use-product-viewed-analytics.d.ts +23 -0
  25. package/analytics/hooks/use-product-viewed-analytics.js +34 -0
  26. package/analytics/hooks/use-product-viewed-analytics.js.map +7 -0
  27. package/analytics/replo-first-party-pixel.d.ts +12 -0
  28. package/analytics/replo-first-party-pixel.js +31 -0
  29. package/analytics/replo-first-party-pixel.js.map +7 -0
  30. package/analytics/replo-pixel-script.d.ts +13 -0
  31. package/analytics/replo-pixel-script.js +30 -0
  32. package/analytics/replo-pixel-script.js.map +7 -0
  33. package/analytics/sinks/ga4-sink.d.ts +15 -0
  34. package/analytics/sinks/ga4-sink.js +60 -0
  35. package/analytics/sinks/ga4-sink.js.map +7 -0
  36. package/analytics/sinks/meta-sink.d.ts +14 -0
  37. package/analytics/sinks/meta-sink.js +56 -0
  38. package/analytics/sinks/meta-sink.js.map +7 -0
  39. package/analytics/sinks/pinterest-sink.d.ts +14 -0
  40. package/analytics/sinks/pinterest-sink.js +76 -0
  41. package/analytics/sinks/pinterest-sink.js.map +7 -0
  42. package/analytics/sinks/reddit-sink.d.ts +14 -0
  43. package/analytics/sinks/reddit-sink.js +80 -0
  44. package/analytics/sinks/reddit-sink.js.map +7 -0
  45. package/analytics/sinks/replo-sink.d.ts +14 -0
  46. package/analytics/sinks/replo-sink.js +57 -0
  47. package/analytics/sinks/replo-sink.js.map +7 -0
  48. package/analytics/sinks/snapchat-sink.d.ts +14 -0
  49. package/analytics/sinks/snapchat-sink.js +67 -0
  50. package/analytics/sinks/snapchat-sink.js.map +7 -0
  51. package/analytics/sinks/tiktok-sink.d.ts +14 -0
  52. package/analytics/sinks/tiktok-sink.js +79 -0
  53. package/analytics/sinks/tiktok-sink.js.map +7 -0
  54. package/analytics/sinks/window-sink.d.ts +18 -0
  55. package/analytics/sinks/window-sink.js +46 -0
  56. package/analytics/sinks/window-sink.js.map +7 -0
  57. package/analytics/utils/analytics-utils.d.ts +7 -0
  58. package/analytics/utils/analytics-utils.js +130 -0
  59. package/analytics/utils/analytics-utils.js.map +7 -0
  60. package/analytics/utils/navigation-tracker.d.ts +16 -0
  61. package/analytics/utils/navigation-tracker.js +44 -0
  62. package/analytics/utils/navigation-tracker.js.map +7 -0
  63. package/analytics/utils/shopify-funnel-gate.d.ts +8 -0
  64. package/analytics/utils/shopify-funnel-gate.js +22 -0
  65. package/analytics/utils/shopify-funnel-gate.js.map +7 -0
  66. package/cart/buy-now-action.d.ts +21 -0
  67. package/cart/buy-now-action.js +104 -0
  68. package/cart/buy-now-action.js.map +7 -0
  69. package/cart/cart-actions.d.ts +63 -0
  70. package/cart/cart-actions.js +90 -0
  71. package/cart/cart-actions.js.map +7 -0
  72. package/cart/cart-loader.d.ts +9 -0
  73. package/cart/cart-loader.js +11 -0
  74. package/cart/cart-loader.js.map +7 -0
  75. package/cart/cart-provider.d.ts +85 -0
  76. package/cart/cart-provider.js +372 -0
  77. package/cart/cart-provider.js.map +7 -0
  78. package/cart/cart-schema.d.ts +74 -0
  79. package/cart/cart-schema.js +69 -0
  80. package/cart/cart-schema.js.map +7 -0
  81. package/cart/cart-types.d.ts +132 -0
  82. package/cart/cart-types.js +8 -0
  83. package/cart/cart-types.js.map +7 -0
  84. package/cart/gateways/cart-constants.d.ts +1 -0
  85. package/cart/gateways/cart-constants.js +5 -0
  86. package/cart/gateways/cart-constants.js.map +7 -0
  87. package/cart/gateways/cart-gateway.d.ts +10 -0
  88. package/cart/gateways/cart-gateway.js +19 -0
  89. package/cart/gateways/cart-gateway.js.map +7 -0
  90. package/cart/gateways/cookie-cart-gateway.d.ts +28 -0
  91. package/cart/gateways/cookie-cart-gateway.js +109 -0
  92. package/cart/gateways/cookie-cart-gateway.js.map +7 -0
  93. package/cart/gateways/default-cart-gateway.d.ts +19 -0
  94. package/cart/gateways/default-cart-gateway.js +30 -0
  95. package/cart/gateways/default-cart-gateway.js.map +7 -0
  96. package/cart/gateways/resolver-cart-gateway.d.ts +2 -0
  97. package/cart/gateways/resolver-cart-gateway.js +109 -0
  98. package/cart/gateways/resolver-cart-gateway.js.map +7 -0
  99. package/cart/gateways/stripe-cart-gateway.d.ts +19 -0
  100. package/cart/gateways/stripe-cart-gateway.js +28 -0
  101. package/cart/gateways/stripe-cart-gateway.js.map +7 -0
  102. package/cart/hooks/use-add-to-cart.d.ts +63 -0
  103. package/cart/hooks/use-add-to-cart.js +124 -0
  104. package/cart/hooks/use-add-to-cart.js.map +7 -0
  105. package/cart/hooks/use-buy-now.d.ts +49 -0
  106. package/cart/hooks/use-buy-now.js +62 -0
  107. package/cart/hooks/use-buy-now.js.map +7 -0
  108. package/cart/utils/cart-utils.d.ts +43 -0
  109. package/cart/utils/cart-utils.js +46 -0
  110. package/cart/utils/cart-utils.js.map +7 -0
  111. package/cart/utils/cookie-cart-persistence.d.ts +55 -0
  112. package/cart/utils/cookie-cart-persistence.js +166 -0
  113. package/cart/utils/cookie-cart-persistence.js.map +7 -0
  114. package/cart/utils/variant-to-cart-line.d.ts +74 -0
  115. package/cart/utils/variant-to-cart-line.js +162 -0
  116. package/cart/utils/variant-to-cart-line.js.map +7 -0
  117. package/chunk-UJCSKKID.mjs +27 -0
  118. package/consent/consent-action.d.ts +9 -0
  119. package/consent/consent-action.js +25 -0
  120. package/consent/consent-action.js.map +7 -0
  121. package/consent/consent-actions.d.ts +28 -0
  122. package/consent/consent-actions.js +36 -0
  123. package/consent/consent-actions.js.map +7 -0
  124. package/consent/consent-store.d.ts +69 -0
  125. package/consent/consent-store.js +194 -0
  126. package/consent/consent-store.js.map +7 -0
  127. package/consent/cookie.d.ts +36 -0
  128. package/consent/cookie.js +82 -0
  129. package/consent/cookie.js.map +7 -0
  130. package/consent/replo-scripts.d.ts +23 -0
  131. package/consent/replo-scripts.js +102 -0
  132. package/consent/replo-scripts.js.map +7 -0
  133. package/consent/script-registration-store.d.ts +25 -0
  134. package/consent/script-registration-store.js +61 -0
  135. package/consent/script-registration-store.js.map +7 -0
  136. package/consent/script-snippets.d.ts +24 -0
  137. package/consent/script-snippets.js +122 -0
  138. package/consent/script-snippets.js.map +7 -0
  139. package/consent/types.d.ts +34 -0
  140. package/consent/types.js +1 -0
  141. package/consent/types.js.map +7 -0
  142. package/consent/window-api.d.ts +41 -0
  143. package/consent/window-api.js +73 -0
  144. package/consent/window-api.js.map +7 -0
  145. package/env.d.ts +9 -0
  146. package/env.js +42 -0
  147. package/env.js.map +7 -0
  148. package/gateways/canopy-api.d.ts +83 -0
  149. package/gateways/canopy-api.js +197 -0
  150. package/gateways/canopy-api.js.map +7 -0
  151. package/hooks/use-client-value.d.ts +10 -0
  152. package/hooks/use-client-value.js +12 -0
  153. package/hooks/use-client-value.js.map +7 -0
  154. package/hooks/use-formatted-price.d.ts +15 -0
  155. package/hooks/use-formatted-price.js +20 -0
  156. package/hooks/use-formatted-price.js.map +7 -0
  157. package/lib/canopy-error.d.ts +25 -0
  158. package/lib/canopy-error.js +24 -0
  159. package/lib/canopy-error.js.map +7 -0
  160. package/lib/get-query-client.d.ts +2 -0
  161. package/lib/get-query-client.js +34 -0
  162. package/lib/get-query-client.js.map +7 -0
  163. package/lib/integration-utils.d.ts +2 -0
  164. package/lib/integration-utils.js +9 -0
  165. package/lib/integration-utils.js.map +7 -0
  166. package/lib/shopify-utils.d.ts +10 -0
  167. package/lib/shopify-utils.js +7 -0
  168. package/lib/shopify-utils.js.map +7 -0
  169. package/lib/url-utils.d.ts +4 -0
  170. package/lib/url-utils.js +8 -0
  171. package/lib/url-utils.js.map +7 -0
  172. package/loaders/collection-loader.d.ts +11 -0
  173. package/loaders/collection-loader.js +55 -0
  174. package/loaders/collection-loader.js.map +7 -0
  175. package/loaders/collection-products-loader.d.ts +12 -0
  176. package/loaders/collection-products-loader.js +58 -0
  177. package/loaders/collection-products-loader.js.map +7 -0
  178. package/loaders/contentful-entries-loader.d.ts +17 -0
  179. package/loaders/contentful-entries-loader.js +78 -0
  180. package/loaders/contentful-entries-loader.js.map +7 -0
  181. package/loaders/contentful-entry-loader.d.ts +14 -0
  182. package/loaders/contentful-entry-loader.js +60 -0
  183. package/loaders/contentful-entry-loader.js.map +7 -0
  184. package/loaders/invoke-loader-action.d.ts +11 -0
  185. package/loaders/invoke-loader-action.js +14 -0
  186. package/loaders/invoke-loader-action.js.map +7 -0
  187. package/loaders/invoke-loader-server.d.ts +18 -0
  188. package/loaders/invoke-loader-server.js +40 -0
  189. package/loaders/invoke-loader-server.js.map +7 -0
  190. package/loaders/invoke-loader.d.ts +16 -0
  191. package/loaders/invoke-loader.js +37 -0
  192. package/loaders/invoke-loader.js.map +7 -0
  193. package/loaders/levanta-product-loader.d.ts +11 -0
  194. package/loaders/levanta-product-loader.js +53 -0
  195. package/loaders/levanta-product-loader.js.map +7 -0
  196. package/loaders/loader-error-boundary.d.ts +25 -0
  197. package/loaders/loader-error-boundary.js +24 -0
  198. package/loaders/loader-error-boundary.js.map +7 -0
  199. package/loaders/loader-keys.d.ts +34 -0
  200. package/loaders/loader-keys.js +26 -0
  201. package/loaders/loader-keys.js.map +7 -0
  202. package/loaders/loader-schemas.d.ts +946 -0
  203. package/loaders/loader-schemas.js +353 -0
  204. package/loaders/loader-schemas.js.map +7 -0
  205. package/loaders/metaobject-loader.d.ts +12 -0
  206. package/loaders/metaobject-loader.js +54 -0
  207. package/loaders/metaobject-loader.js.map +7 -0
  208. package/loaders/metaobjects-loader.d.ts +14 -0
  209. package/loaders/metaobjects-loader.js +60 -0
  210. package/loaders/metaobjects-loader.js.map +7 -0
  211. package/loaders/okendo-review-aggregate-loader.d.ts +17 -0
  212. package/loaders/okendo-review-aggregate-loader.js +56 -0
  213. package/loaders/okendo-review-aggregate-loader.js.map +7 -0
  214. package/loaders/okendo-reviews-loader.d.ts +23 -0
  215. package/loaders/okendo-reviews-loader.js +54 -0
  216. package/loaders/okendo-reviews-loader.js.map +7 -0
  217. package/loaders/prefetch-loaders.d.ts +33 -0
  218. package/loaders/prefetch-loaders.js +46 -0
  219. package/loaders/prefetch-loaders.js.map +7 -0
  220. package/loaders/preview-placeholder.d.ts +25 -0
  221. package/loaders/preview-placeholder.js +222 -0
  222. package/loaders/preview-placeholder.js.map +7 -0
  223. package/loaders/product-loader-args.d.ts +21 -0
  224. package/loaders/product-loader-args.js +17 -0
  225. package/loaders/product-loader-args.js.map +7 -0
  226. package/loaders/product-loader.d.ts +10 -0
  227. package/loaders/product-loader.js +52 -0
  228. package/loaders/product-loader.js.map +7 -0
  229. package/loaders/rebuy-recommendations-loader.d.ts +165 -0
  230. package/loaders/rebuy-recommendations-loader.js +57 -0
  231. package/loaders/rebuy-recommendations-loader.js.map +7 -0
  232. package/loaders/reviews-io-review-aggregate-loader.d.ts +12 -0
  233. package/loaders/reviews-io-review-aggregate-loader.js +43 -0
  234. package/loaders/reviews-io-review-aggregate-loader.js.map +7 -0
  235. package/loaders/reviews-io-reviews-loader.d.ts +14 -0
  236. package/loaders/reviews-io-reviews-loader.js +65 -0
  237. package/loaders/reviews-io-reviews-loader.js.map +7 -0
  238. package/loaders/smile-earning-rules-loader.d.ts +19 -0
  239. package/loaders/smile-earning-rules-loader.js +44 -0
  240. package/loaders/smile-earning-rules-loader.js.map +7 -0
  241. package/loaders/smile-rewards-loader.d.ts +19 -0
  242. package/loaders/smile-rewards-loader.js +44 -0
  243. package/loaders/smile-rewards-loader.js.map +7 -0
  244. package/loaders/smile-vip-tiers-loader.d.ts +18 -0
  245. package/loaders/smile-vip-tiers-loader.js +43 -0
  246. package/loaders/smile-vip-tiers-loader.js.map +7 -0
  247. package/loaders/statsig-experiment-loader.d.ts +20 -0
  248. package/loaders/statsig-experiment-loader.js +81 -0
  249. package/loaders/statsig-experiment-loader.js.map +7 -0
  250. package/loaders/yotpo-review-aggregate-loader.d.ts +12 -0
  251. package/loaders/yotpo-review-aggregate-loader.js +50 -0
  252. package/loaders/yotpo-review-aggregate-loader.js.map +7 -0
  253. package/loaders/yotpo-reviews-loader.d.ts +14 -0
  254. package/loaders/yotpo-reviews-loader.js +65 -0
  255. package/loaders/yotpo-reviews-loader.js.map +7 -0
  256. package/money.d.ts +9 -0
  257. package/money.js +13 -0
  258. package/money.js.map +7 -0
  259. package/next/config.d.ts +4 -0
  260. package/next/config.js +91 -0
  261. package/next/config.js.map +7 -0
  262. package/next/image-loader.d.ts +2 -0
  263. package/next/image-loader.js +52 -0
  264. package/next/image-loader.js.map +7 -0
  265. package/next/open-next.d.ts +2 -0
  266. package/next/open-next.js +24 -0
  267. package/next/open-next.js.map +7 -0
  268. package/orders/order-status-schema.d.ts +161 -0
  269. package/orders/order-status-schema.js +83 -0
  270. package/orders/order-status-schema.js.map +7 -0
  271. package/orders/order-status.d.ts +11 -0
  272. package/orders/order-status.js +41 -0
  273. package/orders/order-status.js.map +7 -0
  274. package/package.json +216 -0
  275. package/providers/query-provider.d.ts +1 -0
  276. package/providers/query-provider.js +12 -0
  277. package/providers/query-provider.js.map +7 -0
  278. package/providers/replo-provider.d.ts +1 -0
  279. package/providers/replo-provider.js +22 -0
  280. package/providers/replo-provider.js.map +7 -0
@@ -0,0 +1,197 @@
1
+ import { z } from "zod";
2
+ import { cartSchema } from "../cart/cart-schema";
3
+ import { getEnv } from "../env";
4
+ import { CanopyError } from "../lib/canopy-error";
5
+ import { buildBaseUrl } from "../lib/url-utils";
6
+ class CanopyApiGatewayError extends CanopyError {
7
+ statusCode;
8
+ constructor(config) {
9
+ const resolvedStatusCode = config.statusCode ?? 500;
10
+ super({
11
+ message: config.message,
12
+ cause: config.cause,
13
+ additionalData: {
14
+ ...config.additionalData,
15
+ statusCode: resolvedStatusCode
16
+ }
17
+ });
18
+ this.statusCode = resolvedStatusCode;
19
+ }
20
+ }
21
+ class UnknownCanopyApiError extends CanopyApiGatewayError {
22
+ constructor(config) {
23
+ super({ ...config, statusCode: 502 });
24
+ }
25
+ }
26
+ const loaderInvokeResponseSchema = z.object({
27
+ data: z.unknown(),
28
+ meta: z.object({
29
+ cache: z.object({
30
+ hit: z.boolean(),
31
+ ttlSeconds: z.number()
32
+ })
33
+ }).optional()
34
+ });
35
+ class CanopyApiGateway {
36
+ apiHost;
37
+ baseUrl;
38
+ constructor(apiHost) {
39
+ this.apiHost = apiHost;
40
+ this.baseUrl = buildBaseUrl(apiHost);
41
+ }
42
+ static REQUEST_TIMEOUT_MILLISECONDS = 1e4;
43
+ static async setup() {
44
+ const env = await getEnv();
45
+ return new CanopyApiGateway(env.CANOPY_API_HOST);
46
+ }
47
+ async _post(url, body) {
48
+ const abortController = new AbortController();
49
+ const timeoutId = setTimeout(() => {
50
+ abortController.abort();
51
+ }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);
52
+ try {
53
+ return await fetch(url, {
54
+ method: "POST",
55
+ headers: { "Content-Type": "application/json" },
56
+ body: JSON.stringify(body),
57
+ signal: abortController.signal
58
+ });
59
+ } catch (error) {
60
+ let message = `Failed to POST to canopy-api at ${url}: ${error}`;
61
+ if (process.env.NODE_ENV !== "production") {
62
+ message += `
63
+
64
+ apiHost: ${this.apiHost}
65
+ If you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;
66
+ }
67
+ throw new UnknownCanopyApiError({ message, cause: error });
68
+ } finally {
69
+ clearTimeout(timeoutId);
70
+ }
71
+ }
72
+ async _get(url) {
73
+ const abortController = new AbortController();
74
+ const timeoutId = setTimeout(() => {
75
+ abortController.abort();
76
+ }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);
77
+ try {
78
+ return await fetch(url, { signal: abortController.signal });
79
+ } catch (error) {
80
+ let message = `Failed to GET from canopy-api at ${url}: ${error}`;
81
+ if (process.env.NODE_ENV !== "production") {
82
+ message += `
83
+
84
+ apiHost: ${this.apiHost}
85
+ If you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;
86
+ }
87
+ throw new UnknownCanopyApiError({ message, cause: error });
88
+ } finally {
89
+ clearTimeout(timeoutId);
90
+ }
91
+ }
92
+ async invokeLoader({
93
+ loaderKey,
94
+ args
95
+ }) {
96
+ const env = await getEnv();
97
+ const response = await this._post(`${this.baseUrl}/api/v1/loaders/invoke`, {
98
+ projectId: env.PROJECT_ID,
99
+ loaderKey,
100
+ args
101
+ });
102
+ if (!response.ok) {
103
+ const errorBody = await response.text().catch(() => "");
104
+ throw new CanopyApiGatewayError({
105
+ message: `Failed to invoke loader ${loaderKey}: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : ""}`,
106
+ statusCode: response.status
107
+ });
108
+ }
109
+ return loaderInvokeResponseSchema.parse(await response.json());
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Cart operations (canopy-api-backed ExternalCart)
113
+ // ---------------------------------------------------------------------------
114
+ async _cartPost(path, body) {
115
+ const env = await getEnv();
116
+ const url = `${this.baseUrl}/api/v1/cart${path}`;
117
+ const abortController = new AbortController();
118
+ const timeoutId = setTimeout(() => {
119
+ abortController.abort();
120
+ }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);
121
+ let response;
122
+ try {
123
+ response = await fetch(url, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json"
127
+ },
128
+ body: JSON.stringify({ ...body, projectId: env.PROJECT_ID }),
129
+ signal: abortController.signal
130
+ });
131
+ } catch (error) {
132
+ throw new UnknownCanopyApiError({
133
+ message: `Failed to POST cart operation to ${url}: ${error}`,
134
+ cause: error
135
+ });
136
+ } finally {
137
+ clearTimeout(timeoutId);
138
+ }
139
+ if (!response.ok) {
140
+ const text = await response.text().catch(() => "");
141
+ throw new CanopyApiGatewayError({
142
+ message: `Cart operation ${path} failed: ${response.status} ${response.statusText} \u2014 ${text}`,
143
+ statusCode: response.status
144
+ });
145
+ }
146
+ const json = await response.json();
147
+ return cartSchema.parse(json.data);
148
+ }
149
+ async cartGet({ cartId }) {
150
+ try {
151
+ return await this._cartPost("/get", { cartId });
152
+ } catch (error) {
153
+ if (error instanceof CanopyApiGatewayError && error.statusCode === 404) {
154
+ return null;
155
+ }
156
+ throw error;
157
+ }
158
+ }
159
+ async cartCreate(options) {
160
+ return this._cartPost("/create", {
161
+ lines: options?.lines,
162
+ discountCodes: options?.discountCodes
163
+ });
164
+ }
165
+ async cartAddLines({
166
+ cartId,
167
+ lines
168
+ }) {
169
+ return this._cartPost("/add-lines", { cartId, lines });
170
+ }
171
+ async cartUpdateLines({
172
+ cartId,
173
+ lines
174
+ }) {
175
+ return this._cartPost("/update-lines", { cartId, lines });
176
+ }
177
+ async cartRemoveLines({
178
+ cartId,
179
+ lineIds
180
+ }) {
181
+ return this._cartPost("/remove-lines", { cartId, lineIds });
182
+ }
183
+ async cartUpdateDiscountCodes({
184
+ cartId,
185
+ discountCodes
186
+ }) {
187
+ return this._cartPost("/update-discount-codes", { cartId, discountCodes });
188
+ }
189
+ }
190
+ async function getCanopyApiGateway() {
191
+ return CanopyApiGateway.setup();
192
+ }
193
+ export {
194
+ CanopyApiGatewayError,
195
+ getCanopyApiGateway
196
+ };
197
+ //# sourceMappingURL=canopy-api.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../gateways/canopy-api.ts"],
4
+ "sourcesContent": ["import type { Cart } from \"../cart/cart-schema\";\n\nimport { z } from \"zod\";\n\nimport { cartSchema } from \"../cart/cart-schema\";\nimport { getEnv } from \"../env\";\nimport { CanopyError } from \"../lib/canopy-error\";\nimport { buildBaseUrl } from \"../lib/url-utils\";\n\ntype CanopyApiGatewayErrorConfig = {\n message: string;\n statusCode?: number;\n cause?: unknown;\n additionalData?: Record<string, unknown>;\n};\n\nexport class CanopyApiGatewayError extends CanopyError {\n statusCode: number;\n\n constructor(config: CanopyApiGatewayErrorConfig) {\n const resolvedStatusCode = config.statusCode ?? 500;\n super({\n message: config.message,\n cause: config.cause,\n additionalData: {\n ...config.additionalData,\n statusCode: resolvedStatusCode,\n },\n });\n this.statusCode = resolvedStatusCode;\n }\n}\n\nclass UnknownCanopyApiError extends CanopyApiGatewayError {\n constructor(config: Omit<CanopyApiGatewayErrorConfig, \"statusCode\">) {\n super({ ...config, statusCode: 502 });\n }\n}\n\nconst loaderInvokeResponseSchema = z.object({\n data: z.unknown(),\n meta: z\n .object({\n cache: z.object({\n hit: z.boolean(),\n ttlSeconds: z.number(),\n }),\n })\n .optional(),\n});\n\nclass CanopyApiGateway {\n private apiHost: string;\n private baseUrl: string;\n constructor(apiHost: string) {\n this.apiHost = apiHost;\n this.baseUrl = buildBaseUrl(apiHost);\n }\n\n private static readonly REQUEST_TIMEOUT_MILLISECONDS = 10_000;\n\n static async setup() {\n const env = await getEnv();\n\n return new CanopyApiGateway(env.CANOPY_API_HOST);\n }\n\n private async _post(url: string, body: unknown) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: abortController.signal,\n });\n } catch (error) {\n let message = `Failed to POST to canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async _get(url: string) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, { signal: abortController.signal });\n } catch (error) {\n let message = `Failed to GET from canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async invokeLoader({\n loaderKey,\n args,\n }: {\n loaderKey: string;\n args: Record<string, unknown>;\n }) {\n const env = await getEnv();\n const response = await this._post(`${this.baseUrl}/api/v1/loaders/invoke`, {\n projectId: env.PROJECT_ID,\n loaderKey,\n args,\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Failed to invoke loader ${loaderKey}: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : \"\"}`,\n statusCode: response.status,\n });\n }\n\n return loaderInvokeResponseSchema.parse(await response.json());\n }\n\n // ---------------------------------------------------------------------------\n // Cart operations (canopy-api-backed ExternalCart)\n // ---------------------------------------------------------------------------\n\n private async _cartPost(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Cart> {\n const env = await getEnv();\n const url = `${this.baseUrl}/api/v1/cart${path}`;\n\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ ...body, projectId: env.PROJECT_ID }),\n signal: abortController.signal,\n });\n } catch (error) {\n throw new UnknownCanopyApiError({\n message: `Failed to POST cart operation to ${url}: ${error}`,\n cause: error,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Cart operation ${path} failed: ${response.status} ${response.statusText} \u2014 ${text}`,\n statusCode: response.status,\n });\n }\n\n // Legacy cast - may or may not be necessary\n // eslint-disable-next-line replo/no-as-cast\n const json = (await response.json()) as { data: unknown };\n return cartSchema.parse(json.data);\n }\n\n async cartGet({ cartId }: { cartId: string }): Promise<Cart | null> {\n try {\n return await this._cartPost(\"/get\", { cartId });\n } catch (error) {\n if (error instanceof CanopyApiGatewayError && error.statusCode === 404) {\n return null;\n }\n throw error;\n }\n }\n\n async cartCreate(options?: {\n lines?: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n discountCodes?: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/create\", {\n lines: options?.lines,\n discountCodes: options?.discountCodes,\n });\n }\n\n async cartAddLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/add-lines\", { cartId, lines });\n }\n\n async cartUpdateLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n lineId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-lines\", { cartId, lines });\n }\n\n async cartRemoveLines({\n cartId,\n lineIds,\n }: {\n cartId: string;\n lineIds: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/remove-lines\", { cartId, lineIds });\n }\n\n async cartUpdateDiscountCodes({\n cartId,\n discountCodes,\n }: {\n cartId: string;\n discountCodes: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-discount-codes\", { cartId, discountCodes });\n }\n}\n\nexport async function getCanopyApiGateway() {\n return CanopyApiGateway.setup();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * NOTE (Max, 2025-09-15): This hook is to be used for values that aren't available on the server (such as window.location).
3
+ *
4
+ * This hook will use a dummy value both on the server & on hydration. Then,
5
+ * after hydration, this useEffect will update the value to the actual value.
6
+ *
7
+ * The useEffect doesn't have any dependencies - it's designed to only run once. So if you expect your read() function to return
8
+ * different values through the lifecycle of the component, you should not use this hook.
9
+ */
10
+ export declare function useClientValue<ValueType>(readValue: () => ValueType, initialValue: ValueType): ValueType;
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ function useClientValue(readValue, initialValue) {
3
+ const [value, setValue] = React.useState(initialValue);
4
+ React.useEffect(() => {
5
+ setValue(readValue());
6
+ }, []);
7
+ return value;
8
+ }
9
+ export {
10
+ useClientValue
11
+ };
12
+ //# sourceMappingURL=use-client-value.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../hooks/use-client-value.ts"],
4
+ "sourcesContent": ["import * as React from \"react\";\n\n/**\n * NOTE (Max, 2025-09-15): This hook is to be used for values that aren't available on the server (such as window.location).\n *\n * This hook will use a dummy value both on the server & on hydration. Then,\n * after hydration, this useEffect will update the value to the actual value.\n *\n * The useEffect doesn't have any dependencies - it's designed to only run once. So if you expect your read() function to return\n * different values through the lifecycle of the component, you should not use this hook.\n */\n\nexport function useClientValue<ValueType>(\n readValue: () => ValueType,\n initialValue: ValueType,\n) {\n const [value, setValue] = React.useState<ValueType>(initialValue);\n\n // eslint-disable-next-line replo/no-use-effect -- (Noah, 2026-07-20): Legacy effect - may or may not be necessary\n React.useEffect(() => {\n setValue(readValue());\n }, []);\n return value;\n}\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AAYhB,SAAS,eACd,WACA,cACA;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAoB,YAAY;AAGhE,QAAM,UAAU,MAAM;AACpB,aAAS,UAAU,CAAC;AAAA,EACtB,GAAG,CAAC,CAAC;AACL,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Use `useFormattedPrice` to format prices with proper currency and locale.
3
+ * @returns A formatted price string based on the price and currency code. It
4
+ * already takes into account the user's locale/browser.
5
+ * @example
6
+ * ```tsx
7
+ * import { useFormattedPrice } from "@replohq/sdk/hooks/use-formatted-price";
8
+ *
9
+ * const originalPrice = 1999; // integer minor units
10
+ * const originalPriceFormatted = useFormattedPrice(originalPrice, "USD");
11
+ * // Display the formatted price
12
+ * <p>{originalPriceFormatted}</p>;
13
+ * ```
14
+ */
15
+ export declare const useFormattedPrice: (price: number | null, currency: string | null) => string | null;
@@ -0,0 +1,20 @@
1
+ import { formatAmount } from "../_vendor/schemas/money.mjs";
2
+ import { useClientValue } from "./use-client-value";
3
+ const useFormattedPrice = (price, currency) => {
4
+ const navigatorLanguage = useClientValue(
5
+ () => window.navigator.language,
6
+ "en-US"
7
+ );
8
+ if (price === null) {
9
+ return null;
10
+ }
11
+ return formatAmount({
12
+ amount: price,
13
+ currencyCode: currency ?? "USD",
14
+ locale: navigatorLanguage
15
+ });
16
+ };
17
+ export {
18
+ useFormattedPrice
19
+ };
20
+ //# sourceMappingURL=use-formatted-price.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../hooks/use-formatted-price.ts"],
4
+ "sourcesContent": ["/**\n * Use useFormattedPrice() hook to display prices with proper currency symbols and formatting. Pass in a price in integer minor units (Stripe convention) and a currency code (e.g., 1999 and \"USD\") and it returns a formatted string like \"$19.99\".\n * @module\n */\nimport { formatAmount } from \"schemas/money\";\n\nimport { useClientValue } from \"./use-client-value\";\n\n/**\n * Use `useFormattedPrice` to format prices with proper currency and locale.\n * @returns A formatted price string based on the price and currency code. It\n * already takes into account the user's locale/browser.\n * @example\n * ```tsx\n * import { useFormattedPrice } from \"@replohq/sdk/hooks/use-formatted-price\";\n *\n * const originalPrice = 1999; // integer minor units\n * const originalPriceFormatted = useFormattedPrice(originalPrice, \"USD\");\n * // Display the formatted price\n * <p>{originalPriceFormatted}</p>;\n * ```\n */\nexport const useFormattedPrice = (\n price: number | null,\n currency: string | null,\n) => {\n const navigatorLanguage = useClientValue(\n () => window.navigator.language,\n \"en-US\",\n );\n\n // NOTE (Max, 2026-08-03): guard on null, not falsiness \u2014 0 is a real amount\n // (a free item), so `if (!price)` would render no price at all for it.\n if (price === null) {\n return null;\n }\n\n return formatAmount({\n amount: price,\n currencyCode: currency ?? \"USD\",\n locale: navigatorLanguage,\n });\n};\n"],
5
+ "mappings": "AAIA,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB;AAgBxB,MAAM,oBAAoB,CAC/B,OACA,aACG;AACH,QAAM,oBAAoB;AAAA,IACxB,MAAM,OAAO,UAAU;AAAA,IACvB;AAAA,EACF;AAIA,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AAEA,SAAO,aAAa;AAAA,IAClB,QAAQ;AAAA,IACR,cAAc,YAAY;AAAA,IAC1B,QAAQ;AAAA,EACV,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ export type CanopyErrorConfig = {
2
+ /**
3
+ * Message of the error. If not provided, defaults to the error's class name.
4
+ */
5
+ message?: string;
6
+ /**
7
+ * Additional context about the error (ids, contextual variables, etc.),
8
+ * surfaced when the error is logged.
9
+ */
10
+ additionalData?: Record<string, unknown>;
11
+ /**
12
+ * Original cause of the error, useful when catching and re-throwing with more
13
+ * context.
14
+ */
15
+ cause?: unknown;
16
+ };
17
+ /**
18
+ * Base error that all SDK errors inherit from. Deliberately minimal and
19
+ * self-contained; it does not depend on the `schemas` workspace package.
20
+ */
21
+ export declare abstract class CanopyError extends Error {
22
+ config: CanopyErrorConfig;
23
+ id: string;
24
+ constructor(config: CanopyErrorConfig);
25
+ }
@@ -0,0 +1,24 @@
1
+ class CanopyError extends Error {
2
+ config;
3
+ id;
4
+ constructor(config) {
5
+ super(config.message);
6
+ this.message = config.message ?? this.constructor.name;
7
+ this.config = config;
8
+ this.id = crypto.randomUUID();
9
+ const cause = config.cause;
10
+ if (cause) {
11
+ this.cause = cause;
12
+ if (cause instanceof CanopyError) {
13
+ this.config.additionalData = {
14
+ ...this.config.additionalData,
15
+ ...cause.config.additionalData
16
+ };
17
+ }
18
+ }
19
+ }
20
+ }
21
+ export {
22
+ CanopyError
23
+ };
24
+ //# sourceMappingURL=canopy-error.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../lib/canopy-error.ts"],
4
+ "sourcesContent": ["// NOTE (Ryan, 2026-07-23, REPL-27611): Local error base for the SDK's\n// published-page code. Mirrors the minimal surface of `schemas`' `ReploError`\n// (a `config` bag, a stable `id`, and cause chaining) that canopy's scoped\n// error classes actually use, so nothing shipped to a generated site imports\n// from `schemas`.\n\nexport type CanopyErrorConfig = {\n /**\n * Message of the error. If not provided, defaults to the error's class name.\n */\n message?: string;\n /**\n * Additional context about the error (ids, contextual variables, etc.),\n * surfaced when the error is logged.\n */\n additionalData?: Record<string, unknown>;\n /**\n * Original cause of the error, useful when catching and re-throwing with more\n * context.\n */\n cause?: unknown;\n};\n\n/**\n * Base error that all SDK errors inherit from. Deliberately minimal and\n * self-contained; it does not depend on the `schemas` workspace package.\n */\nexport abstract class CanopyError extends Error {\n config: CanopyErrorConfig;\n id: string;\n\n constructor(config: CanopyErrorConfig) {\n super(config.message);\n this.message = config.message ?? this.constructor.name;\n this.config = config;\n // crypto.randomUUID is natively available in browsers, Node, and the\n // Cloudflare Workers runtime \u2014 the three environments the SDK runs in.\n this.id = crypto.randomUUID();\n\n const cause = config.cause;\n if (cause) {\n this.cause = cause;\n if (cause instanceof CanopyError) {\n this.config.additionalData = {\n ...this.config.additionalData,\n ...cause.config.additionalData,\n };\n }\n }\n }\n}\n"],
5
+ "mappings": "AA2BO,MAAe,oBAAoB,MAAM;AAAA,EAC9C;AAAA,EACA;AAAA,EAEA,YAAY,QAA2B;AACrC,UAAM,OAAO,OAAO;AACpB,SAAK,UAAU,OAAO,WAAW,KAAK,YAAY;AAClD,SAAK,SAAS;AAGd,SAAK,KAAK,OAAO,WAAW;AAE5B,UAAM,QAAQ,OAAO;AACrB,QAAI,OAAO;AACT,WAAK,QAAQ;AACb,UAAI,iBAAiB,aAAa;AAChC,aAAK,OAAO,iBAAiB;AAAA,UAC3B,GAAG,KAAK,OAAO;AAAA,UACf,GAAG,MAAM,OAAO;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ import { QueryClient } from "@tanstack/react-query";
2
+ export declare function getQueryClient(): QueryClient;
@@ -0,0 +1,34 @@
1
+ import {
2
+ defaultShouldDehydrateQuery,
3
+ isServer,
4
+ QueryClient
5
+ } from "@tanstack/react-query";
6
+ const STALE_TIME_MILLISECONDS = 60 * 1e3;
7
+ const makeQueryClient = () => {
8
+ return new QueryClient({
9
+ defaultOptions: {
10
+ queries: {
11
+ staleTime: STALE_TIME_MILLISECONDS
12
+ },
13
+ dehydrate: {
14
+ shouldDehydrateQuery: (query) => {
15
+ return defaultShouldDehydrateQuery(query) || query.state.status === "pending";
16
+ }
17
+ }
18
+ }
19
+ });
20
+ };
21
+ let browserQueryClient;
22
+ function getQueryClient() {
23
+ if (isServer) {
24
+ return makeQueryClient();
25
+ }
26
+ if (!browserQueryClient) {
27
+ browserQueryClient = makeQueryClient();
28
+ }
29
+ return browserQueryClient;
30
+ }
31
+ export {
32
+ getQueryClient
33
+ };
34
+ //# sourceMappingURL=get-query-client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../lib/get-query-client.ts"],
4
+ "sourcesContent": ["import {\n defaultShouldDehydrateQuery,\n isServer,\n QueryClient,\n} from \"@tanstack/react-query\";\n\nconst STALE_TIME_MILLISECONDS = 60 * 1000;\n\nconst makeQueryClient = () => {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: STALE_TIME_MILLISECONDS,\n },\n dehydrate: {\n shouldDehydrateQuery: (query) => {\n return (\n defaultShouldDehydrateQuery(query) ||\n query.state.status === \"pending\"\n );\n },\n },\n },\n });\n};\n\nlet browserQueryClient: QueryClient | undefined;\n\nexport function getQueryClient() {\n if (isServer) {\n return makeQueryClient();\n }\n\n if (!browserQueryClient) {\n browserQueryClient = makeQueryClient();\n }\n\n return browserQueryClient;\n}\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,0BAA0B,KAAK;AAErC,MAAM,kBAAkB,MAAM;AAC5B,SAAO,IAAI,YAAY;AAAA,IACrB,gBAAgB;AAAA,MACd,SAAS;AAAA,QACP,WAAW;AAAA,MACb;AAAA,MACA,WAAW;AAAA,QACT,sBAAsB,CAAC,UAAU;AAC/B,iBACE,4BAA4B,KAAK,KACjC,MAAM,MAAM,WAAW;AAAA,QAE3B;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAI;AAEG,SAAS,iBAAiB;AAC/B,MAAI,UAAU;AACZ,WAAO,gBAAgB;AAAA,EACzB;AAEA,MAAI,CAAC,oBAAoB;AACvB,yBAAqB,gBAAgB;AAAA,EACvC;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export type CheckoutProvider = "shopify" | "stripe" | "none";
2
+ export declare function getCheckoutProvider(): Promise<CheckoutProvider>;
@@ -0,0 +1,9 @@
1
+ import { getEnv } from "../env";
2
+ async function getCheckoutProvider() {
3
+ const env = await getEnv();
4
+ return env.CHECKOUT_PROVIDER ?? "none";
5
+ }
6
+ export {
7
+ getCheckoutProvider
8
+ };
9
+ //# sourceMappingURL=integration-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../lib/integration-utils.ts"],
4
+ "sourcesContent": ["import { getEnv } from \"../env\";\n\nexport type CheckoutProvider = \"shopify\" | \"stripe\" | \"none\";\n\nexport async function getCheckoutProvider(): Promise<CheckoutProvider> {\n const env = await getEnv();\n return env.CHECKOUT_PROVIDER ?? \"none\";\n}\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AAIvB,eAAsB,sBAAiD;AACrE,QAAM,MAAM,MAAM,OAAO;AACzB,SAAO,IAAI,qBAAqB;AAClC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Utility functions for working with Shopify IDs and GIDs
3
+ */
4
+ /**
5
+ * Converts a legacy resource ID to a Shopify GraphQL ID
6
+ * @param id - The legacy resource ID (e.g., "123456")
7
+ * @param type - The Shopify resource type
8
+ * @returns The full Shopify GID (e.g., "gid://shopify/ProductVariant/123456")
9
+ */
10
+ export declare function convertLegacyResourceIdToGraphQLId(id: string, type: "Product" | "ProductVariant"): string;
@@ -0,0 +1,7 @@
1
+ function convertLegacyResourceIdToGraphQLId(id, type) {
2
+ return `gid://shopify/${type}/${id}`;
3
+ }
4
+ export {
5
+ convertLegacyResourceIdToGraphQLId
6
+ };
7
+ //# sourceMappingURL=shopify-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../lib/shopify-utils.ts"],
4
+ "sourcesContent": ["/**\n * Utility functions for working with Shopify IDs and GIDs\n */\n\n/**\n * Converts a legacy resource ID to a Shopify GraphQL ID\n * @param id - The legacy resource ID (e.g., \"123456\")\n * @param type - The Shopify resource type\n * @returns The full Shopify GID (e.g., \"gid://shopify/ProductVariant/123456\")\n */\nexport function convertLegacyResourceIdToGraphQLId(\n id: string,\n type: \"Product\" | \"ProductVariant\",\n): string {\n return `gid://shopify/${type}/${id}`;\n}\n"],
5
+ "mappings": "AAUO,SAAS,mCACd,IACA,MACQ;AACR,SAAO,iBAAiB,IAAI,IAAI,EAAE;AACpC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Builds a base URL from an API host, using http for local hosts and https otherwise.
3
+ */
4
+ export declare function buildBaseUrl(apiHost: string): string;
@@ -0,0 +1,8 @@
1
+ function buildBaseUrl(apiHost) {
2
+ const isLocal = apiHost.startsWith("localhost") || apiHost.startsWith("127.0.0.1") || apiHost.startsWith("host.docker.internal");
3
+ return `${isLocal ? "http" : "https"}://${apiHost}`;
4
+ }
5
+ export {
6
+ buildBaseUrl
7
+ };
8
+ //# sourceMappingURL=url-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../lib/url-utils.ts"],
4
+ "sourcesContent": ["/**\n * Builds a base URL from an API host, using http for local hosts and https otherwise.\n */\nexport function buildBaseUrl(apiHost: string): string {\n const isLocal =\n apiHost.startsWith(\"localhost\") ||\n apiHost.startsWith(\"127.0.0.1\") ||\n apiHost.startsWith(\"host.docker.internal\");\n return `${isLocal ? \"http\" : \"https\"}://${apiHost}`;\n}\n"],
5
+ "mappings": "AAGO,SAAS,aAAa,SAAyB;AACpD,QAAM,UACJ,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,sBAAsB;AAC3C,SAAO,GAAG,UAAU,SAAS,OAAO,MAAM,OAAO;AACnD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ import type { FullCollection } from "./loader-schemas";
3
+ type CollectionLoaderProps = {
4
+ loaderKey: string;
5
+ handle?: string;
6
+ collectionGid?: string;
7
+ children: (data: FullCollection) => ReactNode;
8
+ fallback?: ReactNode;
9
+ };
10
+ export declare function CollectionLoader({ loaderKey, handle, collectionGid, children, fallback, }: CollectionLoaderProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { Fragment, jsx } from "react/jsx-runtime";
3
+ import { Suspense } from "react";
4
+ import { useSuspenseQuery } from "@tanstack/react-query";
5
+ import { invokeLoader } from "./invoke-loader";
6
+ import { LoaderErrorBoundary } from "./loader-error-boundary";
7
+ import { fullCollectionSchema } from "./loader-schemas";
8
+ import { useReportPreviewPlaceholderRender } from "./preview-placeholder";
9
+ function CollectionLoaderInner({
10
+ loaderKey,
11
+ handle,
12
+ collectionGid,
13
+ children
14
+ }) {
15
+ const queryResult = useSuspenseQuery({
16
+ queryKey: [loaderKey, { handle, collectionGid }],
17
+ queryFn: async () => {
18
+ const loadedData = await invokeLoader({
19
+ loaderKey,
20
+ args: { handle, collectionGid }
21
+ });
22
+ return fullCollectionSchema.parse(loadedData);
23
+ }
24
+ });
25
+ useReportPreviewPlaceholderRender(queryResult.data);
26
+ return /* @__PURE__ */ jsx(Fragment, { children: children(queryResult.data) });
27
+ }
28
+ function CollectionLoader({
29
+ loaderKey,
30
+ handle,
31
+ collectionGid,
32
+ children,
33
+ fallback
34
+ }) {
35
+ return /* @__PURE__ */ jsx(
36
+ LoaderErrorBoundary,
37
+ {
38
+ fallback: fallback ?? null,
39
+ resetKey: `${loaderKey}:${handle ?? ""}:${collectionGid ?? ""}`,
40
+ children: /* @__PURE__ */ jsx(Suspense, { fallback: fallback ?? null, children: /* @__PURE__ */ jsx(
41
+ CollectionLoaderInner,
42
+ {
43
+ loaderKey,
44
+ handle,
45
+ collectionGid,
46
+ children
47
+ }
48
+ ) })
49
+ }
50
+ );
51
+ }
52
+ export {
53
+ CollectionLoader
54
+ };
55
+ //# sourceMappingURL=collection-loader.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../loaders/collection-loader.tsx"],
4
+ "sourcesContent": ["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport type { FullCollection } from \"./loader-schemas\";\n\nimport { Suspense } from \"react\";\n\nimport { useSuspenseQuery } from \"@tanstack/react-query\";\n\nimport { invokeLoader } from \"./invoke-loader\";\nimport { LoaderErrorBoundary } from \"./loader-error-boundary\";\nimport { fullCollectionSchema } from \"./loader-schemas\";\nimport { useReportPreviewPlaceholderRender } from \"./preview-placeholder\";\n\ntype CollectionLoaderProps = {\n loaderKey: string;\n handle?: string;\n collectionGid?: string;\n children: (data: FullCollection) => ReactNode;\n fallback?: ReactNode;\n};\n\nfunction CollectionLoaderInner({\n loaderKey,\n handle,\n collectionGid,\n children,\n}: Omit<CollectionLoaderProps, \"fallback\">) {\n const queryResult = useSuspenseQuery({\n queryKey: [loaderKey, { handle, collectionGid }],\n queryFn: async () => {\n const loadedData = await invokeLoader({\n loaderKey,\n args: { handle, collectionGid },\n });\n return fullCollectionSchema.parse(loadedData);\n },\n });\n useReportPreviewPlaceholderRender(queryResult.data);\n\n return <>{children(queryResult.data)}</>;\n}\n\nexport function CollectionLoader({\n loaderKey,\n handle,\n collectionGid,\n children,\n fallback,\n}: CollectionLoaderProps) {\n return (\n <LoaderErrorBoundary\n fallback={fallback ?? null}\n resetKey={`${loaderKey}:${handle ?? \"\"}:${collectionGid ?? \"\"}`}\n >\n <Suspense fallback={fallback ?? null}>\n <CollectionLoaderInner\n loaderKey={loaderKey}\n handle={handle}\n collectionGid={collectionGid}\n >\n {children}\n </CollectionLoaderInner>\n </Suspense>\n </LoaderErrorBoundary>\n );\n}\n"],
5
+ "mappings": ";AAwCS;AAnCT,SAAS,gBAAgB;AAEzB,SAAS,wBAAwB;AAEjC,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,SAAS,yCAAyC;AAUlD,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4C;AAC1C,QAAM,cAAc,iBAAiB;AAAA,IACnC,UAAU,CAAC,WAAW,EAAE,QAAQ,cAAc,CAAC;AAAA,IAC/C,SAAS,YAAY;AACnB,YAAM,aAAa,MAAM,aAAa;AAAA,QACpC;AAAA,QACA,MAAM,EAAE,QAAQ,cAAc;AAAA,MAChC,CAAC;AACD,aAAO,qBAAqB,MAAM,UAAU;AAAA,IAC9C;AAAA,EACF,CAAC;AACD,oCAAkC,YAAY,IAAI;AAElD,SAAO,gCAAG,mBAAS,YAAY,IAAI,GAAE;AACvC;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,YAAY;AAAA,MACtB,UAAU,GAAG,SAAS,IAAI,UAAU,EAAE,IAAI,iBAAiB,EAAE;AAAA,MAE7D,8BAAC,YAAS,UAAU,YAAY,MAC9B;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UAEC;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from "react";
2
+ import type { PaginatedProducts } from "./loader-schemas";
3
+ type CollectionProductsLoaderProps = {
4
+ loaderKey: string;
5
+ collectionGid: string;
6
+ first?: number;
7
+ after?: string;
8
+ children: (data: PaginatedProducts) => ReactNode;
9
+ fallback?: ReactNode;
10
+ };
11
+ export declare function CollectionProductsLoader({ loaderKey, collectionGid, first, after, children, fallback, }: CollectionProductsLoaderProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};