@iqual/nuxt-icms 1.6.1 → 1.6.2-rc.2

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 (260) hide show
  1. package/README.md +86 -0
  2. package/__tests__/anchors.spec.ts +43 -0
  3. package/__tests__/contract/blokkliComponents.spec.ts +59 -0
  4. package/__tests__/contract/componentFragments.spec.ts +24 -0
  5. package/__tests__/contract/languages.spec.ts +63 -0
  6. package/__tests__/contract/project.ts +144 -0
  7. package/__tests__/hreflang.spec.ts +39 -0
  8. package/{tests → __tests__}/pages.spec.ts +14 -5
  9. package/app.config.ts +15 -0
  10. package/app.vue +3 -21
  11. package/assets/css/backend.css +56 -0
  12. package/assets/css/base/base.css +2 -2
  13. package/assets/css/base/typography.css +2 -2
  14. package/assets/css/ckeditor.css +3 -4
  15. package/assets/css/components/decorations.css +1 -1
  16. package/assets/css/components/editor.css +1 -1
  17. package/assets/css/components/grid-container.css +19 -19
  18. package/assets/css/fe-fixes.css +6 -12
  19. package/assets/css/theme/colors/color-primary.css +33 -11
  20. package/assets/css/theme/colors/color-secondary.css +33 -11
  21. package/assets/css/utilities/utilities.css +1 -1
  22. package/assets/symbols/anchor.svg +3 -0
  23. package/blokkli.editAdapter.ts +56 -0
  24. package/bundles.ts +6 -6
  25. package/cache/__tests__/pageCache.spec.ts +540 -0
  26. package/cache/cacheTagRegistry.ts +124 -0
  27. package/cache/enabledForRequest.ts +21 -0
  28. package/cache/multiCache.serverOptions.ts +58 -0
  29. package/cache/pageCache.ts +360 -0
  30. package/components/Blokkli/Anchor/icon.svg +3 -0
  31. package/components/Blokkli/Anchor/index.vue +13 -0
  32. package/components/Blokkli/AnchorNavigation/icon.svg +8 -0
  33. package/components/Blokkli/AnchorNavigation/index.vue +9 -0
  34. package/components/Blokkli/Card/Element/index.vue +0 -1
  35. package/components/Blokkli/Collapsible/Element/index.vue +5 -0
  36. package/components/Blokkli/FAQ/Element/index.vue +0 -1
  37. package/components/Blokkli/Fragment/Separator/icon.svg +4 -0
  38. package/components/Blokkli/Fragment/Separator/index.vue +87 -0
  39. package/components/Blokkli/Gallery/index.vue +1 -3
  40. package/components/Blokkli/IconText/Element/index.vue +0 -1
  41. package/components/Blokkli/Introduction/index.vue +0 -1
  42. package/components/Blokkli/Listing/icon.svg +9 -0
  43. package/components/Blokkli/Listing/index.vue +24 -0
  44. package/components/Blokkli/Logo/Element/index.vue +0 -1
  45. package/components/Blokkli/Table/index.vue +0 -1
  46. package/components/Blokkli/Team/Element/index.vue +0 -1
  47. package/components/Blokkli/Team/Layout/index.vue +0 -1
  48. package/components/Blokkli/Testimonial/Element/index.vue +0 -1
  49. package/components/Blokkli/Text/index.vue +0 -1
  50. package/components/DesignSystem/Atoms/Button.vue +3 -0
  51. package/components/DesignSystem/Atoms/GridRow.vue +3 -1
  52. package/components/DesignSystem/Molecules/ButtonGroup.vue +5 -4
  53. package/components/DesignSystem/Molecules/IconText.vue +6 -6
  54. package/components/DesignSystem/Molecules/MediaLightbox.vue +8 -8
  55. package/components/DesignSystem/Molecules/MediaSlider.vue +9 -11
  56. package/components/DesignSystem/Molecules/Partnership.vue +7 -4
  57. package/components/DesignSystem/Molecules/Table.vue +35 -19
  58. package/components/DesignSystem/Molecules/TitleComposition.vue +46 -15
  59. package/components/DesignSystem/Molecules/iFrame.vue +2 -2
  60. package/components/DesignSystem/Organisms/CallToAction.vue +8 -7
  61. package/components/DesignSystem/Organisms/CallToActionFeature.vue +8 -7
  62. package/components/DesignSystem/Organisms/CallToActionFullwidth.vue +9 -8
  63. package/components/DesignSystem/Organisms/ContentBlock.vue +26 -16
  64. package/components/DesignSystem/Organisms/FacetedListing.vue +38 -17
  65. package/components/DesignSystem/Organisms/FactCard.vue +6 -7
  66. package/components/DesignSystem/Organisms/FeatureCard.vue +14 -14
  67. package/components/DesignSystem/Organisms/FlipTeaser.vue +19 -19
  68. package/components/DesignSystem/Organisms/HeaderHero.vue +11 -11
  69. package/components/DesignSystem/Organisms/HeaderTextImage.vue +10 -10
  70. package/components/DesignSystem/Organisms/LinkList.vue +20 -12
  71. package/components/DesignSystem/Organisms/LogoElement.vue +7 -4
  72. package/components/DesignSystem/Organisms/Map.vue +1 -1
  73. package/components/DesignSystem/Organisms/SearchResults.vue +15 -6
  74. package/components/DesignSystem/Organisms/SpotlightCard.vue +18 -18
  75. package/components/DesignSystem/Organisms/TeamElement.vue +19 -18
  76. package/components/DesignSystem/Organisms/TeaserCard.vue +16 -17
  77. package/components/DesignSystem/Organisms/TeaserSimple.vue +4 -31
  78. package/components/DesignSystem/Organisms/Testimonial.vue +13 -13
  79. package/components/DesignSystem/Organisms/TextCard.vue +10 -10
  80. package/components/DesignSystem/Organisms/TimelineElement.vue +20 -19
  81. package/components/DesignSystem/Organisms/TwoColumnTextElement.vue +5 -5
  82. package/components/DesignSystem/README.md +59 -40
  83. package/components/Drupal/Breadcrumb/index.vue +25 -12
  84. package/components/Drupal/Messages/index.vue +9 -4
  85. package/components/Drupal/Webform/index.vue +271 -51
  86. package/components/Drupal/Webform/mutation.graphql +17 -1
  87. package/components/Form/HiddenInput.vue +1 -4
  88. package/components/Form/SelectInput.vue +1 -1
  89. package/components/Form/SubmitButton.vue +2 -2
  90. package/components/Form/TextAreaInput.vue +3 -3
  91. package/components/Form/TextEmailInput.vue +1 -1
  92. package/components/Form/TextInput.vue +1 -1
  93. package/components/Form/TextPhoneInput.vue +1 -1
  94. package/components/Form/WebformMarkup.vue +1 -1
  95. package/components/Form/composables/useFormDefaultValue.ts +9 -13
  96. package/components/Form/composables/useFormElement.ts +5 -3
  97. package/components/Form/utils/types.ts +3 -3
  98. package/components/Media/Image/fragment.graphql +4 -0
  99. package/components/Media/Image/index.vue +27 -17
  100. package/components/Media/RemoteVideo/index.vue +3 -3
  101. package/components/Media/Video/index.vue +1 -1
  102. package/components/Node/Icms/Page/SearchResult/fragment.graphql +1 -1
  103. package/components/Node/Icms/Page/SearchResult/index.vue +2 -2
  104. package/components/Node/Icms/Page/Teaser/index.vue +1 -1
  105. package/components/Node/Icms/Page/index.vue +8 -2
  106. package/components/Paragraph/Anchor/fragment.graphql +4 -0
  107. package/components/Paragraph/Anchor/index.vue +48 -0
  108. package/components/Paragraph/AnchorNavigation/fragment.graphql +3 -0
  109. package/components/Paragraph/AnchorNavigation/index.vue +154 -0
  110. package/components/Paragraph/BlokkliFragment/fragment.graphql +5 -0
  111. package/components/Paragraph/Button/Element/fragment.graphql +1 -1
  112. package/components/Paragraph/Button/Element/index.vue +13 -8
  113. package/components/Paragraph/Card/Element/index.vue +1 -1
  114. package/components/Paragraph/Card/Layout/index.vue +9 -9
  115. package/components/Paragraph/Collapsible/Element/index.vue +2 -2
  116. package/components/Paragraph/Collapsible/Layout/index.vue +19 -18
  117. package/components/Paragraph/Contact/index.vue +17 -17
  118. package/components/Paragraph/Downloads/fragment.graphql +1 -1
  119. package/components/Paragraph/Downloads/index.vue +13 -13
  120. package/components/Paragraph/FAQ/Element/index.vue +4 -3
  121. package/components/Paragraph/FAQ/Layout/index.vue +42 -21
  122. package/components/Paragraph/FromLibrary/fragment.graphql +17 -0
  123. package/components/Paragraph/Gallery/index.vue +14 -14
  124. package/components/Paragraph/HeroImage/index.vue +26 -19
  125. package/components/Paragraph/IconText/Element/index.vue +4 -4
  126. package/components/Paragraph/IconText/Layout/index.vue +12 -12
  127. package/components/Paragraph/Introduction/index.vue +17 -17
  128. package/components/Paragraph/LinkLists/Element/fragment.graphql +1 -1
  129. package/components/Paragraph/LinkLists/Element/index.vue +5 -5
  130. package/components/Paragraph/LinkLists/Layout/index.vue +9 -9
  131. package/components/Paragraph/Listing/fragment.graphql +14 -0
  132. package/components/Paragraph/Listing/index.vue +91 -0
  133. package/components/Paragraph/Logo/Element/fragment.graphql +1 -1
  134. package/components/Paragraph/Logo/Element/index.vue +7 -4
  135. package/components/Paragraph/Media/index.vue +23 -21
  136. package/components/Paragraph/Partnerships/Element/fragment.graphql +1 -1
  137. package/components/Paragraph/Partnerships/Element/index.vue +10 -7
  138. package/components/Paragraph/Partnerships/Layout/index.vue +13 -13
  139. package/components/Paragraph/SingleTestimonial/index.vue +13 -13
  140. package/components/Paragraph/Table/index.vue +12 -12
  141. package/components/Paragraph/Team/Element/index.vue +16 -14
  142. package/components/Paragraph/Team/Layout/index.vue +9 -9
  143. package/components/Paragraph/Teasers/index.vue +14 -17
  144. package/components/Paragraph/Testimonial/Element/index.vue +19 -18
  145. package/components/Paragraph/Testimonial/Layout/index.vue +24 -18
  146. package/components/Paragraph/Text/fragment.graphql +1 -1
  147. package/components/Paragraph/Text/index.vue +8 -8
  148. package/components/Paragraph/TextMedia/index.vue +22 -22
  149. package/components/Paragraph/Timeline/Element/index.vue +19 -19
  150. package/components/Paragraph/Timeline/Layout/index.vue +51 -20
  151. package/components/Paragraph/TwoColumnText/Element/index.vue +13 -13
  152. package/components/Paragraph/TwoColumnText/Layout/index.vue +1 -1
  153. package/components/Paragraph/iFrame/index.vue +8 -8
  154. package/components/Search/Facets/FacetDropdownUI.vue +25 -31
  155. package/components/Search/Facets/FacetedSearchUI.vue +72 -48
  156. package/components/Search/Facets/fragment.graphql +20 -15
  157. package/components/Section/Footer/index.vue +55 -32
  158. package/components/Section/Header/Buttons.vue +4 -5
  159. package/components/Section/Header/DesktopMainMenu.vue +5 -4
  160. package/components/Section/Header/DesktopMainMenuDropdown.vue +5 -5
  161. package/components/Section/Header/LanguageDropdown.vue +2 -3
  162. package/components/Section/Header/Logo.vue +3 -3
  163. package/components/Section/Header/MainButtons.vue +4 -5
  164. package/components/Section/Header/MetaButtons.vue +5 -6
  165. package/components/Section/Header/MetaMenu.vue +1 -1
  166. package/components/Section/Header/MobileMainMenu.vue +1 -1
  167. package/components/Section/Header/MobileMainMenuItem.vue +2 -2
  168. package/components/Section/Header/index.vue +6 -13
  169. package/composables/__tests__/useImageSources.spec.ts +91 -0
  170. package/composables/breadcrumb.ts +12 -7
  171. package/composables/useAccordionScroll.ts +12 -4
  172. package/composables/useAllowedFragments.ts +14 -0
  173. package/composables/useAnchors.ts +48 -0
  174. package/composables/useChildTitleWeight.ts +1 -1
  175. package/composables/useCurrentLangcodeEnum.ts +1 -1
  176. package/composables/useCurrentNodeId.ts +8 -0
  177. package/composables/useDrupalUrl.ts +15 -0
  178. package/composables/useEnrichFacetLabels.ts +8 -6
  179. package/composables/useEntityRegistry.ts +3 -3
  180. package/composables/useGlobalConfig.ts +1 -2
  181. package/composables/useGraphqlCacheability.ts +12 -1
  182. package/composables/useHreflangLinks.ts +54 -0
  183. package/composables/useImageSources.ts +70 -28
  184. package/composables/useInitData.ts +1 -2
  185. package/composables/useIsHomepage.ts +1 -1
  186. package/composables/useLinkUrl.ts +30 -0
  187. package/composables/useMediaLightbox.ts +62 -50
  188. package/composables/useMenu.ts +1 -3
  189. package/composables/useMessageBus.ts +14 -7
  190. package/composables/useMobileMenuOverlayBus.ts +1 -1
  191. package/composables/useNavigationOffset.ts +49 -6
  192. package/composables/usePageCache.ts +33 -0
  193. package/composables/useSiteShell.ts +11 -0
  194. package/composables/useTheme.ts +17 -13
  195. package/config/__tests__/stubs/nuxt-multi-cache-config.ts +2 -0
  196. package/config/blokkli.ts +7 -13
  197. package/config/easyTexts.ts +1 -1
  198. package/config/graphqlMiddleware.ts +11 -4
  199. package/config/index.ts +5 -2
  200. package/config/languageNegotiation.ts +1 -1
  201. package/config/multiCache.ts +6 -1
  202. package/config/postcss-drupal/postcss.config.cjs +2 -2
  203. package/config/runtimeConfig.ts +7 -2
  204. package/config/svgIconSprite.ts +1 -2
  205. package/config/vuepal.ts +16 -18
  206. package/error.vue +1 -22
  207. package/layouts/default.vue +2 -6
  208. package/layouts/error.vue +7 -15
  209. package/modules/blokkliFragments.ts +76 -0
  210. package/modules/dynamicEntityComponentResolver.ts +47 -47
  211. package/modules/dynamicParagraphResolver.ts +43 -8
  212. package/modules/pageCache.ts +106 -0
  213. package/nuxt.config.ts +62 -33
  214. package/package.json +23 -22
  215. package/pages/[...slug]/index.vue +25 -4
  216. package/pages/index.vue +3 -3
  217. package/pages/node/[nid]/index.vue +2 -3
  218. package/patches/@blokkli+editor+2.0.0-alpha.13.patch +15 -1
  219. package/plugins/currentNode.ts +17 -0
  220. package/plugins/graphqlMiddleware.ts +10 -2
  221. package/plugins/initData.ts +15 -7
  222. package/plugins/registerNodeIcmsPageComponents.ts +6 -2
  223. package/plugins/siteShell.client.ts +94 -0
  224. package/queries/facetTermLabels.graphql +1 -5
  225. package/queries/fragment.linkUrl.graphql +12 -0
  226. package/queries/query.contentSearch.graphql +17 -2
  227. package/queries/query.initData.graphql +1 -1
  228. package/scripts/create-shade.ts +13 -4
  229. package/scripts/update-icms.ts +3 -1
  230. package/server/__tests__/graphqlMiddleware.serverOptions.spec.ts +2 -2
  231. package/server/__tests__/turnstile.spec.ts +119 -0
  232. package/server/api/icms/blokkli/last-changed.get.ts +24 -0
  233. package/server/api/icon/[...params].ts +3 -9
  234. package/server/graphqlMiddleware.serverOptions.ts +9 -8
  235. package/server/helpers/index.ts +0 -1
  236. package/server/middleware/turnstile.ts +51 -0
  237. package/server/plugins/frameAncestors.ts +20 -0
  238. package/server/plugins/pageCache.ts +59 -0
  239. package/server/utils/__tests__/accessDeniedHandover.spec.ts +49 -0
  240. package/server/utils/__tests__/backendRequestHeaders.spec.ts +115 -0
  241. package/server/utils/accessDeniedHandover.ts +48 -0
  242. package/server/utils/backendRequestHeaders.ts +51 -0
  243. package/server/utils/cacheability.ts +4 -4
  244. package/shared/__tests__/blokkliState.spec.ts +54 -0
  245. package/shared/__tests__/csp.spec.ts +53 -0
  246. package/shared/__tests__/drupalUrl.spec.ts +50 -0
  247. package/shared/blokkliState.ts +40 -0
  248. package/shared/csp.ts +47 -0
  249. package/shared/drupalUrl.ts +30 -0
  250. package/tailwind.config.ts +0 -5
  251. package/types/breadcrumb.ts +22 -0
  252. package/types/global.ts +15 -14
  253. package/types/langcode.ts +16 -0
  254. package/types/layerConfig.ts +84 -0
  255. package/utils/pageCache.ts +95 -0
  256. package/utils/turnstile.ts +35 -0
  257. package/composables/useWebformEnrichment.ts +0 -68
  258. package/server/__tests__/graphqlMiddleware.ts +0 -41
  259. package/server/multiCache.serverOptions.ts +0 -32
  260. package/vitest.config.mts +0 -28
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The `icms` key every ICMS layer may add to its Nuxt config.
3
+ *
4
+ * It is read by the dynamicParagraphResolver and
5
+ * dynamicEntityComponentResolver modules, which merge the fragment names of
6
+ * all layers into the generated GraphQL fragments.
7
+ */
8
+ export type IcmsLayerConfig = {
9
+ /**
10
+ * Entity types that get a page component (e.g. `Node`).
11
+ */
12
+ pageComponentTypes?: string[]
13
+
14
+ /**
15
+ * Whether editors may place blökkli fragments (`defineBlokkliFragment()`).
16
+ *
17
+ * Defaults to `true`: every fragment component in any layer is offered. A
18
+ * project sets `false` to disable the feature; the `blokkliFragments` module
19
+ * then removes the editor's fragments feature.
20
+ */
21
+ blokkliFragments?: boolean
22
+
23
+ /**
24
+ * Fragment names contributed by the layer's paragraph components.
25
+ */
26
+ components?: {
27
+ fragments?: string[]
28
+ /**
29
+ * Fragments of wrapper paragraphs that embed another paragraph (e.g.
30
+ * `paragraphFromLibrary`). They are spread into `paragraph` only — the
31
+ * embedded paragraph uses `paragraphInLibrary`, which leaves them out so
32
+ * the fragments do not spread themselves.
33
+ */
34
+ wrapperFragments?: string[]
35
+ teaserFragments?: string[]
36
+ searchResultFragments?: string[]
37
+ }
38
+
39
+ /**
40
+ * Additional fragments spread into the generated entity fragments.
41
+ */
42
+ extendFragments?: {
43
+ entity?: string[]
44
+ teaser?: string[]
45
+ searchResult?: string[]
46
+ }
47
+ }
48
+
49
+ declare module '@nuxt/schema' {
50
+ interface NuxtConfig {
51
+ icms?: IcmsLayerConfig
52
+ }
53
+
54
+ interface NuxtOptions {
55
+ icms?: IcmsLayerConfig
56
+ }
57
+
58
+ /**
59
+ * Re-declared because nuxt-graphql-middleware augments `nuxt/schema` instead
60
+ * of `@nuxt/schema`, which does not merge into the hook types used by
61
+ * `nuxt.hooks.hookOnce()`.
62
+ *
63
+ * The `blokkli:alter-*` hooks are declared here because @blokkli/editor ships
64
+ * them without types; only the members the ICMS modules use are described.
65
+ */
66
+ interface NuxtHooks {
67
+ 'nuxt-graphql-middleware:init': (context: {
68
+ addDocument: (name: string, source: string) => void
69
+ schemaHasType: (name: string) => boolean
70
+ }) => void | Promise<void>
71
+ 'blokkli:alter-features': (context: {
72
+ features: {
73
+ getDefinition: () => { id: string } | null | undefined
74
+ disable: () => void
75
+ }[]
76
+ }) => void | Promise<void>
77
+ 'blokkli:alter-blocks': (context: {
78
+ blocks: {
79
+ type: 'main' | 'context' | 'fragment' | null
80
+ definition?: { name?: string } | null
81
+ }[]
82
+ }) => void | Promise<void>
83
+ }
84
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Page cache configuration (ICMS-268), shared by the Nitro server options and
3
+ * plugin and by the usePageCache() composable. Kept outside `server/` so the
4
+ * app can import it too.
5
+ */
6
+
7
+ /**
8
+ * How the SSR responses are cached (ICMS-268).
9
+ *
10
+ * - `default`: no shared cache in front of Nuxt. Anonymous HTML is cached in
11
+ * the bounded in-memory route cache of nuxt-multi-cache and sent with a
12
+ * short browser-only Cache-Control; no s-maxage and no Cache-Tag header.
13
+ * - `varnish`: a shared cache (Varnish) sits in front of Nuxt. Responses carry
14
+ * s-maxage and Cache-Tag for it, and the in-memory route cache stays off so
15
+ * that Varnish never re-caches a page Nuxt has not purged yet.
16
+ */
17
+ export type PageCacheMode = 'default' | 'varnish'
18
+
19
+ export type PageCacheConfig = {
20
+ mode: PageCacheMode
21
+ /** Browser max-age (seconds) for cacheable anonymous responses. */
22
+ browserMaxAge: number
23
+ /**
24
+ * Byte budget of the in-memory route (HTML) cache. 0 disables it, `auto`
25
+ * derives it from the memory limit of the container.
26
+ */
27
+ routeMaxSize: number | 'auto'
28
+ /** Store route cache entries gzipped (about 8× smaller). */
29
+ routeCompression: boolean
30
+ /** Upper bound (seconds) for how long a page stays in the route cache. */
31
+ routeMaxAge: number
32
+ /** Byte budget of the in-memory data cache. */
33
+ dataMaxSize: number
34
+ }
35
+
36
+ const MB = 1024 * 1024
37
+
38
+ export const PAGE_CACHE_DEFAULTS: PageCacheConfig = Object.freeze({
39
+ mode: 'default',
40
+ browserMaxAge: 300,
41
+ routeMaxSize: 'auto',
42
+ routeCompression: true,
43
+ routeMaxAge: 7 * 86_400,
44
+ dataMaxSize: 8 * MB,
45
+ })
46
+
47
+ /**
48
+ * The runtime config defaults. In development (`nuxi dev`) the route cache is
49
+ * off: nothing purges it locally, so anonymous pages would stay stale until
50
+ * the dev server restarts. NUXT_PAGE_CACHE_ROUTE_MAX_SIZE=auto turns it on.
51
+ */
52
+ export function pageCacheDefaults(dev: boolean): PageCacheConfig {
53
+ return { ...PAGE_CACHE_DEFAULTS, ...(dev ? { routeMaxSize: 0 } : {}) }
54
+ }
55
+
56
+ function toBoolean(value: unknown, fallback: boolean): boolean {
57
+ if (typeof value === 'boolean') {
58
+ return value
59
+ }
60
+ if (value === 'true' || value === '1') {
61
+ return true
62
+ }
63
+ if (value === 'false' || value === '0') {
64
+ return false
65
+ }
66
+ return fallback
67
+ }
68
+
69
+ function toNonNegativeNumber<T>(value: unknown, fallback: T): number | T {
70
+ const number = typeof value === 'string' ? Number(value) : value
71
+ return typeof number === 'number' && Number.isFinite(number) && number >= 0
72
+ ? Math.floor(number)
73
+ : fallback
74
+ }
75
+
76
+ /**
77
+ * Normalize the `pageCache` runtime config; values may arrive as strings from
78
+ * NUXT_PAGE_CACHE_* environment variables.
79
+ */
80
+ export function resolvePageCacheConfig(
81
+ input?: Partial<Record<keyof PageCacheConfig, unknown>> | null,
82
+ ): PageCacheConfig {
83
+ const d = PAGE_CACHE_DEFAULTS
84
+ return {
85
+ mode: input?.mode === 'varnish' ? 'varnish' : 'default',
86
+ browserMaxAge: toNonNegativeNumber(input?.browserMaxAge, d.browserMaxAge),
87
+ routeMaxSize:
88
+ input?.routeMaxSize === 'auto'
89
+ ? 'auto'
90
+ : toNonNegativeNumber(input?.routeMaxSize, d.routeMaxSize),
91
+ routeCompression: toBoolean(input?.routeCompression, d.routeCompression),
92
+ routeMaxAge: toNonNegativeNumber(input?.routeMaxAge, d.routeMaxAge),
93
+ dataMaxSize: toNonNegativeNumber(input?.dataMaxSize, d.dataMaxSize),
94
+ }
95
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared constants and helpers for the Turnstile spam protection.
3
+ *
4
+ * Kept free of Nitro auto-imports and outside `server/` so both the middleware and the
5
+ * frontend component can use them, and so they are unit testable.
6
+ */
7
+
8
+ /**
9
+ * Header carrying the Turnstile token from the browser to the Nuxt server.
10
+ *
11
+ * The token cannot travel as a GraphQL variable: `submitWebform` forwards its
12
+ * variables to Drupal, which knows nothing about Turnstile.
13
+ */
14
+ export const TURNSTILE_TOKEN_HEADER = 'x-turnstile-token'
15
+
16
+ /**
17
+ * Marks a rejected submission, so the form can show the right message.
18
+ */
19
+ export const TURNSTILE_ERROR_CODE = 'TURNSTILE_VERIFICATION_FAILED'
20
+
21
+ /**
22
+ * Whether a request is a webform submission and therefore has to be verified.
23
+ *
24
+ * Matched on the operation suffix rather than the full route so a project that
25
+ * changes `graphqlMiddleware.serverApiPrefix` stays protected.
26
+ */
27
+ export function isWebformSubmissionRequest(path?: string | null): boolean {
28
+ if (!path) {
29
+ return false
30
+ }
31
+
32
+ const pathname = path.split('?')[0] ?? ''
33
+
34
+ return pathname.endsWith('/mutation/submitWebform')
35
+ }
@@ -1,68 +0,0 @@
1
- // Composable for webform field enrichment system
2
- // Allows bundles to register custom field processors
3
-
4
- interface FormElement {
5
- metadata?: {
6
- key?: string
7
- type?: string
8
- [key: string]: any
9
- }
10
- [key: string]: any
11
- }
12
-
13
- interface EnrichmentContext {
14
- formElement: FormElement
15
- props: Record<string, any>
16
- }
17
-
18
- interface SubmissionContext {
19
- data: Record<string, any>
20
- elements: Array<{ element: string; value: any }>
21
- props: Record<string, any>
22
- }
23
-
24
- type EnrichmentFunction = (context: EnrichmentContext) => FormElement | null
25
- type SubmissionProcessor = (context: SubmissionContext) => void
26
-
27
- const enrichmentFunctions: EnrichmentFunction[] = []
28
- const submissionProcessors: SubmissionProcessor[] = []
29
-
30
- export function useWebformEnrichment() {
31
- const registerEnrichment = (fn: EnrichmentFunction) => {
32
- if (!enrichmentFunctions.includes(fn)) {
33
- enrichmentFunctions.push(fn)
34
- }
35
- }
36
-
37
- const registerSubmissionProcessor = (fn: SubmissionProcessor) => {
38
- if (!submissionProcessors.includes(fn)) {
39
- submissionProcessors.push(fn)
40
- }
41
- }
42
-
43
- const enrichFormElement = (formElement: FormElement, props: Record<string, any>): FormElement => {
44
- // Try each registered enrichment function
45
- for (const fn of enrichmentFunctions) {
46
- const result = fn({ formElement, props })
47
- if (result) {
48
- return result
49
- }
50
- }
51
- // Return original if no enrichment applied
52
- return formElement
53
- }
54
-
55
- const processSubmission = (context: SubmissionContext) => {
56
- // Run all submission processors
57
- for (const fn of submissionProcessors) {
58
- fn(context)
59
- }
60
- }
61
-
62
- return {
63
- registerEnrichment,
64
- registerSubmissionProcessor,
65
- enrichFormElement,
66
- processSubmission,
67
- }
68
- }
@@ -1,41 +0,0 @@
1
- import { describe, expect, test, vi } from 'vitest'
2
- import graphqlMiddlewareConfig from './../graphqlMiddleware.serverOptions'
3
- import type { H3Event } from 'h3'
4
-
5
- vi.mock('#graphql-server-options', () => {
6
- return {
7
- defineGraphqlServerOptions(v: unknown) {
8
- return v
9
- },
10
- }
11
- })
12
-
13
- const metaMock = vi.hoisted(() => ({
14
- importMetaClient: false,
15
- }))
16
-
17
- vi.mock('~~/shared/importMeta', () => metaMock)
18
-
19
- describe('The nuxt-graphql-middleware config', () => {
20
- test('Passes appropriate incoming headers', async () => {
21
- metaMock.importMetaClient = false
22
- expect(graphqlMiddlewareConfig.serverFetchOptions).toBeDefined()
23
-
24
- const event = {
25
- node: {
26
- req: {
27
- headers: {
28
- foobar: 'test',
29
- cookie: 'my_cookie',
30
- },
31
- },
32
- },
33
- } as unknown as H3Event
34
-
35
- const result = await graphqlMiddlewareConfig!.serverFetchOptions!(event)
36
- // @ts-ignore
37
- expect(result.headers?.foobar).toBeUndefined()
38
- // @ts-ignore
39
- expect(result.headers?.cookie).toEqual('my_cookie')
40
- })
41
- })
@@ -1,32 +0,0 @@
1
- import { defineMultiCacheOptions } from 'nuxt-multi-cache/server-options'
2
- import { getHeaders } from 'h3'
3
- import type { H3Event } from 'h3'
4
- import lruCacheDriver from 'unstorage/drivers/lru-cache'
5
-
6
- export default defineMultiCacheOptions(() => {
7
- return {
8
- api: {},
9
- data: {},
10
- enabledForRequest: (event: H3Event) => {
11
- const hasSession = (event.node.req.headers.cookie || '').includes('SSESS')
12
- return Promise.resolve(!hasSession)
13
- },
14
- route: {
15
- storage: {
16
- driver: lruCacheDriver({
17
- max: 10000,
18
- }),
19
- },
20
- buildCacheKey: (event: H3Event) => {
21
- const path = (event.path || '')
22
- .replaceAll('/', '__')
23
- .replaceAll('?', '__')
24
- .replaceAll('&', '__')
25
-
26
- const headers = getHeaders(event)
27
- const cookie = headers.cookie || 'anonymous'
28
- return path + cookie
29
- },
30
- },
31
- }
32
- })
package/vitest.config.mts DELETED
@@ -1,28 +0,0 @@
1
- import { defineVitestConfig } from '@nuxt/test-utils/config'
2
- import path from 'path'
3
-
4
- export default defineVitestConfig({
5
- test: {
6
- globals: true,
7
- environment: 'nuxt',
8
- alias: {
9
- '~': path.resolve(__dirname, './app'),
10
- '~~': path.resolve(__dirname, './'),
11
- },
12
- coverage: {
13
- all: true,
14
- provider: 'v8',
15
- reporter: ['text', 'json', 'html'],
16
- include: [
17
- 'app/**/*.*',
18
- 'components/**/*.*',
19
- 'composables/**/*.*',
20
- 'config/**/*.*',
21
- 'layouts/**/*.*',
22
- 'middleware/**/*.*',
23
- 'plugins/**/*.*',
24
- '/**/*.*',
25
- ],
26
- },
27
- },
28
- })