@studyportals/fawkes 7.3.3 → 7.3.5
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/README.md +176 -176
- package/dist/src/programmes/policies/CountryAttendanceDegree.js +1 -1
- package/dist/src/programmes/policies/CountryDurationDegree.js +1 -1
- package/dist/src/programmes/policies/CountryEducationalForm.js +1 -1
- package/dist/src/programmes/policies/DegreeCountryDiscipline.js +1 -1
- package/dist/src/programmes/policies/DisciplineCountryEducationalForm.js +1 -1
- package/dist/src/programmes/policies/DisciplineEducationalForm.js +1 -1
- package/dist/src/structured-data/ProgrammeStructuredDataFactory.d.ts +18 -0
- package/dist/src/structured-data/ProgrammeStructuredDataFactory.js +88 -0
- package/dist/src/structured-data/SearchStructuredDataFactory.d.ts +35 -0
- package/dist/src/structured-data/SearchStructuredDataFactory.js +154 -0
- package/dist/src/structured-data/dto/BreadcrumbDTO.d.ts +5 -0
- package/dist/src/structured-data/dto/EntityDTO.d.ts +7 -0
- package/dist/src/structured-data/dto/FAQItemDto.d.ts +4 -0
- package/dist/src/structured-data/dto/OfferDTO.d.ts +5 -0
- package/dist/src/structured-data/dto/ReviewRatingDTO.d.ts +4 -0
- package/dist/src/structured-data/dto/index.d.ts +5 -0
- package/dist/src/structured-data/dto/index.js +5 -0
- package/dist/src/structured-data/index.d.ts +2 -0
- package/dist/src/structured-data/index.js +2 -0
- package/dist/src/structured-data/interface/StructuredDataCurrencyConversionService.d.ts +3 -0
- package/dist/src/structured-data/interface/StructuredDataCurrencyConversionService.js +1 -0
- package/dist/structured-data/index.d.ts +1 -0
- package/dist/structured-data/index.js +1 -0
- package/dist/structured-data-seo/index.d.ts +1 -0
- package/dist/structured-data-seo/index.js +1 -0
- package/dist/structured-markup-seo/index.d.ts +1 -0
- package/dist/structured-markup-seo/index.js +1 -0
- package/package.json +105 -95
- package/dist/src/common/IPolicyMetaData.d.ts +0 -6
- package/dist/src/organisations/OrganisationsRuleBasedIndexabilityPolicy.d.ts +0 -11
- package/dist/src/organisations/OrganisationsRuleBasedIndexabilityPolicy.js +0 -19
- package/dist/src/organisations/rules/OnlineAttendanceRule.d.ts +0 -6
- package/dist/src/organisations/rules/OnlineAttendanceRule.js +0 -14
- package/dist/src/programmes/IProgrammeSearchApplicationState.d.ts +0 -4
- package/dist/src/programmes/IProgrammeSearchDependencies.d.ts +0 -7
- package/dist/src/programmes/IProgrammesSeoDependencies.d.ts +0 -2
- package/dist/src/programmes/policies/DisciplineCountryDegree.d.ts +0 -16
- package/dist/src/programmes/policies/DisciplineCountryDegree.js +0 -53
- package/dist/src/programmes/rules/DegreeAttendanceSpecificRule.d.ts +0 -10
- package/dist/src/programmes/rules/DegreeAttendanceSpecificRule.js +0 -42
- package/dist/src/programmes/rules/DegreeCountrySpecificRule.d.ts +0 -10
- package/dist/src/programmes/rules/DegreeCountrySpecificRule.js +0 -43
- package/dist/src/programmes/rules/DegreeCountryTuitionFeeRule.d.ts +0 -11
- package/dist/src/programmes/rules/DegreeCountryTuitionFeeRule.js +0 -40
- package/dist/src/programmes/rules/MBACountryAttendanceRule.d.ts +0 -11
- package/dist/src/programmes/rules/MBACountryAttendanceRule.js +0 -40
- package/dist/src/programmes/rules/MasterOfArtsDisciplineRule.d.ts +0 -10
- package/dist/src/programmes/rules/MasterOfArtsDisciplineRule.js +0 -41
- package/dist/src/programmes/rules/MasterOfEducationCountryDisciplineRule.d.ts +0 -11
- package/dist/src/programmes/rules/MasterOfEducationCountryDisciplineRule.js +0 -69
- package/dist/src/programmes/rules/MasterOfLawsCountryAttendanceRule.d.ts +0 -12
- package/dist/src/programmes/rules/MasterOfLawsCountryAttendanceRule.js +0 -43
- package/dist/src/programmes/rules/MasterOfLawsCountryTuitionFeeRule.d.ts +0 -12
- package/dist/src/programmes/rules/MasterOfLawsCountryTuitionFeeRule.js +0 -46
- package/dist/src/programmes/rules/MasterOfPhilosophyCountryAttendanceRule.d.ts +0 -12
- package/dist/src/programmes/rules/MasterOfPhilosophyCountryAttendanceRule.js +0 -43
- package/dist/src/programmes/rules/MasterOfScienceDisciplineRule.d.ts +0 -10
- package/dist/src/programmes/rules/MasterOfScienceDisciplineRule.js +0 -41
- package/dist/src/programmes/rules/OnlineAttendanceRule.d.ts +0 -5
- package/dist/src/programmes/rules/OnlineAttendanceRule.js +0 -11
- package/dist/src/sitemap-generator/IPageNumberProvider.d.ts +0 -3
- /package/dist/src/{common/IPolicyMetaData.js → structured-data/dto/BreadcrumbDTO.js} +0 -0
- /package/dist/src/{programmes/IProgrammeSearchApplicationState.js → structured-data/dto/EntityDTO.js} +0 -0
- /package/dist/src/{programmes/IProgrammeSearchDependencies.js → structured-data/dto/FAQItemDto.js} +0 -0
- /package/dist/src/{programmes/IProgrammesSeoDependencies.js → structured-data/dto/OfferDTO.js} +0 -0
- /package/dist/src/{sitemap-generator/IPageNumberProvider.js → structured-data/dto/ReviewRatingDTO.js} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export class SearchStructuredDataFactory {
|
|
2
|
+
async buildStructuredData(title, description, cards, faqItems = [], breadcrumbs = []) {
|
|
3
|
+
const entities = [];
|
|
4
|
+
const ratings = [];
|
|
5
|
+
const offersData = [];
|
|
6
|
+
for (const card of cards) {
|
|
7
|
+
const reviewRating = this.getRating(card);
|
|
8
|
+
const offer = await this.getOfferData(card);
|
|
9
|
+
if (reviewRating)
|
|
10
|
+
ratings.push(reviewRating);
|
|
11
|
+
if (offer)
|
|
12
|
+
offersData.push(offer);
|
|
13
|
+
entities.push({ card, offer, reviewRating });
|
|
14
|
+
}
|
|
15
|
+
const data = {
|
|
16
|
+
"@context": "https://schema.org",
|
|
17
|
+
"@type": "SearchResultsPage",
|
|
18
|
+
"name": title,
|
|
19
|
+
description,
|
|
20
|
+
"mainEntity": entities.map(entity => this.buildStructuredDataForCard(entity))
|
|
21
|
+
};
|
|
22
|
+
data.aggregateRating = this.constructOptionalPageAggregateRating(ratings);
|
|
23
|
+
data.offers = this.constructOptionalPageOffers(offersData);
|
|
24
|
+
data.hasPart = this.constructOptionalFaqPage(faqItems);
|
|
25
|
+
data.breadcrumb = this.constructOptionalBreadcrumbs(breadcrumbs);
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the rating for a card.
|
|
30
|
+
* This method should be overridden by subclasses to provide the specific rating logic.
|
|
31
|
+
* If no rating is available, return undefined and no aggregate rating will be constructed for the card & page.
|
|
32
|
+
*
|
|
33
|
+
* @param card
|
|
34
|
+
* @protected
|
|
35
|
+
*/
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
37
|
+
getRating(card) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the offer data for a card.
|
|
42
|
+
* This method should be overridden by subclasses to provide the specific offer logic.
|
|
43
|
+
* If no offer is available, return undefined and no aggregate offer will be constructed for the card & page.
|
|
44
|
+
*
|
|
45
|
+
* @param card
|
|
46
|
+
* @protected
|
|
47
|
+
*/
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
49
|
+
async getOfferData(card) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
constructOptionalPageAggregateRating(ratings) {
|
|
53
|
+
if (ratings.length <= 0)
|
|
54
|
+
return;
|
|
55
|
+
let totalQuantity = 0;
|
|
56
|
+
let totalRatingValue = 0;
|
|
57
|
+
for (const rating of ratings) {
|
|
58
|
+
totalQuantity += rating.quantity;
|
|
59
|
+
totalRatingValue += rating.rating;
|
|
60
|
+
}
|
|
61
|
+
const totalAverageRating = Math.round((totalRatingValue / ratings.length) * 10) / 10;
|
|
62
|
+
return this.constructAggregateRating({ rating: totalAverageRating, quantity: totalQuantity });
|
|
63
|
+
}
|
|
64
|
+
constructOptionalPageOffers(offersData) {
|
|
65
|
+
if (offersData.length <= 0)
|
|
66
|
+
return;
|
|
67
|
+
let minPrice = -1;
|
|
68
|
+
let maxPrice = 0;
|
|
69
|
+
let totalPrice = 0;
|
|
70
|
+
for (const offer of offersData) {
|
|
71
|
+
if (offer.price < minPrice || minPrice === -1) {
|
|
72
|
+
minPrice = offer.price;
|
|
73
|
+
}
|
|
74
|
+
if (offer.price > maxPrice) {
|
|
75
|
+
maxPrice = offer.price;
|
|
76
|
+
}
|
|
77
|
+
totalPrice += offer.price;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
"@type": "AggregateOffer",
|
|
81
|
+
"lowPrice": Math.max(minPrice, 0),
|
|
82
|
+
"highPrice": maxPrice,
|
|
83
|
+
"priceCurrency": "USD",
|
|
84
|
+
"offerCount": offersData.length,
|
|
85
|
+
"priceSpecification": {
|
|
86
|
+
"@type": "PriceSpecification",
|
|
87
|
+
"price": Math.round(totalPrice / offersData.length),
|
|
88
|
+
"priceCurrency": "USD"
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
constructOptionalFaqPage(faqItems) {
|
|
93
|
+
if (faqItems.length <= 0)
|
|
94
|
+
return;
|
|
95
|
+
return {
|
|
96
|
+
"@type": "FAQPage",
|
|
97
|
+
"mainEntity": faqItems.map(item => ({
|
|
98
|
+
"@type": "Question",
|
|
99
|
+
"name": item.question,
|
|
100
|
+
"acceptedAnswer": {
|
|
101
|
+
"@type": "Answer",
|
|
102
|
+
"text": this.stripHtmlWithListRetain(item.answer)
|
|
103
|
+
}
|
|
104
|
+
}))
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
constructOptionalBreadcrumbs(breadcrumbs) {
|
|
108
|
+
if (breadcrumbs.length <= 0)
|
|
109
|
+
return;
|
|
110
|
+
return {
|
|
111
|
+
"@type": "BreadcrumbList",
|
|
112
|
+
"itemListElement": breadcrumbs.map((breadcrumb) => ({
|
|
113
|
+
"@type": "ListItem",
|
|
114
|
+
"@id": `${breadcrumb.url}#listitem#thing`,
|
|
115
|
+
"name": breadcrumb.name,
|
|
116
|
+
"position": breadcrumb.position,
|
|
117
|
+
"item": breadcrumb.url
|
|
118
|
+
}))
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
constructAggregateRating(reviewRating) {
|
|
122
|
+
if (!reviewRating)
|
|
123
|
+
return;
|
|
124
|
+
return {
|
|
125
|
+
"@type": "AggregateRating",
|
|
126
|
+
"ratingValue": reviewRating.rating,
|
|
127
|
+
"reviewCount": reviewRating.quantity,
|
|
128
|
+
"bestRating": 5,
|
|
129
|
+
"worstRating": 1
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
constructOffer(offerData) {
|
|
133
|
+
if (!offerData)
|
|
134
|
+
return;
|
|
135
|
+
return {
|
|
136
|
+
"@type": "Offer",
|
|
137
|
+
"price": offerData.price,
|
|
138
|
+
"priceCurrency": "USD",
|
|
139
|
+
"availability": "https://schema.org/InStock",
|
|
140
|
+
"validFrom": offerData.validFrom ? offerData.validFrom.toISOString().split('T')[0] : undefined,
|
|
141
|
+
"category": "tuition"
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
stripHtmlWithListRetain(html) {
|
|
145
|
+
const tempDiv = document.createElement('div');
|
|
146
|
+
tempDiv.innerHTML = html;
|
|
147
|
+
// Replace all <li> elements with bullet points manually
|
|
148
|
+
tempDiv.querySelectorAll('li').forEach(li => {
|
|
149
|
+
li.textContent = '• ' + li.textContent;
|
|
150
|
+
});
|
|
151
|
+
// Get the text content, which includes newlines for block elements
|
|
152
|
+
return tempDiv.textContent?.trim() ?? '';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../src/structured-data';
|
package/package.json
CHANGED
|
@@ -1,95 +1,105 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@studyportals/fawkes",
|
|
3
|
-
"version": "7.3.
|
|
4
|
-
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
|
-
"files": [
|
|
6
|
-
"./dist"
|
|
7
|
-
],
|
|
8
|
-
"scripts": {
|
|
9
|
-
"prepush": "npm run test",
|
|
10
|
-
"precommit": "npm run lint",
|
|
11
|
-
"compile": "npx tsc && tsc-alias && rm -r ./dist/tests",
|
|
12
|
-
"build": "npm run clean && npm run compile",
|
|
13
|
-
"clean": "rimraf \"!(node_modules)/**/dist\"",
|
|
14
|
-
"prepare-deployment": "npm run test && npm run build",
|
|
15
|
-
"publish-major": "npm run prepare-deployment && npm version major && npm publish",
|
|
16
|
-
"publish-beta": "npm run prepare-deployment && npm version prerelease && npm publish --tag beta --access=public",
|
|
17
|
-
"publish-patch": "npm run prepare-deployment && npm version patch && npm publish",
|
|
18
|
-
"publish-minor": "npm run prepare-deployment && npm version minor && npm publish",
|
|
19
|
-
"prepare": "husky install",
|
|
20
|
-
"test": "vitest run --coverage",
|
|
21
|
-
"test:dev": "vitest --coverage tests/programmes",
|
|
22
|
-
"lint": "eslint . --ext .ts",
|
|
23
|
-
"lint:fix": "eslint . --ext .ts --fix",
|
|
24
|
-
"prettier:fix": "npx prettier --use-tabs --ignore-path .gitignore --write ."
|
|
25
|
-
},
|
|
26
|
-
"exports": {
|
|
27
|
-
"./organisations-search-seo": {
|
|
28
|
-
"import": "./dist/organisations-seo/index.js",
|
|
29
|
-
"require": "./dist/organisations-seo/index.js",
|
|
30
|
-
"types": "./dist/organisations-seo/index.d.ts"
|
|
31
|
-
},
|
|
32
|
-
"./programmes-search-seo": {
|
|
33
|
-
"import": "./dist/programmes-seo/index.js",
|
|
34
|
-
"require": "./dist/programmes-seo/index.js",
|
|
35
|
-
"types": "./dist/programmes-seo/index.d.ts"
|
|
36
|
-
},
|
|
37
|
-
"./scholarships-search-seo": {
|
|
38
|
-
"import": "./dist/scholarships-seo/index.js",
|
|
39
|
-
"require": "./dist/scholarships-seo/index.js",
|
|
40
|
-
"types": "./dist/scholarships-seo/index.d.ts"
|
|
41
|
-
},
|
|
42
|
-
"./sitemap-generator-seo": {
|
|
43
|
-
"import": "./dist/sitemap-generator-seo/index.js",
|
|
44
|
-
"require": "./dist/sitemap-generator-seo/index.js",
|
|
45
|
-
"types": "./dist/sitemap-generator-seo/index.d.ts"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@studyportals/fawkes",
|
|
3
|
+
"version": "7.3.5",
|
|
4
|
+
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
|
+
"files": [
|
|
6
|
+
"./dist"
|
|
7
|
+
],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"prepush": "npm run test",
|
|
10
|
+
"precommit": "npm run lint",
|
|
11
|
+
"compile": "npx tsc && tsc-alias && rm -r ./dist/tests",
|
|
12
|
+
"build": "npm run clean && npm run compile",
|
|
13
|
+
"clean": "rimraf \"!(node_modules)/**/dist\"",
|
|
14
|
+
"prepare-deployment": "npm run test && npm run build",
|
|
15
|
+
"publish-major": "npm run prepare-deployment && npm version major && npm publish",
|
|
16
|
+
"publish-beta": "npm run prepare-deployment && npm version prerelease && npm publish --tag beta --access=public",
|
|
17
|
+
"publish-patch": "npm run prepare-deployment && npm version patch && npm publish",
|
|
18
|
+
"publish-minor": "npm run prepare-deployment && npm version minor && npm publish",
|
|
19
|
+
"prepare": "husky install",
|
|
20
|
+
"test": "vitest run --coverage",
|
|
21
|
+
"test:dev": "vitest --coverage tests/programmes",
|
|
22
|
+
"lint": "eslint . --ext .ts",
|
|
23
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
24
|
+
"prettier:fix": "npx prettier --use-tabs --ignore-path .gitignore --write ."
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
"./organisations-search-seo": {
|
|
28
|
+
"import": "./dist/organisations-seo/index.js",
|
|
29
|
+
"require": "./dist/organisations-seo/index.js",
|
|
30
|
+
"types": "./dist/organisations-seo/index.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./programmes-search-seo": {
|
|
33
|
+
"import": "./dist/programmes-seo/index.js",
|
|
34
|
+
"require": "./dist/programmes-seo/index.js",
|
|
35
|
+
"types": "./dist/programmes-seo/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./scholarships-search-seo": {
|
|
38
|
+
"import": "./dist/scholarships-seo/index.js",
|
|
39
|
+
"require": "./dist/scholarships-seo/index.js",
|
|
40
|
+
"types": "./dist/scholarships-seo/index.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./sitemap-generator-seo": {
|
|
43
|
+
"import": "./dist/sitemap-generator-seo/index.js",
|
|
44
|
+
"require": "./dist/sitemap-generator-seo/index.js",
|
|
45
|
+
"types": "./dist/sitemap-generator-seo/index.d.ts"
|
|
46
|
+
},
|
|
47
|
+
"./structured-data-seo": {
|
|
48
|
+
"import": "./dist/structured-data-seo/index.js",
|
|
49
|
+
"require": "./dist/structured-data-seo/index.js",
|
|
50
|
+
"types": "./dist/structured-data-seo/index.d.ts"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"typesVersions": {
|
|
54
|
+
"*": {
|
|
55
|
+
"organisations-search-seo": [
|
|
56
|
+
"dist/organisations-seo/index.d.ts"
|
|
57
|
+
],
|
|
58
|
+
"programmes-search-seo": [
|
|
59
|
+
"dist/programmes-seo/index.d.ts"
|
|
60
|
+
],
|
|
61
|
+
"scholarships-search-seo": [
|
|
62
|
+
"dist/scholarships-seo/index.d.ts"
|
|
63
|
+
],
|
|
64
|
+
"sitemap-generator-seo": [
|
|
65
|
+
"dist/sitemap-generator-seo/index.d.ts"
|
|
66
|
+
],
|
|
67
|
+
"structured-data-seo": [
|
|
68
|
+
"dist/structured-data-seo/index.d.ts"
|
|
69
|
+
],
|
|
70
|
+
"*": [
|
|
71
|
+
"dist/index.d.ts"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"author": "The Jedi Council",
|
|
76
|
+
"license": "ISC",
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@adobe/structured-data-validator": "^1.4.1",
|
|
79
|
+
"@studyportals/code-style": "^2.2.1",
|
|
80
|
+
"@studyportals/webpack-helper": "^6.0.6",
|
|
81
|
+
"@vitest/coverage-istanbul": "^2.1.8",
|
|
82
|
+
"husky": "^8.0.3",
|
|
83
|
+
"jsdom": "^26.0.0",
|
|
84
|
+
"prettier": "^3.5.3",
|
|
85
|
+
"tsc-alias": "^1.8.11",
|
|
86
|
+
"typemoq": "^2.1.0",
|
|
87
|
+
"typescript": "^5.7.3",
|
|
88
|
+
"vitest": "^2.1.8",
|
|
89
|
+
"schema-dts": "^1.1.5",
|
|
90
|
+
"ts-loader": "^9.5.2"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"@studyportals/domain-client": "^6.2.0",
|
|
94
|
+
"@studyportals/ranking-api-interface": "^1.3.12",
|
|
95
|
+
"@studyportals/search-filters": "^4.9.1",
|
|
96
|
+
"@studyportals/static-domain-data": "^6.1.0"
|
|
97
|
+
},
|
|
98
|
+
"optionalDependencies": {
|
|
99
|
+
"@rollup/rollup-linux-x64-gnu": "4.24.0"
|
|
100
|
+
},
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=18 <=22",
|
|
103
|
+
"npm": ">=8 <=10"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { PortalType } from '@studyportals/domain-client';
|
|
2
|
-
import { IOrganisationsSeoDependencies } from './types/IOrganisationsSeoDependencies';
|
|
3
|
-
import { SortingOptions } from '../enums/SortingOptions';
|
|
4
|
-
import { BaseSeoIndexabilityPolicy } from '../common/policies/BaseSeoIndexabilityPolicy';
|
|
5
|
-
export declare abstract class OrganisationsRuleBasedIndexabilityPolicy extends BaseSeoIndexabilityPolicy<IOrganisationsSeoDependencies> {
|
|
6
|
-
protected readonly indexablePortalTypes: PortalType[];
|
|
7
|
-
protected abstract readonly sortingOption: SortingOptions;
|
|
8
|
-
constructor(dependencies: IOrganisationsSeoDependencies);
|
|
9
|
-
protected isIndexable(): Promise<boolean>;
|
|
10
|
-
private matchesSortingOption;
|
|
11
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { PortalType } from '@studyportals/domain-client';
|
|
2
|
-
import { BaseSeoIndexabilityPolicy } from '../common/policies/BaseSeoIndexabilityPolicy';
|
|
3
|
-
export class OrganisationsRuleBasedIndexabilityPolicy extends BaseSeoIndexabilityPolicy {
|
|
4
|
-
indexablePortalTypes = [PortalType.MASTER];
|
|
5
|
-
constructor(dependencies) {
|
|
6
|
-
super(dependencies);
|
|
7
|
-
}
|
|
8
|
-
async isIndexable() {
|
|
9
|
-
const isIndexable = await super.isIndexable();
|
|
10
|
-
if (!isIndexable) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
return this.indexablePortalTypes.includes(this.dependencies.portalType) && this.matchesSortingOption();
|
|
14
|
-
}
|
|
15
|
-
matchesSortingOption() {
|
|
16
|
-
const dependencies = this.dependencies;
|
|
17
|
-
return dependencies.sortingState.getSelectedOption() === this.sortingOption;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { FilterKey } from '@studyportals/search-filters';
|
|
2
|
-
import { BaseFilterKeyValueRule } from '../../common/rules/BaseFilterKeyValueRule';
|
|
3
|
-
export class OnlineAttendanceRule extends BaseFilterKeyValueRule {
|
|
4
|
-
constructor() {
|
|
5
|
-
const filterKeyValues = new Map([[FilterKey.DELIVERY_METHOD, ['online']]]);
|
|
6
|
-
super(filterKeyValues);
|
|
7
|
-
}
|
|
8
|
-
getName() {
|
|
9
|
-
return 'OnlineAttendanceRule';
|
|
10
|
-
}
|
|
11
|
-
getDescription() {
|
|
12
|
-
return 'Indexable if attendance is online';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { ISearchDependencies } from '../common/ISearchDependencies';
|
|
2
|
-
import { IProgrammeSearchApplicationState } from './IProgrammeSearchApplicationState';
|
|
3
|
-
import { PortalType } from '@studyportals/domain-client';
|
|
4
|
-
export interface IProgrammeSearchDependencies extends ISearchDependencies {
|
|
5
|
-
portalType: PortalType;
|
|
6
|
-
applicationState: IProgrammeSearchApplicationState;
|
|
7
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { FilterKey } from '@studyportals/search-filters';
|
|
2
|
-
import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
|
|
3
|
-
import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
|
|
4
|
-
import { FilterCombinations } from '../../enums/FilterCombinations';
|
|
5
|
-
import { IndexableDegreeRule } from '../rules/IndexableDegreeRule';
|
|
6
|
-
import { ProgrammesBaseIndexabilityPolicy } from '../ProgrammesBaseIndexabilityPolicy';
|
|
7
|
-
import { IProgrammeSeoDependencies } from '../types/IProgrammeSeoDependencies';
|
|
8
|
-
export declare class DisciplineCountryDegree extends ProgrammesBaseIndexabilityPolicy {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly description: string;
|
|
11
|
-
readonly filterKeys: FilterKey[];
|
|
12
|
-
protected readonly rules: (SingleValueSelectedForFilterRule | OnlyFiltersSelectedRule | IndexableDegreeRule)[];
|
|
13
|
-
constructor(dependencies: IProgrammeSeoDependencies);
|
|
14
|
-
protected generateUrls(): Promise<string[]>;
|
|
15
|
-
get filterCombination(): FilterCombinations;
|
|
16
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { FilterKey } from '@studyportals/search-filters';
|
|
2
|
-
import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
|
|
3
|
-
import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
|
|
4
|
-
import { FilterCombinations } from '../../enums/FilterCombinations';
|
|
5
|
-
import { IndexableDegreeRule } from '../rules/IndexableDegreeRule';
|
|
6
|
-
import { ProgrammesBaseIndexabilityPolicy } from '../ProgrammesBaseIndexabilityPolicy';
|
|
7
|
-
import { DisciplinePresenter } from '../../presenters/DisciplinePresenter';
|
|
8
|
-
import { CountryPresenter } from '../../presenters/CountryPresenter';
|
|
9
|
-
import { DegreePresenter } from '../../presenters/DegreePresenter';
|
|
10
|
-
export class DisciplineCountryDegree extends ProgrammesBaseIndexabilityPolicy {
|
|
11
|
-
name = 'Discipline Country Degree Policy';
|
|
12
|
-
description = `Determines indexing rules for pages filtered by discipline,
|
|
13
|
-
country, and degree type.`;
|
|
14
|
-
filterKeys = [FilterKey.DISCIPLINES, FilterKey.COUNTRY, FilterKey.DEGREE_TYPE];
|
|
15
|
-
rules = [
|
|
16
|
-
new SingleValueSelectedForFilterRule(FilterKey.DISCIPLINES),
|
|
17
|
-
new SingleValueSelectedForFilterRule(FilterKey.COUNTRY),
|
|
18
|
-
new SingleValueSelectedForFilterRule(FilterKey.DEGREE_TYPE),
|
|
19
|
-
new OnlyFiltersSelectedRule([FilterKey.DISCIPLINES, FilterKey.COUNTRY, FilterKey.DEGREE_TYPE]),
|
|
20
|
-
new IndexableDegreeRule()
|
|
21
|
-
];
|
|
22
|
-
constructor(dependencies) {
|
|
23
|
-
super(dependencies);
|
|
24
|
-
this.initateBaseRules();
|
|
25
|
-
}
|
|
26
|
-
async generateUrls() {
|
|
27
|
-
const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
|
|
28
|
-
const countryFragments = CountryPresenter.getInstance().getFragments();
|
|
29
|
-
const degreeFragments = DegreePresenter.getInstance().getFragments();
|
|
30
|
-
const paths = [];
|
|
31
|
-
for (const pageNumber of this.indexablePageNumbers) {
|
|
32
|
-
for (const discipline of disciplineFragments) {
|
|
33
|
-
for (const country of countryFragments) {
|
|
34
|
-
for (const degree of degreeFragments) {
|
|
35
|
-
const filterKeyValues = new Map([
|
|
36
|
-
[FilterKey.DISCIPLINES, [discipline.id]],
|
|
37
|
-
[FilterKey.COUNTRY, [country.id]],
|
|
38
|
-
[FilterKey.DEGREE_TYPE, [degree.id]]
|
|
39
|
-
]);
|
|
40
|
-
const result = await this.checkRulesForSitemapWithPageNumber(filterKeyValues, pageNumber);
|
|
41
|
-
if (result) {
|
|
42
|
-
paths.push(this.getPathWithPageNumber(`${degree.path}/${discipline.path}/${country.path}`, pageNumber));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return paths;
|
|
49
|
-
}
|
|
50
|
-
get filterCombination() {
|
|
51
|
-
return FilterCombinations.DISCIPLINE_COUNTRY_DEGREE;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { ISearchDependencies } from '../../common/ISearchDependencies';
|
|
2
|
-
import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
|
|
3
|
-
import { BaseProgrammeRule } from '../BaseProgrammeRule';
|
|
4
|
-
export declare class DegreeAttendanceSpecificRule extends BaseProgrammeRule {
|
|
5
|
-
private readonly validCombinations;
|
|
6
|
-
getName(): string;
|
|
7
|
-
getDescription(): string;
|
|
8
|
-
forSearch(dependencies: ISearchDependencies): Promise<boolean>;
|
|
9
|
-
forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
|
|
10
|
-
}
|