vuetify-nuxt-module 0.5.9 → 0.5.11

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/README.md CHANGED
@@ -32,7 +32,7 @@
32
32
  - ⚡ **Fully Tree Shakable**: by default, only the needed Vuetify components are imported
33
33
  - 🛠️ **Versatile**: custom Vuetify [directives](https://vuetifyjs.com/en/getting-started/installation/#manual-steps) and [labs components](https://vuetifyjs.com/en/labs/introduction/) registration
34
34
  - ✨ **Configurable Styles**: configure your variables using [Vuetify SASS Variables](https://vuetifyjs.com/en/features/sass-variables/)
35
- - 💥 **SSR**: automatic SSR detection and configuration
35
+ - 💥 **SSR**: automatic SSR detection and configuration including [HTTP Client hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints)
36
36
  - 🔩 **Nuxt Layers and Hooks**: load your Vuetify configuration using [Nuxt Layers](https://nuxt.com/docs/getting-started/layers#layers) or using a custom module via `vuetify:registerModule` [Nuxt Hook](https://nuxt.com/docs/guide/going-further/hooks#nuxt-hooks-build-time)
37
37
  - 📥 **Vuetify Configuration File**: configure your Vuetify options using a custom `vuetify.config` file, no dev server restart needed
38
38
  - 🔥 **Pure CSS Icons**: no more font/js icons, use the new `unocss-mdi` icon set or build your own with UnoCSS Preset Icons
@@ -22,3 +22,24 @@ declare module 'virtual:vuetify-icons-configuration' {
22
22
  export const isDev: boolean
23
23
  export function iconsConfiguration(): IconOptions
24
24
  }
25
+
26
+ declare module 'virtual:vuetify-ssr-client-hints-configuration' {
27
+ export interface SSRClientHintsConfiguration {
28
+ reloadOnFirstRequest: boolean
29
+ viewportSize: boolean
30
+ prefersColorScheme: boolean
31
+ prefersReducedMotion: boolean
32
+ clientWidth?: number
33
+ clientHeight?: number
34
+ prefersColorSchemeOptions?: {
35
+ baseUrl: string
36
+ defaultTheme: string
37
+ themeNames: string[]
38
+ cookieName: string
39
+ darkThemeName: string
40
+ lightThemeName: string
41
+ useBrowserThemeOnly: boolean
42
+ }
43
+ }
44
+ export const ssrClientHintsConfiguration: SSRClientHintsConfiguration
45
+ }
package/dist/module.d.ts CHANGED
@@ -5,6 +5,7 @@ import * as vuetify_locale from 'vuetify/locale';
5
5
  import * as vuetify_labs_components from 'vuetify/labs/components';
6
6
  import * as vuetify_directives from 'vuetify/directives';
7
7
  import * as vuetify_components from 'vuetify/components';
8
+ import { UnwrapNestedRefs } from 'vue';
8
9
 
9
10
  type DateAdapter = 'vuetify' | 'date-fns' | 'moment' | 'luxon' | 'dayjs' | 'js-joda' | 'date-fns-jalali' | 'jalaali' | 'hijri' | 'custom';
10
11
  /**
@@ -204,6 +205,78 @@ interface MOptions {
204
205
  * @default false
205
206
  */
206
207
  includeTransformAssetsUrls?: boolean;
208
+ /**
209
+ * Vuetify SSR client hints.
210
+ *
211
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints
212
+ */
213
+ ssrClientHints?: {
214
+ /**
215
+ * Should the module reload the page on first request?
216
+ *
217
+ * @default false
218
+ */
219
+ reloadOnFirstRequest?: boolean;
220
+ /**
221
+ * Enable `Sec-CH-Viewport-Width` and `Sec-CH-Viewport-Height` headers?
222
+ *
223
+ * @see https://wicg.github.io/responsive-image-client-hints/#sec-ch-viewport-width
224
+ * @see https://wicg.github.io/responsive-image-client-hints/#sec-ch-viewport-height
225
+ *
226
+ * @default false
227
+ */
228
+ viewportSize?: boolean;
229
+ /**
230
+ * Enable `Sec-CH-Prefers-Color-Scheme` header?
231
+ *
232
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme
233
+ *
234
+ * @default false
235
+ */
236
+ prefersColorScheme?: boolean;
237
+ /**
238
+ * The options for `prefersColorScheme`, `prefersColorScheme` must be enabled.
239
+ *
240
+ * If you want the module to handle the color scheme for you, you should configure this option, otherwise you'll need to add your custom implementation.
241
+ */
242
+ prefersColorSchemeOptions?: {
243
+ /**
244
+ * The name for the cookie.
245
+ *
246
+ * @default 'color-scheme'
247
+ */
248
+ cookieName?: string;
249
+ /**
250
+ * The name for the dark theme.
251
+ *
252
+ * @default 'dark'
253
+ */
254
+ darkThemeName?: string;
255
+ /**
256
+ * The name for the light theme.
257
+ *
258
+ * @default 'light'
259
+ */
260
+ lightThemeName?: string;
261
+ /**
262
+ * Use the browser theme only?
263
+ *
264
+ * This flag can be used when your application provides a custom dark and light themes,
265
+ * but will not provide a theme switcher, that's, using by default the browser theme.
266
+ *
267
+ * @default false
268
+ */
269
+ useBrowserThemeOnly?: boolean;
270
+ };
271
+ /**
272
+ * Enable `Sec-CH-Prefers-Reduced-Motion` header?
273
+ *
274
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Reduced-Motion
275
+ *
276
+ * @default false
277
+ */
278
+ prefersReducedMotion?: boolean;
279
+ };
207
280
  }
208
281
  interface ModuleOptions {
209
282
  moduleOptions?: MOptions;
@@ -223,6 +296,54 @@ interface InlineModuleOptions extends Omit<ModuleOptions, 'vuetifyOptions'> {
223
296
  interface ExternalVuetifyOptions extends VOptions {
224
297
  config?: boolean;
225
298
  }
299
+ /**
300
+ * Request headers received from the client in SSR.
301
+ */
302
+ interface SSRClientHints {
303
+ /**
304
+ * Is the first request the browser hits the server?
305
+ */
306
+ firstRequest: boolean;
307
+ /**
308
+ * The browser supports prefer-color-scheme client hints?
309
+ */
310
+ prefersColorSchemeAvailable: boolean;
311
+ /**
312
+ * The browser supports prefer-reduced-motion client hints?
313
+ */
314
+ prefersReducedMotionAvailable: boolean;
315
+ /**
316
+ * The browser supports viewport-height client hints?
317
+ */
318
+ viewportHeightAvailable: boolean;
319
+ /**
320
+ * The browser supports viewport-width client hints?
321
+ */
322
+ viewportWidthAvailable: boolean;
323
+ prefersColorScheme?: 'dark' | 'light' | 'no-preference';
324
+ prefersReducedMotion?: 'no-preference' | 'reduce';
325
+ viewportHeight?: number;
326
+ viewportWidth?: number;
327
+ /**
328
+ * The theme name from the cookie.
329
+ */
330
+ colorSchemeFromCookie?: string;
331
+ colorSchemeCookie?: string;
332
+ }
333
+ interface SSRClientHintsConfiguration {
334
+ enabled: boolean;
335
+ viewportSize: boolean;
336
+ prefersColorScheme: boolean;
337
+ prefersReducedMotion: boolean;
338
+ prefersColorSchemeOptions?: {
339
+ baseUrl: string;
340
+ defaultTheme: string;
341
+ themeNames: string[];
342
+ cookieName: string;
343
+ darkThemeName: string;
344
+ lightThemeName: string;
345
+ };
346
+ }
226
347
  declare module '@nuxt/schema' {
227
348
  interface NuxtConfig {
228
349
  vuetify?: ModuleOptions;
@@ -234,6 +355,10 @@ declare module '@nuxt/schema' {
234
355
  declare module '#app' {
235
356
  interface NuxtApp {
236
357
  $vuetify: ReturnType<typeof vuetify['createVuetify']>;
358
+ /**
359
+ * Request headers received from the client in SSR.
360
+ */
361
+ $ssrClientHints: UnwrapNestedRefs<SSRClientHints>;
237
362
  }
238
363
  interface RuntimeNuxtHooks {
239
364
  'vuetify:configuration': (options: {
@@ -244,9 +369,14 @@ declare module '#app' {
244
369
  isDev: boolean;
245
370
  vuetifyOptions: VuetifyOptions;
246
371
  }) => Promise<void> | void;
372
+ 'vuetify:ssr-client-hints': (options: {
373
+ vuetifyOptions: VuetifyOptions;
374
+ ssrClientHints: SSRClientHints;
375
+ ssrClientHintsConfiguration: SSRClientHintsConfiguration;
376
+ }) => Promise<void> | void;
247
377
  }
248
378
  }
249
379
 
250
380
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
251
381
 
252
- export { ComponentName, Components, DateAdapter, DateOptions, DirectiveName, Directives, ExternalVuetifyOptions, FontAwesomeSvgIconSet, FontIconSet, IconFontName, IconSetName, IconsOptions, InlineModuleOptions, JSSVGIconSet, LabComponentName, LabComponents, MOptions, ModuleOptions, VOptions, VuetifyLocale, _default as default };
382
+ export { ComponentName, Components, DateAdapter, DateOptions, DirectiveName, Directives, ExternalVuetifyOptions, FontAwesomeSvgIconSet, FontIconSet, IconFontName, IconSetName, IconsOptions, InlineModuleOptions, JSSVGIconSet, LabComponentName, LabComponents, MOptions, ModuleOptions, SSRClientHints, SSRClientHintsConfiguration, VOptions, VuetifyLocale, _default as default };
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.5.9"
7
+ "version": "0.5.11"
8
8
  }
package/dist/module.mjs CHANGED
@@ -12,7 +12,7 @@ import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify';
12
12
  import { isAbsolute, join, relative } from 'pathe';
13
13
  import { normalizePath as normalizePath$1 } from 'vite';
14
14
 
15
- const version = "0.5.9";
15
+ const version = "0.5.11";
16
16
 
17
17
  const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration";
18
18
  const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `/@nuxt-vuetify-configuration/${VIRTUAL_VUETIFY_CONFIGURATION.slice("virtual:".length)}`;
@@ -20,10 +20,13 @@ const VIRTUAL_VUETIFY_DATE_CONFIGURATION = "virtual:vuetify-date-configuration";
20
20
  const RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION = `/@nuxt-vuetify-configuration/${VIRTUAL_VUETIFY_DATE_CONFIGURATION.slice("virtual:".length)}`;
21
21
  const VIRTUAL_VUETIFY_ICONS_CONFIGURATION = "virtual:vuetify-icons-configuration";
22
22
  const RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION = `/@nuxt-vuetify-configuration/${VIRTUAL_VUETIFY_ICONS_CONFIGURATION.slice("virtual:".length)}`;
23
+ const VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION = "virtual:vuetify-ssr-client-hints-configuration";
24
+ const RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION = `/@nuxt-vuetify-configuration/${VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION.slice("virtual:".length)}`;
23
25
  const RESOLVED_VIRTUAL_MODULES = [
24
26
  RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION,
25
27
  RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION,
26
- RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION
28
+ RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION,
29
+ RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION
27
30
  ];
28
31
 
29
32
  async function loadVuetifyConfiguration(cwd = process.cwd(), configOrPath = cwd, defaults = {}, extraConfigSources = []) {
@@ -335,6 +338,58 @@ function convertFontSetsToObjectNotation(sets) {
335
338
  return result;
336
339
  }
337
340
 
341
+ const disabledClientHints = Object.freeze({
342
+ enabled: false,
343
+ reloadOnFirstRequest: false,
344
+ viewportSize: false,
345
+ prefersColorScheme: false,
346
+ prefersReducedMotion: false
347
+ });
348
+ function prepareSSRClientHints(baseUrl, ctx) {
349
+ if (!ctx.isSSR || ctx.isNuxtGenerate)
350
+ return disabledClientHints;
351
+ const { ssrClientHints: ssrClientHintsConfiguration } = ctx.moduleOptions;
352
+ const clientHints = {
353
+ enabled: false,
354
+ reloadOnFirstRequest: ssrClientHintsConfiguration?.reloadOnFirstRequest ?? false,
355
+ viewportSize: ssrClientHintsConfiguration?.viewportSize ?? false,
356
+ prefersColorScheme: ssrClientHintsConfiguration?.prefersColorScheme ?? false,
357
+ prefersReducedMotion: ssrClientHintsConfiguration?.prefersReducedMotion ?? false
358
+ };
359
+ clientHints.enabled = clientHints.viewportSize || clientHints.prefersColorScheme || clientHints.prefersReducedMotion;
360
+ if (clientHints.enabled && clientHints.prefersColorScheme && ssrClientHintsConfiguration?.prefersColorSchemeOptions) {
361
+ const theme = ctx.vuetifyOptions.theme;
362
+ if (!theme)
363
+ throw new Error("Vuetify theme is disabled");
364
+ const themes = theme.themes;
365
+ if (!themes)
366
+ throw new Error("Vuetify themes is missing in theme!");
367
+ const defaultTheme = theme.defaultTheme;
368
+ if (!defaultTheme)
369
+ throw new Error("Vuetify default theme is missing in theme!");
370
+ if (!themes[defaultTheme])
371
+ throw new Error(`Missing default theme ${defaultTheme} in the Vuetify themes!`);
372
+ const darkThemeName = ssrClientHintsConfiguration.prefersColorSchemeOptions?.darkThemeName ?? "dark";
373
+ if (!themes[darkThemeName])
374
+ throw new Error(`Missing theme ${darkThemeName} in the Vuetify themes!`);
375
+ const lightThemeName = ssrClientHintsConfiguration.prefersColorSchemeOptions?.lightThemeName ?? "light";
376
+ if (!themes[lightThemeName])
377
+ throw new Error(`Missing theme ${lightThemeName} in the Vuetify themes!`);
378
+ if (darkThemeName === lightThemeName)
379
+ throw new Error("Vuetify dark theme and light theme are the same, change darkThemeName or lightThemeName!");
380
+ clientHints.prefersColorSchemeOptions = {
381
+ baseUrl,
382
+ defaultTheme,
383
+ themeNames: Array.from(Object.keys(themes)),
384
+ cookieName: ssrClientHintsConfiguration.prefersColorSchemeOptions?.cookieName ?? "color-scheme",
385
+ darkThemeName,
386
+ lightThemeName,
387
+ useBrowserThemeOnly: ssrClientHintsConfiguration.prefersColorSchemeOptions?.useBrowserThemeOnly ?? false
388
+ };
389
+ }
390
+ return clientHints;
391
+ }
392
+
338
393
  async function load(options, nuxt, ctx) {
339
394
  var _a;
340
395
  const {
@@ -386,6 +441,7 @@ async function load(options, nuxt, ctx) {
386
441
  ctx.vuetifyOptions = configuration.vuetifyOptions;
387
442
  ctx.vuetifyFilesToWatch = Array.from(vuetifyConfigurationFilesToWatch);
388
443
  ctx.icons = prepareIcons(ctx.unocss, ctx.logger, vuetifyAppOptions);
444
+ ctx.ssrClientHints = prepareSSRClientHints(nuxt.options.app.baseURL ?? "/", ctx);
389
445
  if (ctx.icons.enabled) {
390
446
  ctx.icons.local?.forEach((css) => nuxt.options.css.push(css));
391
447
  if (ctx.icons.cdn?.length) {
@@ -904,7 +960,7 @@ ${unocss}
904
960
  if (ctx.icons.unocss && ctx.icons.unocssAliases) {
905
961
  ctx.icons.imports.unshift("// @unocss-include");
906
962
  const prefix = `${ctx.icons.unocssIconPrefix}mdi:`;
907
- unocss = `const aliases = ${JSON.stringify({
963
+ unocss = `const aliases = JSON.parse('${JSON.stringify({
908
964
  collapse: `${prefix}chevron-up`,
909
965
  complete: `${prefix}check`,
910
966
  cancel: `${prefix}close-circle`,
@@ -943,7 +999,7 @@ ${unocss}
943
999
  plus: `${prefix}plus`,
944
1000
  minus: `${prefix}minus`,
945
1001
  calendar: `${prefix}calendar`
946
- })}
1002
+ })}');
947
1003
  `;
948
1004
  }
949
1005
  return {
@@ -1004,6 +1060,31 @@ export function dateConfiguration() {
1004
1060
  }
1005
1061
  }
1006
1062
 
1063
+ function vuetifySSRClientHintsPlugin(ctx) {
1064
+ return {
1065
+ name: "vuetify:ssr-client-hints:nuxt",
1066
+ enforce: "pre",
1067
+ resolveId(id) {
1068
+ if (id === VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION)
1069
+ return RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION;
1070
+ },
1071
+ async load(id) {
1072
+ if (id === RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION) {
1073
+ const data = {
1074
+ reloadOnFirstRequest: ctx.ssrClientHints.reloadOnFirstRequest,
1075
+ viewportSize: ctx.ssrClientHints.viewportSize,
1076
+ prefersColorScheme: ctx.ssrClientHints.prefersColorScheme,
1077
+ prefersReducedMotion: ctx.ssrClientHints.prefersReducedMotion,
1078
+ clientWidth: ctx.vuetifyOptions.ssr?.clientWidth,
1079
+ clientHeight: ctx.vuetifyOptions.ssr?.clientHeight,
1080
+ prefersColorSchemeOptions: ctx.ssrClientHints.prefersColorSchemeOptions
1081
+ };
1082
+ return `export const ssrClientHintsConfiguration = JSON.parse('${JSON.stringify(data)}');`;
1083
+ }
1084
+ }
1085
+ };
1086
+ }
1087
+
1007
1088
  function configureVite(configKey, nuxt, ctx) {
1008
1089
  nuxt.hook("vite:extend", ({ config }) => checkVuetifyPlugins(config));
1009
1090
  nuxt.hook("vite:extendConfig", (viteInlineConfig) => {
@@ -1020,6 +1101,8 @@ function configureVite(configKey, nuxt, ctx) {
1020
1101
  viteInlineConfig.plugins.push(vuetifyConfigurationPlugin(ctx));
1021
1102
  viteInlineConfig.plugins.push(vuetifyIconsPlugin(ctx));
1022
1103
  viteInlineConfig.plugins.push(vuetifyDateConfigurationPlugin(ctx));
1104
+ if (ctx.ssrClientHints.enabled)
1105
+ viteInlineConfig.plugins.push(vuetifySSRClientHintsPlugin(ctx));
1023
1106
  });
1024
1107
  }
1025
1108
 
@@ -1075,6 +1158,16 @@ function configureNuxt(configKey, nuxt, ctx) {
1075
1158
  meta: { docsUrl: `https://vuetifyjs.com/en/api/${toKebabCase(name)}/` }
1076
1159
  })));
1077
1160
  }
1161
+ if (ctx.ssrClientHints.enabled) {
1162
+ addPlugin({
1163
+ src: ctx.resolver.resolve(runtimeDir, "plugins/vuetify-client-hints.client"),
1164
+ mode: "client"
1165
+ });
1166
+ addPlugin({
1167
+ src: ctx.resolver.resolve(runtimeDir, "plugins/vuetify-client-hints.server"),
1168
+ mode: "server"
1169
+ });
1170
+ }
1078
1171
  addPlugin({
1079
1172
  src: ctx.resolver.resolve(runtimeDir, `plugins/vuetify${ctx.i18n ? "-sync" : ""}`)
1080
1173
  });
@@ -1130,9 +1223,11 @@ const module = defineNuxtModule({
1130
1223
  vuetifyFilesToWatch: [],
1131
1224
  isSSR: nuxt.options.ssr,
1132
1225
  isDev: nuxt.options.dev,
1226
+ isNuxtGenerate: nuxt.options._generate,
1133
1227
  unocss: hasNuxtModule("@unocss/nuxt", nuxt),
1134
1228
  i18n: hasNuxtModule("@nuxtjs/i18n", nuxt),
1135
1229
  icons: void 0,
1230
+ ssrClientHints: void 0,
1136
1231
  componentsPromise: void 0,
1137
1232
  labComponentsPromise: void 0
1138
1233
  };
@@ -0,0 +1,19 @@
1
+ export interface ClientHintRequestFeatures {
2
+ firstRequest: boolean;
3
+ prefersColorSchemeAvailable: boolean;
4
+ prefersReducedMotionAvailable: boolean;
5
+ viewportHeightAvailable: boolean;
6
+ viewportWidthAvailable: boolean;
7
+ }
8
+ export interface ClientHintsRequest extends ClientHintRequestFeatures {
9
+ prefersColorScheme?: 'dark' | 'light' | 'no-preference';
10
+ prefersReducedMotion?: 'no-preference' | 'reduce';
11
+ viewportHeight?: number;
12
+ viewportWidth?: number;
13
+ colorSchemeFromCookie?: string;
14
+ colorSchemeCookie?: string;
15
+ }
16
+ export interface SSRClientHints {
17
+ ssrClientHints: ClientHintsRequest;
18
+ }
19
+ export declare const VuetifyHTTPClientHints = "vuetify:nuxt:ssr-client-hints";
@@ -0,0 +1 @@
1
+ export const VuetifyHTTPClientHints = "vuetify:nuxt:ssr-client-hints";
@@ -0,0 +1,51 @@
1
+ /// <reference types="node" />
2
+ export type DetectedInfoType = 'browser' | 'node' | 'bot-device' | 'bot' | 'react-native';
3
+ interface DetectedInfo<T extends DetectedInfoType, N extends string, O, V = null> {
4
+ readonly type: T;
5
+ readonly name: N;
6
+ readonly version: V;
7
+ readonly os: O;
8
+ }
9
+ export declare class BrowserInfo implements DetectedInfo<'browser', Browser, OperatingSystem | null, string> {
10
+ readonly name: Browser;
11
+ readonly version: string;
12
+ readonly os: OperatingSystem | null;
13
+ readonly type = "browser";
14
+ constructor(name: Browser, version: string, os: OperatingSystem | null);
15
+ }
16
+ export declare class NodeInfo implements DetectedInfo<'node', 'node', NodeJS.Platform, string> {
17
+ readonly version: string;
18
+ readonly type = "node";
19
+ readonly name: "node";
20
+ readonly os: NodeJS.Platform;
21
+ constructor(version: string);
22
+ }
23
+ export declare class SearchBotDeviceInfo implements DetectedInfo<'bot-device', Browser, OperatingSystem | null, string> {
24
+ readonly name: Browser;
25
+ readonly version: string;
26
+ readonly os: OperatingSystem | null;
27
+ readonly bot: string;
28
+ readonly type = "bot-device";
29
+ constructor(name: Browser, version: string, os: OperatingSystem | null, bot: string);
30
+ }
31
+ export declare class BotInfo implements DetectedInfo<'bot', 'bot', null, null> {
32
+ readonly type = "bot";
33
+ readonly bot: true;
34
+ readonly name: "bot";
35
+ readonly version: null;
36
+ readonly os: null;
37
+ }
38
+ export declare class ReactNativeInfo implements DetectedInfo<'react-native', 'react-native', null, null> {
39
+ readonly type = "react-native";
40
+ readonly name: "react-native";
41
+ readonly version: null;
42
+ readonly os: null;
43
+ }
44
+ export type Browser = 'aol' | 'arc' | 'brave' | 'edge' | 'edge-ios' | 'yandexbrowser' | 'kakaotalk' | 'samsung' | 'silk' | 'miui' | 'beaker' | 'edge-chromium' | 'chrome' | 'chromium-webview' | 'phantomjs' | 'crios' | 'firefox' | 'fxios' | 'opera-mini' | 'opera' | 'pie' | 'netfront' | 'ie' | 'bb10' | 'android' | 'ios' | 'safari' | 'facebook' | 'instagram' | 'ios-webview' | 'curl' | 'searchbot';
45
+ export type OperatingSystem = 'iOS' | 'Android OS' | 'BlackBerry OS' | 'Windows Mobile' | 'Amazon OS' | 'Windows 3.11' | 'Windows 95' | 'Windows 98' | 'Windows 2000' | 'Windows XP' | 'Windows Server 2003' | 'Windows Vista' | 'Windows 7' | 'Windows 8' | 'Windows 8.1' | 'Windows 10' | 'Windows ME' | 'Windows CE' | 'Open BSD' | 'Sun OS' | 'Linux' | 'Mac OS' | 'QNX' | 'BeOS' | 'OS/2' | 'Chrome OS';
46
+ export declare function detect(userAgent?: string): BrowserInfo | SearchBotDeviceInfo | BotInfo | NodeInfo | ReactNativeInfo | null;
47
+ export declare function browserName(ua: string): Browser | null;
48
+ export declare function parseUserAgent(ua: string): BrowserInfo | SearchBotDeviceInfo | BotInfo | null;
49
+ export declare function detectOS(ua: string): OperatingSystem | null;
50
+ export declare function getNodeVersion(): NodeInfo | null;
51
+ export {};
@@ -0,0 +1,180 @@
1
+ import process from "node:process";
2
+ export class BrowserInfo {
3
+ constructor(name, version, os) {
4
+ this.name = name;
5
+ this.version = version;
6
+ this.os = os;
7
+ }
8
+ type = "browser";
9
+ }
10
+ export class NodeInfo {
11
+ constructor(version) {
12
+ this.version = version;
13
+ }
14
+ type = "node";
15
+ name = "node";
16
+ os = process.platform;
17
+ }
18
+ export class SearchBotDeviceInfo {
19
+ constructor(name, version, os, bot) {
20
+ this.name = name;
21
+ this.version = version;
22
+ this.os = os;
23
+ this.bot = bot;
24
+ }
25
+ type = "bot-device";
26
+ }
27
+ export class BotInfo {
28
+ type = "bot";
29
+ bot = true;
30
+ // NOTE: deprecated test name instead
31
+ name = "bot";
32
+ version = null;
33
+ os = null;
34
+ }
35
+ export class ReactNativeInfo {
36
+ type = "react-native";
37
+ name = "react-native";
38
+ version = null;
39
+ os = null;
40
+ }
41
+ const SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
42
+ const SEARCHBOT_OS_REGEX = /(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
43
+ const REQUIRED_VERSION_PARTS = 3;
44
+ const userAgentRules = [
45
+ ["aol", /AOLShield\/([0-9\._]+)/],
46
+ ["brave", /Brave\/([0-9\._]+)/],
47
+ ["edge", /Edge\/([0-9\._]+)/],
48
+ ["edge-ios", /EdgiOS\/([0-9\._]+)/],
49
+ ["yandexbrowser", /YaBrowser\/([0-9\._]+)/],
50
+ ["kakaotalk", /KAKAOTALK\s([0-9\.]+)/],
51
+ ["samsung", /SamsungBrowser\/([0-9\.]+)/],
52
+ ["silk", /\bSilk\/([0-9._-]+)\b/],
53
+ ["miui", /MiuiBrowser\/([0-9\.]+)$/],
54
+ ["beaker", /BeakerBrowser\/([0-9\.]+)/],
55
+ ["edge-chromium", /EdgA?\/([0-9\.]+)/],
56
+ [
57
+ "chromium-webview",
58
+ /(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/
59
+ ],
60
+ ["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
61
+ ["phantomjs", /PhantomJS\/([0-9\.]+)(:?\s|$)/],
62
+ ["crios", /CriOS\/([0-9\.]+)(:?\s|$)/],
63
+ ["firefox", /Firefox\/([0-9\.]+)(?:\s|$)/],
64
+ ["fxios", /FxiOS\/([0-9\.]+)/],
65
+ ["opera-mini", /Opera Mini.*Version\/([0-9\.]+)/],
66
+ ["opera", /Opera\/([0-9\.]+)(?:\s|$)/],
67
+ ["opera", /OPR\/([0-9\.]+)(:?\s|$)/],
68
+ ["pie", /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
69
+ ["pie", /^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/],
70
+ ["netfront", /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
71
+ ["ie", /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
72
+ ["ie", /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
73
+ ["ie", /MSIE\s(7\.0)/],
74
+ ["bb10", /BB10;\sTouch.*Version\/([0-9\.]+)/],
75
+ ["android", /Android\s([0-9\.]+)/],
76
+ ["ios", /Version\/([0-9\._]+).*Mobile.*Safari.*/],
77
+ ["safari", /Version\/([0-9\._]+).*Safari/],
78
+ ["facebook", /FB[AS]V\/([0-9\.]+)/],
79
+ ["instagram", /Instagram\s([0-9\.]+)/],
80
+ ["ios-webview", /AppleWebKit\/([0-9\.]+).*Mobile/],
81
+ ["ios-webview", /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
82
+ ["curl", /^curl\/([0-9\.]+)$/],
83
+ ["searchbot", SEARCHBOX_UA_REGEX]
84
+ ];
85
+ const operatingSystemRules = [
86
+ ["iOS", /iP(hone|od|ad)/],
87
+ ["Android OS", /Android/],
88
+ ["BlackBerry OS", /BlackBerry|BB10/],
89
+ ["Windows Mobile", /IEMobile/],
90
+ ["Amazon OS", /Kindle/],
91
+ ["Windows 3.11", /Win16/],
92
+ ["Windows 95", /(Windows 95)|(Win95)|(Windows_95)/],
93
+ ["Windows 98", /(Windows 98)|(Win98)/],
94
+ ["Windows 2000", /(Windows NT 5.0)|(Windows 2000)/],
95
+ ["Windows XP", /(Windows NT 5.1)|(Windows XP)/],
96
+ ["Windows Server 2003", /(Windows NT 5.2)/],
97
+ ["Windows Vista", /(Windows NT 6.0)/],
98
+ ["Windows 7", /(Windows NT 6.1)/],
99
+ ["Windows 8", /(Windows NT 6.2)/],
100
+ ["Windows 8.1", /(Windows NT 6.3)/],
101
+ ["Windows 10", /(Windows NT 10.0)/],
102
+ ["Windows ME", /Windows ME/],
103
+ ["Windows CE", /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
104
+ ["Open BSD", /OpenBSD/],
105
+ ["Sun OS", /SunOS/],
106
+ ["Chrome OS", /CrOS/],
107
+ ["Linux", /(Linux)|(X11)/],
108
+ ["Mac OS", /(Mac_PowerPC)|(Macintosh)/],
109
+ ["QNX", /QNX/],
110
+ ["BeOS", /BeOS/],
111
+ ["OS/2", /OS\/2/]
112
+ ];
113
+ export function detect(userAgent) {
114
+ if (userAgent)
115
+ return parseUserAgent(userAgent);
116
+ if (typeof document === "undefined" && typeof navigator !== "undefined" && navigator.product === "ReactNative")
117
+ return new ReactNativeInfo();
118
+ if (typeof navigator !== "undefined")
119
+ return parseUserAgent(navigator.userAgent);
120
+ return getNodeVersion();
121
+ }
122
+ function matchUserAgent(ua) {
123
+ return ua !== "" && userAgentRules.reduce(
124
+ (matched, [browser, regex]) => {
125
+ if (matched)
126
+ return matched;
127
+ const uaMatch = regex.exec(ua);
128
+ return !!uaMatch && [browser, uaMatch];
129
+ },
130
+ false
131
+ );
132
+ }
133
+ export function browserName(ua) {
134
+ const data = matchUserAgent(ua);
135
+ return data ? data[0] : null;
136
+ }
137
+ export function parseUserAgent(ua) {
138
+ const matchedRule = matchUserAgent(ua);
139
+ if (!matchedRule)
140
+ return null;
141
+ const [name, match] = matchedRule;
142
+ if (name === "searchbot")
143
+ return new BotInfo();
144
+ let versionParts = match[1] && match[1].split(".").join("_").split("_").slice(0, 3);
145
+ if (versionParts) {
146
+ if (versionParts.length < REQUIRED_VERSION_PARTS) {
147
+ versionParts = [
148
+ ...versionParts,
149
+ ...createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length)
150
+ ];
151
+ }
152
+ } else {
153
+ versionParts = [];
154
+ }
155
+ const version = versionParts.join(".");
156
+ const os = detectOS(ua);
157
+ const searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
158
+ if (searchBotMatch && searchBotMatch[1])
159
+ return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
160
+ return new BrowserInfo(name, version, os);
161
+ }
162
+ export function detectOS(ua) {
163
+ for (let ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
164
+ const [os, regex] = operatingSystemRules[ii];
165
+ const match = regex.exec(ua);
166
+ if (match)
167
+ return os;
168
+ }
169
+ return null;
170
+ }
171
+ export function getNodeVersion() {
172
+ const isNode = typeof process !== "undefined" && process.version;
173
+ return isNode ? new NodeInfo(process.version.slice(1)) : null;
174
+ }
175
+ function createVersionParts(count) {
176
+ const output = [];
177
+ for (let ii = 0; ii < count; ii++)
178
+ output.push("0");
179
+ return output;
180
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,83 @@
1
+ import { ssrClientHintsConfiguration } from "virtual:vuetify-ssr-client-hints-configuration";
2
+ import { reactive, watch } from "vue";
3
+ import { VuetifyHTTPClientHints } from "./client-hints.mjs";
4
+ import { defineNuxtPlugin, useNuxtApp, useState } from "#imports";
5
+ export default defineNuxtPlugin((nuxtApp) => {
6
+ const state = useState(VuetifyHTTPClientHints);
7
+ const {
8
+ firstRequest,
9
+ prefersColorSchemeAvailable,
10
+ prefersReducedMotionAvailable,
11
+ viewportHeightAvailable,
12
+ viewportWidthAvailable
13
+ } = state.value.ssrClientHints;
14
+ const {
15
+ reloadOnFirstRequest,
16
+ viewportSize,
17
+ prefersReducedMotion,
18
+ prefersColorScheme,
19
+ prefersColorSchemeOptions
20
+ } = ssrClientHintsConfiguration;
21
+ if (firstRequest && reloadOnFirstRequest) {
22
+ if (prefersColorScheme) {
23
+ const themeCookie = state.value.ssrClientHints.colorSchemeCookie;
24
+ if (prefersColorSchemeOptions && themeCookie) {
25
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
26
+ const cookieName = prefersColorSchemeOptions.cookieName;
27
+ const parseCookieName = `${cookieName}=`;
28
+ const cookieEntry = `${parseCookieName}${state.value.ssrClientHints.colorSchemeFromCookie ?? prefersColorSchemeOptions.defaultTheme};`;
29
+ const newThemeName = prefersDark ? prefersColorSchemeOptions.darkThemeName : prefersColorSchemeOptions.lightThemeName;
30
+ document.cookie = themeCookie.replace(cookieEntry, `${cookieName}=${newThemeName};`);
31
+ window.location.reload();
32
+ } else if (prefersColorSchemeAvailable) {
33
+ window.location.reload();
34
+ }
35
+ }
36
+ if (prefersReducedMotion && prefersReducedMotionAvailable)
37
+ window.location.reload();
38
+ if (viewportSize && viewportHeightAvailable)
39
+ window.location.reload();
40
+ if (viewportSize && viewportWidthAvailable)
41
+ window.location.reload();
42
+ }
43
+ if (viewportSize || prefersColorScheme && prefersColorSchemeOptions) {
44
+ nuxtApp.hook("vuetify:before-create", ({ vuetifyOptions }) => {
45
+ if (viewportSize) {
46
+ const clientWidth = state.value.ssrClientHints.viewportWidth;
47
+ const clientHeight = state.value.ssrClientHints.viewportHeight;
48
+ vuetifyOptions.ssr = typeof clientWidth === "number" ? {
49
+ clientWidth,
50
+ clientHeight
51
+ } : true;
52
+ }
53
+ if (prefersColorScheme && prefersColorSchemeOptions)
54
+ vuetifyOptions.theme.defaultTheme = state.value.ssrClientHints.colorSchemeFromCookie ?? prefersColorSchemeOptions.defaultTheme;
55
+ });
56
+ if (prefersColorScheme && prefersColorSchemeOptions) {
57
+ const themeCookie = state.value.ssrClientHints.colorSchemeCookie;
58
+ if (themeCookie) {
59
+ nuxtApp.hook("app:beforeMount", () => {
60
+ const vuetify = useNuxtApp().$vuetify;
61
+ const cookieName = prefersColorSchemeOptions.cookieName;
62
+ const parseCookieName = `${cookieName}=`;
63
+ const cookieEntry = `${parseCookieName}${state.value.ssrClientHints.colorSchemeFromCookie ?? prefersColorSchemeOptions.defaultTheme};`;
64
+ watch(vuetify.theme.global.name, (newThemeName) => {
65
+ document.cookie = themeCookie.replace(cookieEntry, `${cookieName}=${newThemeName};`);
66
+ });
67
+ if (prefersColorSchemeOptions.useBrowserThemeOnly) {
68
+ const { darkThemeName, lightThemeName } = prefersColorSchemeOptions;
69
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
70
+ prefersDark.addEventListener("change", (e) => {
71
+ vuetify.theme.global.name.value = e.matches ? darkThemeName : lightThemeName;
72
+ });
73
+ }
74
+ });
75
+ }
76
+ }
77
+ }
78
+ return {
79
+ provide: reactive({
80
+ ssrClientHints: state
81
+ })
82
+ };
83
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,239 @@
1
+ import {
2
+ ssrClientHintsConfiguration
3
+ } from "virtual:vuetify-ssr-client-hints-configuration";
4
+ import { reactive } from "vue";
5
+ import { parseUserAgent } from "./detect-browser.mjs";
6
+ import { VuetifyHTTPClientHints } from "./client-hints.mjs";
7
+ import {
8
+ defineNuxtPlugin,
9
+ useCookie,
10
+ useNuxtApp,
11
+ useRequestEvent,
12
+ useState
13
+ } from "#imports";
14
+ export default defineNuxtPlugin((nuxtApp) => {
15
+ const event = useRequestEvent();
16
+ const state = useState(VuetifyHTTPClientHints);
17
+ const request = event.node.req;
18
+ const response = event.node.res;
19
+ const requestHeaders = request.headers ?? {};
20
+ const userAgentHeader = readClientHeader("user-agent", requestHeaders);
21
+ const userAgent = userAgentHeader ? parseUserAgent(userAgentHeader) : null;
22
+ const clientHintsRequest = collectClientHints(userAgent, ssrClientHintsConfiguration, requestHeaders);
23
+ writeClientHintsResponseHeaders(clientHintsRequest, ssrClientHintsConfiguration, response);
24
+ state.value = {
25
+ ssrClientHints: clientHintsRequest
26
+ };
27
+ state.value.ssrClientHints.colorSchemeCookie = writeThemeCookie(
28
+ clientHintsRequest,
29
+ ssrClientHintsConfiguration
30
+ );
31
+ nuxtApp.hook("vuetify:before-create", async ({ vuetifyOptions }) => {
32
+ const clientWidth = clientHintsRequest.viewportWidth;
33
+ const clientHeight = clientHintsRequest.viewportHeight;
34
+ vuetifyOptions.ssr = typeof clientWidth === "number" ? {
35
+ clientWidth,
36
+ clientHeight
37
+ } : true;
38
+ if (clientHintsRequest.colorSchemeFromCookie)
39
+ vuetifyOptions.theme.defaultTheme = clientHintsRequest.colorSchemeFromCookie;
40
+ await nuxtApp.hooks.callHook("vuetify:ssr-client-hints", {
41
+ vuetifyOptions,
42
+ ssrClientHintsConfiguration,
43
+ ssrClientHints: state.value
44
+ });
45
+ });
46
+ return {
47
+ provide: reactive({
48
+ ssrClientHints: state
49
+ })
50
+ };
51
+ });
52
+ const AcceptClientHintsHeaders = {
53
+ prefersColorScheme: "Sec-CH-Prefers-Color-Scheme",
54
+ prefersReducedMotion: "Sec-CH-Prefers-Reduced-Motion",
55
+ viewportHeight: "Sec-CH-Viewport-Height",
56
+ viewportWidth: "Sec-CH-Viewport-Width"
57
+ };
58
+ const chromiumBasedBrowserFeatures = {
59
+ prefersColorScheme: (_, v) => v[0] >= 93,
60
+ prefersReducedMotion: (_, v) => v[0] >= 108,
61
+ viewportHeight: (_, v) => v[0] >= 108,
62
+ viewportWidth: (_, v) => v[0] >= 108
63
+ };
64
+ const allowedBrowsers = [
65
+ // 'edge',
66
+ // 'edge-ios',
67
+ ["chrome", chromiumBasedBrowserFeatures],
68
+ ["edge-chromium", chromiumBasedBrowserFeatures],
69
+ ["chromium-webview", chromiumBasedBrowserFeatures],
70
+ ["opera", {
71
+ prefersColorScheme: (android, v) => v[0] >= (android ? 66 : 79),
72
+ prefersReducedMotion: (android, v) => v[0] >= (android ? 73 : 94),
73
+ viewportHeight: (android, v) => v[0] >= (android ? 73 : 94),
74
+ viewportWidth: (android, v) => v[0] >= (android ? 73 : 94)
75
+ }]
76
+ ];
77
+ const AcceptClientHintsRequestHeaders = Object.entries(AcceptClientHintsHeaders).reduce((acc, [key, value]) => {
78
+ acc[key] = value.toLowerCase();
79
+ return acc;
80
+ }, {});
81
+ const ClientHeaders = ["Accept-CH", "Vary", "Critical-CH"];
82
+ function readClientHeader(name, headers) {
83
+ const value = headers[name];
84
+ if (Array.isArray(value))
85
+ return value[0];
86
+ return value;
87
+ }
88
+ function browserFeatureAvailable(userAgent, feature) {
89
+ if (userAgent == null || userAgent.type !== "browser")
90
+ return false;
91
+ try {
92
+ const browserName = userAgent.name;
93
+ const android = userAgent.os?.toLowerCase().startsWith("android") ?? false;
94
+ const versions = userAgent.version.split(".").map((v) => Number.parseInt(v));
95
+ return allowedBrowsers.some(([name, check]) => {
96
+ if (browserName !== name)
97
+ return false;
98
+ try {
99
+ return check[feature](android, versions);
100
+ } catch {
101
+ return false;
102
+ }
103
+ });
104
+ } catch {
105
+ return false;
106
+ }
107
+ }
108
+ function lookupClientHints(userAgent, ssrClientHintsConfiguration2) {
109
+ const features = {
110
+ firstRequest: true,
111
+ prefersColorSchemeAvailable: false,
112
+ prefersReducedMotionAvailable: false,
113
+ viewportHeightAvailable: false,
114
+ viewportWidthAvailable: false
115
+ };
116
+ if (userAgent == null || userAgent.type !== "browser")
117
+ return features;
118
+ if (ssrClientHintsConfiguration2.prefersColorScheme)
119
+ features.prefersColorSchemeAvailable = browserFeatureAvailable(userAgent, "prefersColorScheme");
120
+ if (ssrClientHintsConfiguration2.prefersReducedMotion)
121
+ features.prefersReducedMotionAvailable = browserFeatureAvailable(userAgent, "prefersReducedMotion");
122
+ if (ssrClientHintsConfiguration2.viewportSize) {
123
+ features.viewportHeightAvailable = browserFeatureAvailable(userAgent, "viewportHeight");
124
+ features.viewportWidthAvailable = browserFeatureAvailable(userAgent, "viewportWidth");
125
+ }
126
+ return features;
127
+ }
128
+ function collectClientHints(userAgent, ssrClientHintsConfiguration2, headers) {
129
+ const hints = lookupClientHints(userAgent, ssrClientHintsConfiguration2);
130
+ if (ssrClientHintsConfiguration2.prefersColorScheme) {
131
+ if (ssrClientHintsConfiguration2.prefersColorSchemeOptions) {
132
+ const cookieName = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieName;
133
+ const cookieValue = readClientHeader("cookie", headers)?.split(";").find((c) => c.trim().startsWith(`${cookieName}=`));
134
+ if (cookieValue) {
135
+ const value = cookieValue.split("=")?.[1].trim();
136
+ if (ssrClientHintsConfiguration2.prefersColorSchemeOptions.themeNames.includes(value)) {
137
+ hints.colorSchemeFromCookie = value;
138
+ hints.firstRequest = false;
139
+ }
140
+ }
141
+ }
142
+ if (!hints.colorSchemeFromCookie) {
143
+ const value = hints.prefersColorSchemeAvailable ? readClientHeader(AcceptClientHintsRequestHeaders.prefersColorScheme, headers)?.toLowerCase() : void 0;
144
+ if (value === "dark" || value === "light" || value === "no-preference") {
145
+ hints.prefersColorScheme = value;
146
+ hints.firstRequest = false;
147
+ }
148
+ if (ssrClientHintsConfiguration2.prefersColorSchemeOptions) {
149
+ if (!value || value === "no-preference") {
150
+ hints.colorSchemeFromCookie = ssrClientHintsConfiguration2.prefersColorSchemeOptions.defaultTheme;
151
+ } else {
152
+ hints.colorSchemeFromCookie = value === "dark" ? ssrClientHintsConfiguration2.prefersColorSchemeOptions.darkThemeName : ssrClientHintsConfiguration2.prefersColorSchemeOptions.lightThemeName;
153
+ }
154
+ }
155
+ }
156
+ }
157
+ if (hints.prefersReducedMotionAvailable && ssrClientHintsConfiguration2.prefersReducedMotion) {
158
+ const value = readClientHeader(AcceptClientHintsRequestHeaders.prefersReducedMotion, headers)?.toLowerCase();
159
+ if (value === "no-preference" || value === "reduce") {
160
+ hints.prefersReducedMotion = value;
161
+ hints.firstRequest = false;
162
+ }
163
+ }
164
+ if (hints.viewportHeightAvailable && ssrClientHintsConfiguration2.viewportSize) {
165
+ const header = readClientHeader(AcceptClientHintsRequestHeaders.viewportHeight, headers);
166
+ if (header) {
167
+ hints.firstRequest = false;
168
+ try {
169
+ hints.viewportHeight = Number.parseInt(header);
170
+ } catch {
171
+ hints.viewportHeight = ssrClientHintsConfiguration2.clientHeight;
172
+ }
173
+ }
174
+ } else {
175
+ hints.viewportHeight = ssrClientHintsConfiguration2.clientHeight;
176
+ }
177
+ if (hints.viewportWidthAvailable && ssrClientHintsConfiguration2.viewportSize) {
178
+ const header = readClientHeader(AcceptClientHintsRequestHeaders.viewportWidth, headers);
179
+ if (header) {
180
+ hints.firstRequest = false;
181
+ try {
182
+ hints.viewportWidth = Number.parseInt(header);
183
+ } catch {
184
+ hints.viewportWidth = ssrClientHintsConfiguration2.clientWidth;
185
+ }
186
+ }
187
+ } else {
188
+ hints.viewportWidth = ssrClientHintsConfiguration2.clientWidth;
189
+ }
190
+ return hints;
191
+ }
192
+ function writeClientHintHeaders(key, headers) {
193
+ ClientHeaders.forEach((header) => {
194
+ headers[header] = (headers[header] ? headers[header] : []).concat(key);
195
+ });
196
+ }
197
+ function withNuxtAppRendered(callback) {
198
+ const nuxtApp = useNuxtApp();
199
+ const unhook = nuxtApp.hooks.hookOnce("app:rendered", callback);
200
+ nuxtApp.hooks.hookOnce("app:error", () => {
201
+ unhook();
202
+ return callback();
203
+ });
204
+ }
205
+ function writeClientHintsResponseHeaders(clientHintsRequest, ssrClientHintsConfiguration2, response) {
206
+ const headers = {};
207
+ if (ssrClientHintsConfiguration2.prefersColorScheme && clientHintsRequest.prefersColorSchemeAvailable)
208
+ writeClientHintHeaders(AcceptClientHintsHeaders.prefersColorScheme, headers);
209
+ if (ssrClientHintsConfiguration2.prefersReducedMotion && clientHintsRequest.prefersReducedMotionAvailable)
210
+ writeClientHintHeaders(AcceptClientHintsHeaders.prefersReducedMotion, headers);
211
+ if (ssrClientHintsConfiguration2.viewportSize && clientHintsRequest.viewportHeightAvailable && clientHintsRequest.viewportWidthAvailable) {
212
+ writeClientHintHeaders(AcceptClientHintsHeaders.viewportHeight, headers);
213
+ writeClientHintHeaders(AcceptClientHintsHeaders.viewportWidth, headers);
214
+ }
215
+ if (Object.keys(headers).length === 0)
216
+ return;
217
+ withNuxtAppRendered(() => {
218
+ Object.entries(headers).forEach(([key, value]) => {
219
+ response.setHeader(key, value);
220
+ });
221
+ });
222
+ }
223
+ function writeThemeCookie(clientHintsRequest, ssrClientHintsConfiguration2) {
224
+ if (!ssrClientHintsConfiguration2.prefersColorScheme || !ssrClientHintsConfiguration2.prefersColorSchemeOptions)
225
+ return;
226
+ const cookieName = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieName;
227
+ const themeName = clientHintsRequest.colorSchemeFromCookie ?? ssrClientHintsConfiguration2.prefersColorSchemeOptions.defaultTheme;
228
+ const path = ssrClientHintsConfiguration2.prefersColorSchemeOptions.baseUrl;
229
+ const date = /* @__PURE__ */ new Date();
230
+ const expires = new Date(date.setDate(date.getDate() + 365));
231
+ if (!clientHintsRequest.firstRequest || !ssrClientHintsConfiguration2.reloadOnFirstRequest) {
232
+ useCookie(cookieName, {
233
+ path,
234
+ expires,
235
+ sameSite: "lax"
236
+ }).value = themeName;
237
+ }
238
+ return `${cookieName}=${themeName}; Path=${path}; Expires=${expires.toUTCString()}; SameSite=Lax`;
239
+ }
package/dist/types.d.ts CHANGED
@@ -12,4 +12,4 @@ declare module 'nuxt/schema' {
12
12
  }
13
13
 
14
14
 
15
- export { ComponentName, Components, DateAdapter, DateOptions, DirectiveName, Directives, ExternalVuetifyOptions, FontAwesomeSvgIconSet, FontIconSet, IconFontName, IconSetName, IconsOptions, InlineModuleOptions, JSSVGIconSet, LabComponentName, LabComponents, MOptions, ModuleOptions, VOptions, VuetifyLocale, default } from './module'
15
+ export { ComponentName, Components, DateAdapter, DateOptions, DirectiveName, Directives, ExternalVuetifyOptions, FontAwesomeSvgIconSet, FontIconSet, IconFontName, IconSetName, IconsOptions, InlineModuleOptions, JSSVGIconSet, LabComponentName, LabComponents, MOptions, ModuleOptions, SSRClientHints, SSRClientHintsConfiguration, VOptions, VuetifyLocale, default } from './module'
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "vuetify-nuxt-module",
3
3
  "type": "module",
4
- "version": "0.5.9",
5
- "packageManager": "pnpm@8.7.4",
4
+ "version": "0.5.11",
5
+ "packageManager": "pnpm@8.7.6",
6
6
  "description": "Zero-Config Nuxt Module for Vuetify",
7
7
  "author": "userquin <userquin@gmail.com>",
8
8
  "license": "MIT",