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.
- package/LICENSE +21 -0
- package/README.md +293 -29
- package/dist/module.json +1 -1
- package/dist/module.mjs +25 -6
- package/dist/runtime/components/polaris/ShAvatar.vue +4 -2
- package/dist/runtime/components/polaris/ShBadge.vue +4 -2
- package/dist/runtime/components/polaris/ShBanner.vue +4 -2
- package/dist/runtime/components/polaris/ShBox.vue +4 -2
- package/dist/runtime/components/polaris/ShButton.d.vue.ts +11 -3
- package/dist/runtime/components/polaris/ShButton.vue +10 -2
- package/dist/runtime/components/polaris/ShButton.vue.d.ts +11 -3
- package/dist/runtime/components/polaris/ShButtonGroup.vue +4 -2
- package/dist/runtime/components/polaris/ShCheckbox.d.vue.ts +12 -5
- package/dist/runtime/components/polaris/ShCheckbox.vue +22 -5
- package/dist/runtime/components/polaris/ShCheckbox.vue.d.ts +12 -5
- package/dist/runtime/components/polaris/ShChip.vue +4 -2
- package/dist/runtime/components/polaris/ShChoice.vue +4 -2
- package/dist/runtime/components/polaris/ShChoiceList.d.vue.ts +12 -4
- package/dist/runtime/components/polaris/ShChoiceList.vue +21 -3
- package/dist/runtime/components/polaris/ShChoiceList.vue.d.ts +12 -4
- package/dist/runtime/components/polaris/ShClickable.vue +4 -2
- package/dist/runtime/components/polaris/ShClickableChip.vue +4 -2
- package/dist/runtime/components/polaris/ShColorField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShColorField.vue +24 -4
- package/dist/runtime/components/polaris/ShColorField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShColorPicker.d.vue.ts +12 -4
- package/dist/runtime/components/polaris/ShColorPicker.vue +21 -3
- package/dist/runtime/components/polaris/ShColorPicker.vue.d.ts +12 -4
- package/dist/runtime/components/polaris/ShDateField.d.vue.ts +20 -4
- package/dist/runtime/components/polaris/ShDateField.vue +25 -3
- package/dist/runtime/components/polaris/ShDateField.vue.d.ts +20 -4
- package/dist/runtime/components/polaris/ShDatePicker.d.vue.ts +18 -4
- package/dist/runtime/components/polaris/ShDatePicker.vue +24 -3
- package/dist/runtime/components/polaris/ShDatePicker.vue.d.ts +18 -4
- package/dist/runtime/components/polaris/ShDivider.vue +4 -2
- package/dist/runtime/components/polaris/ShDropZone.d.vue.ts +14 -4
- package/dist/runtime/components/polaris/ShDropZone.vue +22 -3
- package/dist/runtime/components/polaris/ShDropZone.vue.d.ts +14 -4
- package/dist/runtime/components/polaris/ShEmailField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShEmailField.vue +24 -4
- package/dist/runtime/components/polaris/ShEmailField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShGrid.vue +4 -2
- package/dist/runtime/components/polaris/ShGridItem.vue +4 -2
- package/dist/runtime/components/polaris/ShHeading.vue +4 -2
- package/dist/runtime/components/polaris/ShIcon.vue +4 -2
- package/dist/runtime/components/polaris/ShImage.vue +4 -2
- package/dist/runtime/components/polaris/ShLink.vue +4 -2
- package/dist/runtime/components/polaris/ShMenu.vue +4 -2
- package/dist/runtime/components/polaris/ShModal.vue +4 -2
- package/dist/runtime/components/polaris/ShMoneyField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShMoneyField.vue +24 -4
- package/dist/runtime/components/polaris/ShMoneyField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShNumberField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShNumberField.vue +24 -4
- package/dist/runtime/components/polaris/ShNumberField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShOption.vue +4 -2
- package/dist/runtime/components/polaris/ShOptionGroup.vue +4 -2
- package/dist/runtime/components/polaris/ShPage.vue +4 -2
- package/dist/runtime/components/polaris/ShParagraph.vue +4 -2
- package/dist/runtime/components/polaris/ShPasswordField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShPasswordField.vue +24 -4
- package/dist/runtime/components/polaris/ShPasswordField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShPopover.d.vue.ts +1 -0
- package/dist/runtime/components/polaris/ShPopover.vue +5 -2
- package/dist/runtime/components/polaris/ShPopover.vue.d.ts +1 -0
- package/dist/runtime/components/polaris/ShQueryContainer.vue +4 -2
- package/dist/runtime/components/polaris/ShSearchField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShSearchField.vue +24 -4
- package/dist/runtime/components/polaris/ShSearchField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShSection.vue +4 -2
- package/dist/runtime/components/polaris/ShSelect.d.vue.ts +12 -4
- package/dist/runtime/components/polaris/ShSelect.vue +22 -4
- package/dist/runtime/components/polaris/ShSelect.vue.d.ts +12 -4
- package/dist/runtime/components/polaris/ShSpinner.vue +4 -2
- package/dist/runtime/components/polaris/ShStack.vue +4 -2
- package/dist/runtime/components/polaris/ShSwitch.d.vue.ts +12 -5
- package/dist/runtime/components/polaris/ShSwitch.vue +22 -5
- package/dist/runtime/components/polaris/ShSwitch.vue.d.ts +12 -5
- package/dist/runtime/components/polaris/ShTable.vue +4 -2
- package/dist/runtime/components/polaris/ShTableHeader.vue +4 -2
- package/dist/runtime/components/polaris/ShTableRow.vue +4 -2
- package/dist/runtime/components/polaris/ShText.vue +4 -2
- package/dist/runtime/components/polaris/ShTextArea.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShTextArea.vue +24 -4
- package/dist/runtime/components/polaris/ShTextArea.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShTextField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShTextField.vue +24 -4
- package/dist/runtime/components/polaris/ShTextField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/ShThumbnail.vue +4 -2
- package/dist/runtime/components/polaris/ShTooltip.d.vue.ts +4 -1
- package/dist/runtime/components/polaris/ShTooltip.vue +6 -1
- package/dist/runtime/components/polaris/ShTooltip.vue.d.ts +4 -1
- package/dist/runtime/components/polaris/ShUrlField.d.vue.ts +16 -4
- package/dist/runtime/components/polaris/ShUrlField.vue +24 -4
- package/dist/runtime/components/polaris/ShUrlField.vue.d.ts +16 -4
- package/dist/runtime/components/polaris/utils.d.ts +7 -0
- package/dist/runtime/components/polaris/utils.js +13 -0
- package/dist/runtime/composables/useAppBridge.d.ts +4 -1
- package/dist/runtime/composables/useAppBridge.js +24 -13
- package/dist/runtime/composables/useShopifyFetch.js +3 -1
- package/dist/runtime/middleware/shopify-auth.js +10 -4
- package/dist/runtime/pages/auth-login.d.vue.ts +3 -0
- package/dist/runtime/pages/auth-login.vue +90 -0
- package/dist/runtime/pages/auth-login.vue.d.ts +3 -0
- package/dist/runtime/server/index.d.ts +2 -0
- package/dist/runtime/server/index.js +4 -0
- package/dist/runtime/server/plugins/shopify-defaults.d.ts +8 -0
- package/dist/runtime/server/plugins/shopify-defaults.js +12 -0
- package/dist/runtime/server/routes/auth-callback.d.ts +1 -1
- package/dist/runtime/server/routes/auth-exit-iframe.d.ts +1 -1
- package/dist/runtime/server/routes/auth-session-token.d.ts +1 -1
- package/dist/runtime/server/routes/auth.d.ts +1 -1
- package/dist/runtime/server/services/shopify.js +3 -3
- package/dist/runtime/server/utils/authenticate-admin.js +21 -6
- package/dist/runtime/server/utils/clients.d.ts +24 -5
- package/dist/runtime/server/utils/clients.js +21 -2
- package/dist/runtime/server/utils/helpers.js +13 -11
- package/dist/runtime/server/utils/unauthenticated-storefront.d.ts +1 -5
- package/dist/runtime/server/utils/unauthenticated-storefront.js +2 -8
- package/dist/runtime/types.d.ts +33 -2
- package/package.json +10 -5
- package/dist/runtime/components/ShopifyAppProvider.d.vue.ts +0 -13
- package/dist/runtime/components/ShopifyAppProvider.vue +0 -11
- package/dist/runtime/components/ShopifyAppProvider.vue.d.ts +0 -13
- package/dist/runtime/plugins/polaris.d.ts +0 -1
- 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
|
|
18
|
+
declare var __VLS_13: {};
|
|
19
19
|
type __VLS_Slots = {} & {
|
|
20
|
-
default?: (props: typeof
|
|
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, {
|
|
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
|
-
*
|
|
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
|
-
|
|
2
|
-
{},
|
|
3
|
-
{
|
|
1
|
+
function createUnavailableProxy(reason) {
|
|
2
|
+
return new Proxy({}, {
|
|
4
3
|
get(_, prop) {
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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';
|
|
@@ -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").
|
|
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").
|
|
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").
|
|
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").
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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(
|
|
35
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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;
|