@dargmuesli/nuxt-vio 8.2.4 → 8.3.4

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/app.config.ts CHANGED
@@ -6,27 +6,11 @@ export default defineAppConfig({
6
6
  headers: {
7
7
  csp: {
8
8
  default: {
9
- 'Cross-Origin-Opener-Policy': 'same-origin',
10
- // 'Cross-Origin-Embedder-Policy', 'require-corp') // https://stackoverflow.com/questions/71904052/getting-notsameoriginafterdefaultedtosameoriginbycoep-error-with-helmet
11
- 'Cross-Origin-Resource-Policy': 'same-origin',
12
- // 'Expect-CT', 'max-age=0') // deprecated (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT)
13
9
  NEL: '\'{"report_to":"default","max_age":31536000,"include_subdomains":true}\'',
14
- 'Origin-Agent-Cluster': '?1',
15
- 'Permissions-Policy': '',
16
- 'Referrer-Policy': 'no-referrer',
17
10
  'Report-To':
18
11
  '\'{"group":"default":"max_age":31536000:"endpoints":[{"url":"https://dargmuesli.report-uri.com/a/d/g"}]:"include_subdomains":true}\'',
19
- 'X-Content-Type-Options': 'nosniff',
20
- 'X-DNS-Prefetch-Control': 'off',
21
- 'X-Download-Options': 'noopen',
22
- 'X-Frame-Options': 'SAMEORIGIN',
23
- 'X-Permitted-Cross-Domain-Policies': 'none',
24
- 'X-XSS-Protection': '1; mode=block', // TODO: set back to `0` once CSP does not use `unsafe-*` anymore (https://github.com/maevsi/maevsi/issues/1047)
25
- },
26
- production: {
27
- 'Strict-Transport-Security':
28
- 'max-age=31536000; includeSubDomains; preload',
29
12
  },
13
+ production: {} as Record<string, string>,
30
14
  },
31
15
  },
32
16
  },
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <h1>{{ `${statusCode ? `${statusCode} - ` : ''}${statusReason}` }}</h1>
3
+ <h1>{{ title }}</h1>
4
4
  <div>
5
5
  {{ description }}
6
6
  </div>
@@ -29,9 +29,14 @@ const props = withDefaults(defineProps<Props>(), {
29
29
  const runtimeConfig = useRuntimeConfig()
30
30
  const { locale, t } = useI18n()
31
31
 
32
- // computations
33
- const statusReason = computed(() => {
34
- return status(props.statusCode, locale.value) || t('error')
32
+ // data
33
+ const title = `${props.statusCode ? `${props.statusCode} - ` : ''}${
34
+ status(props.statusCode, locale.value) || t('error')
35
+ }`
36
+
37
+ // initialization
38
+ useHeadDefault({
39
+ title,
35
40
  })
36
41
  </script>
37
42
 
@@ -25,7 +25,7 @@
25
25
  </template>
26
26
 
27
27
  <script setup lang="ts">
28
- import { NuxtLinkProps } from '#app'
28
+ import type { NuxtLinkProps } from '#app'
29
29
 
30
30
  interface Props {
31
31
  ariaLabel?: string
@@ -14,9 +14,9 @@
14
14
  </template>
15
15
 
16
16
  <script setup lang="ts">
17
- import { UnwrapRef } from 'vue'
17
+ import type { UnwrapRef } from 'vue'
18
18
 
19
- import { ApiData } from '@dargmuesli/nuxt-vio/types/api'
19
+ import type { ApiData } from '@dargmuesli/nuxt-vio/types/api'
20
20
 
21
21
  interface Props {
22
22
  api: UnwrapRef<ApiData>
@@ -1,4 +1,4 @@
1
- import { Dayjs } from 'dayjs'
1
+ import type { Dayjs } from 'dayjs'
2
2
 
3
3
  export const useDateTime = () => {
4
4
  const { $dayjs, ssrContext } = useNuxtApp()
@@ -1,6 +1,6 @@
1
1
  import { consola } from 'consola'
2
2
  import Swal from 'sweetalert2'
3
- import { Ref } from 'vue'
3
+ import type { Ref } from 'vue'
4
4
 
5
5
  export const useFireError = () => {
6
6
  const { t } = useI18n()
package/error.vue CHANGED
@@ -10,7 +10,7 @@
10
10
  </template>
11
11
 
12
12
  <script setup lang="ts">
13
- import { NuxtError } from 'nuxt/app'
13
+ import type { NuxtError } from 'nuxt/app'
14
14
 
15
15
  interface Props {
16
16
  error: NuxtError
package/nuxt.config.ts CHANGED
@@ -43,13 +43,44 @@ export default defineNuxtConfig(
43
43
  },
44
44
  modules: [
45
45
  '@dargmuesli/nuxt-cookie-control',
46
+ '@nuxt/devtools',
46
47
  '@nuxt/image',
47
48
  '@nuxtjs/color-mode',
48
49
  '@nuxtjs/html-validator',
49
50
  '@nuxtjs/i18n',
50
51
  '@nuxtjs/tailwindcss',
51
- '@nuxtseo/module',
52
52
  '@pinia/nuxt',
53
+ // nuxt-security: remove invalid `'none'`s
54
+ (_options, nuxt) => {
55
+ const nuxtConfigSecurity = nuxt.options.security
56
+
57
+ if (
58
+ typeof nuxtConfigSecurity.headers !== 'boolean' &&
59
+ nuxtConfigSecurity.headers.contentSecurityPolicy &&
60
+ typeof nuxtConfigSecurity.headers.contentSecurityPolicy !==
61
+ 'boolean' &&
62
+ typeof nuxtConfigSecurity.headers.contentSecurityPolicy !== 'string'
63
+ ) {
64
+ for (const [key, value] of Object.entries(
65
+ nuxtConfigSecurity.headers.contentSecurityPolicy,
66
+ )) {
67
+ if (!Array.isArray(value)) continue
68
+
69
+ const valueFiltered = value.filter((x) => x !== "'none'")
70
+
71
+ if (valueFiltered.length) {
72
+ ;(
73
+ nuxtConfigSecurity.headers.contentSecurityPolicy as Record<
74
+ string,
75
+ any
76
+ >
77
+ )[key] = valueFiltered
78
+ }
79
+ }
80
+ }
81
+ },
82
+ 'nuxt-security',
83
+ '@nuxtseo/module',
53
84
  ],
54
85
  nitro: {
55
86
  compressPublicAssets: true,
@@ -136,6 +167,146 @@ export default defineNuxtConfig(
136
167
  linkChecker: {
137
168
  failOnError: true,
138
169
  },
170
+ robots: {
171
+ credits: false,
172
+ },
173
+ security: {
174
+ headers: {
175
+ contentSecurityPolicy: defu(
176
+ {
177
+ // Cloudflare
178
+ ...(process.env.NODE_ENV === 'production'
179
+ ? {
180
+ 'connect-src': [`${SITE_URL}/cdn-cgi/rum`],
181
+ 'script-src-elem': [
182
+ 'https://static.cloudflareinsights.com',
183
+ ],
184
+ }
185
+ : {}),
186
+ },
187
+ {
188
+ // Google Analytics 4 (https://developers.google.com/tag-platform/tag-manager/web/csp)
189
+ 'connect-src': [
190
+ 'https://*.analytics.google.com',
191
+ 'https://*.google-analytics.com',
192
+ 'https://*.googletagmanager.com',
193
+ ],
194
+ 'img-src': [
195
+ 'https://*.google-analytics.com',
196
+ 'https://*.googletagmanager.com',
197
+ ],
198
+ 'script-src-elem': ['https://*.googletagmanager.com'],
199
+ },
200
+ {
201
+ // vio
202
+ 'manifest-src': [`${SITE_URL}/site.webmanifest`],
203
+ 'script-src-elem': [
204
+ 'https://polyfill.io/v3/polyfill.min.js', // ESLint plugin compat
205
+ ],
206
+ },
207
+ {
208
+ // @nuxt/devtools
209
+ 'frame-src': [
210
+ ...(process.env.NODE_ENV === 'development'
211
+ ? ['http://localhost:3000/__nuxt_devtools__/client/']
212
+ : []),
213
+ ],
214
+ },
215
+ {
216
+ // nuxt-link-checker
217
+ 'connect-src': [
218
+ ...(process.env.NODE_ENV === 'development'
219
+ ? ['http://localhost:3000/api/__link_checker__/inspect']
220
+ : []),
221
+ ],
222
+ },
223
+ {
224
+ // nuxt-og-image
225
+ ...(process.env.NODE_ENV === 'development'
226
+ ? {
227
+ 'font-src': ['https://fonts.gstatic.com/s/inter/'],
228
+ 'frame-ancestors': ["'self'"],
229
+ 'frame-src': ["'self'"],
230
+ 'script-src-elem': ['https://cdn.tailwindcss.com/'],
231
+ 'style-src': [
232
+ // TODO: replace with `style-src-elem` once Webkit supports it
233
+ 'https://cdn.jsdelivr.net/npm/gardevoir https://fonts.googleapis.com/css2',
234
+ ],
235
+ }
236
+ : {}),
237
+ },
238
+ {
239
+ // nuxt-simple-sitemap
240
+ 'script-src-elem': [`${SITE_URL}/__sitemap__/style.xsl`],
241
+ },
242
+ {
243
+ // nuxt
244
+ 'connect-src': [
245
+ ...(process.env.NODE_ENV === 'development'
246
+ ? [
247
+ 'http://localhost:3000/_nuxt/', // Nuxt development
248
+ 'https://localhost:3000/_nuxt/', // Nuxt development
249
+ 'ws://localhost:3000/_nuxt/', // Nuxt development
250
+ 'wss://localhost:3000/_nuxt/', // Nuxt development
251
+ ]
252
+ : ["'self'"]), // Nuxt build metadata and payloads
253
+ ],
254
+ 'img-src': [
255
+ "'self'", // TODO: replace with `"'nonce-{{nonce}}'",`
256
+ 'data:', // external link icon
257
+ ],
258
+ 'script-src-elem': ["'nonce-{{nonce}}'"],
259
+ 'style-src': [
260
+ // TODO: replace with `style-src-elem` once Webkit supports it
261
+ "'self'", // TODO: replace with `"'nonce-{{nonce}}'",` (https://github.com/vitejs/vite/pull/11864)
262
+ "'unsafe-inline'", // TODO: replace with `"'nonce-{{nonce}}'",` (https://github.com/vitejs/vite/pull/11864)
263
+ ],
264
+ },
265
+ {
266
+ // base
267
+ 'base-uri': ["'none'"], // does not fallback to `default-src`
268
+ 'child-src': [],
269
+ 'connect-src': [],
270
+ 'default-src': ["'none'"],
271
+ 'font-src': [],
272
+ 'form-action': ["'none'"], // does not fallback to `default-src`
273
+ 'frame-ancestors': ["'none'"], // does not fallback to `default-src`
274
+ 'frame-src': [],
275
+ 'img-src': [],
276
+ 'media-src': [],
277
+ 'navigate-to': [],
278
+ 'object-src': [],
279
+ 'prefetch-src': [],
280
+ 'report-to': [],
281
+ 'report-uri': [],
282
+ // TODO: evaluate header (https://github.com/maevsi/maevsi/issues/830) // https://stackoverflow.com/questions/62081028/this-document-requires-trustedscripturl-assignment
283
+ // 'require-trusted-types-for': ["'script'"], // csp-evaluator
284
+ sandbox: [],
285
+ 'script-src': [],
286
+ 'script-src-attr': [],
287
+ 'script-src-elem': [],
288
+ 'style-src': [],
289
+ 'style-src-attr': [],
290
+ 'style-src-elem': [],
291
+ 'upgrade-insecure-requests': false, // TODO: set to `process.env.NODE_ENV === 'production'` or `true` when tests run on https
292
+ 'worker-src': [],
293
+ },
294
+ ),
295
+ crossOriginEmbedderPolicy: false, // https://stackoverflow.com/questions/71904052/getting-notsameoriginafterdefaultedtosameoriginbycoep-error-with-helmet
296
+ strictTransportSecurity:
297
+ process.env.NODE_ENV === 'production'
298
+ ? {
299
+ maxAge: 31536000,
300
+ includeSubdomains: true,
301
+ preload: true,
302
+ }
303
+ : false,
304
+ xXSSProtection: '1; mode=block', // TODO: set back to `0` once CSP does not use `unsafe-*` anymore (https://github.com/maevsi/maevsi/issues/1047)
305
+ },
306
+ nonce: {
307
+ enabled: true,
308
+ },
309
+ },
139
310
  seo: {
140
311
  splash: false,
141
312
  },
@@ -145,6 +316,7 @@ export default defineNuxtConfig(
145
316
  url: SITE_URL,
146
317
  },
147
318
  sitemap: {
319
+ credits: false,
148
320
  exclude: I18N_MODULE_CONFIG.locales.map(
149
321
  (locale) =>
150
322
  `/${locale.code !== 'en' ? `${locale.code}/` : ''}api/pages/**`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dargmuesli/nuxt-vio",
3
- "version": "8.2.4",
3
+ "version": "8.3.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/dargmuesli/vio.git"
@@ -37,6 +37,7 @@
37
37
  "build": "pnpm run build:node",
38
38
  "build:node": "nuxt build .playground",
39
39
  "build:static": "nuxt generate .playground",
40
+ "build:static:test": "cross-env NODE_ENV=production SITE_URL=http://localhost:3002 pnpm run build:static",
40
41
  "dev": "pnpm run start:dev",
41
42
  "generate": "pnpm run build:static",
42
43
  "lint:fix": "pnpm run lint:js --fix && pnpm run lint:ts --fix && pnpm run lint:style --fix",
@@ -50,6 +51,7 @@
50
51
  "start:dev": "nuxt dev .playground",
51
52
  "start:node": "node .playground/.output/server/index.mjs",
52
53
  "start:static": "serve .playground/.output/public",
54
+ "start:static:test": "cross-env NODE_ENV=production PORT=3002 SITE_URL=http://localhost:3002 pnpm run start:static",
53
55
  "test:e2e:docker:br": "pnpm run test:e2e:docker:build && pnpm run test:e2e:docker:run",
54
56
  "test:e2e:docker:build": "docker build -t test-e2e_base --build-arg UID=$(id -u) --build-arg GID=$(id -g) --target test-e2e_base ..",
55
57
  "test:e2e:docker:run": "docker run --rm -v \"$PWD/..:/srv/app\" -v \"$(pnpm store path):/srv/.pnpm-store\" test-e2e_base",
@@ -66,57 +68,66 @@
66
68
  },
67
69
  "dependencies": {
68
70
  "@axe-core/playwright": "4.8.1",
69
- "@dargmuesli/nuxt-cookie-control": "7.0.0-beta.1",
71
+ "@dargmuesli/nuxt-cookie-control": "7.0.1",
70
72
  "@heroicons/vue": "2.0.18",
71
73
  "@http-util/status-i18n": "0.8.1",
72
74
  "@intlify/eslint-plugin-vue-i18n": "3.0.0-next.4",
73
- "@nuxt/image": "1.0.0-rc.3",
75
+ "@nuxt/devtools": "1.0.0",
76
+ "@nuxt/image": "1.0.0",
74
77
  "@nuxtjs/color-mode": "3.3.0",
75
78
  "@nuxtjs/eslint-config-typescript": "12.1.0",
76
79
  "@nuxtjs/html-validator": "1.5.2",
77
- "@nuxtjs/i18n": "8.0.0-rc.5",
80
+ "@nuxtjs/i18n": "npm:@nuxtjs/i18n-edge@8.0.0-rc.5-28296269.d5d5540",
78
81
  "@nuxtjs/tailwindcss": "6.8.0",
79
- "@nuxtseo/module": "2.0.0-beta.37",
82
+ "@nuxtseo/module": "2.0.0-beta.39",
80
83
  "@pinia/nuxt": "0.5.1",
81
84
  "@playwright/test": "1.39.0",
82
85
  "@tailwindcss/forms": "0.5.6",
83
86
  "@tailwindcss/typography": "0.5.10",
84
- "@types/cookie": "0.5.2",
85
- "@types/lodash-es": "4.17.9",
86
- "@urql/core": "4.1.3",
87
+ "@types/lodash-es": "4.17.10",
88
+ "@unhead/vue": "1.7.4",
89
+ "@urql/core": "4.1.4",
87
90
  "@urql/devtools": "2.0.3",
88
91
  "@urql/exchange-graphcache": "6.3.3",
89
92
  "@urql/vue": "1.1.2",
90
93
  "@vuelidate/core": "2.0.3",
91
94
  "@vuelidate/validators": "2.0.4",
92
95
  "clipboard": "2.0.11",
93
- "cookie": "0.5.0",
96
+ "consola": "3.2.3",
97
+ "cookie-es": "1.0.0",
94
98
  "cross-env": "7.0.3",
95
99
  "dayjs": "2.0.0-alpha.4",
96
- "eslint": "8.51.0",
100
+ "defu": "6.1.2",
101
+ "eslint": "8.52.0",
97
102
  "eslint-config-prettier": "9.0.0",
98
103
  "eslint-plugin-compat": "4.2.0",
99
104
  "eslint-plugin-nuxt": "4.0.0",
100
105
  "eslint-plugin-prettier": "5.0.1",
101
106
  "eslint-plugin-yml": "1.10.0",
107
+ "h3": "1.8.2",
102
108
  "is-https": "4.0.0",
103
109
  "jiti": "1.20.0",
104
110
  "jose": "4.15.4",
105
- "lint-staged": "15.0.1",
111
+ "lint-staged": "15.0.2",
106
112
  "lodash-es": "4.17.21",
107
- "nuxt": "3.7.4",
113
+ "nuxt": "3.8.0",
114
+ "nuxt-security": "1.0.0-rc.2",
115
+ "ofetch": "1.3.3",
108
116
  "pinia": "2.1.7",
109
117
  "prettier": "3.0.3",
110
118
  "prettier-plugin-tailwindcss": "0.5.6",
111
119
  "serve": "14.2.1",
112
- "stylelint": "15.10.3",
120
+ "stylelint": "15.11.0",
113
121
  "stylelint-config-recommended-vue": "1.5.0",
114
122
  "stylelint-config-standard": "34.0.0",
115
123
  "stylelint-no-unsupported-browser-features": "7.0.0",
116
124
  "sweetalert2": "11.7.32",
117
125
  "tailwindcss": "3.3.3",
118
- "vue": "3.3.4",
126
+ "ufo": "1.3.1",
127
+ "unhead": "1.7.4",
128
+ "vue": "3.3.6",
119
129
  "vue-gtag": "2.0.1",
130
+ "vue-router": "4.2.5",
120
131
  "vue-tsc": "1.8.19"
121
132
  },
122
133
  "peerDependencies": {
@@ -87,7 +87,7 @@ export default defineConfig({
87
87
  NUXT_PUBLIC_VIO_IS_TESTING: 'true',
88
88
  },
89
89
  timeout: process.env.NODE_ENV === 'production' ? 10000 : 100000,
90
- url: process.env.SITE_URL || SITE_URL,
90
+ url: SITE_URL,
91
91
  reuseExistingServer: !process.env.CI,
92
92
  },
93
93
 
@@ -1,26 +1,15 @@
1
1
  import { appendHeader, defineEventHandler } from 'h3'
2
2
  import type { H3Event } from 'h3'
3
- import { AppConfig } from 'nuxt/schema'
3
+ import type { AppConfig } from 'nuxt/schema'
4
4
 
5
5
  import { TIMEZONE_HEADER_KEY } from '../../utils/constants'
6
6
  import { getTimezone } from '../../utils/networking'
7
7
 
8
8
  export default defineEventHandler(async (event) => {
9
9
  setRequestHeader(event, TIMEZONE_HEADER_KEY, await getTimezone(event))
10
- // setContentSecurityPolicy(event);
11
10
  setResponseHeaders(event)
12
11
  })
13
12
 
14
- // const setContentSecurityPolicy = (event: H3Event) => {
15
- // const config = useAppConfig();
16
-
17
- // appendHeader(
18
- // event,
19
- // "Content-Security-Policy",
20
- // getCspAsString(config.public.vio.server.middleware.headers.csp)
21
- // );
22
- // };
23
-
24
13
  const setRequestHeader = (event: H3Event, name: string, value?: string) => {
25
14
  event.node.req.headers[name] = value
26
15
  }
package/store/auth.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { decodeJwt, JWTPayload } from 'jose'
1
+ import { decodeJwt, type JWTPayload } from 'jose'
2
2
  import { defineStore } from 'pinia'
3
3
  import { ref } from 'vue'
4
4
 
package/utils/auth.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http'
2
2
 
3
3
  import { consola } from 'consola'
4
- import { parse, serialize } from 'cookie'
4
+ import { parse, serialize } from 'cookie-es'
5
5
  import { decodeJwt } from 'jose'
6
- import { Store } from 'pinia'
6
+ import type { Store } from 'pinia'
7
7
 
8
8
  import { useVioAuthStore } from '../store/auth'
9
9
  import { xhrPromise } from '../utils/networking'
@@ -2,7 +2,7 @@ import { CombinedError } from '@urql/core'
2
2
  import { H3Event, getCookie } from 'h3'
3
3
 
4
4
  import { ofetch } from 'ofetch'
5
- import { Ref } from 'vue'
5
+ import { type Ref } from 'vue'
6
6
 
7
7
  import type { ApiData, BackendError } from '../types/api'
8
8
  import { TIMEZONE_COOKIE_NAME } from './constants'
package/utils/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RouteLocationRaw } from '#vue-router'
1
+ import { type RouteLocationRaw } from '#vue-router'
2
2
 
3
3
  export const append = (path: string, pathToAppend?: RouteLocationRaw) =>
4
4
  path + (path.endsWith('/') ? '' : '/') + (pathToAppend ?? '')
@@ -1,2 +0,0 @@
1
- export const getCspAsString = (csp = {} as Record<string, Array<string>>) =>
2
- Object.keys(csp).reduce((p, c) => (p += `${c} ${csp[c].join(' ')};`), '')