kmcom-nuxt-layers 2.5.0 → 2.6.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.
- package/layers/core/app/utils/browserInfo.test.ts +81 -0
- package/layers/core/app/utils/featureClasses.test.ts +103 -0
- package/layers/core/app/utils/regex.test.ts +31 -0
- package/layers/feeds/nuxt.config.ts +7 -10
- package/layers/feeds/server/utils/feed-author.test.ts +71 -0
- package/layers/feeds/server/utils/feed-config.test.ts +162 -0
- package/layers/forms/app/utils/contact-schema.test.ts +75 -0
- package/layers/forms/app/utils/fieldProps.test.ts +141 -0
- package/layers/layout/app/utils/gridPlacementStyle.test.ts +310 -0
- package/layers/{metadata-comicvine → metadata/providers/comicvine}/nuxt.config.ts +2 -2
- package/layers/{metadata-comicvine → metadata/providers/comicvine}/server/utils/metadata-comicvine/client.ts +13 -6
- package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +201 -0
- package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.ts +74 -0
- package/layers/{metadata-comicvine → metadata/providers/comicvine}/server/utils/metadata-comicvine/provider.ts +26 -9
- package/layers/{metadata-comicvine → metadata/providers/comicvine}/server/utils/metadata-comicvine/types.ts +1 -0
- package/layers/{metadata-comicvine → metadata/providers/comicvine}/tsconfig.json +1 -1
- package/layers/{metadata-google-books → metadata/providers/google-books}/nuxt.config.ts +2 -2
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +76 -0
- package/layers/{metadata-google-books → metadata/providers/google-books}/server/utils/metadata-google-books/client.ts +20 -2
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +144 -0
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/normalise.ts +48 -0
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/provider.ts +28 -0
- package/layers/{metadata-google-books → metadata/providers/google-books}/server/utils/metadata-google-books/types.ts +9 -1
- package/layers/{metadata-google-books → metadata/providers/google-books}/tsconfig.json +1 -1
- package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/nuxt.config.ts +1 -1
- package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +25 -0
- package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/server/utils/metadata-openlibrary/client.ts +14 -6
- package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +142 -0
- package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/server/utils/metadata-openlibrary/normalise.ts +30 -24
- package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/server/utils/metadata-openlibrary/provider.ts +5 -1
- package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/tsconfig.json +1 -1
- package/layers/metadata/providers/themoviedb/nuxt.config.ts +36 -0
- package/layers/metadata/providers/themoviedb/package.json +12 -0
- package/layers/metadata/providers/themoviedb/server/plugins/tmdb-register.ts +3 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +31 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.ts +60 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +162 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.ts +83 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/provider.ts +37 -0
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/types.ts +81 -0
- package/layers/metadata/providers/themoviedb/tsconfig.json +5 -0
- package/layers/metadata/server/api/metadata/lookup.get.ts +7 -2
- package/layers/metadata/server/api/metadata/search.get.ts +7 -2
- package/layers/metadata/server/api/metadata/status.get.ts +24 -0
- package/layers/metadata/server/api/metadata/sync.post.ts +14 -4
- package/layers/metadata/server/utils/metadata/cache.test.ts +39 -0
- package/layers/metadata/server/utils/metadata/cache.ts +15 -3
- package/layers/metadata/server/utils/metadata/errors.ts +1 -1
- package/layers/metadata/server/utils/metadata/provider-registry.test.ts +54 -0
- package/layers/metadata/server/utils/metadata/search.test.ts +198 -0
- package/layers/metadata/server/utils/metadata/search.ts +9 -4
- package/layers/navigation/app/utils/site.test.ts +41 -0
- package/layers/seo/app/utils/seoConfig.test.ts +58 -0
- package/layers/theme/server/utils/accent-css.test.ts +56 -0
- package/layers/theme/server/utils/fouc-config.test.ts +74 -0
- package/layers/visual/app/utils/colorTokens.test.ts +51 -0
- package/layers/visual/app/utils/gradientStyle.test.ts +76 -0
- package/layers/visual/app/utils/responsiveSizes.test.ts +71 -0
- package/package.json +12 -4
- package/layers/metadata-comicvine/server/utils/metadata-comicvine/normalise.ts +0 -55
- package/layers/metadata-google-books/server/utils/metadata-google-books/normalise.ts +0 -39
- package/layers/metadata-google-books/server/utils/metadata-google-books/provider.ts +0 -21
- /package/layers/{metadata-comicvine → metadata/providers/comicvine}/package.json +0 -0
- /package/layers/{metadata-comicvine → metadata/providers/comicvine}/server/plugins/comicvine-register.ts +0 -0
- /package/layers/{metadata-google-books → metadata/providers/google-books}/package.json +0 -0
- /package/layers/{metadata-google-books → metadata/providers/google-books}/server/plugins/google-books-register.ts +0 -0
- /package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/package.json +0 -0
- /package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/server/plugins/openlibrary-register.ts +0 -0
- /package/layers/{metadata-openlibrary → metadata/providers/openlibrary}/server/utils/metadata-openlibrary/types.ts +0 -0
|
@@ -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.
|
|
4
|
+
"version": "2.6.0",
|
|
5
5
|
"description": "Composable Nuxt 4 layers for building scalable Vue applications",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./layers/core": "./layers/core/nuxt.config.ts",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"./layers/routing": "./layers/routing/nuxt.config.ts",
|
|
30
30
|
"./layers/starter": "./layers/starter/nuxt.config.ts",
|
|
31
31
|
"./layers/metadata": "./layers/metadata/nuxt.config.ts",
|
|
32
|
-
"./layers/metadata
|
|
33
|
-
"./layers/metadata
|
|
34
|
-
"./layers/metadata
|
|
32
|
+
"./layers/metadata/providers/comicvine": "./layers/metadata/providers/comicvine/nuxt.config.ts",
|
|
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",
|
|
35
|
+
"./layers/metadata/providers/themoviedb": "./layers/metadata/providers/themoviedb/nuxt.config.ts"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"layers/*/nuxt.config.ts",
|
|
@@ -42,6 +43,10 @@
|
|
|
42
43
|
"layers/*/app/**",
|
|
43
44
|
"layers/*/public/**",
|
|
44
45
|
"layers/*/server/**",
|
|
46
|
+
"layers/*/providers/*/nuxt.config.ts",
|
|
47
|
+
"layers/*/providers/*/package.json",
|
|
48
|
+
"layers/*/providers/*/tsconfig.json",
|
|
49
|
+
"layers/*/providers/*/server/**",
|
|
45
50
|
"types/**",
|
|
46
51
|
"docs/"
|
|
47
52
|
],
|
|
@@ -204,6 +209,7 @@
|
|
|
204
209
|
"typescript": "^6",
|
|
205
210
|
"vite-plugin-checker": "^0.14.1",
|
|
206
211
|
"vitest": "^4.1.8",
|
|
212
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
207
213
|
"vue": "^3.5.35",
|
|
208
214
|
"vue-tsc": "3.3.3",
|
|
209
215
|
"zod": "^4.4.3",
|
|
@@ -247,6 +253,7 @@
|
|
|
247
253
|
"fix:app": "f(){ pnpm turbo run lint --filter \"./apps/$1\" -- --fix && prettier --write \"apps/$1\"; }; f",
|
|
248
254
|
"typecheck": "vue-tsc --noEmit -p tsconfig.typecheck.json",
|
|
249
255
|
"test": "vitest run",
|
|
256
|
+
"test:coverage": "vitest run --coverage",
|
|
250
257
|
"test:watch": "vitest",
|
|
251
258
|
"test:unit": "vitest run --project unit",
|
|
252
259
|
"test:vitest": "vitest run --project vitest",
|
|
@@ -284,6 +291,7 @@
|
|
|
284
291
|
"layer:rebuild:layout": "pnpm build:layout && pnpm layer:generate:layout && pnpm layer:layout",
|
|
285
292
|
"layer:rebuild:motion": "pnpm build:motion && pnpm layer:generate:motion && pnpm layer:motion",
|
|
286
293
|
"browserlist": "npx update-browserslist-db@latest",
|
|
294
|
+
"kill": "pkill -f 'nuxt|vite|node.*dev' || true",
|
|
287
295
|
"deploy": "turbo run build --filter playground",
|
|
288
296
|
"build:netlify": "pnpm -F playground build:netlify",
|
|
289
297
|
"dev:netlify": "pnpm -F playground dev:netlify"
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { MetadataRecord, MetadataCreator } from '#layers/metadata/shared/types'
|
|
2
|
-
import type { ComicVineIssue, ComicVineVolume } from './types'
|
|
3
|
-
|
|
4
|
-
// fallow-ignore-next-line complexity
|
|
5
|
-
export function normaliseComicVineIssue(issue: ComicVineIssue): MetadataRecord {
|
|
6
|
-
const creators: MetadataCreator[] = (issue.person_credits ?? []).map((p) => ({
|
|
7
|
-
name: p.name,
|
|
8
|
-
role: p.role,
|
|
9
|
-
providerId: String(p.id),
|
|
10
|
-
}))
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
id: `comicvine:issue:${issue.id}`,
|
|
14
|
-
provider: 'comicvine',
|
|
15
|
-
providerId: String(issue.id),
|
|
16
|
-
mediaType: 'comic',
|
|
17
|
-
title: issue.volume?.name ? `${issue.volume.name} #${issue.issue_number}` : (issue.name ?? `Issue #${issue.issue_number}`),
|
|
18
|
-
subtitle: issue.name ?? undefined,
|
|
19
|
-
description: issue.deck ?? issue.description ?? undefined,
|
|
20
|
-
creators: creators.length ? creators : undefined,
|
|
21
|
-
publisher: issue.publisher?.name ?? undefined,
|
|
22
|
-
publishedAt: issue.cover_date ?? issue.store_date ?? undefined,
|
|
23
|
-
coverUrl: issue.image?.medium_url ?? issue.image?.original_url ?? undefined,
|
|
24
|
-
identifiers: { comicVineId: String(issue.id) },
|
|
25
|
-
sourceUrl: issue.site_detail_url,
|
|
26
|
-
raw: issue,
|
|
27
|
-
lastSyncedAt: new Date().toISOString(),
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// fallow-ignore-next-line complexity
|
|
32
|
-
export function normaliseComicVineVolume(volume: ComicVineVolume): MetadataRecord {
|
|
33
|
-
const creators: MetadataCreator[] = (volume.people ?? []).map((p) => ({
|
|
34
|
-
name: p.name,
|
|
35
|
-
role: p.role,
|
|
36
|
-
providerId: String(p.id),
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
id: `comicvine:volume:${volume.id}`,
|
|
41
|
-
provider: 'comicvine',
|
|
42
|
-
providerId: String(volume.id),
|
|
43
|
-
mediaType: 'comic',
|
|
44
|
-
title: volume.name,
|
|
45
|
-
description: volume.deck ?? volume.description ?? undefined,
|
|
46
|
-
creators: creators.length ? creators : undefined,
|
|
47
|
-
publisher: volume.publisher?.name ?? undefined,
|
|
48
|
-
publishedAt: volume.start_year ?? undefined,
|
|
49
|
-
coverUrl: volume.image?.medium_url ?? volume.image?.original_url ?? undefined,
|
|
50
|
-
identifiers: { comicVineId: String(volume.id) },
|
|
51
|
-
sourceUrl: volume.site_detail_url,
|
|
52
|
-
raw: volume,
|
|
53
|
-
lastSyncedAt: new Date().toISOString(),
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { MetadataRecord, MetadataCreator } from '#layers/metadata/shared/types'
|
|
2
|
-
import type { GoogleBooksVolume } from './types'
|
|
3
|
-
|
|
4
|
-
// fallow-ignore-next-line complexity
|
|
5
|
-
export function normaliseGoogleBooksVolume(volume: GoogleBooksVolume): MetadataRecord {
|
|
6
|
-
const { volumeInfo } = volume
|
|
7
|
-
|
|
8
|
-
const creators: MetadataCreator[] = (volumeInfo.authors ?? []).map((name) => ({
|
|
9
|
-
name,
|
|
10
|
-
role: 'author',
|
|
11
|
-
}))
|
|
12
|
-
|
|
13
|
-
const isbn10 = volumeInfo.industryIdentifiers?.find((i) => i.type === 'ISBN_10')?.identifier
|
|
14
|
-
const isbn13 = volumeInfo.industryIdentifiers?.find((i) => i.type === 'ISBN_13')?.identifier
|
|
15
|
-
|
|
16
|
-
const cover = volumeInfo.imageLinks?.thumbnail ?? volumeInfo.imageLinks?.smallThumbnail
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
id: `google-books:volume:${volume.id}`,
|
|
20
|
-
provider: 'google-books',
|
|
21
|
-
providerId: volume.id,
|
|
22
|
-
mediaType: 'book',
|
|
23
|
-
title: volumeInfo.title,
|
|
24
|
-
subtitle: volumeInfo.subtitle,
|
|
25
|
-
description: volumeInfo.description,
|
|
26
|
-
creators: creators.length ? creators : undefined,
|
|
27
|
-
publisher: volumeInfo.publisher,
|
|
28
|
-
publishedAt: volumeInfo.publishedDate,
|
|
29
|
-
coverUrl: cover ? cover.replace('http://', 'https://') : undefined,
|
|
30
|
-
identifiers: {
|
|
31
|
-
isbn10,
|
|
32
|
-
isbn13,
|
|
33
|
-
googleBooksId: volume.id,
|
|
34
|
-
},
|
|
35
|
-
sourceUrl: volumeInfo.canonicalVolumeLink ?? volumeInfo.infoLink,
|
|
36
|
-
raw: volume,
|
|
37
|
-
lastSyncedAt: new Date().toISOString(),
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { MetadataProvider } from '#layers/metadata/shared/types'
|
|
2
|
-
|
|
3
|
-
export const googleBooksProvider: MetadataProvider = {
|
|
4
|
-
id: 'google-books',
|
|
5
|
-
label: 'Google Books',
|
|
6
|
-
mediaTypes: ['book', 'graphic-novel', 'collected-edition'],
|
|
7
|
-
|
|
8
|
-
async search({ query, limit = 10 }) {
|
|
9
|
-
const res = await searchGoogleBooks(query, limit)
|
|
10
|
-
return (res.items ?? []).map(normaliseGoogleBooksVolume)
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
async lookup({ providerId }) {
|
|
14
|
-
const volume = await fetchGoogleBooksVolume(providerId)
|
|
15
|
-
return normaliseGoogleBooksVolume(volume)
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
async sync({ providerId }) {
|
|
19
|
-
return (await googleBooksProvider.lookup!({ provider: 'google-books', providerId }))!
|
|
20
|
-
},
|
|
21
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|