gothamstoryblok 1.0.15

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 (49) hide show
  1. package/README.md +84 -0
  2. package/dist/module.d.mts +11 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +50 -0
  5. package/dist/runtime/composables/useAsyncGothamStoryblok.d.ts +11 -0
  6. package/dist/runtime/composables/useAsyncGothamStoryblok.js +62 -0
  7. package/dist/runtime/composables/useLanguage.d.ts +6 -0
  8. package/dist/runtime/composables/useLanguage.js +15 -0
  9. package/dist/runtime/composables/useSeo.d.ts +1 -0
  10. package/dist/runtime/composables/useSeo.js +69 -0
  11. package/dist/runtime/composables/useSizes.d.ts +23 -0
  12. package/dist/runtime/composables/useSizes.js +70 -0
  13. package/dist/runtime/composables/useStoryblokEditor.d.ts +3 -0
  14. package/dist/runtime/composables/useStoryblokEditor.js +10 -0
  15. package/dist/runtime/plugins/01.setupLanguages.d.ts +2 -0
  16. package/dist/runtime/plugins/01.setupLanguages.js +26 -0
  17. package/dist/runtime/plugins/02.utils.d.ts +30 -0
  18. package/dist/runtime/plugins/02.utils.js +74 -0
  19. package/dist/runtime/plugins/03.labels.d.ts +6 -0
  20. package/dist/runtime/plugins/03.labels.js +50 -0
  21. package/dist/runtime/plugins/04.images.d.ts +8 -0
  22. package/dist/runtime/plugins/04.images.js +39 -0
  23. package/dist/runtime/plugins/05.datasources.d.ts +8 -0
  24. package/dist/runtime/plugins/05.datasources.js +19 -0
  25. package/dist/runtime/plugins/test.d.ts +2 -0
  26. package/dist/runtime/plugins/test.js +4 -0
  27. package/dist/runtime/server/api/storyblok/all.get.d.ts +2 -0
  28. package/dist/runtime/server/api/storyblok/all.get.js +16 -0
  29. package/dist/runtime/server/api/storyblok/cache.get.d.ts +2 -0
  30. package/dist/runtime/server/api/storyblok/cache.get.js +15 -0
  31. package/dist/runtime/server/api/storyblok/clearCache.get.d.ts +2 -0
  32. package/dist/runtime/server/api/storyblok/clearCache.get.js +66 -0
  33. package/dist/runtime/server/api/storyblok/clearCache.post.d.ts +2 -0
  34. package/dist/runtime/server/api/storyblok/clearCache.post.js +15 -0
  35. package/dist/runtime/server/api/storyblok/datasources.get.d.ts +2 -0
  36. package/dist/runtime/server/api/storyblok/datasources.get.js +41 -0
  37. package/dist/runtime/server/api/storyblok/getall.get.d.ts +2 -0
  38. package/dist/runtime/server/api/storyblok/getall.get.js +34 -0
  39. package/dist/runtime/server/api/storyblok/links.get.d.ts +2 -0
  40. package/dist/runtime/server/api/storyblok/links.get.js +17 -0
  41. package/dist/runtime/server/api/storyblok/request.get.d.ts +2 -0
  42. package/dist/runtime/server/api/storyblok/request.get.js +17 -0
  43. package/dist/runtime/server/api/storyblok/space.get.d.ts +2 -0
  44. package/dist/runtime/server/api/storyblok/space.get.js +14 -0
  45. package/dist/runtime/server/tsconfig.json +3 -0
  46. package/dist/runtime/server/utils/storyblok.d.ts +5 -0
  47. package/dist/runtime/server/utils/storyblok.js +183 -0
  48. package/dist/types.d.mts +3 -0
  49. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ <!--
