@dargmuesli/nuxt-vio 8.2.4 → 8.3.4

Sign up to get free protection for your applications and to get access to all the features.
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(' ')};`), '')