kmcom-nuxt-layers 2.5.1 → 2.6.2

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 (58) hide show
  1. package/layers/core/app/utils/browserInfo.test.ts +81 -0
  2. package/layers/core/app/utils/featureClasses.test.ts +103 -0
  3. package/layers/core/app/utils/regex.test.ts +31 -0
  4. package/layers/feeds/nuxt.config.ts +7 -10
  5. package/layers/feeds/server/utils/feed-author.test.ts +71 -0
  6. package/layers/feeds/server/utils/feed-config.test.ts +162 -0
  7. package/layers/forms/app/utils/contact-schema.test.ts +75 -0
  8. package/layers/forms/app/utils/fieldProps.test.ts +141 -0
  9. package/layers/layout/app/utils/gridPlacementStyle.test.ts +310 -0
  10. package/layers/metadata/providers/comicvine/nuxt.config.ts +1 -1
  11. package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/client.ts +13 -6
  12. package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +201 -0
  13. package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.ts +34 -15
  14. package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/provider.ts +26 -9
  15. package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/types.ts +1 -0
  16. package/layers/metadata/providers/comicvine/tsconfig.json +1 -1
  17. package/layers/metadata/providers/google-books/nuxt.config.ts +1 -1
  18. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +76 -0
  19. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/client.ts +20 -2
  20. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +144 -0
  21. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/normalise.ts +21 -12
  22. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/provider.ts +11 -4
  23. package/layers/metadata/providers/google-books/server/utils/metadata-google-books/types.ts +9 -1
  24. package/layers/metadata/providers/google-books/tsconfig.json +1 -1
  25. package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +25 -0
  26. package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.ts +14 -6
  27. package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +142 -0
  28. package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.ts +30 -24
  29. package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/provider.ts +5 -1
  30. package/layers/metadata/providers/openlibrary/tsconfig.json +1 -1
  31. package/layers/metadata/providers/themoviedb/nuxt.config.ts +36 -0
  32. package/layers/metadata/providers/themoviedb/package.json +12 -0
  33. package/layers/metadata/providers/themoviedb/server/plugins/tmdb-register.ts +3 -0
  34. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +31 -0
  35. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.ts +60 -0
  36. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +162 -0
  37. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.ts +83 -0
  38. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/provider.ts +37 -0
  39. package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/types.ts +81 -0
  40. package/layers/metadata/providers/themoviedb/tsconfig.json +5 -0
  41. package/layers/metadata/server/api/metadata/lookup.get.ts +7 -2
  42. package/layers/metadata/server/api/metadata/search.get.ts +7 -2
  43. package/layers/metadata/server/api/metadata/status.get.ts +24 -0
  44. package/layers/metadata/server/api/metadata/sync.post.ts +14 -4
  45. package/layers/metadata/server/utils/metadata/cache.test.ts +39 -0
  46. package/layers/metadata/server/utils/metadata/cache.ts +15 -3
  47. package/layers/metadata/server/utils/metadata/errors.ts +1 -1
  48. package/layers/metadata/server/utils/metadata/provider-registry.test.ts +54 -0
  49. package/layers/metadata/server/utils/metadata/search.test.ts +198 -0
  50. package/layers/metadata/server/utils/metadata/search.ts +9 -4
  51. package/layers/navigation/app/utils/site.test.ts +41 -0
  52. package/layers/seo/app/utils/seoConfig.test.ts +58 -0
  53. package/layers/theme/server/utils/accent-css.test.ts +56 -0
  54. package/layers/theme/server/utils/fouc-config.test.ts +74 -0
  55. package/layers/visual/app/utils/colorTokens.test.ts +51 -0
  56. package/layers/visual/app/utils/gradientStyle.test.ts +76 -0
  57. package/layers/visual/app/utils/responsiveSizes.test.ts +71 -0
  58. package/package.json +7 -3
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { resolveUiColorToken } from './colorTokens'
4
+
5
+ describe('resolveUiColorToken', () => {
6
+ it('defaults to primary-500 when no options are given', () => {
7
+ expect(resolveUiColorToken({})).toBe('var(--ui-color-primary-500)')
8
+ })
9
+
10
+ it('uses the provided color and shade', () => {
11
+ expect(resolveUiColorToken({ color: 'secondary', shade: 200 })).toBe(
12
+ 'var(--ui-color-secondary-200)'
13
+ )
14
+ })
15
+
16
+ it('applies opacity via color-mix', () => {
17
+ expect(resolveUiColorToken({ color: 'primary', shade: 500, opacity: 50 })).toBe(
18
+ 'color-mix(in srgb, var(--ui-color-primary-500) 50%, transparent)'
19
+ )
20
+ })
21
+
22
+ it('resolves custom color to customColor value', () => {
23
+ expect(resolveUiColorToken({ color: 'custom', customColor: '#123456' })).toBe('#123456')
24
+ })
25
+
26
+ it('resolves custom color to transparent when customColor is missing', () => {
27
+ expect(resolveUiColorToken({ color: 'custom' })).toBe('transparent')
28
+ })
29
+
30
+ it('resolves transparent color regardless of other options', () => {
31
+ expect(resolveUiColorToken({ color: 'transparent', shade: 900, opacity: 10 })).toBe(
32
+ 'transparent'
33
+ )
34
+ })
35
+
36
+ it('resolves white without opacity to a hex value', () => {
37
+ expect(resolveUiColorToken({ color: 'white' })).toBe('#ffffff')
38
+ })
39
+
40
+ it('resolves white with opacity to an rgb() value', () => {
41
+ expect(resolveUiColorToken({ color: 'white', opacity: 40 })).toBe('rgb(255 255 255 / 0.4)')
42
+ })
43
+
44
+ it('resolves black without opacity to a hex value', () => {
45
+ expect(resolveUiColorToken({ color: 'black' })).toBe('#000000')
46
+ })
47
+
48
+ it('resolves black with opacity to an rgb() value', () => {
49
+ expect(resolveUiColorToken({ color: 'black', opacity: 25 })).toBe('rgb(0 0 0 / 0.25)')
50
+ })
51
+ })
@@ -0,0 +1,76 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import type { GradientConfig } from '../types/gradient'
4
+ import { buildGradientStyle, resolveGradientColor } from './gradientStyle'
5
+
6
+ describe('resolveGradientColor', () => {
7
+ it('delegates to resolveUiColorToken for a gradient stop', () => {
8
+ expect(resolveGradientColor({ color: 'primary', shade: 500 })).toBe(
9
+ 'var(--ui-color-primary-500)'
10
+ )
11
+ })
12
+
13
+ it('applies opacity for a gradient stop', () => {
14
+ expect(resolveGradientColor({ color: 'secondary', shade: 300, opacity: 50 })).toBe(
15
+ 'color-mix(in srgb, var(--ui-color-secondary-300) 50%, transparent)'
16
+ )
17
+ })
18
+ })
19
+
20
+ describe('buildGradientStyle', () => {
21
+ const from = { color: 'primary', shade: 500 } as const
22
+ const to = { color: 'secondary', shade: 500 } as const
23
+
24
+ it('builds a linear gradient defaulting direction to "to bottom right"', () => {
25
+ const cfg: GradientConfig = { from, to }
26
+ expect(buildGradientStyle(cfg)).toEqual({
27
+ backgroundImage:
28
+ 'linear-gradient(to bottom right, var(--ui-color-primary-500), var(--ui-color-secondary-500))',
29
+ })
30
+ })
31
+
32
+ it('maps every named linear direction', () => {
33
+ const directions: Array<[NonNullable<GradientConfig['direction']>, string]> = [
34
+ ['to-t', 'to top'],
35
+ ['to-tr', 'to top right'],
36
+ ['to-r', 'to right'],
37
+ ['to-br', 'to bottom right'],
38
+ ['to-b', 'to bottom'],
39
+ ['to-bl', 'to bottom left'],
40
+ ['to-l', 'to left'],
41
+ ['to-tl', 'to top left'],
42
+ ]
43
+
44
+ for (const [direction, expected] of directions) {
45
+ const cfg: GradientConfig = { from, to, direction }
46
+ expect(buildGradientStyle(cfg)).toEqual({
47
+ backgroundImage: `linear-gradient(${expected}, var(--ui-color-primary-500), var(--ui-color-secondary-500))`,
48
+ })
49
+ }
50
+ })
51
+
52
+ it('includes the via stop between from and to when provided', () => {
53
+ const via = { color: 'info', shade: 400 } as const
54
+ const cfg: GradientConfig = { from, to, via }
55
+ expect(buildGradientStyle(cfg)).toEqual({
56
+ backgroundImage:
57
+ 'linear-gradient(to bottom right, var(--ui-color-primary-500), var(--ui-color-info-400), var(--ui-color-secondary-500))',
58
+ })
59
+ })
60
+
61
+ it('builds a radial gradient', () => {
62
+ const cfg: GradientConfig = { from, to, shape: 'radial' }
63
+ expect(buildGradientStyle(cfg)).toEqual({
64
+ backgroundImage:
65
+ 'radial-gradient(circle, var(--ui-color-primary-500), var(--ui-color-secondary-500))',
66
+ })
67
+ })
68
+
69
+ it('builds a conic gradient', () => {
70
+ const cfg: GradientConfig = { from, to, shape: 'conic' }
71
+ expect(buildGradientStyle(cfg)).toEqual({
72
+ backgroundImage:
73
+ 'conic-gradient(var(--ui-color-primary-500), var(--ui-color-secondary-500))',
74
+ })
75
+ })
76
+ })
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import type { ResponsiveSizes } from '../types/media'
4
+ import { buildResponsiveSizesQueries } from './responsiveSizes'
5
+
6
+ describe('buildResponsiveSizesQueries', () => {
7
+ it('returns only the default query when no breakpoints are set', () => {
8
+ const sizes: ResponsiveSizes = { default: '100vw' }
9
+ expect(buildResponsiveSizesQueries(sizes)).toEqual(['100vw'])
10
+ })
11
+
12
+ it('always places the default query last', () => {
13
+ const sizes: ResponsiveSizes = { default: '100vw', md: '50vw' }
14
+ const queries = buildResponsiveSizesQueries(sizes)
15
+ expect(queries.at(-1)).toBe('100vw')
16
+ })
17
+
18
+ it('sorts tailwind breakpoints from largest to smallest min-width', () => {
19
+ const sizes: ResponsiveSizes = {
20
+ default: '100vw',
21
+ sm: '90vw',
22
+ lg: '50vw',
23
+ '2xl': '25vw',
24
+ }
25
+
26
+ expect(buildResponsiveSizesQueries(sizes)).toEqual([
27
+ '(min-width: 1536px) 25vw',
28
+ '(min-width: 1024px) 50vw',
29
+ '(min-width: 640px) 90vw',
30
+ '100vw',
31
+ ])
32
+ })
33
+
34
+ it('mixes device, phone, and tablet breakpoints sorted by pixel value', () => {
35
+ const sizes: ResponsiveSizes = {
36
+ default: '100vw',
37
+ wide: '20vw',
38
+ desktop: '40vw',
39
+ 'phone-md': '95vw',
40
+ }
41
+
42
+ expect(buildResponsiveSizesQueries(sizes)).toEqual([
43
+ '(min-width: 1920px) 20vw',
44
+ '(min-width: 1024px) 40vw',
45
+ '(min-width: 375px) 95vw',
46
+ '100vw',
47
+ ])
48
+ })
49
+
50
+ it('prepends landscape before portrait, both before breakpoints', () => {
51
+ const sizes: ResponsiveSizes = {
52
+ default: '100vw',
53
+ landscape: '80vw',
54
+ portrait: '90vw',
55
+ md: '50vw',
56
+ }
57
+
58
+ expect(buildResponsiveSizesQueries(sizes)).toEqual([
59
+ '(orientation: landscape) 80vw',
60
+ '(orientation: portrait) 90vw',
61
+ '(min-width: 768px) 50vw',
62
+ '100vw',
63
+ ])
64
+ })
65
+
66
+ it('omits orientation queries when not provided', () => {
67
+ const sizes: ResponsiveSizes = { default: '100vw' }
68
+ const queries = buildResponsiveSizesQueries(sizes)
69
+ expect(queries.some((q) => q.includes('orientation'))).toBe(false)
70
+ })
71
+ })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kmcom-nuxt-layers",
3
3
  "private": false,
