itube-specs 0.0.767 → 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 +6 -6
- 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;
|
|
@@ -64,7 +64,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
64
64
|
marginClass: '_mb-12'
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
type: '
|
|
67
|
+
type: 'email',
|
|
68
68
|
value: 'from',
|
|
69
69
|
label: 'email',
|
|
70
70
|
hideLabel: true,
|
|
@@ -113,7 +113,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
113
113
|
marginClass: '_mb-12 _mt-4'
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
|
-
type: '
|
|
116
|
+
type: 'email',
|
|
117
117
|
value: 'from',
|
|
118
118
|
label: 'email',
|
|
119
119
|
hideLabel: true,
|
|
@@ -153,7 +153,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
153
153
|
marginClass: '_mb-12 _mt-4'
|
|
154
154
|
},
|
|
155
155
|
{
|
|
156
|
-
type: '
|
|
156
|
+
type: 'email',
|
|
157
157
|
value: 'from',
|
|
158
158
|
label: 'email',
|
|
159
159
|
hideLabel: true,
|
|
@@ -185,7 +185,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
185
185
|
marginClass: '_mb-12'
|
|
186
186
|
})),
|
|
187
187
|
{
|
|
188
|
-
type: '
|
|
188
|
+
type: 'email',
|
|
189
189
|
value: 'from',
|
|
190
190
|
label: 'email',
|
|
191
191
|
hideLabel: true,
|
|
@@ -247,7 +247,7 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
247
247
|
marginClass: '_mt-4 _mb-12'
|
|
248
248
|
},
|
|
249
249
|
{
|
|
250
|
-
type: '
|
|
250
|
+
type: 'email',
|
|
251
251
|
value: 'from',
|
|
252
252
|
label: 'email',
|
|
253
253
|
hideLabel: true,
|
|
@@ -265,4 +265,4 @@ export const reportFormsScheme: Array<IReportScheme> = [
|
|
|
265
265
|
},
|
|
266
266
|
],
|
|
267
267
|
},
|
|
268
|
-
]
|
|
268
|
+
];
|
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
|
|