shopify-nuxt 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +275 -0
  2. package/dist/module.d.mts +7 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +134 -0
  5. package/dist/runtime/components/ShopifyAppProvider.d.vue.ts +13 -0
  6. package/dist/runtime/components/ShopifyAppProvider.vue +11 -0
  7. package/dist/runtime/components/ShopifyAppProvider.vue.d.ts +13 -0
  8. package/dist/runtime/components/polaris/ShAppNav.d.vue.ts +13 -0
  9. package/dist/runtime/components/polaris/ShAppNav.vue +9 -0
  10. package/dist/runtime/components/polaris/ShAppNav.vue.d.ts +13 -0
  11. package/dist/runtime/components/polaris/ShAvatar.d.vue.ts +19 -0
  12. package/dist/runtime/components/polaris/ShAvatar.vue +15 -0
  13. package/dist/runtime/components/polaris/ShAvatar.vue.d.ts +19 -0
  14. package/dist/runtime/components/polaris/ShBadge.d.vue.ts +20 -0
  15. package/dist/runtime/components/polaris/ShBadge.vue +16 -0
  16. package/dist/runtime/components/polaris/ShBadge.vue.d.ts +20 -0
  17. package/dist/runtime/components/polaris/ShBanner.d.vue.ts +19 -0
  18. package/dist/runtime/components/polaris/ShBanner.vue +15 -0
  19. package/dist/runtime/components/polaris/ShBanner.vue.d.ts +19 -0
  20. package/dist/runtime/components/polaris/ShBox.d.vue.ts +39 -0
  21. package/dist/runtime/components/polaris/ShBox.vue +35 -0
  22. package/dist/runtime/components/polaris/ShBox.vue.d.ts +39 -0
  23. package/dist/runtime/components/polaris/ShButton.d.vue.ts +28 -0
  24. package/dist/runtime/components/polaris/ShButton.vue +24 -0
  25. package/dist/runtime/components/polaris/ShButton.vue.d.ts +28 -0
  26. package/dist/runtime/components/polaris/ShButtonGroup.d.vue.ts +17 -0
  27. package/dist/runtime/components/polaris/ShButtonGroup.vue +13 -0
  28. package/dist/runtime/components/polaris/ShButtonGroup.vue.d.ts +17 -0
  29. package/dist/runtime/components/polaris/ShCheckbox.d.vue.ts +28 -0
  30. package/dist/runtime/components/polaris/ShCheckbox.vue +24 -0
  31. package/dist/runtime/components/polaris/ShCheckbox.vue.d.ts +28 -0
  32. package/dist/runtime/components/polaris/ShChip.d.vue.ts +17 -0
  33. package/dist/runtime/components/polaris/ShChip.vue +13 -0
  34. package/dist/runtime/components/polaris/ShChip.vue.d.ts +17 -0
  35. package/dist/runtime/components/polaris/ShChoice.d.vue.ts +20 -0
  36. package/dist/runtime/components/polaris/ShChoice.vue +16 -0
  37. package/dist/runtime/components/polaris/ShChoice.vue.d.ts +20 -0
  38. package/dist/runtime/components/polaris/ShChoiceList.d.vue.ts +23 -0
  39. package/dist/runtime/components/polaris/ShChoiceList.vue +19 -0
  40. package/dist/runtime/components/polaris/ShChoiceList.vue.d.ts +23 -0
  41. package/dist/runtime/components/polaris/ShClickable.d.vue.ts +48 -0
  42. package/dist/runtime/components/polaris/ShClickable.vue +44 -0
  43. package/dist/runtime/components/polaris/ShClickable.vue.d.ts +48 -0
  44. package/dist/runtime/components/polaris/ShClickableChip.d.vue.ts +24 -0
  45. package/dist/runtime/components/polaris/ShClickableChip.vue +20 -0
  46. package/dist/runtime/components/polaris/ShClickableChip.vue.d.ts +24 -0
  47. package/dist/runtime/components/polaris/ShColorField.d.vue.ts +29 -0
  48. package/dist/runtime/components/polaris/ShColorField.vue +25 -0
  49. package/dist/runtime/components/polaris/ShColorField.vue.d.ts +29 -0
  50. package/dist/runtime/components/polaris/ShColorPicker.d.vue.ts +19 -0
  51. package/dist/runtime/components/polaris/ShColorPicker.vue +15 -0
  52. package/dist/runtime/components/polaris/ShColorPicker.vue.d.ts +19 -0
  53. package/dist/runtime/components/polaris/ShDateField.d.vue.ts +34 -0
  54. package/dist/runtime/components/polaris/ShDateField.vue +30 -0
  55. package/dist/runtime/components/polaris/ShDateField.vue.d.ts +34 -0
  56. package/dist/runtime/components/polaris/ShDatePicker.d.vue.ts +25 -0
  57. package/dist/runtime/components/polaris/ShDatePicker.vue +21 -0
  58. package/dist/runtime/components/polaris/ShDatePicker.vue.d.ts +25 -0
  59. package/dist/runtime/components/polaris/ShDivider.d.vue.ts +17 -0
  60. package/dist/runtime/components/polaris/ShDivider.vue +13 -0
  61. package/dist/runtime/components/polaris/ShDivider.vue.d.ts +17 -0
  62. package/dist/runtime/components/polaris/ShDropZone.d.vue.ts +25 -0
  63. package/dist/runtime/components/polaris/ShDropZone.vue +21 -0
  64. package/dist/runtime/components/polaris/ShDropZone.vue.d.ts +25 -0
  65. package/dist/runtime/components/polaris/ShEmailField.d.vue.ts +30 -0
  66. package/dist/runtime/components/polaris/ShEmailField.vue +26 -0
  67. package/dist/runtime/components/polaris/ShEmailField.vue.d.ts +30 -0
  68. package/dist/runtime/components/polaris/ShGrid.d.vue.ts +50 -0
  69. package/dist/runtime/components/polaris/ShGrid.vue +46 -0
  70. package/dist/runtime/components/polaris/ShGrid.vue.d.ts +50 -0
  71. package/dist/runtime/components/polaris/ShGridItem.d.vue.ts +41 -0
  72. package/dist/runtime/components/polaris/ShGridItem.vue +37 -0
  73. package/dist/runtime/components/polaris/ShGridItem.vue.d.ts +41 -0
  74. package/dist/runtime/components/polaris/ShHeading.d.vue.ts +18 -0
  75. package/dist/runtime/components/polaris/ShHeading.vue +14 -0
  76. package/dist/runtime/components/polaris/ShHeading.vue.d.ts +18 -0
  77. package/dist/runtime/components/polaris/ShIcon.d.vue.ts +20 -0
  78. package/dist/runtime/components/polaris/ShIcon.vue +16 -0
  79. package/dist/runtime/components/polaris/ShIcon.vue.d.ts +20 -0
  80. package/dist/runtime/components/polaris/ShImage.d.vue.ts +18 -0
  81. package/dist/runtime/components/polaris/ShImage.vue +14 -0
  82. package/dist/runtime/components/polaris/ShImage.vue.d.ts +18 -0
  83. package/dist/runtime/components/polaris/ShLink.d.vue.ts +24 -0
  84. package/dist/runtime/components/polaris/ShLink.vue +20 -0
  85. package/dist/runtime/components/polaris/ShLink.vue.d.ts +24 -0
  86. package/dist/runtime/components/polaris/ShListItem.d.vue.ts +13 -0
  87. package/dist/runtime/components/polaris/ShListItem.vue +9 -0
  88. package/dist/runtime/components/polaris/ShListItem.vue.d.ts +13 -0
  89. package/dist/runtime/components/polaris/ShMenu.d.vue.ts +16 -0
  90. package/dist/runtime/components/polaris/ShMenu.vue +12 -0
  91. package/dist/runtime/components/polaris/ShMenu.vue.d.ts +16 -0
  92. package/dist/runtime/components/polaris/ShModal.d.vue.ts +19 -0
  93. package/dist/runtime/components/polaris/ShModal.vue +15 -0
  94. package/dist/runtime/components/polaris/ShModal.vue.d.ts +19 -0
  95. package/dist/runtime/components/polaris/ShMoneyField.d.vue.ts +30 -0
  96. package/dist/runtime/components/polaris/ShMoneyField.vue +26 -0
  97. package/dist/runtime/components/polaris/ShMoneyField.vue.d.ts +30 -0
  98. package/dist/runtime/components/polaris/ShNumberField.d.vue.ts +34 -0
  99. package/dist/runtime/components/polaris/ShNumberField.vue +30 -0
  100. package/dist/runtime/components/polaris/ShNumberField.vue.d.ts +34 -0
  101. package/dist/runtime/components/polaris/ShOption.d.vue.ts +19 -0
  102. package/dist/runtime/components/polaris/ShOption.vue +15 -0
  103. package/dist/runtime/components/polaris/ShOption.vue.d.ts +19 -0
  104. package/dist/runtime/components/polaris/ShOptionGroup.d.vue.ts +17 -0
  105. package/dist/runtime/components/polaris/ShOptionGroup.vue +13 -0
  106. package/dist/runtime/components/polaris/ShOptionGroup.vue.d.ts +17 -0
  107. package/dist/runtime/components/polaris/ShOrderedList.d.vue.ts +13 -0
  108. package/dist/runtime/components/polaris/ShOrderedList.vue +9 -0
  109. package/dist/runtime/components/polaris/ShOrderedList.vue.d.ts +13 -0
  110. package/dist/runtime/components/polaris/ShPage.d.vue.ts +17 -0
  111. package/dist/runtime/components/polaris/ShPage.vue +13 -0
  112. package/dist/runtime/components/polaris/ShPage.vue.d.ts +17 -0
  113. package/dist/runtime/components/polaris/ShParagraph.d.vue.ts +21 -0
  114. package/dist/runtime/components/polaris/ShParagraph.vue +17 -0
  115. package/dist/runtime/components/polaris/ShParagraph.vue.d.ts +21 -0
  116. package/dist/runtime/components/polaris/ShPasswordField.d.vue.ts +30 -0
  117. package/dist/runtime/components/polaris/ShPasswordField.vue +26 -0
  118. package/dist/runtime/components/polaris/ShPasswordField.vue.d.ts +30 -0
  119. package/dist/runtime/components/polaris/ShPopover.d.vue.ts +21 -0
  120. package/dist/runtime/components/polaris/ShPopover.vue +17 -0
  121. package/dist/runtime/components/polaris/ShPopover.vue.d.ts +21 -0
  122. package/dist/runtime/components/polaris/ShQueryContainer.d.vue.ts +16 -0
  123. package/dist/runtime/components/polaris/ShQueryContainer.vue +12 -0
  124. package/dist/runtime/components/polaris/ShQueryContainer.vue.d.ts +16 -0
  125. package/dist/runtime/components/polaris/ShSearchField.d.vue.ts +30 -0
  126. package/dist/runtime/components/polaris/ShSearchField.vue +26 -0
  127. package/dist/runtime/components/polaris/ShSearchField.vue.d.ts +30 -0
  128. package/dist/runtime/components/polaris/ShSection.d.vue.ts +18 -0
  129. package/dist/runtime/components/polaris/ShSection.vue +14 -0
  130. package/dist/runtime/components/polaris/ShSection.vue.d.ts +18 -0
  131. package/dist/runtime/components/polaris/ShSelect.d.vue.ts +26 -0
  132. package/dist/runtime/components/polaris/ShSelect.vue +22 -0
  133. package/dist/runtime/components/polaris/ShSelect.vue.d.ts +26 -0
  134. package/dist/runtime/components/polaris/ShSpinner.d.vue.ts +17 -0
  135. package/dist/runtime/components/polaris/ShSpinner.vue +13 -0
  136. package/dist/runtime/components/polaris/ShSpinner.vue.d.ts +17 -0
  137. package/dist/runtime/components/polaris/ShStack.d.vue.ts +46 -0
  138. package/dist/runtime/components/polaris/ShStack.vue +42 -0
  139. package/dist/runtime/components/polaris/ShStack.vue.d.ts +46 -0
  140. package/dist/runtime/components/polaris/ShSwitch.d.vue.ts +27 -0
  141. package/dist/runtime/components/polaris/ShSwitch.vue +23 -0
  142. package/dist/runtime/components/polaris/ShSwitch.vue.d.ts +27 -0
  143. package/dist/runtime/components/polaris/ShTable.d.vue.ts +20 -0
  144. package/dist/runtime/components/polaris/ShTable.vue +16 -0
  145. package/dist/runtime/components/polaris/ShTable.vue.d.ts +20 -0
  146. package/dist/runtime/components/polaris/ShTableBody.d.vue.ts +13 -0
  147. package/dist/runtime/components/polaris/ShTableBody.vue +9 -0
  148. package/dist/runtime/components/polaris/ShTableBody.vue.d.ts +13 -0
  149. package/dist/runtime/components/polaris/ShTableCell.d.vue.ts +13 -0
  150. package/dist/runtime/components/polaris/ShTableCell.vue +9 -0
  151. package/dist/runtime/components/polaris/ShTableCell.vue.d.ts +13 -0
  152. package/dist/runtime/components/polaris/ShTableHeader.d.vue.ts +17 -0
  153. package/dist/runtime/components/polaris/ShTableHeader.vue +13 -0
  154. package/dist/runtime/components/polaris/ShTableHeader.vue.d.ts +17 -0
  155. package/dist/runtime/components/polaris/ShTableHeaderRow.d.vue.ts +13 -0
  156. package/dist/runtime/components/polaris/ShTableHeaderRow.vue +9 -0
  157. package/dist/runtime/components/polaris/ShTableHeaderRow.vue.d.ts +13 -0
  158. package/dist/runtime/components/polaris/ShTableRow.d.vue.ts +16 -0
  159. package/dist/runtime/components/polaris/ShTableRow.vue +12 -0
  160. package/dist/runtime/components/polaris/ShTableRow.vue.d.ts +16 -0
  161. package/dist/runtime/components/polaris/ShText.d.vue.ts +22 -0
  162. package/dist/runtime/components/polaris/ShText.vue +18 -0
  163. package/dist/runtime/components/polaris/ShText.vue.d.ts +22 -0
  164. package/dist/runtime/components/polaris/ShTextArea.d.vue.ts +31 -0
  165. package/dist/runtime/components/polaris/ShTextArea.vue +27 -0
  166. package/dist/runtime/components/polaris/ShTextArea.vue.d.ts +31 -0
  167. package/dist/runtime/components/polaris/ShTextField.d.vue.ts +33 -0
  168. package/dist/runtime/components/polaris/ShTextField.vue +29 -0
  169. package/dist/runtime/components/polaris/ShTextField.vue.d.ts +33 -0
  170. package/dist/runtime/components/polaris/ShThumbnail.d.vue.ts +18 -0
  171. package/dist/runtime/components/polaris/ShThumbnail.vue +14 -0
  172. package/dist/runtime/components/polaris/ShThumbnail.vue.d.ts +18 -0
  173. package/dist/runtime/components/polaris/ShTooltip.d.vue.ts +13 -0
  174. package/dist/runtime/components/polaris/ShTooltip.vue +9 -0
  175. package/dist/runtime/components/polaris/ShTooltip.vue.d.ts +13 -0
  176. package/dist/runtime/components/polaris/ShUnorderedList.d.vue.ts +13 -0
  177. package/dist/runtime/components/polaris/ShUnorderedList.vue +9 -0
  178. package/dist/runtime/components/polaris/ShUnorderedList.vue.d.ts +13 -0
  179. package/dist/runtime/components/polaris/ShUrlField.d.vue.ts +30 -0
  180. package/dist/runtime/components/polaris/ShUrlField.vue +26 -0
  181. package/dist/runtime/components/polaris/ShUrlField.vue.d.ts +30 -0
  182. package/dist/runtime/components/polaris/types.d.ts +1 -0
  183. package/dist/runtime/components/polaris/types.js +0 -0
  184. package/dist/runtime/composables/useAppBridge.d.ts +8 -0
  185. package/dist/runtime/composables/useAppBridge.js +21 -0
  186. package/dist/runtime/composables/useShopifyFetch.d.ts +4 -0
  187. package/dist/runtime/composables/useShopifyFetch.js +32 -0
  188. package/dist/runtime/middleware/shopify-auth.d.ts +2 -0
  189. package/dist/runtime/middleware/shopify-auth.js +11 -0
  190. package/dist/runtime/plugin.d.ts +1 -0
  191. package/dist/runtime/plugin.js +1 -0
  192. package/dist/runtime/plugins/app-bridge.d.ts +12 -0
  193. package/dist/runtime/plugins/app-bridge.js +28 -0
  194. package/dist/runtime/plugins/polaris.d.ts +1 -0
  195. package/dist/runtime/plugins/polaris.js +0 -0
  196. package/dist/runtime/server/index.d.ts +2 -0
  197. package/dist/runtime/server/index.js +7 -0
  198. package/dist/runtime/server/routes/auth-callback.d.ts +6 -0
  199. package/dist/runtime/server/routes/auth-callback.js +45 -0
  200. package/dist/runtime/server/routes/auth-exit-iframe.d.ts +6 -0
  201. package/dist/runtime/server/routes/auth-exit-iframe.js +11 -0
  202. package/dist/runtime/server/routes/auth-session-token.d.ts +7 -0
  203. package/dist/runtime/server/routes/auth-session-token.js +27 -0
  204. package/dist/runtime/server/routes/auth.d.ts +6 -0
  205. package/dist/runtime/server/routes/auth.js +23 -0
  206. package/dist/runtime/server/services/shopify.d.ts +38 -0
  207. package/dist/runtime/server/services/shopify.js +89 -0
  208. package/dist/runtime/server/test-helpers/index.d.ts +25 -0
  209. package/dist/runtime/server/test-helpers/index.js +25 -0
  210. package/dist/runtime/server/test-helpers/package.json +4 -0
  211. package/dist/runtime/server/tsconfig.json +3 -0
  212. package/dist/runtime/server/utils/authenticate-admin.d.ts +17 -0
  213. package/dist/runtime/server/utils/authenticate-admin.js +197 -0
  214. package/dist/runtime/server/utils/authenticate-flow.d.ts +17 -0
  215. package/dist/runtime/server/utils/authenticate-flow.js +70 -0
  216. package/dist/runtime/server/utils/authenticate-fulfillment-service.d.ts +16 -0
  217. package/dist/runtime/server/utils/authenticate-fulfillment-service.js +54 -0
  218. package/dist/runtime/server/utils/authenticate-pos.d.ts +18 -0
  219. package/dist/runtime/server/utils/authenticate-pos.js +31 -0
  220. package/dist/runtime/server/utils/authenticate-public.d.ts +17 -0
  221. package/dist/runtime/server/utils/authenticate-public.js +31 -0
  222. package/dist/runtime/server/utils/authenticate-webhook.d.ts +26 -0
  223. package/dist/runtime/server/utils/authenticate-webhook.js +76 -0
  224. package/dist/runtime/server/utils/clients.d.ts +25 -0
  225. package/dist/runtime/server/utils/clients.js +36 -0
  226. package/dist/runtime/server/utils/helpers.d.ts +10 -0
  227. package/dist/runtime/server/utils/helpers.js +96 -0
  228. package/dist/runtime/server/utils/login.d.ts +17 -0
  229. package/dist/runtime/server/utils/login.js +44 -0
  230. package/dist/runtime/server/utils/register-webhooks.d.ts +19 -0
  231. package/dist/runtime/server/utils/register-webhooks.js +5 -0
  232. package/dist/runtime/server/utils/unauthenticated-admin.d.ts +21 -0
  233. package/dist/runtime/server/utils/unauthenticated-admin.js +15 -0
  234. package/dist/runtime/server/utils/unauthenticated-storefront.d.ts +24 -0
  235. package/dist/runtime/server/utils/unauthenticated-storefront.js +21 -0
  236. package/dist/runtime/types.d.ts +141 -0
  237. package/dist/runtime/types.js +11 -0
  238. package/dist/types.d.mts +3 -0
  239. package/package.json +66 -0