2
+ Get your module up and running quickly.
3
+
4
+ Find and replace all on all files (CMD+SHIFT+F):
5
+ - Name: My Module
6
+ - Package name: my-module
7
+ - Description: My new Nuxt module
8
+ -->
9
+
10
+ # My Module
11
+
12
+ [![npm version][npm-version-src]][npm-version-href]
13
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
14
+ [![License][license-src]][license-href]
15
+ [![Nuxt][nuxt-src]][nuxt-href]
16
+
17
+ My new Nuxt module for doing amazing things.
18
+
19
+ - [✨ &nbsp;Release Notes](/CHANGELOG.md)
20
+ <!-- - [🏀 Online playground](https://stackblitz.com/github/your-org/my-module?file=playground%2Fapp.vue) -->
21
+ <!-- - [📖 &nbsp;Documentation](https://example.com) -->
22
+
23
+ ## Features
24
+
25
+ <!-- Highlight some of the features your module provide here -->
26
+ - ⛰ &nbsp;Foo
27
+ - 🚠 &nbsp;Bar
28
+ - 🌲 &nbsp;Baz
29
+
30
+ ## Quick Setup
31
+
32
+ Install the module to your Nuxt application with one command:
33
+
34
+ ```bash
35
+ npx nuxi module add my-module
36
+ ```
37
+
38
+ That's it! You can now use My Module in your Nuxt app ✨
39
+
40
+
41
+ ## Contribution
42
+
43
+ <details>
44
+ <summary>Local development</summary>
45
+
46
+ ```bash
47
+ # Install dependencies
48
+ npm install
49
+
50
+ # Generate type stubs
51
+ npm run dev:prepare
52
+
53
+ # Develop with the playground
54
+ npm run dev
55
+
56
+ # Build the playground
57
+ npm run dev:build
58
+
59
+ # Run ESLint
60
+ npm run lint
61
+
62
+ # Run Vitest
63
+ npm run test
64
+ npm run test:watch
65
+
66
+ # Release new version
67
+ npm run release
68
+ ```
69
+
70
+ </details>
71
+
72
+
73
+ <!-- Badges -->
74
+ [npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=020420&colorB=00DC82
75
+ [npm-version-href]: https://npmjs.com/package/my-module
76
+
77
+ [npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=020420&colorB=00DC82
78
+ [npm-downloads-href]: https://npm.chart.dev/my-module
79
+
80
+ [license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=020420&colorB=00DC82
81
+ [license-href]: https://npmjs.com/package/my-module
82
+
83
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt
84
+ [nuxt-href]: https://nuxt.com
@@ -0,0 +1,11 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ expire: number;
5
+ version: 'draft' | 'published';
6
+ key: string;
7
+ }
8
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
9
+
10
+ export { _default as default };
11
+ export type { ModuleOptions };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "gothamstoryblok",
3
+ "configKey": "gothamstoryblok",
4
+ "version": "1.0.15",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,50 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { defineNuxtModule, createResolver, addPlugin, addServerHandler, addImportsDir } from '@nuxt/kit';
5
+
6
+ const currentDir = dirname(fileURLToPath(import.meta.url));
7
+ const addServerRoutes = (resolver) => {
8
+ const basePath = "/api/storyblok";
9
+ const sourcePath = "./runtime/server/api/storyblok";
10
+ const targetDir = join(currentDir, sourcePath);
11
+ const files = readdirSync(targetDir);
12
+ const cleanedFiles = files.filter((e) => e.includes(".js") || e.includes(".ts")).map((e) => e.replace(".js", "").replace(".ts", ""));
13
+ cleanedFiles.forEach((handler) => {
14
+ const [route, method] = handler.split(".");
15
+ addServerHandler({
16
+ route: `${basePath}/${route}`,
17
+ handler: resolver.resolve(`${sourcePath}/${route}.${method}`)
18
+ });
19
+ });
20
+ };
21
+ const addPlugins = (resolver) => {
22
+ addPlugin(resolver.resolve("./runtime/plugins/01.setupLanguages"));
23
+ addPlugin(resolver.resolve("./runtime/plugins/02.utils"));
24
+ addPlugin(resolver.resolve("./runtime/plugins/03.labels"));
25
+ addPlugin(resolver.resolve("./runtime/plugins/04.images"));
26
+ addPlugin(resolver.resolve("./runtime/plugins/05.datasources"));
27
+ };
28
+ const addComposables = (resolver) => {
29
+ addImportsDir(resolver.resolve("runtime/composables"));
30
+ };
31
+ const module$1 = defineNuxtModule({
32
+ meta: {
33
+ name: "gothamstoryblok",
34
+ configKey: "gothamstoryblok"
35
+ },
36
+ defaults: {
37
+ expire: 1,
38
+ version: "draft",
39
+ key: ""
40
+ },
41
+ setup(_options, _nuxt) {
42
+ _nuxt.options.runtimeConfig.gothamstoryblok = { ..._options };
43
+ const resolver = createResolver(import.meta.url);
44
+ addPlugins(resolver);
45
+ addServerRoutes(resolver);
46
+ addComposables(resolver);
47
+ }
48
+ });
49
+
50
+ export { module$1 as default };
@@ -0,0 +1,11 @@
1
+ export function useAsyncGothamStoryblok(url: any, options: any, bridgeOptions: any, nocomponent?: boolean): Promise<{
2
+ story?: undefined;
3
+ stories?: undefined;
4
+ rels?: undefined;
5
+ refresh?: undefined;
6
+ } | {
7
+ story: import("vue").Ref<any, any>;
8
+ stories: import("vue").Ref<any, any>;
9
+ rels: import("vue").Ref<any, any>;
10
+ refresh: () => void;
11
+ }>;
@@ -0,0 +1,62 @@
1
+ import { useStoryblokBridge } from '@storyblok/vue'
2
+ import { useNuxtApp, useState, useAsyncData, createError, useLanguage, onMounted } from '#imports'
3
+
4
+ export const useAsyncGothamStoryblok = async (url, options, bridgeOptions, nocomponent = false) => {
5
+ const { $__ } = useNuxtApp()
6
+ const uniqueKey = `${JSON.stringify(options)}${url}`
7
+ const story = useState(`${uniqueKey}-state`)
8
+ const stories = useState(`${uniqueKey}-stories-state`)
9
+ const rels = useState(`${uniqueKey}-rels-state`)
10
+ if (!nocomponent) {
11
+ onMounted(() => {
12
+ if (!stories.value && story.value && story.value.id) {
13
+ useStoryblokBridge(
14
+ story.value.id,
15
+ evStory => (story.value = evStory),
16
+ bridgeOptions,
17
+ )
18
+ }
19
+ })
20
+ }
21
+ var refresh = () => {}
22
+ if (!story.value) {
23
+ const { data, error, refresh: refreshAsync } = await useAsyncData(uniqueKey, async () => {
24
+ var params = {
25
+ fullSlug: url,
26
+ }
27
+ if (options) params = { ...params, ...options }
28
+ const { currentLanguage, defaultLanguage } = useLanguage()
29
+ if (!params.language && currentLanguage.value != defaultLanguage) params.language = currentLanguage.value
30
+
31
+ if (params.isEditor) {
32
+ const { $__clearCache } = useNuxtApp()
33
+ await $__clearCache(params)
34
+ }
35
+
36
+ return $fetch('/api/storyblok/request', { query: params })
37
+ })
38
+ refresh = refreshAsync
39
+
40
+ if (error.value) throw error.value
41
+
42
+ if (!data?.value && !nocomponent) throw createError({ statusCode: 404, statusMessage: $__('errorPageNotFound'), fatal: true })
43
+ if (data?.value) {
44
+ if (data.value.total) {
45
+ stories.value = data.value.stories
46
+ return { ...data.value }
47
+ }
48
+ if (Array.isArray(data.value)) {
49
+ stories.value = data.value
50
+ }
51
+ else {
52
+ story.value = data.value
53
+ }
54
+
55
+ if (options?.resolve_relations) {
56
+ story.value = data.value?.story
57
+ rels.value = data.value?.rels
58
+ }
59
+ }
60
+ }
61
+ return { story, stories, rels, refresh }
62
+ }
@@ -0,0 +1,6 @@
1
+ export function useLanguage(): {
2
+ languages: import("vue").Ref<undefined, undefined>;
3
+ currentLanguage: import("vue").Ref<undefined, undefined>;
4
+ defaultLanguage: import("vue").Ref<undefined, undefined>;
5
+ slugList: import("vue").Ref<never[], never[]>;
6
+ };
@@ -0,0 +1,15 @@
1
+ import { useState } from '#imports'
2
+
3
+ export const useLanguage = () => {
4
+ const languages = useState('languages', () => undefined)
5
+ const currentLanguage = useState('currentLanguage', () => undefined)
6
+ const defaultLanguage = useState('defaultLanguage', () => undefined)
7
+ const slugList = useState('slugList', () => [])
8
+
9
+ return {
10
+ languages,
11
+ currentLanguage,
12
+ defaultLanguage,
13
+ slugList,
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ export function useSeo(story: any): void;
@@ -0,0 +1,69 @@
1
+ import { renderRichText } from '@storyblok/js'
2
+ import { useRequestURL, useRoute, useNuxtApp, useLanguage, useHead } from '#imports'
3
+
4
+ const hostChecker = (array, string) => {
5
+ for (var i in array) {
6
+ if (string?.includes(array[i])) return true
7
+ }
8
+ return false
9
+ }
10
+ export const useSeo = (story) => {
11
+ const richText = data => renderRichText(data)
12
+ const { host } = useRequestURL()
13
+ const route = useRoute()
14
+ const { defaultLanguage } = useLanguage()
15
+ const { $__, $__img } = useNuxtApp()
16
+ var siteName = $__('configSiteName') != '' ? `${$__('configSiteName')} - ` : ''
17
+ const metadata
18
+ = story.value?.content?.metadata
19
+ || {
20
+ title: null,
21
+ description: $__('configSiteDescription'),
22
+ image: $__img('configSiteImage'),
23
+ follow: 'follow',
24
+ }
25
+
26
+ var { title, description, image, follow } = metadata
27
+ title = title || story?.value?.content?.title || story?.name
28
+
29
+ if (typeof title == 'object' && title?.type) {
30
+ const rendered = richText(title)
31
+ title = rendered?.replace(/<[^>]*>?/g, '')
32
+ }
33
+ if (!title && siteName && siteName?.includes(' - ')) {
34
+ siteName = siteName.replace(' - ', '')
35
+ title = ''
36
+ }
37
+ title = siteName + title
38
+
39
+ description = description || story?.value?.content?.previewText || $__('configSiteDescription')
40
+
41
+ const noFollow = follow == 'nofollow' || hostChecker(['development', 'staging'], host)
42
+ follow = noFollow ? 'noindex, nofollow' : 'index, follow'
43
+
44
+ const path = route.fullPath || route.path
45
+
46
+ useHead({
47
+ title: title,
48
+ htmlAttrs: {
49
+ lang: route.params.lang || defaultLanguage.value,
50
+ },
51
+ link: [
52
+ { rel: 'canonical', href: 'https://' + host + path },
53
+ ],
54
+ meta: [
55
+ { property: 'og:url', content: 'https://' + host + path },
56
+ { property: 'og:image:width', content: '1200' },
57
+ { property: 'og:image:height', content: '630' },
58
+ { property: 'og:image', content: image },
59
+ { property: 'og:description', content: description },
60
+ { property: 'og:site_name', content: $__('configSiteName') },
61
+ { property: 'og:title', content: title },
62
+ { property: 'og:type', content: 'article' },
63
+ { name: 'apple-mobile-web-app-title', content: title },
64
+ { name: 'application-name', content: $__('configSiteName') },
65
+ { name: 'description', content: description },
66
+ { name: 'robots', content: follow },
67
+ ],
68
+ })
69
+ }
@@ -0,0 +1,23 @@
1
+ export function useSizes(): {
2
+ reference: import("vue").Ref<undefined, undefined>;
3
+ sizes: import("vue").Ref<{
4
+ width: number;
5
+ height: number;
6
+ fr: number;
7
+ }, {
8
+ width: number;
9
+ height: number;
10
+ fr: number;
11
+ }>;
12
+ scroll: import("vue").Ref<{
13
+ top: number;
14
+ left: number;
15
+ }, {
16
+ top: number;
17
+ left: number;
18
+ }>;
19
+ observe: (el: any, fn: any) => void;
20
+ mw: (size: any) => number;
21
+ mh: (size: any) => number;
22
+ init: () => void;
23
+ };
@@ -0,0 +1,70 @@
1
+ import { useState, watch } from '#imports'
2
+
3
+ export const useSizes = () => {
4
+ const gridCount = 80
5
+ const reference = useState('reference', () => undefined)
6
+
7
+ const mw = (size) => {
8
+ if (size) return sizes.value.fr * size
9
+ return 0
10
+ }
11
+
12
+ const mh = (size) => {
13
+ if (size) return sizes.value.height / 100 * size
14
+ return 0
15
+ }
16
+
17
+ const sizes = useState('sizes', () => ({ width: 0, height: 0, fr: 0 }))
18
+ const scroll = useState('scroll', () => ({ top: 0, left: 0 }))
19
+
20
+ const resizeListener = () => {
21
+ reference.value = reference.value ?? window
22
+ // se mi riferisco alla window devo recuperare la inner altrimenti recupero la client
23
+ const width = (reference.value.clientWidth ?? reference.value.innerWidth)
24
+ const height = (reference.value.clientHeight ?? reference.value.innerHeight)
25
+
26
+ sizes.value.fr = width / gridCount
27
+ sizes.value.width = width
28
+ sizes.value.height = height
29
+ }
30
+ const scrollListener = () => {
31
+ const top = reference.value.scrollY !== undefined ? reference.value.scrollY : reference.value.scrollTop
32
+ const left = reference.value.scrollY !== undefined ? reference.value.scrollX : reference.value.scrollLeft
33
+
34
+ scroll.value.top = top
35
+ scroll.value.left = left
36
+ }
37
+
38
+ watch(reference, () => {
39
+ resizeListener()
40
+ })
41
+
42
+ const init = () => {
43
+ if (import.meta.client) {
44
+ resizeListener()
45
+ window.addEventListener('resize', resizeListener)
46
+ window.addEventListener('scroll', scrollListener)
47
+ }
48
+ }
49
+
50
+ const observer = useState('observer', () => null)
51
+ const observerFunctions = useState('observerFunctions', () => [])
52
+ if (import.meta.client && !observer.value) {
53
+ observer.value = new IntersectionObserver((entries) => {
54
+ entries.forEach((entry) => {
55
+ if (entry.isIntersecting) {
56
+ const index = entry.target.dataset.functionIndex
57
+ const fn = observerFunctions.value[index]
58
+ fn()
59
+ }
60
+ })
61
+ }, { threshold: 0.1 })
62
+ }
63
+ const observe = (el, fn) => {
64
+ observer.value.observe(el)
65
+ observerFunctions.value.push(fn)
66
+ el.dataset.functionIndex = observerFunctions.value.indexOf(fn)
67
+ }
68
+
69
+ return { reference, sizes, scroll, observe, mw, mh, init }
70
+ }
@@ -0,0 +1,3 @@
1
+ export function useStoryblokEditor(): {
2
+ sbEditor: import("vue").Ref<boolean, boolean>;
3
+ };
@@ -0,0 +1,10 @@
1
+ import { useRoute, useState } from '#imports'
2
+
3
+ export const useStoryblokEditor = () => {
4
+ const route = useRoute()
5
+ const sbEditor = useState('sbEditor', () => {
6
+ return false
7
+ })
8
+ sbEditor.value = Object.keys(route.query)?.includes('_storyblok_tk[space_id]')
9
+ return { sbEditor }
10
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,26 @@
1
+ import { createError, useLanguage, useRoute } from '#imports'
2
+ import { defineNuxtPlugin } from '#app'
3
+
4
+ export default defineNuxtPlugin({
5
+ name: 'setuplanguages',
6
+ parallel: false,
7
+ async setup() {
8
+ const { languages, currentLanguage, defaultLanguage } = useLanguage()
9
+ const route = useRoute()
10
+ const { fullslug } = route.params
11
+ currentLanguage.value = fullslug && fullslug[0] ? fullslug[0] : undefined
12
+ if (import.meta.server) {
13
+ try {
14
+ const space = await $fetch('/api/storyblok/space')
15
+ languages.value = space.available_languages
16
+ defaultLanguage.value = space.default_language
17
+ if (currentLanguage.value?.length != 2) currentLanguage.value = defaultLanguage.value
18
+ }
19
+ catch (error) {
20
+ console.log(error)
21
+ throw createError({ statusCode: 500, statusMessage: 'Error setupLanguage plugin' })
22
+ }
23
+ }
24
+ if (currentLanguage.value?.length != 2) currentLanguage.value = defaultLanguage.value
25
+ },
26
+ })
@@ -0,0 +1,30 @@
1
+ declare const _default: import("#app").Plugin<{
2
+ __url: (u: any) => any;
3
+ __parseEndpoint: () => {
4
+ endpoint: undefined;
5
+ language?: undefined;
6
+ version?: undefined;
7
+ } | {
8
+ endpoint: any;
9
+ language: any;
10
+ version: unknown;
11
+ };
12
+ __filename: (str: any) => any;
13
+ __formatDate: (d: any) => string;
14
+ __clearCache: (query: any) => Promise<void>;
15
+ }> & import("#app").ObjectPlugin<{
16
+ __url: (u: any) => any;
17
+ __parseEndpoint: () => {
18
+ endpoint: undefined;
19
+ language?: undefined;
20
+ version?: undefined;
21
+ } | {
22
+ endpoint: any;
23
+ language: any;
24
+ version: unknown;
25
+ };
26
+ __filename: (str: any) => any;
27
+ __formatDate: (d: any) => string;
28
+ __clearCache: (query: any) => Promise<void>;
29
+ }>;
30
+ export default _default;
@@ -0,0 +1,74 @@
1
+ import { defineNuxtPlugin } from '#app'
2
+ import { useLanguage, useRoute, useRuntimeConfig, useStoryblokEditor } from '#imports'
3
+
4
+ export default defineNuxtPlugin({
5
+ dependsOn: ['labels', 'images'],
6
+ async setup() {
7
+ return {
8
+ provide: {
9
+ __url: (u) => {
10
+ if (!u) return undefined
11
+ if (u == '#' || u.substring(0, 4) == 'http') return u
12
+ const { languages, currentLanguage, slugList } = useLanguage()
13
+ u = u.substring(0, 1) == '/' ? u : '/' + u
14
+ if (languages.value.length < 2) return u
15
+ u = u.replace(/([^:])\/\//g, '$1/')
16
+ const founded = slugList.value.find(s => (u == s.fullslug || u == '/' + s.fullslug))
17
+ if (founded?.slugs) {
18
+ const sp = u.split('/')
19
+ sp[sp.length - 1] = founded?.slugs[currentLanguage.value]
20
+ u = sp.join('/')
21
+ }
22
+ const prefix = '/' + currentLanguage.value + '/'
23
+ const startString = u.substring(0, 4)
24
+ if (prefix != startString) u = prefix + u
25
+ u = u.replace(/([^:])\/\//g, '$1/')
26
+ return u
27
+ },
28
+ __parseEndpoint: () => {
29
+ const config = useRuntimeConfig()
30
+ const route = useRoute()
31
+ const { languages, currentLanguage, defaultLanguage, slugList } = useLanguage()
32
+
33
+ let language
34
+ let fullslug
35
+
36
+ if (route?.params?.fullslug && route?.params?.fullslug.length > 0) {
37
+ fullslug = JSON.parse(JSON.stringify(route?.params?.fullslug))
38
+ const availableLanguages = languages.value
39
+ if (availableLanguages?.includes(fullslug[0])) {
40
+ language = fullslug[0] != defaultLanguage.value ? fullslug[0] : undefined
41
+ fullslug.shift()
42
+ }
43
+ if (fullslug[0] == '' || !fullslug[0]) fullslug = undefined
44
+ }
45
+
46
+ const endpoint = fullslug || ['homepage']
47
+ const version = config.public.storyblokVersion
48
+ // ECCEZIONE PER I TRANSLATED SLUGS
49
+ if (!route.query['_storyblok_tk[token]'] && slugList.value?.length > 0) {
50
+ let slug = endpoint.pop()
51
+ const founded = slugList.value.find(s => s.slugs[currentLanguage.value] == slug)
52
+ if (!founded?.slug) {
53
+ return { endpoint: undefined }
54
+ }
55
+ else {
56
+ endpoint.push(founded?.slug)
57
+ }
58
+ }
59
+ return { endpoint: endpoint.join('/'), language, version }
60
+ },
61
+ __filename: (str) => {
62
+ return str.replace(/^.*[\\/]/, '')
63
+ },
64
+ __formatDate: d => (new Date(d)).toLocaleDateString('IT-it', { day: '2-digit', month: '2-digit', year: 'numeric' }),
65
+ __clearCache: async (query) => {
66
+ const { sbEditor } = useStoryblokEditor()
67
+ if (sbEditor.value) {
68
+ await $fetch('/api/storyblok/clearCache', { query })
69
+ }
70
+ },
71
+ },
72
+ }
73
+ },
74
+ })
@@ -0,0 +1,6 @@
1
+ declare const _default: import("#app").Plugin<{
2
+ __: (...args: any[]) => any;
3
+ }> & import("#app").ObjectPlugin<{
4
+ __: (...args: any[]) => any;
5
+ }>;
6
+ export default _default;
@@ -0,0 +1,50 @@
1
+ import { defineNuxtPlugin } from '#app'
2
+ import { useFetch, useRoute, useLanguage } from '#imports'
3
+
4
+ export default defineNuxtPlugin({
5
+ name: 'labels',
6
+ parallel: true,
7
+ async setup() {
8
+ const { params } = useRoute()
9
+ const { defaultLanguage, languages } = useLanguage()
10
+
11
+ var labels = {}
12
+ let language = defaultLanguage.value
13
+ if (params?.fullslug && params?.fullslug.length > 0) {
14
+ const fullslug = JSON.parse(JSON.stringify(params?.fullslug))
15
+ if (languages.value?.includes(fullslug[0])) {
16
+ language = fullslug[0]
17
+ }
18
+ }
19
+ const query = {
20
+ starts_with: 'system/labels',
21
+ language,
22
+ }
23
+ const { data: stories } = await useFetch('/api/storyblok/request', {
24
+ query,
25
+ })
26
+ if (stories?.value?.length) {
27
+ for (var story of stories.value) {
28
+ if (story?.content?.items?.length) {
29
+ for (var label of story.content.items) {
30
+ labels[label.key] = label.text
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return {
36
+ provide: {
37
+ __: (...args) => {
38
+ var s = args[0]
39
+ var prefix = args[2]
40
+ const key = prefix ? prefix + '_' + s : s
41
+ s = labels[key] || labels[key] === '' ? labels[key] : s
42
+ for (var i = 1; i < args.length; i++) {
43
+ s = s.replace('%s', args[i])
44
+ }
45
+ return s || ''
46
+ },
47
+ },
48
+ }
49
+ },
50
+ })
@@ -0,0 +1,8 @@
1
+ declare const _default: import("#app").Plugin<{
2
+ __img: (key: any) => any;
3
+ __svg: (svgUrl: any) => Promise<any>;
4
+ }> & import("#app").ObjectPlugin<{
5
+ __img: (key: any) => any;
6
+ __svg: (svgUrl: any) => Promise<any>;
7
+ }>;
8
+ export default _default;
@@ -0,0 +1,39 @@
1
+ import { defineNuxtPlugin, useFetch } from '#app'
2
+
3
+ export default defineNuxtPlugin({
4
+ name: 'images',
5
+ parallel: true,
6
+ async setup() {
7
+ var images = {}
8
+ const { data: stories } = await useFetch('/api/storyblok/request', {
9
+ query: {
10
+ starts_with: 'system/images',
11
+ },
12
+ })
13
+ if (stories?.value?.length) {
14
+ for (var story of stories.value) {
15
+ if (story.content?.items?.length) {
16
+ for (var image of story.content.items) {
17
+ images[image.key] = image.asset
18
+ }
19
+ }
20
+ }
21
+ }
22
+ return {
23
+ provide: {
24
+ __img: (key) => {
25
+ return images[key]
26
+ },
27
+ __svg: async (svgUrl) => {
28
+ if (!svgUrl) return null
29
+ const extension = svgUrl.split('.').pop()
30
+ if (!extension?.includes('svg')) return null
31
+ svgUrl = svgUrl.replace('https://', 'https://s3.amazonaws.com/')
32
+ const response = await $fetch(svgUrl)
33
+ const data = await response.text()
34
+ return data
35
+ },
36
+ },
37
+ }
38
+ },
39
+ })
@@ -0,0 +1,8 @@
1
+ declare const _default: import("#app").Plugin<{
2
+ __datasource: (key: any) => any;
3
+ __datasources: (key: any) => any;
4
+ }> & import("#app").ObjectPlugin<{
5
+ __datasource: (key: any) => any;
6
+ __datasources: (key: any) => any;
7
+ }>;
8
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { defineNuxtPlugin, useFetch } from '#app'
2
+
3
+ export default defineNuxtPlugin({
4
+ async setup() {
5
+ const { data: datasources } = await useFetch('/api/storyblok/datasources')
6
+ return {
7
+ provide: {
8
+ __datasource: (key) => {
9
+ return datasources.value.find(d => d?.value == key)
10
+ },
11
+ __datasources: (key) => {
12
+ if (key) return datasources.value.filter(d => d?.datasource == key)
13
+
14
+ return datasources
15
+ },
16
+ },
17
+ }
18
+ },
19
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import { defineNuxtPlugin } from "#app";
2
+ export default defineNuxtPlugin((_nuxtApp) => {
3
+ console.log("Plugin injected by my-module!");
4
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { getAll } from '../../utils/storyblok.js'
2
+ import { defineCachedEventHandler, getQuery, setHeader } from '#imports'
3
+
4
+ export default defineCachedEventHandler(async (event) => {
5
+ const query = getQuery(event)
6
+ setHeader(event, 'x-origin-url', event.node.req.url)
7
+ return await getAll(query)
8
+ }, {
9
+ maxAge: process.env.DEFAULT_EXPIRE,
10
+ group: 'storyblok',
11
+ shouldInvalidateCache: (e) => {
12
+ const query = getQuery(e)
13
+ const bypass = query.sbToken != undefined
14
+ return bypass
15
+ },
16
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { defineEventHandler, useStorage } from '#imports'
2
+
3
+ export default defineEventHandler(async () => {
4
+ const cacheStorage = useStorage('cache:storyblok:_')
5
+ const cachedKeys = await cacheStorage.getKeys()
6
+ const promises = cachedKeys.map(async (c) => {
7
+ const item = await cacheStorage.getItem(c)
8
+ return {
9
+ name: c,
10
+ ...item,
11
+ }
12
+ })
13
+ const items = await Promise.all(promises)
14
+ return items
15
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,66 @@
1
+ import Cloudflare from 'cloudflare'
2
+ import { clearLinks } from '../../utils/storyblok.js'
3
+ import { defineEventHandler, useRuntimeConfig, getQuery, useStorage } from '#imports'
4
+
5
+ const { cloudflare } = useRuntimeConfig()
6
+
7
+ const { zoneID, email, apiKey } = cloudflare
8
+ let cloudFlareClient
9
+ if (apiKey && email) {
10
+ cloudFlareClient = new Cloudflare({
11
+ apiEmail: email,
12
+ apiKey: apiKey,
13
+ })
14
+ }
15
+ const foundSlugInBody = (body, slug) => {
16
+ if (body?.full_slug && body?.full_slug == slug) return true
17
+ if (!body?.full_slug && Array.isArray(body)) {
18
+ const filtered = body.filter(e => e.full_slug == slug || e.full_slug == slug + '/')
19
+ return filtered.length > 0
20
+ }
21
+ const stringbody = JSON.stringify(body)
22
+ if (stringbody.includes(slug)) return true
23
+ return false
24
+ }
25
+
26
+ export default defineEventHandler(async (event) => {
27
+ // Clear cache Nitro
28
+ const config = useRuntimeConfig()
29
+ const query = getQuery(event)
30
+ const { fullSlug, language, itemId } = query
31
+ const cacheStorage = useStorage('cache:storyblok:_')
32
+ const cachedKeys = await cacheStorage.getKeys()
33
+ const endpoint = language && language != config.defaultLanguage ? `${language}/${fullSlug}` : fullSlug
34
+ console.log('Clear cache Nitro - endpoint', endpoint)
35
+ if (itemId) {
36
+ console.log('ITEM ID', itemId)
37
+ return await cacheStorage.removeItem(itemId)
38
+ }
39
+ // ho rimosso il push nell'array del promise all perché per qualche ragione non svuotava correttamente in locale.
40
+ // così è più lento ma funziona.
41
+ for (var i in cachedKeys) {
42
+ if (fullSlug) {
43
+ const stored = await cacheStorage.getItem(cachedKeys[i])
44
+ const toclear = foundSlugInBody(stored?.value?.body, endpoint)
45
+ if (toclear) {
46
+ console.log('clerearing', cachedKeys[i])
47
+ await cacheStorage.removeItem(cachedKeys[i])
48
+ }
49
+ }
50
+ else {
51
+ await cacheStorage.removeItem(cachedKeys[i])
52
+ }
53
+ }
54
+ console.log('Cleared cache Nitro - endpoint', endpoint)
55
+ await clearLinks()
56
+
57
+ if (cloudFlareClient) {
58
+ try {
59
+ await cloudFlareClient.cache.purge({ zone_id: zoneID, purge_everything: true })
60
+ }
61
+ catch (error) {
62
+ console.log('err purge cloudflare cache', error)
63
+ }
64
+ }
65
+ return true
66
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { clearLinks } from '../../utils/storyblok.js'
2
+ import { defineEventHandler, useStorage } from '#imports'
3
+
4
+ export default defineEventHandler(async () => {
5
+ // Clear cache Nitro
6
+ const cacheStorage = useStorage('cache:storyblok:_')
7
+ const cachedKeys = await cacheStorage.getKeys()
8
+ let promiseArr = []
9
+ for (var i in cachedKeys) promiseArr.push(cacheStorage.removeItem(cachedKeys[i]))
10
+ await Promise.all(promiseArr)
11
+
12
+ await clearLinks()
13
+
14
+ return true
15
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,41 @@
1
+ // https://developers.cloudflare.com/workers/examples/aggregate-requests/
2
+
3
+ import { defineCachedEventHandler, useRuntimeConfig, createError, getQuery } from '#imports'
4
+
5
+ const config = useRuntimeConfig()
6
+ export default defineCachedEventHandler(async () => {
7
+ try {
8
+ let arr = []
9
+ const cv = Date.now()
10
+ const { datasources } = await fetch('https://api.storyblok.com/v2/cdn/datasources?' + new URLSearchParams({ token: config.gothamstoryblok.key, per_page: 100, cv }), { headers: { 'Content-Type': 'application/json' } }).then(response => response.json())
11
+
12
+ const promiseArr = []
13
+ datasources.map((d) => {
14
+ promiseArr.push(
15
+ fetch('https://api.storyblok.com/v2/cdn/datasource_entries?' + new URLSearchParams({ token: config.gothamstoryblok.key, per_page: 100, cv, datasource: d.slug }, { headers: { 'Content-Type': 'application/json' } })),
16
+ )
17
+ })
18
+
19
+ const responses = await Promise.all(promiseArr)
20
+ const response = await Promise.all(responses.map(r => r.json()))
21
+
22
+ for (var i in response) {
23
+ response[i].datasource_entries = response[i].datasource_entries.map(e => ({ ...e, datasource: datasources[i].slug }))
24
+ arr = arr.concat(response[i].datasource_entries)
25
+ }
26
+
27
+ return arr
28
+ }
29
+ catch (error) {
30
+ console.log(error)
31
+ throw createError({ statusCode: 500, statusMessage: 'Error building datasources' })
32
+ }
33
+ }, {
34
+ maxAge: process.env.DEFAULT_EXPIRE,
35
+ group: 'storyblok',
36
+ shouldInvalidateCache: (e) => {
37
+ const query = getQuery(e)
38
+ const bypass = query.sbToken != undefined
39
+ return bypass
40
+ },
41
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,34 @@
1
+ import { storyblokInit, apiPlugin } from '@storyblok/js'
2
+ import { defineCachedEventHandler, useRuntimeConfig, getQuery } from '#imports'
3
+
4
+ const config = useRuntimeConfig()
5
+ const { storyblokApi } = storyblokInit({
6
+ accessToken: config.gothamstoryblok.key,
7
+ use: [apiPlugin],
8
+ })
9
+
10
+ export default defineCachedEventHandler(async (event) => {
11
+ const query = getQuery(event)
12
+ delete query.sbToken
13
+ const params = {
14
+ ...query,
15
+ version: config.gothamstoryblok.version,
16
+ }
17
+ storyblokApi.flushCache()
18
+ const { data: { stories } } = await storyblokApi.get('cdn/stories', params)
19
+
20
+ return stories
21
+ }, {
22
+ maxAge: 60 * 60 * 12,
23
+ group: 'storyblok',
24
+ shouldInvalidateCache: async (e) => {
25
+ const query = getQuery(e)
26
+ const bypass = query.sbToken != undefined
27
+ console.log('bypass', bypass)
28
+ if (bypass) {
29
+ storyblokApi.flushCache()
30
+ await $fetch('/api/storyblok/clearCache')
31
+ }
32
+ return bypass
33
+ },
34
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { links } from '../../utils/storyblok.js'
2
+ import { defineCachedEventHandler, getQuery, setHeader } from '#imports'
3
+
4
+ export default defineCachedEventHandler(async (event) => {
5
+ const query = getQuery(event)
6
+ setHeader(event, 'x-origin-url', event.node.req.url)
7
+ return await links(query)
8
+ }, {
9
+ maxAge: process.env.DEFAULT_EXPIRE,
10
+ group: 'storyblok',
11
+ name: 'links',
12
+ shouldInvalidateCache: (e) => {
13
+ const query = getQuery(e)
14
+ const bypass = query.sbToken != undefined
15
+ return bypass
16
+ },
17
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { request } from '../../utils/storyblok.js'
2
+ import { defineCachedEventHandler, getQuery, setHeader } from '#imports'
3
+
4
+ export default defineCachedEventHandler(async (event) => {
5
+ const query = getQuery(event)
6
+ setHeader(event, 'x-origin-url', event.node.req.url)
7
+ return await request(query)
8
+ }, {
9
+ maxAge: process.env.DEFAULT_EXPIRE,
10
+ group: 'storyblok',
11
+ shouldInvalidateCache: (e) => {
12
+ const query = getQuery(e)
13
+ const bypass = query.sbToken != undefined
14
+ return bypass
15
+ },
16
+
17
+ })
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { space } from '../../utils/storyblok.js'
2
+ import { defineCachedEventHandler, getQuery } from '#imports'
3
+
4
+ export default defineCachedEventHandler(async () => {
5
+ return await space()
6
+ }, {
7
+ maxAge: process.env.DEFAULT_EXPIRE,
8
+ group: 'storyblok',
9
+ shouldInvalidateCache: (e) => {
10
+ const query = getQuery(e)
11
+ const bypass = query.sbToken != undefined
12
+ return bypass
13
+ },
14
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../.nuxt/tsconfig.server.json",
3
+ }
@@ -0,0 +1,5 @@
1
+ export function request(query: any): Promise<any>;
2
+ export function space(): Promise<any>;
3
+ export function getAll(params: any): Promise<unknown>;
4
+ export function links(query: any): Promise<any>;
5
+ export function clearLinks(): Promise<void>;
@@ -0,0 +1,183 @@
1
+ import { useRuntimeConfig, createError, useStorage } from '#imports'
2
+
3
+ const config = useRuntimeConfig()
4
+ const request = async (query) => {
5
+ try {
6
+ const defaultLanguage = config.defaultLanguage
7
+ const cv = Date.now()
8
+ const { find_by, returntotal, isEditor } = query
9
+ if (returntotal) delete query.returntotal
10
+ if (isEditor) delete query.isEditor
11
+ const params = {
12
+ cv,
13
+ version: config.gothamstoryblok.version,
14
+ token: config.gothamstoryblok.key,
15
+ find_by,
16
+ }
17
+ if (query.language && query.language != defaultLanguage) {
18
+ params.language = query.language
19
+ }
20
+ if (query.resolve_relations) {
21
+ params.resolve_relations = query.resolve_relations
22
+ }
23
+ if (query.fullSlug) {
24
+ await checkFullSlug(query.fullSlug)
25
+
26
+ const data = await $fetch(`https://api.storyblok.com/v2/cdn/stories/${query.fullSlug}`, { method: 'GET', query: params })
27
+
28
+ const { story, rels } = data
29
+
30
+ if (params.resolve_relations) return { story, rels }
31
+ return story
32
+ }
33
+
34
+ let { _data: data, headers } = await $fetch.raw('https://api.storyblok.com/v2/cdn/stories', {
35
+ query: {
36
+ cv,
37
+ version: config.gothamstoryblok.version,
38
+ token: config.gothamstoryblok.key,
39
+ ...query,
40
+ },
41
+ })
42
+ const perPage = Number.parseInt(headers.get('per-page'))
43
+ const total = Number.parseInt(headers.get('total'))
44
+ let { stories, rels } = data
45
+ if (!isEditor || isEditor === 'false') stories = stories.filter(s => !s?.content?.metadata?.disbledforlanguage)
46
+
47
+ let res = stories
48
+ if (returntotal) res = { stories, perPage, total }
49
+ if (rels.length) {
50
+ if (returntotal) {
51
+ res.rels = rels
52
+ }
53
+ else {
54
+ res = { stories, rels }
55
+ }
56
+ }
57
+
58
+ return res
59
+ }
60
+ catch (error) {
61
+ throw error
62
+ }
63
+ }
64
+
65
+ const space = async () => {
66
+ const data = await $fetch(`https://api.storyblok.com/v2/cdn/spaces/me`, {
67
+ query: {
68
+ token: config.gothamstoryblok.key,
69
+ },
70
+ })
71
+
72
+ const { space } = data
73
+ const available_languages = [...space.language_codes, config.defaultLanguage]
74
+ space.available_languages = available_languages
75
+ space.default_language = config.defaultLanguage
76
+ delete space.language_codes
77
+ return space
78
+ }
79
+
80
+ const getAll = async (params) => {
81
+ const requestParams = {
82
+ ...params,
83
+ returntotal: true,
84
+ per_page: 100,
85
+ }
86
+
87
+ const getStories = async (page = 1, allStories = []) => {
88
+ try {
89
+ const { stories, total } = await request(requestParams)
90
+ allStories.push(...stories)
91
+ console.log(allStories.length < total)
92
+ if (allStories.length === 100) {
93
+ return getStories(page + 1, allStories)
94
+ }
95
+ return allStories
96
+ }
97
+ catch (error) {
98
+ return error
99
+ }
100
+ }
101
+
102
+ try {
103
+ return await getStories()
104
+ }
105
+ catch (error) {
106
+ return error
107
+ }
108
+ }
109
+
110
+ const links = async (query) => {
111
+ const cv = Date.now()
112
+ try {
113
+ const { links } = await $fetch('https://api.storyblok.com/v2/cdn/links', {
114
+ query: {
115
+ cv,
116
+ version: config.gothamstoryblok.version,
117
+ token: config.gothamstoryblok.key,
118
+ ...query,
119
+ },
120
+ })
121
+
122
+ return links
123
+ }
124
+ catch (error) {
125
+ throw error
126
+ }
127
+ }
128
+
129
+ let linksList = undefined
130
+ let linksListPromise = null
131
+ const loadLinksList = async () => {
132
+ if (linksList) return linksList
133
+ // Se un'altra richiesta la sta già caricando, aspetta quella
134
+ if (!linksListPromise) {
135
+ linksListPromise = (async () => {
136
+ const result = {
137
+ slug: {},
138
+ uuid: {},
139
+ id: {},
140
+ }
141
+
142
+ const links = await $fetch('/api/storyblok/links', { query: { per_page: 1000 } })
143
+
144
+ for (const i in links) {
145
+ const link = links[i]
146
+ const slugParts = link.slug.split('/')
147
+ const slug = slugParts[slugParts.length - 1]
148
+
149
+ if (!result.slug[slug]) result.slug[slug] = link
150
+ if (!result.uuid[link.uuid]) result.uuid[link.uuid] = link
151
+ if (!result.id[link.id]) result.id[link.id] = link
152
+ }
153
+
154
+ linksList = result
155
+ linksListPromise = null // reset per richieste future
156
+ return result
157
+ })()
158
+ }
159
+
160
+ return linksListPromise
161
+ }
162
+
163
+ const checkFullSlug = async (fullSlug) => {
164
+ const cachedLinks = await loadLinksList()
165
+
166
+ const slugs = fullSlug.split('/')
167
+ const slug = slugs[slugs.length - 1]
168
+
169
+ if (!(cachedLinks.slug[slug] || cachedLinks.id[slug] || cachedLinks.uuid[slug])) {
170
+ throw createError({ statusCode: 404, message: 'Not found (fullSlug)' })
171
+ }
172
+ }
173
+
174
+ const clearLinks = async () => {
175
+ linksList = undefined
176
+ linksListPromise = null
177
+
178
+ const cacheLinks = useStorage('cache:storyblok:links')
179
+ const cachedKeys = await cacheLinks.getKeys()
180
+ for (var i in cachedKeys) await cacheLinks.removeItem(cachedKeys[i])
181
+ }
182
+
183
+ export { request, space, getAll, links, clearLinks }
@@ -0,0 +1,3 @@
1
+ export { default } from './module.mjs'
2
+
3
+ export { type ModuleOptions } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "gothamstoryblok",
3
+ "version": "1.0.15",
4
+ "description": "Gotham.Studio | Storyblok and utils module",
5
+ "repository": "Gothamsiti/gothamstoryblok",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types.d.mts",
11
+ "import": "./dist/module.mjs"
12
+ }
13
+ },
14
+ "main": "./dist/module.mjs",
15
+ "typesVersions": {
16
+ "*": {
17
+ ".": [
18
+ "./dist/types.d.mts"
19
+ ]
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "dependencies": {
26
+ "@nuxt/kit": "^4.2.2",
27
+ "@storyblok/js": "^4.4.3",
28
+ "@storyblok/vue": "^9.4.3",
29
+ "cloudflare": "^5.2.0"
30
+ },
31
+ "devDependencies": {
32
+ "@nuxt/devtools": "^3.1.1",
33
+ "@nuxt/eslint-config": "^1.12.1",
34
+ "@nuxt/module-builder": "^1.0.2",
35
+ "@nuxt/schema": "^4.2.2",
36
+ "@nuxt/test-utils": "^3.21.0",
37
+ "@types/node": "latest",
38
+ "changelogen": "^0.6.2",
39
+ "eslint": "^9.39.2",
40
+ "nuxt": "^4.2.2",
41
+ "typescript": "~5.9.3",
42
+ "vitest": "^4.0.16",
43
+ "vue-tsc": "^3.2.1"
44
+ },
45
+ "scripts": {
46
+ "dev": "npm run dev:prepare && nuxi dev playground",
47
+ "dev:build": "nuxi build playground",
48
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
49
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
50
+ "lint": "eslint .",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest watch",
53
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
54
+ }
55
+ }