shopify-nuxt 0.0.1 → 0.0.3

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -29
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +25 -6
  5. package/dist/runtime/components/polaris/ShAvatar.vue +4 -2
  6. package/dist/runtime/components/polaris/ShBadge.vue +4 -2
  7. package/dist/runtime/components/polaris/ShBanner.vue +4 -2
  8. package/dist/runtime/components/polaris/ShBox.vue +4 -2
  9. package/dist/runtime/components/polaris/ShButton.d.vue.ts +11 -3
  10. package/dist/runtime/components/polaris/ShButton.vue +10 -2
  11. package/dist/runtime/components/polaris/ShButton.vue.d.ts +11 -3
  12. package/dist/runtime/components/polaris/ShButtonGroup.vue +4 -2
  13. package/dist/runtime/components/polaris/ShCheckbox.d.vue.ts +12 -5
  14. package/dist/runtime/components/polaris/ShCheckbox.vue +22 -5
  15. package/dist/runtime/components/polaris/ShCheckbox.vue.d.ts +12 -5
  16. package/dist/runtime/components/polaris/ShChip.vue +4 -2
  17. package/dist/runtime/components/polaris/ShChoice.vue +4 -2
  18. package/dist/runtime/components/polaris/ShChoiceList.d.vue.ts +12 -4
  19. package/dist/runtime/components/polaris/ShChoiceList.vue +21 -3
  20. package/dist/runtime/components/polaris/ShChoiceList.vue.d.ts +12 -4
  21. package/dist/runtime/components/polaris/ShClickable.vue +4 -2
  22. package/dist/runtime/components/polaris/ShClickableChip.vue +4 -2
  23. package/dist/runtime/components/polaris/ShColorField.d.vue.ts +16 -4
  24. package/dist/runtime/components/polaris/ShColorField.vue +24 -4
  25. package/dist/runtime/components/polaris/ShColorField.vue.d.ts +16 -4
  26. package/dist/runtime/components/polaris/ShColorPicker.d.vue.ts +12 -4
  27. package/dist/runtime/components/polaris/ShColorPicker.vue +21 -3
  28. package/dist/runtime/components/polaris/ShColorPicker.vue.d.ts +12 -4
  29. package/dist/runtime/components/polaris/ShDateField.d.vue.ts +20 -4
  30. package/dist/runtime/components/polaris/ShDateField.vue +25 -3
  31. package/dist/runtime/components/polaris/ShDateField.vue.d.ts +20 -4
  32. package/dist/runtime/components/polaris/ShDatePicker.d.vue.ts +18 -4
  33. package/dist/runtime/components/polaris/ShDatePicker.vue +24 -3
  34. package/dist/runtime/components/polaris/ShDatePicker.vue.d.ts +18 -4
  35. package/dist/runtime/components/polaris/ShDivider.vue +4 -2
  36. package/dist/runtime/components/polaris/ShDropZone.d.vue.ts +14 -4
  37. package/dist/runtime/components/polaris/ShDropZone.vue +22 -3
  38. package/dist/runtime/components/polaris/ShDropZone.vue.d.ts +14 -4
  39. package/dist/runtime/components/polaris/ShEmailField.d.vue.ts +16 -4
  40. package/dist/runtime/components/polaris/ShEmailField.vue +24 -4
  41. package/dist/runtime/components/polaris/ShEmailField.vue.d.ts +16 -4
  42. package/dist/runtime/components/polaris/ShGrid.vue +4 -2
  43. package/dist/runtime/components/polaris/ShGridItem.vue +4 -2
  44. package/dist/runtime/components/polaris/ShHeading.vue +4 -2
  45. package/dist/runtime/components/polaris/ShIcon.vue +4 -2
  46. package/dist/runtime/components/polaris/ShImage.vue +4 -2
  47. package/dist/runtime/components/polaris/ShLink.vue +4 -2
  48. package/dist/runtime/components/polaris/ShMenu.vue +4 -2
  49. package/dist/runtime/components/polaris/ShModal.vue +4 -2
  50. package/dist/runtime/components/polaris/ShMoneyField.d.vue.ts +16 -4
  51. package/dist/runtime/components/polaris/ShMoneyField.vue +24 -4
  52. package/dist/runtime/components/polaris/ShMoneyField.vue.d.ts +16 -4
  53. package/dist/runtime/components/polaris/ShNumberField.d.vue.ts +16 -4
  54. package/dist/runtime/components/polaris/ShNumberField.vue +24 -4
  55. package/dist/runtime/components/polaris/ShNumberField.vue.d.ts +16 -4
  56. package/dist/runtime/components/polaris/ShOption.vue +4 -2
  57. package/dist/runtime/components/polaris/ShOptionGroup.vue +4 -2
  58. package/dist/runtime/components/polaris/ShPage.vue +4 -2
  59. package/dist/runtime/components/polaris/ShParagraph.vue +4 -2
  60. package/dist/runtime/components/polaris/ShPasswordField.d.vue.ts +16 -4
  61. package/dist/runtime/components/polaris/ShPasswordField.vue +24 -4
  62. package/dist/runtime/components/polaris/ShPasswordField.vue.d.ts +16 -4
  63. package/dist/runtime/components/polaris/ShPopover.d.vue.ts +1 -0
  64. package/dist/runtime/components/polaris/ShPopover.vue +5 -2
  65. package/dist/runtime/components/polaris/ShPopover.vue.d.ts +1 -0
  66. package/dist/runtime/components/polaris/ShQueryContainer.vue +4 -2
  67. package/dist/runtime/components/polaris/ShSearchField.d.vue.ts +16 -4
  68. package/dist/runtime/components/polaris/ShSearchField.vue +24 -4
  69. package/dist/runtime/components/polaris/ShSearchField.vue.d.ts +16 -4
  70. package/dist/runtime/components/polaris/ShSection.vue +4 -2
  71. package/dist/runtime/components/polaris/ShSelect.d.vue.ts +12 -4
  72. package/dist/runtime/components/polaris/ShSelect.vue +22 -4
  73. package/dist/runtime/components/polaris/ShSelect.vue.d.ts +12 -4
  74. package/dist/runtime/components/polaris/ShSpinner.vue +4 -2
  75. package/dist/runtime/components/polaris/ShStack.vue +4 -2
  76. package/dist/runtime/components/polaris/ShSwitch.d.vue.ts +12 -5
  77. package/dist/runtime/components/polaris/ShSwitch.vue +22 -5
  78. package/dist/runtime/components/polaris/ShSwitch.vue.d.ts +12 -5
  79. package/dist/runtime/components/polaris/ShTable.vue +4 -2
  80. package/dist/runtime/components/polaris/ShTableHeader.vue +4 -2
  81. package/dist/runtime/components/polaris/ShTableRow.vue +4 -2
  82. package/dist/runtime/components/polaris/ShText.vue +4 -2
  83. package/dist/runtime/components/polaris/ShTextArea.d.vue.ts +16 -4
  84. package/dist/runtime/components/polaris/ShTextArea.vue +24 -4
  85. package/dist/runtime/components/polaris/ShTextArea.vue.d.ts +16 -4
  86. package/dist/runtime/components/polaris/ShTextField.d.vue.ts +16 -4
  87. package/dist/runtime/components/polaris/ShTextField.vue +24 -4
  88. package/dist/runtime/components/polaris/ShTextField.vue.d.ts +16 -4
  89. package/dist/runtime/components/polaris/ShThumbnail.vue +4 -2
  90. package/dist/runtime/components/polaris/ShTooltip.d.vue.ts +4 -1
  91. package/dist/runtime/components/polaris/ShTooltip.vue +6 -1
  92. package/dist/runtime/components/polaris/ShTooltip.vue.d.ts +4 -1
  93. package/dist/runtime/components/polaris/ShUrlField.d.vue.ts +16 -4
  94. package/dist/runtime/components/polaris/ShUrlField.vue +24 -4
  95. package/dist/runtime/components/polaris/ShUrlField.vue.d.ts +16 -4
  96. package/dist/runtime/components/polaris/utils.d.ts +7 -0
  97. package/dist/runtime/components/polaris/utils.js +13 -0
  98. package/dist/runtime/composables/useAppBridge.d.ts +4 -1
  99. package/dist/runtime/composables/useAppBridge.js +24 -13
  100. package/dist/runtime/composables/useShopifyFetch.js +3 -1
  101. package/dist/runtime/middleware/shopify-auth.js +10 -4
  102. package/dist/runtime/pages/auth-login.d.vue.ts +3 -0
  103. package/dist/runtime/pages/auth-login.vue +90 -0
  104. package/dist/runtime/pages/auth-login.vue.d.ts +3 -0
  105. package/dist/runtime/server/index.d.ts +2 -0
  106. package/dist/runtime/server/index.js +4 -0
  107. package/dist/runtime/server/plugins/shopify-defaults.d.ts +8 -0
  108. package/dist/runtime/server/plugins/shopify-defaults.js +12 -0
  109. package/dist/runtime/server/routes/auth-callback.d.ts +1 -1
  110. package/dist/runtime/server/routes/auth-exit-iframe.d.ts +1 -1
  111. package/dist/runtime/server/routes/auth-session-token.d.ts +1 -1
  112. package/dist/runtime/server/routes/auth.d.ts +1 -1
  113. package/dist/runtime/server/services/shopify.js +3 -3
  114. package/dist/runtime/server/utils/authenticate-admin.js +21 -6
  115. package/dist/runtime/server/utils/clients.d.ts +24 -5
  116. package/dist/runtime/server/utils/clients.js +21 -2
  117. package/dist/runtime/server/utils/helpers.js +13 -11
  118. package/dist/runtime/server/utils/unauthenticated-storefront.d.ts +1 -5
  119. package/dist/runtime/server/utils/unauthenticated-storefront.js +2 -8
  120. package/dist/runtime/types.d.ts +33 -2
  121. package/package.json +10 -5
  122. package/dist/runtime/components/ShopifyAppProvider.d.vue.ts +0 -13
  123. package/dist/runtime/components/ShopifyAppProvider.vue +0 -11
  124. package/dist/runtime/components/ShopifyAppProvider.vue.d.ts +0 -13
  125. package/dist/runtime/plugins/polaris.d.ts +0 -1
  126. package/dist/runtime/plugins/polaris.js +0 -0
