@vesperjs/nuxt 0.1.0

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 (36) hide show
  1. package/README.md +3 -0
  2. package/app/composables/backend/api/create-fetch.ts +3 -0
  3. package/app/composables/backend/api/create-request-fetch.ts +5 -0
  4. package/app/composables/backend/api/index.ts +7 -0
  5. package/app/composables/backend/api/use-api-constants.ts +21 -0
  6. package/app/composables/backend/api/use-http-headers.ts +18 -0
  7. package/app/composables/backend/api/use-mutation-api.ts +64 -0
  8. package/app/composables/backend/api/use-ofetch.ts +24 -0
  9. package/app/composables/backend/api/use-query-api.ts +93 -0
  10. package/app/composables/backend/error/index.ts +2 -0
  11. package/app/composables/backend/error/use-backend-error-info.ts +15 -0
  12. package/app/composables/backend/error/use-external-errors.ts +42 -0
  13. package/app/composables/backend/index.ts +8 -0
  14. package/app/composables/backend/use-alert.ts +94 -0
  15. package/app/composables/index.ts +8 -0
  16. package/app/composables/use-datetime-local.ts +35 -0
  17. package/app/composables/use-entity.ts +13 -0
  18. package/app/composables/use-flash.ts +15 -0
  19. package/app/composables/use-locale.ts +23 -0
  20. package/app/composables/use-more-scroll.ts +70 -0
  21. package/app/composables/use-referer.ts +9 -0
  22. package/app/composables/use-time-zone.ts +86 -0
  23. package/app/composables/util/index.ts +1 -0
  24. package/app/composables/util/use-date.ts +7 -0
  25. package/app.config.ts +7 -0
  26. package/interfaces/error/backend-error-info.ts +4 -0
  27. package/interfaces/error/index.ts +1 -0
  28. package/interfaces/flash.ts +4 -0
  29. package/interfaces/index.ts +5 -0
  30. package/interfaces/resource/beckend-error-resource.ts +5 -0
  31. package/interfaces/resource/errors-resource.ts +3 -0
  32. package/interfaces/resource/index.ts +2 -0
  33. package/nuxt.config.ts +6 -0
  34. package/package.json +33 -0
  35. package/types/error-messages.ts +1 -0
  36. package/types/index.ts +1 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Vesper Nuxt Layer