4
- "version": "2.5.1",
4
+ "version": "2.6.2",
5
5
  "description": "Composable Nuxt 4 layers for building scalable Vue applications",
6
6
  "exports": {
7
7
  "./layers/core": "./layers/core/nuxt.config.ts",
@@ -31,7 +31,8 @@
31
31
  "./layers/metadata": "./layers/metadata/nuxt.config.ts",
32
32
  "./layers/metadata/providers/comicvine": "./layers/metadata/providers/comicvine/nuxt.config.ts",
33
33
  "./layers/metadata/providers/openlibrary": "./layers/metadata/providers/openlibrary/nuxt.config.ts",
34
- "./layers/metadata/providers/google-books": "./layers/metadata/providers/google-books/nuxt.config.ts"
34
+ "./layers/metadata/providers/google-books": "./layers/metadata/providers/google-books/nuxt.config.ts",
35
+ "./layers/metadata/providers/themoviedb": "./layers/metadata/providers/themoviedb/nuxt.config.ts"
35
36
  },
36
37
  "files": [
37
38
  "layers/*/nuxt.config.ts",
@@ -65,7 +66,7 @@
65
66
  "@nuxt/content": "^3.14.0",
66
67
  "@nuxt/eslint": "^1.15.2",
67
68
  "@nuxt/image": "^2.0.0",
68
- "@nuxt/scripts": "^0.11.0",
69
+ "@nuxt/scripts": "^0.11.0 || ^1.0.0",
69
70
  "@nuxt/ui": "^4.8.1",
70
71
  "@nuxtjs/device": "^4.0.0",
71
72
  "@nuxtjs/seo": "^5.0.0",
@@ -208,6 +209,7 @@
208
209
  "typescript": "^6",
209
210
  "vite-plugin-checker": "^0.14.1",
210
211
  "vitest": "^4.1.8",
212
+ "@vitest/coverage-v8": "^4.1.8",
211
213
  "vue": "^3.5.35",
212
214
  "vue-tsc": "3.3.3",
213
215
  "zod": "^4.4.3",
@@ -251,6 +253,7 @@
251
253
  "fix:app": "f(){ pnpm turbo run lint --filter \"./apps/$1\" -- --fix && prettier --write \"apps/$1\"; }; f",
252
254
  "typecheck": "vue-tsc --noEmit -p tsconfig.typecheck.json",
253
255
  "test": "vitest run",
256
+ "test:coverage": "vitest run --coverage",
254
257
  "test:watch": "vitest",
255
258
  "test:unit": "vitest run --project unit",
256
259
  "test:vitest": "vitest run --project vitest",
@@ -288,6 +291,7 @@
288
291
  "layer:rebuild:layout": "pnpm build:layout && pnpm layer:generate:layout && pnpm layer:layout",
289
292
  "layer:rebuild:motion": "pnpm build:motion && pnpm layer:generate:motion && pnpm layer:motion",
290
293
  "browserlist": "npx update-browserslist-db@latest",
294
+ "kill": "pkill -f 'nuxt|vite|node.*dev' || true",
291
295
  "deploy": "turbo run build --filter playground",
292
296
  "build:netlify": "pnpm -F playground build:netlify",
293
297
  "dev:netlify": "pnpm -F playground dev:netlify"