itube-specs 0.0.768 → 0.0.769
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/composables/__tests__/use-get-pure-route-name.test.ts +3 -3
- package/composables/__tests__/use-get-videos-filter-request.test.ts +2 -2
- package/composables/__tests__/use-slug.test.ts +3 -3
- package/composables/fetch/use-fetch-footer-categories.ts +1 -1
- package/composables/fetch/use-fetch-models-by-phrases.ts +1 -1
- package/composables/fetch/use-fetch-related-videos.ts +0 -1
- package/composables/fetch/use-fetch-top-chips-models.ts +1 -1
- package/composables/fetch/use-fetch-top-random-categories.ts +1 -1
- package/composables/use-api-action.ts +13 -13
- package/composables/use-generate-link.ts +5 -5
- package/composables/use-get-videos-filter-request.ts +1 -1
- package/composables/use-lang.ts +1 -1
- package/composables/use-recaptcha.ts +1 -1
- package/composables/use-seo-links.ts +2 -2
- package/composables/use-user.ts +2 -2
- package/lib/contact-forms-scheme.ts +1 -1
- package/lib/contacts/report-issue-items.ts +1 -1
- package/lib/contacts/report-malware-items.ts +1 -1
- package/lib/contacts/report-reasons-items.ts +1 -1
- package/lib/contacts/report-wrong-items.ts +1 -1
- package/lib/report-forms-scheme.ts +1 -1
- package/nuxt.config.ts +3 -3
- package/package.json +10 -3
- package/runtime/utils/check-device-width.ts +2 -2
- package/runtime/utils/cleaners/clean-category-card.ts +1 -1
- package/runtime/utils/cleaners/clean-category-info.ts +1 -2
- package/runtime/utils/cleaners/clean-channel-card.ts +1 -1
- package/runtime/utils/cleaners/clean-channel-info.ts +1 -1
- package/runtime/utils/cleaners/clean-model-info.ts +1 -1
- package/runtime/utils/cleaners/clean-playlist-card.ts +1 -1
- package/runtime/utils/cleaners/clean-playlist-data.ts +1 -1
- package/runtime/utils/cleaners/clean-playlist-video.ts +1 -1
- package/runtime/utils/cleaners/clean-profile-data.ts +1 -1
- package/runtime/utils/cleaners/clean-user-playlists-card.ts +1 -1
- package/runtime/utils/cleaners/clean-video-card.ts +1 -1
- package/runtime/utils/cleaners/clean-video-data.ts +1 -1
- package/runtime/utils/converters/convert-string.ts +0 -0
- package/runtime/utils/is-mobile-device.ts +2 -2
- package/runtime/utils/normalize-url.ts +16 -16
- package/runtime/utils/server/age.ts +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { vi, describe, it, expect } from 'vitest';
|
|
2
2
|
|
|
3
|
+
import { useRoute } from 'vue-router';
|
|
4
|
+
import { useGetPureRouteName } from '../use-get-pure-route-name';
|
|
5
|
+
|
|
3
6
|
vi.mock('vue-router', () => ({
|
|
4
7
|
useRoute: vi.fn(),
|
|
5
8
|
}));
|
|
6
9
|
|
|
7
|
-
import { useRoute } from 'vue-router';
|
|
8
|
-
import { useGetPureRouteName } from '../use-get-pure-route-name';
|
|
9
|
-
|
|
10
10
|
describe('useGetPureRouteName', () => {
|
|
11
11
|
it('убирает суффикс ___en', () => {
|
|
12
12
|
vi.mocked(useRoute).mockReturnValue({ name: 'videos___en' } as any);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { vi, describe, it, expect } from 'vitest';
|
|
2
2
|
|
|
3
|
+
import { useGetVideosFilterRequest } from '../use-get-videos-filter-request';
|
|
4
|
+
|
|
3
5
|
vi.mock('../../runtime', () => ({
|
|
4
6
|
getMultipleQuery: () => ({}),
|
|
5
7
|
}));
|
|
6
8
|
|
|
7
|
-
import { useGetVideosFilterRequest } from '../use-get-videos-filter-request';
|
|
8
|
-
|
|
9
9
|
describe('useGetVideosFilterRequest', () => {
|
|
10
10
|
it('всегда возвращает categories_filter_use_and', () => {
|
|
11
11
|
const route = { query: {} } as any;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @vitest-environment nuxt
|
|
2
2
|
import { vi, describe, it, expect } from 'vitest';
|
|
3
3
|
|
|
4
|
+
import { useRoute } from 'vue-router';
|
|
5
|
+
import { useSlug } from '../use-slug';
|
|
6
|
+
|
|
4
7
|
vi.mock('vue-router', async (importOriginal) => {
|
|
5
8
|
const mod = await importOriginal<typeof import('vue-router')>();
|
|
6
9
|
return { ...mod, useRoute: vi.fn() };
|
|
7
10
|
});
|
|
8
11
|
|
|
9
|
-
import { useRoute } from 'vue-router';
|
|
10
|
-
import { useSlug } from '../use-slug';
|
|
11
|
-
|
|
12
12
|
describe('useSlug', () => {
|
|
13
13
|
it('конвертирует kebab-case slug', () => {
|
|
14
14
|
vi.mocked(useRoute).mockReturnValue({ params: { slug: 'john-doe' } } as any);
|
|
@@ -9,7 +9,7 @@ export const useFetchFooterCategories = async (
|
|
|
9
9
|
const key = AsyncData.CategoriesTopRandomFooter;
|
|
10
10
|
const stateKey = computed(() => `data-${key}-${lang}`);
|
|
11
11
|
|
|
12
|
-
const { data, status
|
|
12
|
+
const { data, status } = await useAsyncData<IChipsItem[]>(
|
|
13
13
|
key,
|
|
14
14
|
() => useApiFetcher<IChipsItem[]>(
|
|
15
15
|
stateKey.value,
|
|
@@ -6,7 +6,6 @@ import type { IVideoCard, PaginatedResponse } from '../../types';
|
|
|
6
6
|
export const useFetchRelatedVideos = async (
|
|
7
7
|
apiMethod: (...args: any[]) => Promise<PaginatedResponse<IVideoCard>>
|
|
8
8
|
) => {
|
|
9
|
-
const { locale } = useI18n();
|
|
10
9
|
const lang = useLang() as Language;
|
|
11
10
|
const route = useRoute();
|
|
12
11
|
const mobile = !!useState<boolean>('isMobile').value;
|
|
@@ -7,39 +7,39 @@ export function useApiAction<T extends any[], R>(
|
|
|
7
7
|
apiMethod: (...args: T) => Promise<R>,
|
|
8
8
|
isGet = false,
|
|
9
9
|
) {
|
|
10
|
-
const loading = ref(false)
|
|
11
|
-
const error = ref<Error | null>(null)
|
|
12
|
-
const data = ref<R | null>(null)
|
|
10
|
+
const loading = ref(false);
|
|
11
|
+
const error = ref<Error | null>(null);
|
|
12
|
+
const data = ref<R | null>(null);
|
|
13
13
|
|
|
14
14
|
const execute = async (
|
|
15
15
|
args: T,
|
|
16
16
|
key?: string
|
|
17
17
|
) => {
|
|
18
|
-
loading.value = true
|
|
19
|
-
error.value = null
|
|
18
|
+
loading.value = true;
|
|
19
|
+
error.value = null;
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
|
-
const result = await apiMethod(...args)
|
|
22
|
+
const result = await apiMethod(...args);
|
|
23
23
|
|
|
24
24
|
if (isGet) {
|
|
25
|
-
data.value = result
|
|
25
|
+
data.value = result;
|
|
26
26
|
|
|
27
27
|
if (key) {
|
|
28
|
-
useState<R | null>(`data-${key}`).value = result
|
|
28
|
+
useState<R | null>(`data-${key}`).value = result;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
return result
|
|
32
|
+
return result;
|
|
33
33
|
} catch (err: any) {
|
|
34
34
|
error.value = createError({
|
|
35
35
|
statusCode: 500,
|
|
36
36
|
statusMessage: 'Error in API action',
|
|
37
37
|
message: err instanceof Error ? err.message : 'Unknown error'
|
|
38
|
-
})
|
|
38
|
+
});
|
|
39
39
|
} finally {
|
|
40
|
-
loading.value = false
|
|
40
|
+
loading.value = false;
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
};
|
|
43
43
|
|
|
44
|
-
return { loading, error, data, execute }
|
|
44
|
+
return { loading, error, data, execute };
|
|
45
45
|
}
|
|
@@ -12,7 +12,7 @@ export const useGenerateLink = () => {
|
|
|
12
12
|
* @returns string - корректный путь для <NuxtLink> и navigateTo()
|
|
13
13
|
*/
|
|
14
14
|
const generateLink = (path: string, localeArg?: Language): string => {
|
|
15
|
-
const cleanPath = path.startsWith('/') ? path : `/${path}
|
|
15
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
16
16
|
|
|
17
17
|
const defaultNiche = useAppConfig().defaultNiche;
|
|
18
18
|
const projectNiches = useAppConfig().niches as Niche[];
|
|
@@ -24,8 +24,8 @@ export const useGenerateLink = () => {
|
|
|
24
24
|
: `/${niche.value}${cleanPath}`;
|
|
25
25
|
|
|
26
26
|
const locale = localeArg || undefined;
|
|
27
|
-
return localePath(withNiche, locale)
|
|
28
|
-
}
|
|
27
|
+
return localePath(withNiche, locale);
|
|
28
|
+
};
|
|
29
29
|
|
|
30
|
-
return { generateLink }
|
|
31
|
-
}
|
|
30
|
+
return { generateLink };
|
|
31
|
+
};
|
package/composables/use-lang.ts
CHANGED
|
@@ -23,10 +23,10 @@ export const useSeoLinks = (baseDomain: string, alonePage: boolean = false, i18n
|
|
|
23
23
|
// Если alonePage === true, пропускаем sort
|
|
24
24
|
if (allowedParams.includes(key) && !(alonePage && key === 'sort')) {
|
|
25
25
|
if (Array.isArray(value)) {
|
|
26
|
-
value.forEach(v => url.searchParams.append(key, v))
|
|
26
|
+
value.forEach(v => url.searchParams.append(key, v));
|
|
27
27
|
}
|
|
28
28
|
else if (value != null) {
|
|
29
|
-
url.searchParams.append(key, String(value))
|
|
29
|
+
url.searchParams.append(key, String(value));
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
});
|
package/composables/use-user.ts
CHANGED
|
@@ -91,7 +91,7 @@ export const useUser = (apiService) => {
|
|
|
91
91
|
|
|
92
92
|
const recoveryPassword = async (form: IRecoveryPasswordForm) => {
|
|
93
93
|
try {
|
|
94
|
-
const {generateLink} = useGenerateLink();
|
|
94
|
+
const { generateLink } = useGenerateLink();
|
|
95
95
|
await apiService.recoveryPassword(form);
|
|
96
96
|
showSuccess('Password recovered');
|
|
97
97
|
await useRouter().push(generateLink('/profile'));
|
|
@@ -110,7 +110,7 @@ export const useUser = (apiService) => {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
const signOut = () => {
|
|
113
|
-
const {generateLink} = useGenerateLink();
|
|
113
|
+
const { generateLink } = useGenerateLink();
|
|
114
114
|
const cookie = useCookie('jwtoken', { path: '/', secure: true, httpOnly: false });
|
|
115
115
|
cookie.value = undefined; // или null
|
|
116
116
|
const domain = useRuntimeConfig().public.xDomain;
|
package/nuxt.config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fileURLToPath } from 'node:url'
|
|
2
|
-
import { defineNuxtConfig } from 'nuxt/config'
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { defineNuxtConfig } from 'nuxt/config';
|
|
3
3
|
|
|
4
4
|
export default defineNuxtConfig({
|
|
5
5
|
modules: ['@nuxt/eslint', '@nuxt/icon', '@nuxtjs/i18n'],
|
|
@@ -20,4 +20,4 @@ export default defineNuxtConfig({
|
|
|
20
20
|
}
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
|
-
})
|
|
23
|
+
});
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "itube-specs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.769",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"prepublishOnly": "npm install && npx nuxi prepare",
|
|
9
9
|
"patch": "npm version patch",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"lint": "eslint . --max-warnings=0",
|
|
11
|
+
"lint:fix": "eslint . --fix",
|
|
12
|
+
"test": "NODE_OPTIONS='--no-warnings' vitest",
|
|
13
|
+
"prepare": "husky"
|
|
14
|
+
},
|
|
15
|
+
"lint-staged": {
|
|
16
|
+
"*.{js,ts,vue}": "eslint --fix --max-warnings=0"
|
|
12
17
|
},
|
|
13
18
|
"engines": {
|
|
14
19
|
"node": ">=22.12.0"
|
|
@@ -50,6 +55,8 @@
|
|
|
50
55
|
"@vue/test-utils": "^2.4.6",
|
|
51
56
|
"eslint": "^9.37.0",
|
|
52
57
|
"happy-dom": "^18.0.1",
|
|
58
|
+
"husky": "^9.1.7",
|
|
59
|
+
"lint-staged": "^17.0.7",
|
|
53
60
|
"nuxt": "^3.21.8",
|
|
54
61
|
"typescript": "^5.9.3",
|
|
55
62
|
"vitest": "^3.2.4",
|
|
@@ -10,7 +10,7 @@ function breakpoints(breakpoints: Record<CssBreakpoints, number>) {
|
|
|
10
10
|
lg: breakpoints.lg,
|
|
11
11
|
xl: breakpoints.xl,
|
|
12
12
|
}
|
|
13
|
-
)
|
|
13
|
+
);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -24,5 +24,5 @@ export const checkDeviceWidth = (projectBreakpoints: Record<CssBreakpoints, numb
|
|
|
24
24
|
return ({
|
|
25
25
|
isMobile,
|
|
26
26
|
isTablet
|
|
27
|
-
})
|
|
27
|
+
});
|
|
28
28
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { IRawCategoryInfo, ICardInfo } from '../../../types';
|
|
2
|
-
import { ThumbSize } from '../../constants/thumb-size';
|
|
3
2
|
import { convertCategoriesToChips } from '../converters/convert-categories-to-chips';
|
|
4
3
|
|
|
5
4
|
/** Нормализует сырые данные страницы категории к типу ICardInfo. */
|
|
@@ -14,4 +13,4 @@ export const cleanCategoryInfo = (card: IRawCategoryInfo): ICardInfo => ({
|
|
|
14
13
|
metaDescription: card.meta_description || '',
|
|
15
14
|
metaTitle: card.meta_title || '',
|
|
16
15
|
header: card.header || '',
|
|
17
|
-
})
|
|
16
|
+
});
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Возвращает true если userAgent соответствует мобильному устройству. */
|
|
2
2
|
export const isMobileDevice = (userAgent: string) => {
|
|
3
|
-
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(userAgent)
|
|
4
|
-
}
|
|
3
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(userAgent);
|
|
4
|
+
};
|
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
/** Нормализует URL: убирает page=1, sort=trending, пустые query, приводит к нижнему регистру, заменяет пробелы на дефисы. */
|
|
2
2
|
export function normalizeUrl(to: { path: string; query: Record<string, any> }) {
|
|
3
|
-
let path = to.path
|
|
4
|
-
let query = { ...to.query }
|
|
3
|
+
let path = to.path;
|
|
4
|
+
let query = { ...to.query };
|
|
5
5
|
|
|
6
6
|
// 1) Удаляем page=1
|
|
7
7
|
if (query.page === '1') {
|
|
8
|
-
const { page, ...rest } = query
|
|
9
|
-
query = rest
|
|
8
|
+
const { page, ...rest } = query;
|
|
9
|
+
query = rest;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// 2) Удаляем sort=trending
|
|
13
13
|
if (query.sort === 'trending') {
|
|
14
|
-
const { sort, ...rest } = query
|
|
15
|
-
query = rest
|
|
14
|
+
const { sort, ...rest } = query;
|
|
15
|
+
query = rest;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// Удаляем query без значений
|
|
19
19
|
query = Object.fromEntries(
|
|
20
20
|
Object.entries(query).filter(([_, v]) => v !== '' && v !== null && v !== undefined)
|
|
21
|
-
)
|
|
21
|
+
);
|
|
22
22
|
|
|
23
23
|
// Приводим query к нижнему регистру (ключи и строковые значения)
|
|
24
|
-
const loweredQuery: Record<string, any> = {}
|
|
24
|
+
const loweredQuery: Record<string, any> = {};
|
|
25
25
|
for (const [key, value] of Object.entries(query)) {
|
|
26
|
-
const lowerKey = key.toLowerCase()
|
|
27
|
-
loweredQuery[lowerKey] = typeof value === 'string' ? value.toLowerCase() : value
|
|
26
|
+
const lowerKey = key.toLowerCase();
|
|
27
|
+
loweredQuery[lowerKey] = typeof value === 'string' ? value.toLowerCase() : value;
|
|
28
28
|
}
|
|
29
|
-
query = loweredQuery
|
|
29
|
+
query = loweredQuery;
|
|
30
30
|
|
|
31
31
|
// 2) Приводим путь к нижнему регистру
|
|
32
|
-
path = path.toLowerCase()
|
|
32
|
+
path = path.toLowerCase();
|
|
33
33
|
|
|
34
34
|
// 3) Заменяем пробелы и %20 на дефисы
|
|
35
35
|
path = path
|
|
36
36
|
.replace(/%20/gi, '-') // encoded
|
|
37
37
|
.replace(/ /g, '-') // raw
|
|
38
|
-
.replace(/\+/g, '-') // some browsers encode spaces as +
|
|
38
|
+
.replace(/\+/g, '-'); // some browsers encode spaces as +
|
|
39
39
|
|
|
40
40
|
// 4) Убираем двойные дефисы
|
|
41
|
-
path = path.replace(/--+/g, '-')
|
|
41
|
+
path = path.replace(/--+/g, '-');
|
|
42
42
|
|
|
43
43
|
// 5) Убираем хвостовой /
|
|
44
44
|
if (path.length > 1) {
|
|
45
|
-
path = path.replace(/\/+$/, '')
|
|
45
|
+
path = path.replace(/\/+$/, '');
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
return { path, query }
|
|
48
|
+
return { path, query };
|
|
49
49
|
}
|
|
@@ -2,7 +2,7 @@ import { getCookie } from 'h3';
|
|
|
2
2
|
import { getClientHeaders } from './get-client-headers';
|
|
3
3
|
|
|
4
4
|
/** Возвращает true если пользователь находится в регионе где требуется верификация возраста (GB, штат KS). */
|
|
5
|
-
export async function isGeoMatch(event,
|
|
5
|
+
export async function isGeoMatch(event, _config) {
|
|
6
6
|
const AGE_VERIFY_REGIONS = ['KS'];
|
|
7
7
|
const AGE_VERIFY_COUNTRIES = ['GB'];
|
|
8
8
|
|