@getvitops/astro 0.1.1 → 0.2.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/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@getvitops/astro",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Astro integration for the Vitops design system — favicons/PWA, web-component scripts, and CSS, via a getvitops() integration + <Head /> component.",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "dist",
8
- "Head.astro",
8
+ "src/components/Head.astro",
9
+ "src/schemas",
9
10
  "virtual.d.ts"
10
11
  ],
11
12
  "type": "module",
@@ -15,7 +16,8 @@
15
16
  "types": "./dist/index.d.mts",
16
17
  "import": "./dist/index.mjs"
17
18
  },
18
- "./Head.astro": "./Head.astro",
19
+ "./Head.astro": "./src/components/Head.astro",
20
+ "./schemas/*": "./src/schemas/*",
19
21
  "./package.json": "./package.json"
20
22
  },
21
23
  "publishConfig": {
@@ -0,0 +1,69 @@
1
+ ---
2
+ export interface Props {
3
+ headline: string;
4
+ description?: string;
5
+ image?: string | string[];
6
+ datePublished: string;
7
+ dateModified?: string;
8
+ author: {
9
+ name: string;
10
+ url?: string;
11
+ } | {
12
+ name: string;
13
+ url?: string;
14
+ }[];
15
+ publisher?: {
16
+ name: string;
17
+ logo?: string;
18
+ };
19
+ mainEntityOfPage?: string;
20
+ articleSection?: string;
21
+ wordCount?: number;
22
+ keywords?: string[];
23
+ }
24
+
25
+ const {
26
+ headline,
27
+ description,
28
+ image,
29
+ datePublished,
30
+ dateModified,
31
+ author,
32
+ publisher,
33
+ mainEntityOfPage,
34
+ articleSection,
35
+ wordCount,
36
+ keywords,
37
+ } = Astro.props;
38
+
39
+ const authors = Array.isArray(author) ? author : [author];
40
+
41
+ const jsonLd = {
42
+ '@context': 'https://schema.org',
43
+ '@type': 'Article',
44
+ headline,
45
+ ...(description && { description }),
46
+ ...(image && { image: Array.isArray(image) ? image : [image] }),
47
+ datePublished,
48
+ ...(dateModified && { dateModified }),
49
+ author: authors.map(a => ({
50
+ '@type': 'Person',
51
+ name: a.name,
52
+ ...(a.url && { url: a.url }),
53
+ })),
54
+ ...(publisher && {
55
+ publisher: {
56
+ '@type': 'Organization',
57
+ name: publisher.name,
58
+ ...(publisher.logo && { logo: { '@type': 'ImageObject', url: publisher.logo } }),
59
+ },
60
+ }),
61
+ ...(mainEntityOfPage && { mainEntityOfPage: { '@type': 'WebPage', '@id': mainEntityOfPage } }),
62
+ ...(articleSection && { articleSection }),
63
+ ...(wordCount && { wordCount }),
64
+ ...(keywords && { keywords: keywords.join(', ') }),
65
+ };
66
+ ---
67
+
68
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
69
+ <slot />
@@ -0,0 +1,81 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ author: string | string[];
5
+ url: string;
6
+ workExample?: {
7
+ isbn?: string;
8
+ bookEdition?: string;
9
+ bookFormat?: 'AudiobookFormat' | 'EBook' | 'GraphicNovel' | 'Hardcover' | 'Paperback';
10
+ potentialAction?: {
11
+ type: 'ReadAction' | 'BorrowAction' | 'BuyAction';
12
+ target: string;
13
+ expectsAcceptanceOf?: {
14
+ availabilityStarts?: string;
15
+ availabilityEnds?: string;
16
+ category?: 'free' | 'subscription' | 'purchase' | 'rental';
17
+ };
18
+ };
19
+ }[];
20
+ sameAs?: string[];
21
+ image?: string;
22
+ description?: string;
23
+ }
24
+
25
+ const {
26
+ name,
27
+ author,
28
+ url,
29
+ workExample,
30
+ sameAs,
31
+ image,
32
+ description,
33
+ } = Astro.props;
34
+
35
+ const authors = Array.isArray(author) ? author : [author];
36
+
37
+ const jsonLd = {
38
+ '@context': 'https://schema.org',
39
+ '@type': 'Book',
40
+ name,
41
+ author: authors.map(a => ({
42
+ '@type': 'Person',
43
+ name: a,
44
+ })),
45
+ url,
46
+ ...(sameAs && { sameAs }),
47
+ ...(image && { image }),
48
+ ...(description && { description }),
49
+ ...(workExample && {
50
+ workExample: workExample.map(we => ({
51
+ '@type': 'Book',
52
+ ...(we.isbn && { isbn: we.isbn }),
53
+ ...(we.bookEdition && { bookEdition: we.bookEdition }),
54
+ ...(we.bookFormat && { bookFormat: `https://schema.org/${we.bookFormat}` }),
55
+ ...(we.potentialAction && {
56
+ potentialAction: {
57
+ '@type': we.potentialAction.type,
58
+ target: {
59
+ '@type': 'EntryPoint',
60
+ urlTemplate: we.potentialAction.target,
61
+ actionPlatform: [
62
+ 'https://schema.org/DesktopWebPlatform',
63
+ 'https://schema.org/AndroidPlatform',
64
+ 'https://schema.org/IOSPlatform',
65
+ ],
66
+ },
67
+ ...(we.potentialAction.expectsAcceptanceOf && {
68
+ expectsAcceptanceOf: {
69
+ '@type': 'Offer',
70
+ ...we.potentialAction.expectsAcceptanceOf,
71
+ },
72
+ }),
73
+ },
74
+ }),
75
+ })),
76
+ }),
77
+ };
78
+ ---
79
+
80
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
81
+ <slot />
@@ -0,0 +1,26 @@
1
+ ---
2
+ export interface BreadcrumbItem {
3
+ name: string;
4
+ url: string;
5
+ }
6
+
7
+ export interface Props {
8
+ items: BreadcrumbItem[];
9
+ }
10
+
11
+ const { items } = Astro.props;
12
+
13
+ const jsonLd = {
14
+ '@context': 'https://schema.org',
15
+ '@type': 'BreadcrumbList',
16
+ itemListElement: items.map((item, index) => ({
17
+ '@type': 'ListItem',
18
+ position: index + 1,
19
+ name: item.name,
20
+ item: item.url,
21
+ })),
22
+ };
23
+ ---
24
+
25
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
26
+ <slot />
@@ -0,0 +1,34 @@
1
+ ---
2
+ export interface CarouselItem {
3
+ type: 'Article' | 'Recipe' | 'Movie' | 'Course' | 'Restaurant' | 'Product';
4
+ name: string;
5
+ url: string;
6
+ image?: string;
7
+ description?: string;
8
+ }
9
+
10
+ export interface Props {
11
+ items: CarouselItem[];
12
+ }
13
+
14
+ const { items } = Astro.props;
15
+
16
+ const jsonLd = {
17
+ '@context': 'https://schema.org',
18
+ '@type': 'ItemList',
19
+ itemListElement: items.map((item, index) => ({
20
+ '@type': 'ListItem',
21
+ position: index + 1,
22
+ item: {
23
+ '@type': item.type,
24
+ name: item.name,
25
+ url: item.url,
26
+ ...(item.image && { image: item.image }),
27
+ ...(item.description && { description: item.description }),
28
+ },
29
+ })),
30
+ };
31
+ ---
32
+
33
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
34
+ <slot />
@@ -0,0 +1,122 @@
1
+ ---
2
+ export interface CourseInstance {
3
+ courseMode: 'online' | 'onsite' | 'blended';
4
+ startDate?: string;
5
+ endDate?: string;
6
+ location?: {
7
+ name: string;
8
+ address?: string;
9
+ };
10
+ instructor?: {
11
+ name: string;
12
+ url?: string;
13
+ };
14
+ courseSchedule?: {
15
+ repeatFrequency: string;
16
+ repeatCount?: number;
17
+ };
18
+ }
19
+
20
+ export interface Course {
21
+ name: string;
22
+ description: string;
23
+ provider: {
24
+ name: string;
25
+ url?: string;
26
+ };
27
+ url?: string;
28
+ image?: string;
29
+ offers?: {
30
+ price: number | string;
31
+ priceCurrency: string;
32
+ availability?: 'InStock' | 'SoldOut' | 'PreOrder';
33
+ validFrom?: string;
34
+ };
35
+ hasCourseInstance?: CourseInstance[];
36
+ educationalLevel?: string;
37
+ about?: string[];
38
+ teaches?: string[];
39
+ financialAidEligible?: boolean;
40
+ inLanguage?: string;
41
+ duration?: string;
42
+ }
43
+
44
+ export interface Props {
45
+ courses: Course[];
46
+ }
47
+
48
+ const { courses } = Astro.props;
49
+
50
+ const jsonLd = {
51
+ '@context': 'https://schema.org',
52
+ '@type': 'ItemList',
53
+ itemListElement: courses.map((course, index) => ({
54
+ '@type': 'ListItem',
55
+ position: index + 1,
56
+ item: {
57
+ '@type': 'Course',
58
+ name: course.name,
59
+ description: course.description,
60
+ provider: {
61
+ '@type': 'Organization',
62
+ name: course.provider.name,
63
+ ...(course.provider.url && { sameAs: course.provider.url }),
64
+ },
65
+ ...(course.url && { url: course.url }),
66
+ ...(course.image && { image: course.image }),
67
+ ...(course.offers && {
68
+ offers: {
69
+ '@type': 'Offer',
70
+ price: course.offers.price,
71
+ priceCurrency: course.offers.priceCurrency,
72
+ ...(course.offers.availability && { availability: `https://schema.org/${course.offers.availability}` }),
73
+ ...(course.offers.validFrom && { validFrom: course.offers.validFrom }),
74
+ },
75
+ }),
76
+ ...(course.hasCourseInstance && {
77
+ hasCourseInstance: course.hasCourseInstance.map(instance => ({
78
+ '@type': 'CourseInstance',
79
+ courseMode: instance.courseMode,
80
+ ...(instance.startDate && { startDate: instance.startDate }),
81
+ ...(instance.endDate && { endDate: instance.endDate }),
82
+ ...(instance.location && {
83
+ location: {
84
+ '@type': 'Place',
85
+ name: instance.location.name,
86
+ ...(instance.location.address && {
87
+ address: {
88
+ '@type': 'PostalAddress',
89
+ streetAddress: instance.location.address,
90
+ },
91
+ }),
92
+ },
93
+ }),
94
+ ...(instance.instructor && {
95
+ instructor: {
96
+ '@type': 'Person',
97
+ name: instance.instructor.name,
98
+ ...(instance.instructor.url && { url: instance.instructor.url }),
99
+ },
100
+ }),
101
+ ...(instance.courseSchedule && {
102
+ courseSchedule: {
103
+ '@type': 'Schedule',
104
+ repeatFrequency: instance.courseSchedule.repeatFrequency,
105
+ ...(instance.courseSchedule.repeatCount && { repeatCount: instance.courseSchedule.repeatCount }),
106
+ },
107
+ }),
108
+ })),
109
+ }),
110
+ ...(course.educationalLevel && { educationalLevel: course.educationalLevel }),
111
+ ...(course.about && { about: course.about }),
112
+ ...(course.teaches && { teaches: course.teaches }),
113
+ ...(course.financialAidEligible && { financialAidEligible: 'true' }),
114
+ ...(course.inLanguage && { inLanguage: course.inLanguage }),
115
+ ...(course.duration && { timeRequired: course.duration }),
116
+ },
117
+ })),
118
+ };
119
+ ---
120
+
121
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
122
+ <slot />
@@ -0,0 +1,127 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ description: string;
5
+ url?: string;
6
+ sameAs?: string;
7
+ identifier?: string | string[];
8
+ keywords?: string[];
9
+ license?: string;
10
+ isAccessibleForFree?: boolean;
11
+ creator?: {
12
+ type: 'Person' | 'Organization';
13
+ name: string;
14
+ url?: string;
15
+ };
16
+ funder?: {
17
+ type: 'Person' | 'Organization';
18
+ name: string;
19
+ };
20
+ includedInDataCatalog?: {
21
+ name: string;
22
+ url?: string;
23
+ };
24
+ distribution?: {
25
+ contentUrl: string;
26
+ encodingFormat: string;
27
+ contentSize?: string;
28
+ }[];
29
+ temporalCoverage?: string;
30
+ spatialCoverage?: {
31
+ type: 'Place';
32
+ geo?: {
33
+ latitude: number;
34
+ longitude: number;
35
+ box?: string;
36
+ };
37
+ };
38
+ variableMeasured?: string | string[];
39
+ measurementTechnique?: string;
40
+ datePublished?: string;
41
+ dateModified?: string;
42
+ }
43
+
44
+ const {
45
+ name,
46
+ description,
47
+ url,
48
+ sameAs,
49
+ identifier,
50
+ keywords,
51
+ license,
52
+ isAccessibleForFree,
53
+ creator,
54
+ funder,
55
+ includedInDataCatalog,
56
+ distribution,
57
+ temporalCoverage,
58
+ spatialCoverage,
59
+ variableMeasured,
60
+ measurementTechnique,
61
+ datePublished,
62
+ dateModified,
63
+ } = Astro.props;
64
+
65
+ const jsonLd = {
66
+ '@context': 'https://schema.org',
67
+ '@type': 'Dataset',
68
+ name,
69
+ description,
70
+ ...(url && { url }),
71
+ ...(sameAs && { sameAs }),
72
+ ...(identifier && { identifier }),
73
+ ...(keywords && { keywords }),
74
+ ...(license && { license }),
75
+ ...(isAccessibleForFree !== undefined && { isAccessibleForFree }),
76
+ ...(creator && {
77
+ creator: {
78
+ '@type': creator.type,
79
+ name: creator.name,
80
+ ...(creator.url && { url: creator.url }),
81
+ },
82
+ }),
83
+ ...(funder && {
84
+ funder: {
85
+ '@type': funder.type,
86
+ name: funder.name,
87
+ },
88
+ }),
89
+ ...(includedInDataCatalog && {
90
+ includedInDataCatalog: {
91
+ '@type': 'DataCatalog',
92
+ name: includedInDataCatalog.name,
93
+ ...(includedInDataCatalog.url && { url: includedInDataCatalog.url }),
94
+ },
95
+ }),
96
+ ...(distribution && {
97
+ distribution: distribution.map(d => ({
98
+ '@type': 'DataDownload',
99
+ contentUrl: d.contentUrl,
100
+ encodingFormat: d.encodingFormat,
101
+ ...(d.contentSize && { contentSize: d.contentSize }),
102
+ })),
103
+ }),
104
+ ...(temporalCoverage && { temporalCoverage }),
105
+ ...(spatialCoverage && {
106
+ spatialCoverage: {
107
+ '@type': 'Place',
108
+ ...(spatialCoverage.geo && {
109
+ geo: spatialCoverage.geo.box
110
+ ? { '@type': 'GeoShape', box: spatialCoverage.geo.box }
111
+ : {
112
+ '@type': 'GeoCoordinates',
113
+ latitude: spatialCoverage.geo.latitude,
114
+ longitude: spatialCoverage.geo.longitude,
115
+ },
116
+ }),
117
+ },
118
+ }),
119
+ ...(variableMeasured && { variableMeasured }),
120
+ ...(measurementTechnique && { measurementTechnique }),
121
+ ...(datePublished && { datePublished }),
122
+ ...(dateModified && { dateModified }),
123
+ };
124
+ ---
125
+
126
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
127
+ <slot />
@@ -0,0 +1,82 @@
1
+ ---
2
+ export interface ForumPost {
3
+ headline: string;
4
+ text: string;
5
+ url: string;
6
+ datePublished: string;
7
+ author: {
8
+ name: string;
9
+ url?: string;
10
+ };
11
+ interactionStatistic?: {
12
+ type: 'ReplyAction' | 'LikeAction' | 'ViewAction';
13
+ count: number;
14
+ }[];
15
+ comment?: {
16
+ text: string;
17
+ datePublished: string;
18
+ author: {
19
+ name: string;
20
+ url?: string;
21
+ };
22
+ }[];
23
+ }
24
+
25
+ export interface Props {
26
+ name: string;
27
+ url: string;
28
+ description?: string;
29
+ mainEntity?: ForumPost[];
30
+ }
31
+
32
+ const { name, url, description, mainEntity } = Astro.props;
33
+
34
+ const jsonLd = {
35
+ '@context': 'https://schema.org',
36
+ '@type': 'DiscussionForumPosting',
37
+ ...(mainEntity && mainEntity.length > 0
38
+ ? {
39
+ mainEntity: mainEntity.map(post => ({
40
+ '@type': 'DiscussionForumPosting',
41
+ headline: post.headline,
42
+ text: post.text,
43
+ url: post.url,
44
+ datePublished: post.datePublished,
45
+ author: {
46
+ '@type': 'Person',
47
+ name: post.author.name,
48
+ ...(post.author.url && { url: post.author.url }),
49
+ },
50
+ ...(post.interactionStatistic && {
51
+ interactionStatistic: post.interactionStatistic.map(stat => ({
52
+ '@type': 'InteractionCounter',
53
+ interactionType: { '@type': stat.type },
54
+ userInteractionCount: stat.count,
55
+ })),
56
+ }),
57
+ ...(post.comment && {
58
+ comment: post.comment.map(c => ({
59
+ '@type': 'Comment',
60
+ text: c.text,
61
+ datePublished: c.datePublished,
62
+ author: {
63
+ '@type': 'Person',
64
+ name: c.author.name,
65
+ ...(c.author.url && { url: c.author.url }),
66
+ },
67
+ })),
68
+ }),
69
+ })),
70
+ }
71
+ : {}),
72
+ isPartOf: {
73
+ '@type': 'WebPage',
74
+ '@id': url,
75
+ name,
76
+ ...(description && { description }),
77
+ },
78
+ };
79
+ ---
80
+
81
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
82
+ <slot />
@@ -0,0 +1,63 @@
1
+ ---
2
+ export interface Props {
3
+ mainEntity: {
4
+ question: string;
5
+ answer: {
6
+ text: string;
7
+ author?: {
8
+ name: string;
9
+ type?: 'Person' | 'Organization';
10
+ };
11
+ dateCreated?: string;
12
+ upvoteCount?: number;
13
+ url?: string;
14
+ };
15
+ eduQuestionType?: 'Practice' | 'Test' | 'Homework' | 'Quiz' | 'Exam';
16
+ learningResourceType?: string;
17
+ assesses?: string;
18
+ educationalLevel?: string;
19
+ }[];
20
+ about?: {
21
+ name: string;
22
+ url?: string;
23
+ };
24
+ }
25
+
26
+ const { mainEntity, about } = Astro.props;
27
+
28
+ const jsonLd = {
29
+ '@context': 'https://schema.org',
30
+ '@type': 'Quiz',
31
+ ...(about && {
32
+ about: {
33
+ '@type': 'Thing',
34
+ name: about.name,
35
+ ...(about.url && { url: about.url }),
36
+ },
37
+ }),
38
+ hasPart: mainEntity.map(qa => ({
39
+ '@type': 'Question',
40
+ eduQuestionType: qa.eduQuestionType || 'Practice',
41
+ ...(qa.learningResourceType && { learningResourceType: qa.learningResourceType }),
42
+ ...(qa.assesses && { assesses: qa.assesses }),
43
+ ...(qa.educationalLevel && { educationalLevel: qa.educationalLevel }),
44
+ text: qa.question,
45
+ acceptedAnswer: {
46
+ '@type': 'Answer',
47
+ text: qa.answer.text,
48
+ ...(qa.answer.author && {
49
+ author: {
50
+ '@type': qa.answer.author.type || 'Person',
51
+ name: qa.answer.author.name,
52
+ },
53
+ }),
54
+ ...(qa.answer.dateCreated && { dateCreated: qa.answer.dateCreated }),
55
+ ...(qa.answer.upvoteCount !== undefined && { upvoteCount: qa.answer.upvoteCount }),
56
+ ...(qa.answer.url && { url: qa.answer.url }),
57
+ },
58
+ })),
59
+ };
60
+ ---
61
+
62
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
63
+ <slot />
@@ -0,0 +1,54 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ url?: string;
5
+ logo?: string;
6
+ sameAs?: string[];
7
+ aggregateRating: {
8
+ ratingValue: number;
9
+ bestRating?: number;
10
+ worstRating?: number;
11
+ ratingCount?: number;
12
+ reviewCount?: number;
13
+ };
14
+ address?: {
15
+ streetAddress?: string;
16
+ addressLocality?: string;
17
+ addressRegion?: string;
18
+ postalCode?: string;
19
+ addressCountry?: string;
20
+ };
21
+ }
22
+
23
+ const { name, url, logo, sameAs, aggregateRating, address } = Astro.props;
24
+
25
+ const jsonLd = {
26
+ '@context': 'https://schema.org',
27
+ '@type': 'EmployerAggregateRating',
28
+ itemReviewed: {
29
+ '@type': 'Organization',
30
+ name,
31
+ ...(url && { url }),
32
+ ...(logo && { logo }),
33
+ ...(sameAs && { sameAs }),
34
+ ...(address && {
35
+ address: {
36
+ '@type': 'PostalAddress',
37
+ ...(address.streetAddress && { streetAddress: address.streetAddress }),
38
+ ...(address.addressLocality && { addressLocality: address.addressLocality }),
39
+ ...(address.addressRegion && { addressRegion: address.addressRegion }),
40
+ ...(address.postalCode && { postalCode: address.postalCode }),
41
+ ...(address.addressCountry && { addressCountry: address.addressCountry }),
42
+ },
43
+ }),
44
+ },
45
+ ratingValue: aggregateRating.ratingValue,
46
+ bestRating: aggregateRating.bestRating ?? 5,
47
+ worstRating: aggregateRating.worstRating ?? 1,
48
+ ...(aggregateRating.ratingCount && { ratingCount: aggregateRating.ratingCount }),
49
+ ...(aggregateRating.reviewCount && { reviewCount: aggregateRating.reviewCount }),
50
+ };
51
+ ---
52
+
53
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
54
+ <slot />