@@ -2,7 +2,6 @@ type __VLS_Props = {
2
2
  autocomplete?: string;
3
3
  maxLength?: number;
4
4
  minLength?: number;
5
- value?: string;
6
5
  defaultValue?: string;
7
6
  details?: string;
8
7
  error?: string;
@@ -14,12 +13,25 @@ type __VLS_Props = {
14
13
  disabled?: boolean;
15
14
  id?: string;
16
15
  name?: string;
16
+ modelValue?: string;
17
17
  };
18
- declare var __VLS_8: {};
18
+ declare var __VLS_13: {};
19
19
  type __VLS_Slots = {} & {
20
- default?: (props: typeof __VLS_8) => any;
20
+ default?: (props: typeof __VLS_13) => any;
21
21
  };
22
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ "update:modelValue": (v: string) => void;
24
+ blur: (event: InputEvent) => void;
25
+ focus: (event: InputEvent) => void;
26
+ input: (event: InputEvent) => void;
27
+ change: (event: InputEvent) => void;
28
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
29
+ onBlur?: ((event: InputEvent) => any) | undefined;
30
+ onFocus?: ((event: InputEvent) => any) | undefined;
31
+ onInput?: ((event: InputEvent) => any) | undefined;
32
+ onChange?: ((event: InputEvent) => any) | undefined;
33
+ "onUpdate:modelValue"?: ((v: string) => any) | undefined;
34
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
35
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
36
  declare const _default: typeof __VLS_export;
25
37
  export default _default;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Merges $attrs with defined (non-undefined) props for forwarding to Polaris custom elements.
3
+ * With `inheritAttrs: false`, Vue-declared props are stripped from $attrs.
4
+ * This utility re-adds only the props that were actually passed by the parent,
5
+ * avoiding setting undefined values on custom elements which can alter their behavior.
6
+ */
7
+ export declare function usePolarisAttrs(props: Record<string, unknown>, exclude?: string[]): import("vue").ComputedRef<Record<string, unknown>>;
@@ -0,0 +1,13 @@
1
+ import { computed, useAttrs } from "vue";
2
+ export function usePolarisAttrs(props, exclude) {
3
+ const attrs = useAttrs();
4
+ return computed(() => {
5
+ const merged = { ...attrs };
6
+ for (const [key, val] of Object.entries(props)) {
7
+ if (val !== void 0 && !exclude?.includes(key)) {
8
+ merged[key] = val;
9
+ }
10
+ }
11
+ return merged;
12
+ });
13
+ }
@@ -3,6 +3,9 @@ import type { ShopifyGlobal } from '@shopify/app-bridge-types';
3
3
  * Returns the `shopify` global variable to use App Bridge APIs
4
4
  * such as `toast` and `resourcePicker`.
5
5
  *
6
- * @returns `shopify` variable or a Proxy that throws when incorrectly accessed in a server context
6
+ * Safe to call in `app.vue` or layouts returns a lazy proxy that only throws
7
+ * when a property is actually accessed outside the Shopify admin context.
8
+ *
9
+ * @returns `shopify` variable or a Proxy that throws when a property is accessed in an unavailable context
7
10
  */
8
11
  export declare function useAppBridge(): ShopifyGlobal;
@@ -1,21 +1,32 @@
1
- const serverProxy = new Proxy(
2
- {},
3
- {
1
+ function createUnavailableProxy(reason) {
2
+ return new Proxy({}, {
4
3
  get(_, prop) {
5
- throw new Error(
6
- `shopify.${String(prop)} can't be used in a server environment. You likely need to move this code into an event handler or onMounted.`
7
- );
4
+ if (typeof prop === "symbol" || prop === "then" || prop === "toJSON") {
5
+ return void 0;
6
+ }
7
+ throw new Error(`shopify.${String(prop)} is not available: ${reason}`);
8
8
  }
9
- }
9
+ });
10
+ }
11
+ const serverProxy = createUnavailableProxy(
12
+ "Cannot use App Bridge in a server environment. Move this code into an event handler or onMounted."
13
+ );
14
+ const clientFallbackProxy = createUnavailableProxy(
15
+ "The shopify global is not defined. You are likely outside the Shopify admin iframe."
10
16
  );
11
17
  export function useAppBridge() {
12
18
  if (typeof window === "undefined") {
13
19
  return serverProxy;
14
20
  }
15
- if (!window.shopify) {
16
- throw new Error(
17
- "The shopify global is not defined. This likely means the App Bridge script tag was not added correctly to this page"
18
- );
19
- }
20
- return window.shopify;
21
+ return new Proxy({}, {
22
+ get(_, prop) {
23
+ if (typeof prop === "symbol" || prop === "then" || prop === "toJSON") {
24
+ return void 0;
25
+ }
26
+ if (window.shopify) {
27
+ return window.shopify[prop];
28
+ }
29
+ return clientFallbackProxy[prop];
30
+ }
31
+ });
21
32
  }
@@ -1,7 +1,9 @@
1
1
  import { useNuxtApp } from "#app";
2
2
  export function useShopifyFetch() {
3
3
  if (import.meta.server) {
4
- throw new Error("useShopifyFetch() can only be used on the client side");
4
+ return async (_url, _options) => {
5
+ return { data: null, response: null };
6
+ };
5
7
  }
6
8
  const nuxtApp = useNuxtApp();
7
9
  return async (url, options = {}) => {
@@ -1,11 +1,17 @@
1
- import { defineNuxtRouteMiddleware } from "#app";
1
+ import { defineNuxtRouteMiddleware, useRuntimeConfig } from "#app";
2
2
  import { navigateTo } from "nuxt/app";
3
- export default defineNuxtRouteMiddleware(() => {
4
- if (import.meta.server || typeof window === "undefined") {
3
+ export default defineNuxtRouteMiddleware((to) => {
4
+ const config = useRuntimeConfig().public.shopify;
5
+ const authPage = config.authPagePath || "/auth";
6
+ if (import.meta.server) {
7
+ const shop2 = to.query.shop;
8
+ if (!shop2) {
9
+ return navigateTo(authPage);
10
+ }
5
11
  return;
6
12
  }
7
13
  const shop = window.shopify?.config?.shop;
8
14
  if (!shop) {
9
- return navigateTo("/auth");
15
+ return navigateTo(authPage);
10
16
  }
11
17
  });
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,90 @@
1
+ <script setup>
2
+ import { navigateTo, useRuntimeConfig } from "#app";
3
+ import { definePageMeta, ref } from "#imports";
4
+ definePageMeta({
5
+ layout: false
6
+ });
7
+ const shop = ref("");
8
+ const error = ref("");
9
+ const loading = ref(false);
10
+ async function handleSubmit() {
11
+ error.value = "";
12
+ const value = shop.value.trim();
13
+ if (!value) {
14
+ error.value = "Please enter your shop domain";
15
+ return;
16
+ }
17
+ loading.value = true;
18
+ try {
19
+ const config = useRuntimeConfig();
20
+ const authPrefix = config.public.shopify.authPathPrefix;
21
+ const domain = value.includes(".myshopify.com") ? value : `${value}.myshopify.com`;
22
+ await navigateTo(`${authPrefix}?shop=${encodeURIComponent(domain)}`, {
23
+ external: true
24
+ });
25
+ } catch (e) {
26
+ error.value = e.message || "Something went wrong";
27
+ loading.value = false;
28
+ }
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <div class="shopify-login">
34
+ <div class="shopify-login-card">
35
+ <div class="shopify-login-header">
36
+ <svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ width="44"
39
+ height="44"
40
+ viewBox="0 0 256 292"
41
+ >
42
+ <path
43
+ fill="#95bf46"
44
+ d="M223.774 57.34c-.201-1.46-1.48-2.268-2.537-2.357a19614 19614 0 0 0-23.383-1.743s-15.507-15.395-17.209-17.099c-1.703-1.703-5.029-1.185-6.32-.805c-.19.056-3.388 1.043-8.678 2.68c-5.18-14.906-14.322-28.604-30.405-28.604c-.444 0-.901.018-1.358.044C129.31 3.407 123.644.779 118.75.779c-37.465 0-55.364 46.835-60.976 70.635c-14.558 4.511-24.9 7.718-26.221 8.133c-8.126 2.549-8.383 2.805-9.45 10.462C21.3 95.806.038 260.235.038 260.235l165.678 31.042l89.77-19.42S223.973 58.8 223.775 57.34M156.49 40.848l-14.019 4.339c.005-.988.01-1.96.01-3.023c0-9.264-1.286-16.723-3.349-22.636c8.287 1.04 13.806 10.469 17.358 21.32m-27.638-19.483c2.304 5.773 3.802 14.058 3.802 25.238c0 .572-.005 1.095-.01 1.624c-9.117 2.824-19.024 5.89-28.953 8.966c5.575-21.516 16.025-31.908 25.161-35.828m-11.131-10.537c1.617 0 3.246.549 4.805 1.622c-12.007 5.65-24.877 19.88-30.312 48.297l-22.886 7.088C75.694 46.16 90.81 10.828 117.72 10.828"
45
+ />
46
+ <path
47
+ fill="#5e8e3e"
48
+ d="M221.237 54.983a19614 19614 0 0 0-23.383-1.743s-15.507-15.395-17.209-17.099c-.637-.634-1.496-.959-2.394-1.099l-12.527 256.233l89.762-19.418S223.972 58.8 223.774 57.34c-.201-1.46-1.48-2.268-2.537-2.357"
49
+ />
50
+ <path
51
+ fill="#fff"
52
+ d="m135.242 104.585l-11.069 32.926s-9.698-5.176-21.586-5.176c-17.428 0-18.305 10.937-18.305 13.693c0 15.038 39.2 20.8 39.2 56.024c0 27.713-17.577 45.558-41.277 45.558c-28.44 0-42.984-17.7-42.984-17.7l7.615-25.16s14.95 12.835 27.565 12.835c8.243 0 11.596-6.49 11.596-11.232c0-19.616-32.16-20.491-32.16-52.724c0-27.129 19.472-53.382 58.778-53.382c15.145 0 22.627 4.338 22.627 4.338"
53
+ />
54
+ </svg>
55
+
56
+ <h1>Log in</h1>
57
+ <p>Enter your shop domain to log in or install this app.</p>
58
+ </div>
59
+
60
+ <form class="shopify-login-form" @submit.prevent="handleSubmit">
61
+ <div class="shopify-login-field">
62
+ <label for="shop">Shop domain</label>
63
+ <div class="shopify-login-input-wrapper">
64
+ <input
65
+ id="shop"
66
+ v-model="shop"
67
+ type="text"
68
+ placeholder="my-shop"
69
+ autocomplete="off"
70
+ autocapitalize="off"
71
+ :disabled="loading"
72
+ />
73
+ <span class="shopify-login-suffix">.myshopify.com</span>
74
+ </div>
75
+ <p v-if="error" class="shopify-login-error">
76
+ {{ error }}
77
+ </p>
78
+ </div>
79
+
80
+ <button type="submit" class="shopify-login-button" :disabled="loading">
81
+ {{ loading ? "Redirecting..." : "Log in" }}
82
+ </button>
83
+ </form>
84
+ </div>
85
+ </div>
86
+ </template>
87
+
88
+ <style scoped>
89
+ .shopify-login{align-items:center;background:#f6f6f7;display:flex;font-family:-apple-system,BlinkMacSystemFont,San Francisco,Segoe UI,Roboto,Helvetica Neue,sans-serif;justify-content:center;min-height:100svh}.shopify-login-card{background:#fff;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.08),0 4px 12px rgba(0,0,0,.04);max-width:400px;padding:2.5rem;width:100%}.shopify-login-header{margin-bottom:2rem;text-align:center}.shopify-login-header h1{color:#202223;font-size:1.5rem;font-weight:600;margin:0 0 .5rem}.shopify-login-header p{color:#6d7175;font-size:.875rem;line-height:1.4;margin:0}.shopify-login-form{display:flex;flex-direction:column;gap:1.25rem}.shopify-login-field label{color:#202223;display:block;font-size:.875rem;font-weight:500;margin-bottom:.375rem}.shopify-login-input-wrapper{border:1px solid #c9cccf;border-radius:8px;display:flex;overflow:hidden;transition:border-color .1s}.shopify-login-input-wrapper:focus-within{border-color:#005bd3;box-shadow:0 0 0 1px #005bd3}.shopify-login-input-wrapper input{border:none;color:#202223;flex:1;font-size:.875rem;min-width:0;outline:none;padding:.625rem .75rem}.shopify-login-input-wrapper input::-moz-placeholder{color:#b5b5b5}.shopify-login-input-wrapper input::placeholder{color:#b5b5b5}.shopify-login-suffix{align-items:center;background:#f6f6f7;border-left:1px solid #c9cccf;color:#6d7175;display:flex;font-size:.875rem;padding:0 .75rem;white-space:nowrap}.shopify-login-error{color:#d72c0d;font-size:.8125rem;margin:.375rem 0 0}.shopify-login-button{background:#008060;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:.875rem;font-weight:600;padding:.625rem 1rem;transition:background .1s}.shopify-login-button:hover:not(:disabled){background:#006e52}.shopify-login-button:disabled{cursor:not-allowed;opacity:.6}
90
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -1,2 +1,4 @@
1
1
  export { configureShopify, getShopifyApi, getResolvedConfig, getSessionStorage } from './services/shopify.js';
2
2
  export { registerShopifyWebhooks } from './utils/register-webhooks.js';
3
+ export { createAdminApiContext, createStorefrontApiContext } from './utils/clients.js';
4
+ export type { AdminApiContext, StorefrontApiContext, GraphQLClient, GraphQLQueryOptions, GraphQLResponse } from './utils/clients.js';
@@ -5,3 +5,7 @@ export {
5
5
  getSessionStorage
6
6
  } from "./services/shopify.js";
7
7
  export { registerShopifyWebhooks } from "./utils/register-webhooks.js";
8
+ export {
9
+ createAdminApiContext,
10
+ createStorefrontApiContext
11
+ } from "./utils/clients.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Built-in Nitro plugin that initializes Shopify with default MemorySessionStorage.
3
+ *
4
+ * If the user calls `configureShopify()` in their own server plugin,
5
+ * it will override these defaults (configureShopify resets cached singletons).
6
+ */
7
+ declare const _default: any;
8
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { MemorySessionStorage } from "@shopify/shopify-app-session-storage-memory";
2
+ import { configureShopify, getResolvedConfig } from "../services/shopify.js";
3
+ export default defineNitroPlugin(() => {
4
+ try {
5
+ const config = getResolvedConfig();
6
+ if (config.sessionStorage) return;
7
+ } catch {
8
+ }
9
+ configureShopify({
10
+ sessionStorage: new MemorySessionStorage()
11
+ });
12
+ });
@@ -2,5 +2,5 @@
2
2
  * Handle the OAuth callback from Shopify.
3
3
  * Validates the callback, stores the session, and redirects to the app.
4
4
  */
5
- declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<import("h3").HTTPResponse>>;
5
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
6
6
  export default _default;
@@ -2,5 +2,5 @@
2
2
  * Render the exit-iframe page.
3
3
  * This page uses App Bridge to redirect the user out of the auth iframe.
4
4
  */
5
- declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<string>>;
5
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string>>;
6
6
  export default _default;
@@ -3,5 +3,5 @@
3
3
  * For embedded apps, this page loads App Bridge which provides a session token
4
4
  * via a search param, then redirects back to the original request.
5
5
  */
6
- declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<string>>;
6
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string>>;
7
7
  export default _default;
@@ -2,5 +2,5 @@
2
2
  * Handle the initial auth request.
3
3
  * For token exchange flow (embedded apps), this redirects to Shopify OAuth.
4
4
  */
5
- declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<import("h3").HTTPResponse>>;
5
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
6
6
  export default _default;
@@ -10,7 +10,7 @@ let _resolvedConfig = null;
10
10
  let _runtimeConfig = null;
11
11
  const LIBRARY_VERSION = "1.0.0";
12
12
  export function configureShopify(config) {
13
- _runtimeConfig = config;
13
+ _runtimeConfig = { ..._runtimeConfig, ...config };
14
14
  _shopifyApi = null;
15
15
  _resolvedConfig = null;
16
16
  }
@@ -49,7 +49,7 @@ export function getShopifyApi() {
49
49
  export function getResolvedConfig() {
50
50
  if (_resolvedConfig) return _resolvedConfig;
51
51
  const nuxtConfig = useRuntimeConfig();
52
- const moduleOpts = nuxtConfig.shopify || {};
52
+ const moduleOpts = nuxtConfig.shopify;
53
53
  const runtime = _runtimeConfig || {};
54
54
  const authPathPrefix = moduleOpts.authPathPrefix || "/_shopify/auth";
55
55
  const auth = {
@@ -62,7 +62,7 @@ export function getResolvedConfig() {
62
62
  _resolvedConfig = {
63
63
  apiKey: moduleOpts.apiKey || "",
64
64
  apiSecretKey: moduleOpts.apiSecretKey || "",
65
- scopes: moduleOpts.scopes || [],
65
+ scopes: moduleOpts.scopes,
66
66
  appUrl: moduleOpts.appUrl || "",
67
67
  apiVersion: moduleOpts.apiVersion || "2025-01",
68
68
  authPathPrefix,
@@ -15,6 +15,8 @@ import {
15
15
  renderAppBridgePage
16
16
  } from "./helpers.js";
17
17
  import { AppDistribution } from "../../types.js";
18
+ const OnlineAccessToken = "urn:shopify:params:oauth:token-type:online-access-token";
19
+ const OfflineAccessToken = "urn:shopify:params:oauth:token-type:offline-access-token";
18
20
  export async function useShopifyAdmin(event) {
19
21
  const api = getShopifyApi();
20
22
  const config = getResolvedConfig();
@@ -131,17 +133,30 @@ export async function useShopifyAdmin(event) {
131
133
  };
132
134
  }
133
135
  async function performTokenExchange(api, config, sessionStorage, shop, sessionToken) {
134
- const { session: onlineSession } = await api.auth.tokenExchange({
135
- shop,
136
- sessionToken,
137
- requestedTokenType: config.useOnlineTokens ? "online" : "offline"
138
- });
136
+ let onlineSession;
137
+ try {
138
+ const result = await api.auth.tokenExchange({
139
+ shop,
140
+ sessionToken,
141
+ requestedTokenType: config.useOnlineTokens ? OnlineAccessToken : OfflineAccessToken
142
+ });
143
+ onlineSession = result.session;
144
+ } catch (error) {
145
+ console.error("[shopify-nuxt] Token exchange failed:", {
146
+ shop,
147
+ requestedTokenType: config.useOnlineTokens ? OnlineAccessToken : OfflineAccessToken,
148
+ scopes: api.config.scopes?.toString(),
149
+ responseBody: error?.response?.body,
150
+ message: error?.message
151
+ });
152
+ throw error;
153
+ }
139
154
  await sessionStorage.storeSession(onlineSession);
140
155
  if (config.useOnlineTokens) {
141
156
  const { session: offlineSession } = await api.auth.tokenExchange({
142
157
  shop,
143
158
  sessionToken,
144
- requestedTokenType: "offline"
159
+ requestedTokenType: OfflineAccessToken
145
160
  });
146
161
  await sessionStorage.storeSession(offlineSession);
147
162
  }
@@ -1,9 +1,23 @@
1
- import type { Session, Shopify } from '@shopify/shopify-api';
1
+ import type { Session, Shopify, ApiVersion } from '@shopify/shopify-api';
2
+ import type { AdminOperations, AllOperations, ApiClientRequestOptions, ReturnData, FetchResponseBody, ResponseWithType } from '@shopify/admin-api-client';
3
+ import type { StorefrontOperations } from '@shopify/storefront-api-client';
4
+ export interface GraphQLQueryOptions<Operation extends keyof Operations, Operations extends AllOperations> {
5
+ /** The variables to pass to the operation. */
6
+ variables?: ApiClientRequestOptions<Operation, Operations>['variables'];
7
+ /** The version of the API to use for the request. */
8
+ apiVersion?: ApiVersion;
9
+ /** Additional headers to include in the request. */
10
+ headers?: Record<string, string>;
11
+ /** The total number of times to try the request if it fails. */
12
+ tries?: number;
13
+ /** An optional AbortSignal to cancel the request. */
14
+ signal?: AbortSignal;
15
+ }
16
+ export type GraphQLResponse<Operation extends keyof Operations, Operations extends AllOperations> = ResponseWithType<FetchResponseBody<ReturnData<Operation, Operations>>>;
17
+ export type GraphQLClient<Operations extends AllOperations> = <Operation extends keyof Operations>(query: Operation, options?: GraphQLQueryOptions<Operation, Operations>) => Promise<GraphQLResponse<Operation, Operations>>;
2
18
  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>;
19
+ /** Make a typed GraphQL request to the Shopify Admin API */
20
+ graphql: GraphQLClient<AdminOperations>;
7
21
  /** Make a REST request to the Shopify Admin API */
8
22
  rest: {
9
23
  get: (params: {
@@ -22,4 +36,9 @@ export interface AdminApiContext {
22
36
  }) => Promise<any>;
23
37
  };
24
38
  }
39
+ export interface StorefrontApiContext {
40
+ /** Make a typed GraphQL request to the Shopify Storefront API */
41
+ graphql: GraphQLClient<StorefrontOperations>;
42
+ }
25
43
  export declare function createAdminApiContext(api: Shopify, session: Session, onError?: (error: any) => void): AdminApiContext;
44
+ export declare function createStorefrontApiContext(api: Shopify, session: Session, onError?: (error: any) => void): StorefrontApiContext;
@@ -3,9 +3,11 @@ export function createAdminApiContext(api, session, onError) {
3
3
  const client = new api.clients.Graphql({ session });
4
4
  try {
5
5
  const response = await client.request(query, {
6
- variables: options?.variables
6
+ variables: options?.variables,
7
+ retries: options?.tries,
8
+ headers: options?.headers
7
9
  });
8
- return response;
10
+ return new Response(JSON.stringify(response));
9
11
  } catch (error) {
10
12
  if (onError) onError(error);
11
13
  throw error;
@@ -34,3 +36,20 @@ export function createAdminApiContext(api, session, onError) {
34
36
  }
35
37
  };
36
38
  }
39
+ export function createStorefrontApiContext(api, session, onError) {
40
+ const graphql = async (query, options) => {
41
+ const client = new api.clients.Storefront({ session });
42
+ try {
43
+ const response = await client.request(query, {
44
+ variables: options?.variables,
45
+ retries: options?.tries,
46
+ headers: options?.headers
47
+ });
48
+ return new Response(JSON.stringify(response));
49
+ } catch (error) {
50
+ if (onError) onError(error);
51
+ throw error;
52
+ }
53
+ };
54
+ return { graphql };
55
+ }
@@ -1,9 +1,5 @@
1
- import {
2
- getHeader,
3
- getQuery,
4
- setResponseHeader,
5
- readRawBody
6
- } from "h3";
1
+ import { getHeader, getQuery, setResponseHeader, readRawBody } from "h3";
2
+ import { isbot } from "isbot";
7
3
  import { getShopifyApi, getResolvedConfig } from "../services/shopify.js";
8
4
  export function getSessionTokenHeader(event) {
9
5
  const authHeader = getHeader(event, "authorization");
@@ -31,16 +27,22 @@ export async function validateSessionToken(sessionToken) {
31
27
  export function ensureCORSHeaders(event) {
32
28
  const config = getResolvedConfig();
33
29
  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");
30
+ setResponseHeader(
31
+ event,
32
+ "Access-Control-Allow-Methods",
33
+ "GET, POST, PUT, DELETE, OPTIONS"
34
+ );
35
+ setResponseHeader(
36
+ event,
37
+ "Access-Control-Allow-Headers",
38
+ "Content-Type, Authorization"
39
+ );
36
40
  setResponseHeader(event, "Access-Control-Allow-Credentials", "true");
37
41
  }
38
42
  export function isBotRequest(event) {
39
43
  const ua = getHeader(event, "user-agent") || "";
40
44
  try {
41
- const isbotModule = require("isbot");
42
- const isbotFn = isbotModule.isbot || isbotModule.default || isbotModule;
43
- return typeof isbotFn === "function" ? isbotFn(ua) : false;
45
+ return isbot(ua);
44
46
  } catch {
45
47
  return false;
46
48
  }
@@ -1,9 +1,5 @@
1
+ import type { StorefrontApiContext } from './clients.js';
1
2
  import type { Session } from '@shopify/shopify-api';
2
- export interface StorefrontApiContext {
3
- graphql: (query: string, options?: {
4
- variables?: Record<string, any>;
5
- }) => Promise<any>;
6
- }
7
3
  /**
8
4
  * Get an unauthenticated Storefront API context for a given shop.
9
5
  *
@@ -1,4 +1,5 @@
1
1
  import { getShopifyApi, getSessionStorage } from "../services/shopify.js";
2
+ import { createStorefrontApiContext } from "./clients.js";
2
3
  export async function useShopifyUnauthenticatedStorefront(shop) {
3
4
  const api = getShopifyApi();
4
5
  const sessionStorage = getSessionStorage();
@@ -9,13 +10,6 @@ export async function useShopifyUnauthenticatedStorefront(shop) {
9
10
  `No offline session found for shop "${shop}". The shop must install the app first.`
10
11
  );
11
12
  }
12
- const storefront = {
13
- graphql: async (query, options) => {
14
- const client = new api.clients.Storefront({ session });
15
- return client.request(query, {
16
- variables: options?.variables
17
- });
18
- }
19
- };
13
+ const storefront = createStorefrontApiContext(api, session);
20
14
  return { storefront, session };
21
15
  }
@@ -19,8 +19,9 @@ export interface ModuleOptions {
19
19
  apiSecretKey: string;
20
20
  /**
21
21
  * The scopes your app needs from the Shopify API.
22
+ * Optional — Shopify reads scopes from `shopify.app.toml` with managed install.
22
23
  */
23
- scopes: string[];
24
+ scopes?: string[];
24
25
  /**
25
26
  * The URL your app is running on. In development, this is typically a tunnel URL.
26
27
  * Can also be set via `NUXT_SHOPIFY_APP_URL` env var.
@@ -46,6 +47,13 @@ export interface ModuleOptions {
46
47
  * @default false
47
48
  */
48
49
  useOnlineTokens?: boolean;
50
+ /**
51
+ * Path to a custom Vue page component for the `/auth` login page.
52
+ * Set to `false` to disable the built-in auth page entirely.
53
+ * When not set, a default login page with a shop domain input is provided.
54
+ * @default undefined (uses built-in page)
55
+ */
56
+ authPage?: string | false;
49
57
  }
50
58
  export type WebhookConfig = Record<string, WebhookHandler | WebhookHandler[]>;
51
59
  export interface AfterAuthOptions {
@@ -124,13 +132,36 @@ export interface AuthConfig {
124
132
  patchSessionTokenPath: string;
125
133
  loginPath: string;
126
134
  }
127
- export interface ResolvedConfig {
135
+ export interface ShopifyServerConfig {
128
136
  apiKey: string;
129
137
  apiSecretKey: string;
130
138
  scopes: string[];
131
139
  appUrl: string;
132
140
  apiVersion: string;
133
141
  authPathPrefix: string;
142
+ distribution: string;
143
+ useOnlineTokens: boolean;
144
+ }
145
+ export interface ShopifyPublicConfig {
146
+ apiKey: string;
147
+ authPagePath: string;
148
+ authPathPrefix: string;
149
+ }
150
+ declare module 'nuxt/schema' {
151
+ interface RuntimeConfig {
152
+ shopify: ShopifyServerConfig;
153
+ }
154
+ interface PublicRuntimeConfig {
155
+ shopify: ShopifyPublicConfig;
156
+ }
157
+ }
158
+ export interface ResolvedConfig {
159
+ apiKey: string;
160
+ apiSecretKey: string;
161
+ scopes?: string[];
162
+ appUrl: string;
163
+ apiVersion: string;
164
+ authPathPrefix: string;
134
165
  distribution: AppDistribution;
135
166
  useOnlineTokens: boolean;
136
167
  auth: AuthConfig;