@@ -0,0 +1,17 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { FlowContext } from '../../types.js';
3
+ /**
4
+ * Authenticate a Shopify Flow extension request.
5
+ *
6
+ * Validates the HMAC signature and returns the flow context:
7
+ *
8
+ * ```ts
9
+ * // server/api/flow.ts
10
+ * export default defineEventHandler(async (event) => {
11
+ * const { admin, session, payload } = await useShopifyFlow(event)
12
+ * // Perform flow extension logic
13
+ * return { success: true }
14
+ * })
15
+ * ```
16
+ */
17
+ export declare function useShopifyFlow(event: H3Event): Promise<FlowContext>;
@@ -0,0 +1,70 @@
1
+ import { getHeader, readRawBody, createError } from "h3";
2
+ import { getShopifyApi, getSessionStorage } from "../services/shopify.js";
3
+ import { createAdminApiContext } from "./clients.js";
4
+ export async function useShopifyFlow(event) {
5
+ const api = getShopifyApi();
6
+ if (event.method !== "POST") {
7
+ throw createError({
8
+ statusCode: 405,
9
+ statusMessage: "Method Not Allowed - Flow requests must be POST"
10
+ });
11
+ }
12
+ const shop = getHeader(event, "x-shopify-shop-domain");
13
+ const hmac = getHeader(event, "x-shopify-hmac-sha256");
14
+ if (!shop || !hmac) {
15
+ throw createError({
16
+ statusCode: 400,
17
+ statusMessage: "Missing required headers"
18
+ });
19
+ }
20
+ const rawBody = await readRawBody(event);
21
+ if (!rawBody) {
22
+ throw createError({
23
+ statusCode: 400,
24
+ statusMessage: "Missing request body"
25
+ });
26
+ }
27
+ const { createHmac: hmacCreate } = await import("node:crypto");
28
+ const generatedHmac = hmacCreate("sha256", api.config.apiSecretKey).update(rawBody, "utf8").digest("base64");
29
+ if (!safeCompare(generatedHmac, hmac)) {
30
+ throw createError({
31
+ statusCode: 401,
32
+ statusMessage: "HMAC validation failed"
33
+ });
34
+ }
35
+ let payload;
36
+ try {
37
+ payload = JSON.parse(rawBody);
38
+ } catch {
39
+ throw createError({
40
+ statusCode: 400,
41
+ statusMessage: "Invalid JSON body"
42
+ });
43
+ }
44
+ const sessionStorage = getSessionStorage();
45
+ const offlineId = api.session.getOfflineId(shop);
46
+ const session = await sessionStorage.loadSession(offlineId);
47
+ if (!session) {
48
+ throw createError({
49
+ statusCode: 400,
50
+ statusMessage: `No session found for shop ${shop}`
51
+ });
52
+ }
53
+ const admin = createAdminApiContext(api, session);
54
+ return {
55
+ session,
56
+ admin,
57
+ payload
58
+ };
59
+ }
60
+ function safeCompare(a, b) {
61
+ if (a.length !== b.length) return false;
62
+ const encoder = new TextEncoder();
63
+ const bufA = encoder.encode(a);
64
+ const bufB = encoder.encode(b);
65
+ let result = 0;
66
+ for (let i = 0; i < bufA.length; i++) {
67
+ result |= bufA[i] ^ bufB[i];
68
+ }
69
+ return result === 0;
70
+ }
@@ -0,0 +1,16 @@
1
+ import type { H3Event } from 'h3';
2
+ /**
3
+ * Authenticate a request from a fulfillment service.
4
+ *
5
+ * ```ts
6
+ * // server/api/fulfillment-service.ts
7
+ * export default defineEventHandler(async (event) => {
8
+ * const { admin, session } = await useShopifyFulfillmentService(event)
9
+ * return { success: true }
10
+ * })
11
+ * ```
12
+ */
13
+ export declare function useShopifyFulfillmentService(event: H3Event): Promise<{
14
+ session: import("@shopify/shopify-api").Session;
15
+ admin: import("./clients.js").AdminApiContext;
16
+ }>;
@@ -0,0 +1,54 @@
1
+ import { getHeader, readRawBody, createError } from "h3";
2
+ import { getShopifyApi, getSessionStorage } from "../services/shopify.js";
3
+ import { createAdminApiContext } from "./clients.js";
4
+ export async function useShopifyFulfillmentService(event) {
5
+ const api = getShopifyApi();
6
+ const shop = getHeader(event, "x-shopify-shop-domain");
7
+ const hmac = getHeader(event, "x-shopify-hmac-sha256");
8
+ if (!shop || !hmac) {
9
+ throw createError({
10
+ statusCode: 400,
11
+ statusMessage: "Missing required headers"
12
+ });
13
+ }
14
+ const rawBody = await readRawBody(event);
15
+ if (!rawBody) {
16
+ throw createError({
17
+ statusCode: 400,
18
+ statusMessage: "Missing request body"
19
+ });
20
+ }
21
+ const { createHmac: hmacCreate } = await import("node:crypto");
22
+ const generatedHmac = hmacCreate("sha256", api.config.apiSecretKey).update(rawBody, "utf8").digest("base64");
23
+ if (!safeCompare(generatedHmac, hmac)) {
24
+ throw createError({
25
+ statusCode: 401,
26
+ statusMessage: "HMAC validation failed"
27
+ });
28
+ }
29
+ const sessionStorage = getSessionStorage();
30
+ const offlineId = api.session.getOfflineId(shop);
31
+ const session = await sessionStorage.loadSession(offlineId);
32
+ if (!session) {
33
+ throw createError({
34
+ statusCode: 400,
35
+ statusMessage: `No session found for shop ${shop}`
36
+ });
37
+ }
38
+ const admin = createAdminApiContext(api, session);
39
+ return {
40
+ session,
41
+ admin
42
+ };
43
+ }
44
+ function safeCompare(a, b) {
45
+ if (a.length !== b.length) return false;
46
+ const encoder = new TextEncoder();
47
+ const bufA = encoder.encode(a);
48
+ const bufB = encoder.encode(b);
49
+ let result = 0;
50
+ for (let i = 0; i < bufA.length; i++) {
51
+ result |= bufA[i] ^ bufB[i];
52
+ }
53
+ return result === 0;
54
+ }
@@ -0,0 +1,18 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { JwtPayload } from '@shopify/shopify-api';
3
+ export interface POSContext {
4
+ sessionToken: JwtPayload;
5
+ cors: (response: Response) => Response;
6
+ }
7
+ /**
8
+ * Authenticate a request from a POS UI extension.
9
+ *
10
+ * ```ts
11
+ * // server/api/pos/widgets.ts
12
+ * export default defineEventHandler(async (event) => {
13
+ * const { sessionToken, cors } = await useShopifyPos(event)
14
+ * return cors(new Response(JSON.stringify({ shop: sessionToken.dest })))
15
+ * })
16
+ * ```
17
+ */
18
+ export declare function useShopifyPos(event: H3Event): Promise<POSContext>;
@@ -0,0 +1,31 @@
1
+ import { createError } from "h3";
2
+ import {
3
+ getSessionTokenHeader,
4
+ getSessionTokenFromUrlParam,
5
+ validateSessionToken
6
+ } from "./helpers.js";
7
+ export async function useShopifyPos(event) {
8
+ const headerToken = getSessionTokenHeader(event);
9
+ const paramToken = getSessionTokenFromUrlParam(event);
10
+ const sessionToken = headerToken || paramToken;
11
+ if (!sessionToken) {
12
+ throw createError({
13
+ statusCode: 401,
14
+ statusMessage: "Missing session token"
15
+ });
16
+ }
17
+ const payload = await validateSessionToken(sessionToken);
18
+ const cors = (response) => {
19
+ response.headers.set("Access-Control-Allow-Origin", "*");
20
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
21
+ response.headers.set(
22
+ "Access-Control-Allow-Headers",
23
+ "Content-Type, Authorization"
24
+ );
25
+ return response;
26
+ };
27
+ return {
28
+ sessionToken: payload,
29
+ cors
30
+ };
31
+ }
@@ -0,0 +1,17 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { PublicContext } from '../../types.js';
3
+ /**
4
+ * Authenticate a public request (e.g., from checkout extensions, app proxy).
5
+ *
6
+ * Returns the decoded session token for verification, but does NOT load a full session.
7
+ *
8
+ * ```ts
9
+ * // server/api/public/widgets.ts
10
+ * export default defineEventHandler(async (event) => {
11
+ * const { sessionToken, cors } = await useShopifyPublic(event)
12
+ * const data = await getWidgets(sessionToken)
13
+ * return cors(new Response(JSON.stringify(data)))
14
+ * })
15
+ * ```
16
+ */
17
+ export declare function useShopifyPublic(event: H3Event): Promise<PublicContext>;
@@ -0,0 +1,31 @@
1
+ import { createError } from "h3";
2
+ import {
3
+ getSessionTokenHeader,
4
+ getSessionTokenFromUrlParam,
5
+ validateSessionToken
6
+ } from "./helpers.js";
7
+ export async function useShopifyPublic(event) {
8
+ const headerToken = getSessionTokenHeader(event);
9
+ const paramToken = getSessionTokenFromUrlParam(event);
10
+ const sessionToken = headerToken || paramToken;
11
+ if (!sessionToken) {
12
+ throw createError({
13
+ statusCode: 401,
14
+ statusMessage: "Missing session token"
15
+ });
16
+ }
17
+ const payload = await validateSessionToken(sessionToken);
18
+ const cors = (response) => {
19
+ response.headers.set("Access-Control-Allow-Origin", "*");
20
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
21
+ response.headers.set(
22
+ "Access-Control-Allow-Headers",
23
+ "Content-Type, Authorization"
24
+ );
25
+ return response;
26
+ };
27
+ return {
28
+ sessionToken: payload,
29
+ cors
30
+ };
31
+ }
@@ -0,0 +1,26 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { WebhookContext } from '../../types.js';
3
+ /**
4
+ * Authenticate a Shopify webhook request.
5
+ *
6
+ * Validates the HMAC signature and returns the webhook context:
7
+ *
8
+ * ```ts
9
+ * // server/api/webhooks.ts
10
+ * export default defineEventHandler(async (event) => {
11
+ * const { topic, shop, session, payload } = await useShopifyWebhook(event)
12
+ *
13
+ * switch (topic) {
14
+ * case 'PRODUCTS_CREATE':
15
+ * // handle product creation
16
+ * break
17
+ * case 'APP_UNINSTALLED':
18
+ * // clean up shop data
19
+ * break
20
+ * }
21
+ *
22
+ * return { success: true }
23
+ * })
24
+ * ```
25
+ */
26
+ export declare function useShopifyWebhook(event: H3Event): Promise<WebhookContext>;
@@ -0,0 +1,76 @@
1
+ import { getHeader, readRawBody, createError } from "h3";
2
+ import {
3
+ getShopifyApi,
4
+ getResolvedConfig,
5
+ getSessionStorage
6
+ } from "../services/shopify.js";
7
+ export async function useShopifyWebhook(event) {
8
+ const api = getShopifyApi();
9
+ const config = getResolvedConfig();
10
+ if (event.method !== "POST") {
11
+ throw createError({
12
+ statusCode: 405,
13
+ statusMessage: "Method Not Allowed - Webhooks must be POST"
14
+ });
15
+ }
16
+ const topic = getHeader(event, "x-shopify-topic");
17
+ const shop = getHeader(event, "x-shopify-shop-domain");
18
+ const hmac = getHeader(event, "x-shopify-hmac-sha256");
19
+ const apiVersion = getHeader(event, "x-shopify-api-version") || config.apiVersion;
20
+ if (!topic || !shop || !hmac) {
21
+ throw createError({
22
+ statusCode: 400,
23
+ statusMessage: "Missing required webhook headers (x-shopify-topic, x-shopify-shop-domain, x-shopify-hmac-sha256)"
24
+ });
25
+ }
26
+ const rawBody = await readRawBody(event);
27
+ if (!rawBody) {
28
+ throw createError({
29
+ statusCode: 400,
30
+ statusMessage: "Missing request body"
31
+ });
32
+ }
33
+ const { createHmac } = await import("node:crypto");
34
+ const generatedHmac = createHmac("sha256", api.config.apiSecretKey).update(rawBody, "utf8").digest("base64");
35
+ if (!safeCompare(generatedHmac, hmac)) {
36
+ throw createError({
37
+ statusCode: 401,
38
+ statusMessage: "Webhook HMAC validation failed"
39
+ });
40
+ }
41
+ let payload;
42
+ try {
43
+ payload = JSON.parse(rawBody);
44
+ } catch {
45
+ throw createError({
46
+ statusCode: 400,
47
+ statusMessage: "Invalid JSON in webhook body"
48
+ });
49
+ }
50
+ let session;
51
+ try {
52
+ const sessionStorage = getSessionStorage();
53
+ const offlineId = api.session.getOfflineId(shop);
54
+ session = await sessionStorage.loadSession(offlineId) || void 0;
55
+ } catch {
56
+ session = void 0;
57
+ }
58
+ return {
59
+ topic: topic.toUpperCase().replace(/\//g, "_"),
60
+ shop,
61
+ session,
62
+ payload,
63
+ apiVersion
64
+ };
65
+ }
66
+ function safeCompare(a, b) {
67
+ if (a.length !== b.length) return false;
68
+ const encoder = new TextEncoder();
69
+ const bufA = encoder.encode(a);
70
+ const bufB = encoder.encode(b);
71
+ let result = 0;
72
+ for (let i = 0; i < bufA.length; i++) {
73
+ result |= bufA[i] ^ bufB[i];
74
+ }
75
+ return result === 0;
76
+ }
@@ -0,0 +1,25 @@
1
+ import type { Session, Shopify } from '@shopify/shopify-api';
2
+ export interface AdminApiContext {
3
+ /** Make a GraphQL request to the Shopify Admin API */
4
+ graphql: (query: string, options?: {
5
+ variables?: Record<string, any>;
6
+ }) => Promise<any>;
7
+ /** Make a REST request to the Shopify Admin API */
8
+ rest: {
9
+ get: (params: {
10
+ path: string;
11
+ }) => Promise<any>;
12
+ post: (params: {
13
+ path: string;
14
+ data?: any;
15
+ }) => Promise<any>;
16
+ put: (params: {
17
+ path: string;
18
+ data?: any;
19
+ }) => Promise<any>;
20
+ delete: (params: {
21
+ path: string;
22
+ }) => Promise<any>;
23
+ };
24
+ }
25
+ export declare function createAdminApiContext(api: Shopify, session: Session, onError?: (error: any) => void): AdminApiContext;
@@ -0,0 +1,36 @@
1
+ export function createAdminApiContext(api, session, onError) {
2
+ const graphql = async (query, options) => {
3
+ const client = new api.clients.Graphql({ session });
4
+ try {
5
+ const response = await client.request(query, {
6
+ variables: options?.variables
7
+ });
8
+ return response;
9
+ } catch (error) {
10
+ if (onError) onError(error);
11
+ throw error;
12
+ }
13
+ };
14
+ const restRequest = async (method, params) => {
15
+ const client = new api.clients.Rest({ session });
16
+ try {
17
+ const response = await client[method.toLowerCase()]({
18
+ path: params.path,
19
+ data: params.data
20
+ });
21
+ return response;
22
+ } catch (error) {
23
+ if (onError) onError(error);
24
+ throw error;
25
+ }
26
+ };
27
+ return {
28
+ graphql,
29
+ rest: {
30
+ get: (params) => restRequest("get", params),
31
+ post: (params) => restRequest("post", params),
32
+ put: (params) => restRequest("put", params),
33
+ delete: (params) => restRequest("delete", params)
34
+ }
35
+ };
36
+ }
@@ -0,0 +1,10 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { JwtPayload } from '@shopify/shopify-api';
3
+ export declare function getSessionTokenHeader(event: H3Event): string | undefined;
4
+ export declare function getSessionTokenFromUrlParam(event: H3Event): string | undefined;
5
+ export declare function getShopFromEvent(event: H3Event): string | undefined;
6
+ export declare function validateSessionToken(sessionToken: string): Promise<JwtPayload>;
7
+ export declare function ensureCORSHeaders(event: H3Event): void;
8
+ export declare function isBotRequest(event: H3Event): boolean;
9
+ export declare function verifyWebhookHmac(event: H3Event): Promise<boolean>;
10
+ export declare function renderAppBridgePage(apiKey: string, _appUrl: string, redirectUrl?: string): string;
@@ -0,0 +1,96 @@
1
+ import {
2
+ getHeader,
3
+ getQuery,
4
+ setResponseHeader,
5
+ readRawBody
6
+ } from "h3";
7
+ import { getShopifyApi, getResolvedConfig } from "../services/shopify.js";
8
+ export function getSessionTokenHeader(event) {
9
+ const authHeader = getHeader(event, "authorization");
10
+ if (authHeader?.startsWith("Bearer ")) {
11
+ return authHeader.slice(7);
12
+ }
13
+ return void 0;
14
+ }
15
+ export function getSessionTokenFromUrlParam(event) {
16
+ const query = getQuery(event);
17
+ return query.id_token;
18
+ }
19
+ export function getShopFromEvent(event) {
20
+ const query = getQuery(event);
21
+ if (query.shop) return query.shop;
22
+ const shopHeader = getHeader(event, "x-shopify-shop-domain");
23
+ if (shopHeader) return shopHeader;
24
+ return void 0;
25
+ }
26
+ export async function validateSessionToken(sessionToken) {
27
+ const api = getShopifyApi();
28
+ const payload = await api.session.decodeSessionToken(sessionToken);
29
+ return payload;
30
+ }
31
+ export function ensureCORSHeaders(event) {
32
+ const config = getResolvedConfig();
33
+ setResponseHeader(event, "Access-Control-Allow-Origin", config.appUrl);
34
+ setResponseHeader(event, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
35
+ setResponseHeader(event, "Access-Control-Allow-Headers", "Content-Type, Authorization");
36
+ setResponseHeader(event, "Access-Control-Allow-Credentials", "true");
37
+ }
38
+ export function isBotRequest(event) {
39
+ const ua = getHeader(event, "user-agent") || "";
40
+ try {
41
+ const isbotModule = require("isbot");
42
+ const isbotFn = isbotModule.isbot || isbotModule.default || isbotModule;
43
+ return typeof isbotFn === "function" ? isbotFn(ua) : false;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ export async function verifyWebhookHmac(event) {
49
+ const api = getShopifyApi();
50
+ const hmac = getHeader(event, "x-shopify-hmac-sha256");
51
+ const topic = getHeader(event, "x-shopify-topic");
52
+ const shop = getHeader(event, "x-shopify-shop-domain");
53
+ if (!hmac || !topic || !shop) {
54
+ return false;
55
+ }
56
+ const rawBody = await readRawBody(event);
57
+ if (!rawBody) return false;
58
+ try {
59
+ const { createHmac } = await import("node:crypto");
60
+ const generatedHmac = createHmac("sha256", api.config.apiSecretKey).update(rawBody, "utf8").digest("base64");
61
+ return safeCompare(generatedHmac, hmac);
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+ function safeCompare(a, b) {
67
+ if (a.length !== b.length) return false;
68
+ const encoder = new TextEncoder();
69
+ const bufA = encoder.encode(a);
70
+ const bufB = encoder.encode(b);
71
+ let result = 0;
72
+ for (let i = 0; i < bufA.length; i++) {
73
+ result |= bufA[i] ^ bufB[i];
74
+ }
75
+ return result === 0;
76
+ }
77
+ export function renderAppBridgePage(apiKey, _appUrl, redirectUrl) {
78
+ const parts = [
79
+ "<!DOCTYPE html>",
80
+ "<html>",
81
+ " <head>",
82
+ ' <meta charset="utf-8" />',
83
+ ' <meta name="shopify-api-key" content="' + apiKey + '" />',
84
+ ' <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"><\/script>',
85
+ " </head>",
86
+ " <body>",
87
+ " <script>"
88
+ ];
89
+ if (redirectUrl) {
90
+ parts.push(" window.open('" + redirectUrl + "', '_top');");
91
+ }
92
+ parts.push(" <\/script>");
93
+ parts.push(" </body>");
94
+ parts.push("</html>");
95
+ return parts.join("\n");
96
+ }
@@ -0,0 +1,17 @@
1
+ import type { H3Event } from 'h3';
2
+ import { type LoginError } from '../../types.js';
3
+ /**
4
+ * Handle merchant login for non-embedded apps (AppStore and SingleMerchant distribution).
5
+ *
6
+ * Validates the shop domain and returns any errors, or redirects to auth flow.
7
+ *
8
+ * ```ts
9
+ * // server/api/auth/login.post.ts
10
+ * export default defineEventHandler(async (event) => {
11
+ * const errors = await useShopifyLogin(event)
12
+ * if (errors) return errors
13
+ * // redirect happened — this won't be reached
14
+ * })
15
+ * ```
16
+ */
17
+ export declare function useShopifyLogin(event: H3Event): Promise<LoginError | never>;
@@ -0,0 +1,44 @@
1
+ import { readBody, getQuery, createError } from "h3";
2
+ import { getResolvedConfig } from "../services/shopify.js";
3
+ import { AppDistribution, LoginErrorType } from "../../types.js";
4
+ export async function useShopifyLogin(event) {
5
+ const config = getResolvedConfig();
6
+ if (config.distribution === AppDistribution.ShopifyAdmin) {
7
+ throw createError({
8
+ statusCode: 400,
9
+ statusMessage: "Login is not available for ShopifyAdmin apps"
10
+ });
11
+ }
12
+ let shop;
13
+ if (event.method === "POST") {
14
+ const body = await readBody(event);
15
+ shop = body?.shop;
16
+ } else {
17
+ const query = getQuery(event);
18
+ shop = query.shop;
19
+ }
20
+ if (!shop) {
21
+ return { shop: LoginErrorType.MissingShop };
22
+ }
23
+ const sanitized = sanitizeShop(shop);
24
+ if (!sanitized) {
25
+ return { shop: LoginErrorType.InvalidShop };
26
+ }
27
+ throw new Response(null, {
28
+ status: 302,
29
+ headers: {
30
+ Location: `${config.auth.path}?shop=${sanitized}`
31
+ }
32
+ });
33
+ }
34
+ function sanitizeShop(shop) {
35
+ shop = shop.replace(/^https?:\/\//, "");
36
+ shop = shop.split("/")[0] || "";
37
+ if (/^[a-z0-9][a-z0-9-]*\.myshopify\.com$/i.test(shop)) {
38
+ return shop;
39
+ }
40
+ if (/^[a-z0-9][a-z0-9-]*$/i.test(shop)) {
41
+ return `${shop}.myshopify.com`;
42
+ }
43
+ return null;
44
+ }
@@ -0,0 +1,19 @@
1
+ import type { Session } from '@shopify/shopify-api';
2
+ /**
3
+ * Register shop-specific webhooks using the Admin GraphQL API.
4
+ *
5
+ * In most cases, app-specific webhooks defined in `shopify.app.toml` are sufficient.
6
+ * Use this only when you need shop-specific webhooks.
7
+ *
8
+ * ```ts
9
+ * // In your afterAuth hook:
10
+ * configureShopify({
11
+ * hooks: {
12
+ * afterAuth: async ({ session }) => {
13
+ * await registerShopifyWebhooks(session)
14
+ * }
15
+ * }
16
+ * })
17
+ * ```
18
+ */
19
+ export declare function registerShopifyWebhooks(session: Session): Promise<import("@shopify/shopify-api").RegisterReturn>;
@@ -0,0 +1,5 @@
1
+ import { getShopifyApi } from "../services/shopify.js";
2
+ export async function registerShopifyWebhooks(session) {
3
+ const api = getShopifyApi();
4
+ return api.webhooks.register({ session });
5
+ }
@@ -0,0 +1,21 @@
1
+ import type { AdminApiContext } from './clients.js';
2
+ import type { Session } from '@shopify/shopify-api';
3
+ /**
4
+ * Get an unauthenticated Admin API context for a given shop.
5
+ *
6
+ * Uses the stored offline session to access the Admin API without an incoming request.
7
+ * Useful for background jobs, cron tasks, or requests from external systems.
8
+ *
9
+ * ```ts
10
+ * // server/api/cron/sync-products.ts
11
+ * export default defineEventHandler(async (event) => {
12
+ * const { admin, session } = await useShopifyUnauthenticatedAdmin('my-shop.myshopify.com')
13
+ * const products = await admin.graphql(`{ products(first: 10) { edges { node { id title } } } }`)
14
+ * return products
15
+ * })
16
+ * ```
17
+ */
18
+ export declare function useShopifyUnauthenticatedAdmin(shop: string): Promise<{
19
+ admin: AdminApiContext;
20
+ session: Session;
21
+ }>;
@@ -0,0 +1,15 @@
1
+ import { getShopifyApi, getSessionStorage } from "../services/shopify.js";
2
+ import { createAdminApiContext } from "./clients.js";
3
+ export async function useShopifyUnauthenticatedAdmin(shop) {
4
+ const api = getShopifyApi();
5
+ const sessionStorage = getSessionStorage();
6
+ const offlineId = api.session.getOfflineId(shop);
7
+ const session = await sessionStorage.loadSession(offlineId);
8
+ if (!session) {
9
+ throw new Error(
10
+ `No offline session found for shop "${shop}". The shop must install the app first.`
11
+ );
12
+ }
13
+ const admin = createAdminApiContext(api, session);
14
+ return { admin, session };
15
+ }