2
+
3
+ This is Vesper for Nuxt.
@@ -0,0 +1,3 @@
1
+ export const createFetch = () => {
2
+ return $fetch.create({})
3
+ }
@@ -0,0 +1,5 @@
1
+ import { useRequestFetch } from 'nuxt/app'
2
+
3
+ export const createRequestFetch = () => {
4
+ return useRequestFetch()
5
+ }
@@ -0,0 +1,7 @@
1
+ export type { QueryAPIOptions } from './use-query-api'
2
+
3
+ export { createFetch } from './create-fetch'
4
+ export { createRequestFetch } from './create-request-fetch'
5
+ export { useOFetch } from './use-ofetch'
6
+ export { useQueryApi } from './use-query-api'
7
+ export { useMutationApi } from './use-mutation-api'
@@ -0,0 +1,21 @@
1
+ import { useRuntimeConfig } from 'nuxt/app'
2
+
3
+ import { computed } from '@vue/reactivity'
4
+
5
+ export const useApiConstants = () => {
6
+ const runtimeConfig = useRuntimeConfig()
7
+
8
+ const baseOrigin = computed<string | undefined>(() => {
9
+ if (import.meta.client) {
10
+ return runtimeConfig.public.backendApiOrigin as string
11
+ } else if (import.meta.server) {
12
+ return runtimeConfig.backendApiOrigin as string
13
+ }
14
+ })
15
+
16
+ const basePath = runtimeConfig.public.backendApiPath as string
17
+
18
+ const baseURL = computed<string>(() => `${baseOrigin.value}${basePath}`)
19
+
20
+ return { baseURL }
21
+ }
@@ -0,0 +1,18 @@
1
+ import { computed, type Ref } from '@vue/reactivity'
2
+
3
+ import { useLocale } from '../../use-locale'
4
+ import { useTimeZone } from '../../use-time-zone'
5
+
6
+ export const useHttpHeaders = () => {
7
+ const { locale } = useLocale()
8
+ const { timeZone } = useTimeZone()
9
+
10
+ const commonHeaders: Ref<Record<string, string>> = computed(() => ({
11
+ 'X-Requested-With': 'XMLHttpRequest',
12
+ Accept: 'application/json',
13
+ 'Accept-Language': locale.value,
14
+ 'Time-Zone': timeZone.value.client,
15
+ }))
16
+
17
+ return { commonHeaders }
18
+ }
@@ -0,0 +1,64 @@
1
+ import type { FetchOptions, FetchError, FetchResponse } from 'ofetch'
2
+
3
+ import { useHttpHeaders } from './use-http-headers'
4
+ import { useApiConstants } from './use-api-constants'
5
+ import { useOFetch } from './use-ofetch'
6
+
7
+ interface MutationAPIOptions {
8
+ method: 'post' | 'put' | 'delete'
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ body?: Record<string, any> | FormData
11
+ token?: string | null
12
+ onRequestError?: ({ error }: { error: Error }) => void
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ onResponseError?: ({ response }: { response: FetchResponse<any> }) => void
15
+ }
16
+
17
+ // eslint-disable-next-line
18
+ export const useMutationApi = async <T = unknown, E = any>(
19
+ url: string,
20
+ { method, body = {}, token = null, onRequestError, onResponseError }: MutationAPIOptions,
21
+ ): Promise<{
22
+ token: string | undefined
23
+ data: T | undefined
24
+ error: FetchError<E> | undefined
25
+ pending: boolean
26
+ }> => {
27
+ const { commonHeaders } = useHttpHeaders()
28
+ const { baseURL } = useApiConstants()
29
+
30
+ const headers: Record<string, string> = commonHeaders.value
31
+
32
+ const tokenRef = ref<string>()
33
+
34
+ if (token) {
35
+ headers.Authorization = `Bearer ${token}`
36
+ tokenRef.value = token
37
+ }
38
+
39
+ const options: FetchOptions<'json'> = {
40
+ baseURL: baseURL.value,
41
+ headers,
42
+ method,
43
+ }
44
+
45
+ if (onRequestError) {
46
+ options.onRequestError = onRequestError
47
+ }
48
+
49
+ if (onResponseError) {
50
+ options.onResponseError = onResponseError
51
+ }
52
+
53
+ if (method == 'post' || method == 'put') {
54
+ options.body = body
55
+ options.onResponse = ({ response }: { response: FetchResponse<T> }) => {
56
+ if ((method == 'post' && !tokenRef.value) || method == 'put')
57
+ tokenRef.value = response.headers.get('Authorization')?.split(' ')[1]
58
+ }
59
+ }
60
+
61
+ const { data, error, pending } = await useOFetch<T, E>(url, options)
62
+
63
+ return { token: tokenRef.value, data, error, pending }
64
+ }
@@ -0,0 +1,24 @@
1
+ import type { $Fetch, FetchOptions, FetchError } from 'ofetch'
2
+
3
+ // eslint-disable-next-line
4
+ export const useOFetch = async <T = unknown, E = any>(
5
+ url: string,
6
+ options?: FetchOptions<'json'>,
7
+ ) => {
8
+ const { $api } = useNuxtApp()
9
+
10
+ const pending = ref<boolean>(true)
11
+
12
+ const data = ref<T>()
13
+ const error = ref<FetchError<E>>()
14
+
15
+ try {
16
+ data.value = await ($api as $Fetch)<T>(url, options)
17
+ } catch (err: unknown) {
18
+ error.value = err as FetchError<E>
19
+ }
20
+
21
+ pending.value = false
22
+
23
+ return { data: data.value, error: error.value, pending: pending.value }
24
+ }
@@ -0,0 +1,93 @@
1
+ import { useAsyncData, useNuxtApp } from 'nuxt/app'
2
+
3
+ import type { FetchOptions, FetchResponse } from 'ofetch'
4
+
5
+ import { ref } from '@vue/reactivity'
6
+
7
+ import { useHttpHeaders } from './use-http-headers'
8
+ import { useApiConstants } from './use-api-constants'
9
+ import { useOFetch } from './use-ofetch'
10
+
11
+ interface SearchParams {
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ [key: string]: any
14
+ }
15
+
16
+ export type QueryAPIOptions = {
17
+ key?: MaybeRefOrGetter<string>
18
+ query?: SearchParams
19
+ token?: string | null
20
+ signal?: AbortSignal
21
+ onRequestError?: ({ error }: { error: Error }) => void
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ onResponseError?: ({ response }: { response: FetchResponse<any> }) => void
24
+ fresh?: boolean
25
+ cache?: boolean
26
+ }
27
+
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ export const useQueryApi = async <T = unknown, E = any>(url: string, options?: QueryAPIOptions) => {
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ const { $api } = useNuxtApp() as any
32
+ const { commonHeaders } = useHttpHeaders()
33
+ const { baseURL } = useApiConstants()
34
+
35
+ const key = options?.key ?? url
36
+
37
+ const tokenRef = ref<string>()
38
+
39
+ const headers: Record<string, string> = commonHeaders.value
40
+
41
+ const fresh: boolean = options?.fresh ?? false
42
+ const cache: boolean = options?.cache ?? true
43
+
44
+ if (options?.token) {
45
+ headers.Authorization = `Bearer ${options.token}`
46
+ tokenRef.value = options.token
47
+ }
48
+
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ const getOptions: FetchOptions<'json', any> = {
51
+ baseURL: baseURL.value,
52
+ method: 'get',
53
+ query: options?.query ?? {},
54
+ headers,
55
+ onResponse({ response }: { response: FetchResponse<T> }) {
56
+ if (!tokenRef.value) tokenRef.value = response.headers.get('Authorization')?.split(' ')[1]
57
+ },
58
+ }
59
+
60
+ if (options?.signal) {
61
+ getOptions.signal = options.signal
62
+ }
63
+
64
+ if (options?.onRequestError) {
65
+ getOptions.onRequestError = options.onRequestError
66
+ }
67
+
68
+ if (options?.onResponseError) {
69
+ getOptions.onResponseError = options.onResponseError
70
+ }
71
+
72
+ if (cache) {
73
+ const { data, error, refresh, pending } = await useAsyncData<T, E>(key, () =>
74
+ $api(url, getOptions),
75
+ )
76
+
77
+ if (fresh) {
78
+ await refresh()
79
+ }
80
+
81
+ return {
82
+ token: tokenRef.value,
83
+ data: data.value,
84
+ error: error.value,
85
+ refresh,
86
+ pending: pending.value,
87
+ }
88
+ } else {
89
+ const { data, error, pending } = await useOFetch<T, E>(url, getOptions)
90
+
91
+ return { token: tokenRef.value, data, error, pending }
92
+ }
93
+ }
@@ -0,0 +1,2 @@
1
+ export { useExternalErrors } from './use-external-errors'
2
+ export { useBackendErrorInfo } from './use-backend-error-info'
@@ -0,0 +1,15 @@
1
+ import type { BackendErrorInfo } from '../../../../interfaces'
2
+
3
+ export function useBackendErrorInfo<R extends object>() {
4
+ const info = ref<BackendErrorInfo<R>>({})
5
+
6
+ const backendErrorInfo = computed<BackendErrorInfo<R>>(() => {
7
+ return info.value as BackendErrorInfo<R>
8
+ })
9
+
10
+ const clearBackendErrorInfo = (): void => {
11
+ info.value = {}
12
+ }
13
+
14
+ return { backendErrorInfo, clearBackendErrorInfo }
15
+ }
@@ -0,0 +1,42 @@
1
+ import type { Flash } from '../../../../interfaces'
2
+ import type { ErrorMessages } from '../../../../types'
3
+
4
+ export function useExternalErrors<P extends string>({ flash }: { flash: Ref<Flash> }) {
5
+ const errors = ref<ErrorMessages<P>>({})
6
+
7
+ const externalErrors = computed<ErrorMessages<P>>({
8
+ get() {
9
+ return errors.value as ErrorMessages<string>
10
+ },
11
+ set(value: ErrorMessages<P>) {
12
+ if (errors.value) {
13
+ for (const key in value) {
14
+ ;(errors.value as ErrorMessages<P>)[key] = value[key] ?? []
15
+ }
16
+ }
17
+ },
18
+ })
19
+
20
+ const clearExternalErrors = (): void => {
21
+ externalErrors.value = {}
22
+ }
23
+
24
+ const isSuccess = (): boolean => {
25
+ let result = true
26
+
27
+ for (const key in errors.value) {
28
+ const error = errors.value as ErrorMessages<string>
29
+ if ((error[key] as string[]).length > 0) result = false
30
+ }
31
+
32
+ if (flash.value.alert) result = false
33
+
34
+ return result
35
+ }
36
+
37
+ return {
38
+ externalErrors,
39
+ clearExternalErrors,
40
+ isSuccess,
41
+ }
42
+ }
@@ -0,0 +1,8 @@
1
+ export type { QueryAPIOptions } from './api'
2
+
3
+ export { createFetch, createRequestFetch, useOFetch, useMutationApi, useQueryApi } from './api'
4
+
5
+ export { useAlert } from './use-alert'
6
+ export { useExternalErrors } from './error'
7
+
8
+ export type { UseAlertType } from './use-alert'
@@ -0,0 +1,94 @@
1
+ import { useNuxtApp } from 'nuxt/app'
2
+
3
+ import type { FetchError } from 'ofetch'
4
+ import type { NuxtError } from 'nuxt/app'
5
+
6
+ import type { Ref } from '@vue/reactivity'
7
+
8
+ import type {
9
+ ErrorsResource,
10
+ BackendErrorInfo,
11
+ BackendErrorResource,
12
+ Flash,
13
+ } from '../../../interfaces'
14
+ import type { ErrorMessages } from '../../../types'
15
+
16
+ import { useBackendErrorInfo } from './error'
17
+
18
+ interface UseAlertOptions {
19
+ flash: Ref<Flash>
20
+ caller?: UseAlertCallerType
21
+ }
22
+
23
+ interface UseAlertCallerType {
24
+ externalErrors?: Ref<ErrorMessages<string>>
25
+ clearAccount?: () => void
26
+ }
27
+
28
+ export function useAlert<BER extends object = BackendErrorResource>({
29
+ flash,
30
+ caller,
31
+ }: UseAlertOptions): {
32
+ backendErrorInfo: Ref<BackendErrorInfo<BER>, BER>
33
+ setError: (
34
+ error:
35
+ | NuxtError<ErrorsResource<ErrorMessages<string>> | BER>
36
+ | FetchError<ErrorsResource<ErrorMessages<string>> | BER>,
37
+ options?: {
38
+ off?: boolean
39
+ },
40
+ ) => void
41
+ } {
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ const { $i18n } = useNuxtApp() as any
44
+ const { backendErrorInfo, clearBackendErrorInfo } = useBackendErrorInfo<BER>()
45
+
46
+ const setError = function (
47
+ error:
48
+ | NuxtError<ErrorsResource<ErrorMessages<string>> | BER>
49
+ | FetchError<ErrorsResource<ErrorMessages<string>> | BER>,
50
+ options?: { off?: boolean },
51
+ ): void {
52
+ const off = options?.off ?? false
53
+
54
+ clearBackendErrorInfo()
55
+ backendErrorInfo.value.status = error.status
56
+ if (off) {
57
+ switch (error.status) {
58
+ case 401:
59
+ // flash.value.alert = $i18n.t('backend.error.login')
60
+ if (caller && 'clearAccount' in caller && caller.clearAccount) caller.clearAccount()
61
+ break
62
+ // default:
63
+ // flash.value.alert = $i18n.t('backend.error.api', { message: error.message })
64
+ }
65
+ } else {
66
+ switch (error.status) {
67
+ case 401:
68
+ flash.value.alert = $i18n.t('backend.error.login')
69
+ if (caller && 'clearAccount' in caller && caller.clearAccount) caller.clearAccount()
70
+ break
71
+ case 404:
72
+ {
73
+ const backendError = error.data as BER
74
+ backendErrorInfo.value.error = backendError
75
+ }
76
+ break
77
+ case 422: {
78
+ if (caller && 'externalErrors' in caller && caller.externalErrors && error.data) {
79
+ const { errors } = error.data as ErrorsResource<ErrorMessages<string>>
80
+ // globalThis.console.log(errors)
81
+ caller.externalErrors.value = errors
82
+ }
83
+ break
84
+ }
85
+ default:
86
+ flash.value.alert = $i18n.t('backend.error.api', { message: error.message })
87
+ }
88
+ }
89
+ }
90
+
91
+ return { backendErrorInfo, setError }
92
+ }
93
+
94
+ export type UseAlertType = ReturnType<typeof useAlert>
@@ -0,0 +1,8 @@
1
+ export type { QueryAPIOptions } from './backend'
2
+
3
+ export { createFetch, createRequestFetch, useOFetch, useMutationApi, useQueryApi } from './backend'
4
+ export { useExternalErrors, useAlert } from './backend'
5
+
6
+ export { useDate } from './util'
7
+
8
+ export type { UseAlertType } from './backend'
@@ -0,0 +1,35 @@
1
+ import { parse, format } from '@formkit/tempo'
2
+
3
+ import { useLocale } from './use-locale'
4
+
5
+ export const useDatetimeLocal = () => {
6
+ const { locale } = useLocale()
7
+
8
+ const parseDT = (datetime: string, format: string): Date => {
9
+ return parse(datetime, format, locale.value)
10
+ }
11
+
12
+ const toISO8601 = (datetime: string): string => {
13
+ return format(parseDT(datetime, 'YYYY/MM/DD HH:mm'), 'YYYY-MM-DDTHH:mm', locale.value)
14
+ }
15
+
16
+ const fromISO8601 = (datetime: string): string => {
17
+ return format(parseDT(datetime, 'YYYY-MM-DDTHH:mm'), 'YYYY/MM/DD HH:mm', locale.value)
18
+ }
19
+
20
+ const upDTL = (datetime: string | null): string | null => {
21
+ return datetime ? toISO8601(datetime) : null
22
+ }
23
+
24
+ const downDTL = (datetime: string | null): string => {
25
+ return datetime ? fromISO8601(datetime) : ''
26
+ }
27
+
28
+ const formatHTML = (datetime: string | null, fmt: string): string => {
29
+ return datetime
30
+ ? format(parse(datetime, 'YYYY/MM/DD HH:mm', locale.value), fmt, locale.value)
31
+ : ''
32
+ }
33
+
34
+ return { upDTL, downDTL, toISO8601, formatHTML }
35
+ }
@@ -0,0 +1,13 @@
1
+ export function useEntity<M extends object, R extends object>() {
2
+ const create = ({ from }: { from: R }): M => {
3
+ const model: Partial<M> = {}
4
+ Object.assign(model, from)
5
+ return model as M
6
+ }
7
+
8
+ const copy = ({ from, to }: { from: R | M; to: M }): void => {
9
+ Object.assign(to, from)
10
+ }
11
+
12
+ return { create, copy }
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { Flash } from '../../interfaces'
2
+
3
+ import { ref } from 'vue'
4
+
5
+ export function useFlash() {
6
+ const flash = ref<Flash>({})
7
+
8
+ const clearFlash = (): void => {
9
+ flash.value = {}
10
+ }
11
+
12
+ return { flash, clearFlash }
13
+ }
14
+
15
+ export type UseFlashType = ReturnType<typeof useFlash>
@@ -0,0 +1,23 @@
1
+ import { useNuxtApp } from 'nuxt/app'
2
+ import { useBrowserLocale } from '#i18n'
3
+
4
+ export const useLocale = () => {
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const { $i18n } = useNuxtApp() as any
7
+ const { locale, availableLocales, fallbackLocale } = $i18n
8
+ const autoDetect = (): void => {
9
+ const browserLocale: string | null = useBrowserLocale()
10
+ // console.log(browserLocale)
11
+ // console.log(locales.value)
12
+
13
+ type AvailableLocales = (typeof $i18n.availableLocales)[number]
14
+
15
+ locale.value = (
16
+ (availableLocales as string[]).includes(browserLocale ?? '')
17
+ ? browserLocale
18
+ : fallbackLocale.value
19
+ ) as AvailableLocales
20
+ }
21
+
22
+ return { locale, autoDetect }
23
+ }
@@ -0,0 +1,70 @@
1
+ import { useState } from 'nuxt/app'
2
+
3
+ export function useMoreScroll({
4
+ key = null,
5
+ page,
6
+ pages,
7
+ }: {
8
+ key?: string | null
9
+ page: number
10
+ pages: number
11
+ }) {
12
+ const toFirstUpper = (str: string): string => {
13
+ return str.charAt(0).toUpperCase() + str.slice(1)
14
+ }
15
+
16
+ const currentPage = useState<number>(
17
+ key ? `currentPageFor${toFirstUpper(key)}` : 'currentPage',
18
+ () => {
19
+ return 1
20
+ },
21
+ )
22
+
23
+ const pagePrev = useState<boolean>(key ? `pagePrevFor${toFirstUpper(key)}` : 'pagePrev', () => {
24
+ return false
25
+ })
26
+ const pageNext = useState<boolean>(key ? `pageNextFor${toFirstUpper(key)}` : 'pageNext', () => {
27
+ return false
28
+ })
29
+
30
+ const minPage = useState<number>(key ? `minPageFor${toFirstUpper(key)}` : 'minPage', () => {
31
+ return 1
32
+ })
33
+ const maxPage = useState<number>(key ? `maxPageFor${toFirstUpper(key)}` : 'maxPage', () => {
34
+ return 1
35
+ })
36
+
37
+ const minMaxPage = () => {
38
+ minPage.value = currentPage.value < page ? currentPage.value : page
39
+ maxPage.value = currentPage.value > page ? currentPage.value : page
40
+ }
41
+
42
+ const pagePrevNext = () => {
43
+ if (currentPage.value == 1) pagePrev.value = false
44
+ if (currentPage.value == pages) pageNext.value = false
45
+ // console.log(`page prev: ${pagePrev.value}`)
46
+ // console.log(`page next: ${pageNext.value}`)
47
+ }
48
+
49
+ const init = () => {
50
+ currentPage.value = page
51
+ pagePrev.value = true
52
+ pageNext.value = true
53
+ minMaxPage()
54
+ pagePrevNext()
55
+ }
56
+
57
+ const decrement = () => {
58
+ currentPage.value = minPage.value - 1
59
+ minMaxPage()
60
+ pagePrevNext()
61
+ }
62
+
63
+ const increment = () => {
64
+ currentPage.value = maxPage.value + 1
65
+ minMaxPage()
66
+ pagePrevNext()
67
+ }
68
+
69
+ return { currentPage, pagePrev, pageNext, minPage, maxPage, init, decrement, increment }
70
+ }
@@ -0,0 +1,9 @@
1
+ import { useState } from 'nuxt/app'
2
+
3
+ export function useReferer() {
4
+ const referers = useState<Record<string, string>>('referers', () => {
5
+ return {}
6
+ })
7
+
8
+ return { referers }
9
+ }
@@ -0,0 +1,86 @@
1
+ import { useRuntimeConfig } from 'nuxt/app'
2
+
3
+ import { computed } from 'vue'
4
+
5
+ import { format, tzDate } from '@formkit/tempo'
6
+
7
+ import { useLocale } from './use-locale'
8
+ import { useDatetimeLocal } from './use-datetime-local'
9
+
10
+ interface TimeZone {
11
+ client: string
12
+ server: string
13
+ }
14
+
15
+ interface TZOptions {
16
+ text: string
17
+ value: string
18
+ }
19
+
20
+ export const useTimeZone = () => {
21
+ const runtimeConfig = useRuntimeConfig()
22
+ const { locale } = useLocale()
23
+ const { toISO8601, formatHTML } = useDatetimeLocal()
24
+
25
+ const serverTimeZone = runtimeConfig.public.timeZone as string
26
+
27
+ const timeZone = computed<TimeZone>(() => ({
28
+ client: Intl.DateTimeFormat().resolvedOptions().timeZone,
29
+ server: serverTimeZone,
30
+ }))
31
+
32
+ const tzOptions = computed<TZOptions[]>(() =>
33
+ Intl.supportedValuesOf('timeZone').map((e) => ({ text: e, value: e })),
34
+ )
35
+
36
+ const tzServerDate = (datetime: string): Date => {
37
+ return tzDate(toISO8601(datetime), timeZone.value.server)
38
+ }
39
+
40
+ const tzClientDate = (datetime: string): Date => {
41
+ return tzDate(toISO8601(datetime), timeZone.value.client)
42
+ }
43
+
44
+ const upTZ = (datetime: string | null): string => {
45
+ return timeZone.value.client === timeZone.value.server
46
+ ? (datetime ?? '')
47
+ : datetime
48
+ ? format({
49
+ date: tzServerDate(datetime),
50
+ format: 'YYYY/MM/DD HH:mm',
51
+ locale: locale.value,
52
+ tz: timeZone.value.client,
53
+ })
54
+ : ''
55
+ }
56
+
57
+ const downTZ = (datetime: string | null): string => {
58
+ return timeZone.value.client === timeZone.value.server
59
+ ? (datetime ?? '')
60
+ : datetime
61
+ ? format({
62
+ date: tzClientDate(datetime),
63
+ format: 'YYYY/MM/DD HH:mm',
64
+ locale: locale.value,
65
+ tz: timeZone.value.server,
66
+ })
67
+ : ''
68
+ }
69
+
70
+ const formatHtmlTZ = (datetime: string | null, fmt: string): string => {
71
+ return timeZone.value.client === timeZone.value.server
72
+ ? formatHTML(datetime, fmt)
73
+ : datetime
74
+ ? format({
75
+ date: tzServerDate(datetime),
76
+ format: fmt,
77
+ locale: locale.value,
78
+ tz: timeZone.value.client,
79
+ })
80
+ : ''
81
+ }
82
+
83
+ return { timeZone, tzOptions, upTZ, downTZ, formatHtmlTZ }
84
+ }
85
+
86
+ // export type UseTimeZoneType = ReturnType<typeof useTimeZone>
@@ -0,0 +1 @@
1
+ export { useDate } from './use-date'
@@ -0,0 +1,7 @@
1
+ export function useDate() {
2
+ const isValidDate = (str: string): boolean => {
3
+ return !!str && !!str.match(/\d{4}\/\d{2}\/\d{2}/) && !isNaN(Date.parse(str))
4
+ }
5
+
6
+ return { isValidDate }
7
+ }
package/app.config.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { defineAppConfig } from 'nuxt/app'
2
+
3
+ export default defineAppConfig({
4
+ myLayer: {
5
+ name: 'Vesper Nuxt layer',
6
+ },
7
+ })
@@ -0,0 +1,4 @@
1
+ export interface BackendErrorInfo<BER> {
2
+ error?: BER
3
+ status?: number
4
+ }
@@ -0,0 +1 @@
1
+ export type { BackendErrorInfo } from './backend-error-info'
@@ -0,0 +1,4 @@
1
+ export interface Flash {
2
+ notice?: string
3
+ alert?: string
4
+ }
@@ -0,0 +1,5 @@
1
+ export type { BackendErrorInfo } from './error'
2
+
3
+ export type { BackendErrorResource, ErrorsResource } from './resource'
4
+
5
+ export type { Flash } from './flash'
@@ -0,0 +1,5 @@
1
+ export interface BackendErrorResource {
2
+ source?: string
3
+ title: string
4
+ errors: string[]
5
+ }
@@ -0,0 +1,3 @@
1
+ export interface ErrorsResource<T> {
2
+ errors: T
3
+ }
@@ -0,0 +1,2 @@
1
+ export type { ErrorsResource } from './errors-resource'
2
+ export type { BackendErrorResource } from './beckend-error-resource'
package/nuxt.config.ts ADDED
@@ -0,0 +1,6 @@
1
+ // https://nuxt.com/docs/api/configuration/nuxt-config
2
+ import { defineNuxtConfig } from 'nuxt/config'
3
+
4
+ export default defineNuxtConfig({
5
+ devtools: { enabled: true },
6
+ })
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@vesperjs/nuxt",
3
+ "version": "0.1.0",
4
+ "files": [
5
+ "app",
6
+ "interfaces",
7
+ "types",
8
+ "nuxt.config.ts",
9
+ "app.config.ts"
10
+ ],
11
+ "type": "module",
12
+ "main": "./nuxt.config.ts",
13
+ "devDependencies": {
14
+ "@nuxt/eslint": "latest",
15
+ "@nuxtjs/i18n": "10.2.3",
16
+ "@types/node": "^24.11.0",
17
+ "eslint": "^9.39.3",
18
+ "nuxt": "^4.3.1",
19
+ "ofetch": "1.5.1",
20
+ "oxfmt": "^0.41.0",
21
+ "typescript": "^5.9.3",
22
+ "vue": "^3.6.0-beta.8"
23
+ },
24
+ "scripts": {
25
+ "dev": "nuxi dev .playground",
26
+ "dev:prepare": "nuxt prepare .playground",
27
+ "build": "nuxt build .playground",
28
+ "generate": "nuxt generate .playground",
29
+ "preview": "nuxt preview .playground",
30
+ "lint": "eslint .",
31
+ "fmt": "oxfmt"
32
+ }
33
+ }
@@ -0,0 +1 @@
1
+ export type ErrorMessages<T extends string> = Partial<Record<T, string[]>>
package/types/index.ts ADDED
@@ -0,0 +1 @@
1
+ export type { ErrorMessages } from './error-messages'