@websolutespa/bom-next 3.0.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +11 -0
  3. package/dist/config.client-B8As5DY0.js +2 -0
  4. package/dist/config.edge-B8As5DY0.js +2 -0
  5. package/dist/config.server-B8As5DY0.js +2 -0
  6. package/dist/exports/client-xcFJNcG7.d.ts +979 -0
  7. package/dist/exports/client-xcFJNcG7.d.ts.map +1 -0
  8. package/dist/exports/client.js +5735 -0
  9. package/dist/exports/client.js.map +1 -0
  10. package/dist/exports/instrumentation-DBO6QH4d.d.ts +13 -0
  11. package/dist/exports/instrumentation-DBO6QH4d.d.ts.map +1 -0
  12. package/dist/exports/instrumentation.js +25 -0
  13. package/dist/exports/instrumentation.js.map +1 -0
  14. package/dist/exports/proxy-Cv7QFVci.d.ts +71 -0
  15. package/dist/exports/proxy-Cv7QFVci.d.ts.map +1 -0
  16. package/dist/exports/proxy.js +254 -0
  17. package/dist/exports/proxy.js.map +1 -0
  18. package/dist/exports/server-BHObJHJ6.d.ts +402 -0
  19. package/dist/exports/server-BHObJHJ6.d.ts.map +1 -0
  20. package/dist/exports/server.js +1974 -0
  21. package/dist/exports/server.js.map +1 -0
  22. package/dist/http-_2M-UFRx.js +61 -0
  23. package/dist/http-_2M-UFRx.js.map +1 -0
  24. package/dist/index-CdYcvudT.d.ts +4 -0
  25. package/dist/index-CtfAJ0y7.d.ts +499 -0
  26. package/dist/index-CtfAJ0y7.d.ts.map +1 -0
  27. package/dist/index.js +201 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/route-DzFwERQP.js +65 -0
  30. package/dist/route-DzFwERQP.js.map +1 -0
  31. package/dist/store-CJzqrHsl.js +73 -0
  32. package/dist/store-CJzqrHsl.js.map +1 -0
  33. package/dist/types-BatUTdbZ.d.ts +15 -0
  34. package/dist/types-BatUTdbZ.d.ts.map +1 -0
  35. package/dist/types-D29_snYY.d.ts +191 -0
  36. package/dist/types-D29_snYY.d.ts.map +1 -0
  37. package/dist/utils-Ca7xh5mm.js +78 -0
  38. package/dist/utils-Ca7xh5mm.js.map +1 -0
  39. package/dist/utils-YwteTbk7.js +23 -0
  40. package/dist/utils-YwteTbk7.js.map +1 -0
  41. package/package.json +156 -0
  42. package/src/client/client.tsx +18 -0
  43. package/src/client/extra.tsx +21 -0
  44. package/src/client/label.tsx +24 -0
  45. package/src/client/layout.tsx +22 -0
  46. package/src/client/local.collection.ts +33 -0
  47. package/src/client/metadata.tsx +217 -0
  48. package/src/client/page.tsx +22 -0
  49. package/src/client/useApi.test.tsx +203 -0
  50. package/src/client/useApi.ts +59 -0
  51. package/src/client/useCollectionApi.test.tsx +201 -0
  52. package/src/client/useCollectionApi.ts +66 -0
  53. package/src/client/useExtra.tsx +12 -0
  54. package/src/client/useHttp.ts +54 -0
  55. package/src/client/useLabel.test.tsx +76 -0
  56. package/src/client/useLabel.tsx +12 -0
  57. package/src/client/useLayout.tsx +13 -0
  58. package/src/client/useLink.tsx +13 -0
  59. package/src/client/useMetaData.tsx +12 -0
  60. package/src/client/usePage.tsx +12 -0
  61. package/src/client/useUser.tsx +12 -0
  62. package/src/client/user.tsx +43 -0
  63. package/src/exports/client.ts +20 -0
  64. package/src/exports/instrumentation.ts +1 -0
  65. package/src/exports/proxy.ts +5 -0
  66. package/src/exports/server.ts +19 -0
  67. package/src/global.d.ts +6 -0
  68. package/src/index.ts +17 -0
  69. package/src/instrumentation/client.ts +19 -0
  70. package/src/instrumentation/sentry/capture.requestError.ts +6 -0
  71. package/src/instrumentation/sentry/capture.routerTransitionStart.ts +4 -0
  72. package/src/instrumentation/sentry/config.client.ts +9 -0
  73. package/src/instrumentation/sentry/config.edge.ts +9 -0
  74. package/src/instrumentation/sentry/config.server.ts +9 -0
  75. package/src/instrumentation/server.ts +24 -0
  76. package/src/instrumentation/types.ts +12 -0
  77. package/src/lib/breadcrumb.ts +21 -0
  78. package/src/lib/const.ts +3 -0
  79. package/src/lib/http.test.ts +25 -0
  80. package/src/lib/http.ts +74 -0
  81. package/src/lib/label.ts +43 -0
  82. package/src/lib/layout.defaults.ts +167 -0
  83. package/src/lib/link.ts +19 -0
  84. package/src/lib/meta-data.ts +60 -0
  85. package/src/lib/open-graph.ts +165 -0
  86. package/src/lib/pagination.ts +78 -0
  87. package/src/lib/payload.config.ts +12 -0
  88. package/src/lib/route.ts +83 -0
  89. package/src/lib/store.ts +63 -0
  90. package/src/lib/structured-data.ts +24 -0
  91. package/src/lib/twitter-card.ts +49 -0
  92. package/src/lib/utils.ts +45 -0
  93. package/src/mock.ts +264 -0
  94. package/src/proxy/protected-route.test.ts +75 -0
  95. package/src/proxy/protected-route.ts +57 -0
  96. package/src/proxy/proxy.test.ts +48 -0
  97. package/src/proxy/proxy.ts +64 -0
  98. package/src/proxy/route-auto-detection.ts +126 -0
  99. package/src/proxy/route-proxy.ts +187 -0
  100. package/src/server/context.ts +147 -0
  101. package/src/server/handlers/auth.handler.ts +27 -0
  102. package/src/server/handlers/captions.handler.ts +32 -0
  103. package/src/server/handlers/collection.handler.test.ts +57 -0
  104. package/src/server/handlers/collection.handler.ts +212 -0
  105. package/src/server/handlers/error-response.ts +45 -0
  106. package/src/server/handlers/index.handler.ts +146 -0
  107. package/src/server/handlers/preview.handler.ts +63 -0
  108. package/src/server/handlers/registry.handler.test.ts +65 -0
  109. package/src/server/handlers/registry.handler.ts +259 -0
  110. package/src/server/handlers/route.handler.ts +161 -0
  111. package/src/server/handlers/sitemap.handler.ts +374 -0
  112. package/src/server/handlers/static-params.handler.ts +111 -0
  113. package/src/server/handlers/vars.handler.ts +167 -0
  114. package/src/server/layout.ts +120 -0
  115. package/src/server/lazy.ts +41 -0
  116. package/src/server/local-api.ts +63 -0
  117. package/src/server/metadata.ts +87 -0
  118. package/src/server/mixer-payload.ts +124 -0
  119. package/src/server/mixer-payload.type-test.ts +27 -0
  120. package/src/server/mixer.ts +439 -0
  121. package/src/server/mock/category.ts +34 -0
  122. package/src/server/mock/collection.ts +21 -0
  123. package/src/server/mock/page.ts +62 -0
  124. package/src/server/mock/route.ts +36 -0
  125. package/src/server/mock/store.ts +159 -0
  126. package/src/server/page.ts +86 -0
  127. package/src/server/session/session.ts +101 -0
  128. package/src/server/utils.ts +23 -0
  129. package/src/server/utmz.ts +11 -0
  130. package/src/types.ts +211 -0
@@ -0,0 +1,60 @@
1
+ import { ILayout, ILocalized, IPage } from '@websolutespa/bom-core';
2
+ import { ContactPoint, Organization, PostalAddress } from 'schema-dts';
3
+ import { OpenGraphType } from './open-graph';
4
+ import { StructuredDataType } from './structured-data';
5
+ import { TwitterCardType } from './twitter-card';
6
+
7
+ export type IBrand = {
8
+ name: string;
9
+ businessName: string;
10
+ appName: string;
11
+ logo: string;
12
+ url: string;
13
+ // contact
14
+ email?: string;
15
+ telephone?: string;
16
+ // address
17
+ address?: string;
18
+ city?: string;
19
+ zipCode?: string;
20
+ provinceCode?: string;
21
+ countryCode?: string;
22
+ twitterName?: string;
23
+ social?: Record<string, string>;
24
+ };
25
+
26
+ export type IAlternate = {
27
+ id: string;
28
+ href: string;
29
+ hrefLang: string;
30
+ };
31
+
32
+ export type IMeta = {
33
+ title: string;
34
+ description?: string;
35
+ image: string;
36
+ slug: string;
37
+ url: string;
38
+ canonical: string;
39
+ alternates: IAlternate[];
40
+ xDefault?: IAlternate;
41
+ keywords?: string;
42
+ robots?: string;
43
+ /*
44
+ relPrev?: string;
45
+ relNext?: string;
46
+ */
47
+ };
48
+
49
+ export type IMetaData = {
50
+ layout: ILayout;
51
+ page: ILocalized<IPage>;
52
+ brand: IBrand;
53
+ contactPoint: ContactPoint;
54
+ meta: IMeta;
55
+ openGraph: OpenGraphType;
56
+ organization: Organization;
57
+ postalAddress: PostalAddress;
58
+ structuredData: StructuredDataType;
59
+ twitterCard?: TwitterCardType;
60
+ };
@@ -0,0 +1,165 @@
1
+ // Open Graph Types
2
+
3
+ export interface OpenGraphBase {
4
+ type: string;
5
+ title?: string;
6
+ image?: string | OpenGraphImage | OpenGraphImage[];
7
+ url?: string;
8
+ description?: string;
9
+ siteName?: string;
10
+ locale?: string;
11
+ localeAlternate?: string[];
12
+ }
13
+
14
+ export interface OpenGraphImage {
15
+ url: string;
16
+ secureUrl?: string;
17
+ type?: string;
18
+ width?: number;
19
+ height?: number;
20
+ alt?: string;
21
+ }
22
+
23
+ export interface OpenGraphAudio {
24
+ url: string;
25
+ secureUrl?: string;
26
+ type?: string;
27
+ }
28
+
29
+ export interface OpenGraphWebsite extends OpenGraphBase {
30
+ type: 'website';
31
+ }
32
+
33
+ export interface OpenGraphArticle extends OpenGraphBase {
34
+ type: 'article';
35
+ publishedTime?: string;
36
+ modifiedTime?: string;
37
+ expirationTime?: string;
38
+ author?: string | string[];
39
+ section?: string;
40
+ tag?: string | string[];
41
+ }
42
+
43
+ export interface OpenGraphBook extends OpenGraphBase {
44
+ type: 'book';
45
+ author?: string | string[];
46
+ isbn?: string;
47
+ releaseDate?: string;
48
+ tag?: string | string[];
49
+ }
50
+
51
+ export interface OpenGraphProfile extends OpenGraphBase {
52
+ type: 'profile';
53
+ firstName?: string;
54
+ lastName?: string;
55
+ username?: string;
56
+ gender?: string;
57
+ }
58
+
59
+ export interface OpenGraphMusic extends OpenGraphBase {
60
+ type: 'music.song' | 'music.album' | 'music.playlist' | 'music.radio_station';
61
+ duration?: number;
62
+ album?: string | string[];
63
+ musician?: string | string[];
64
+ song?: string | string[];
65
+ creator?: string;
66
+ }
67
+
68
+ export interface OpenGraphVideo extends Omit<OpenGraphBase, 'type'> {
69
+ type: 'video.movie' | 'video.episode' | 'video.tv_show' | 'video.other';
70
+ actor?: string | string[];
71
+ director?: string | string[];
72
+ writer?: string | string[];
73
+ duration?: number;
74
+ releaseDate?: string;
75
+ tag?: string | string[];
76
+ series?: string;
77
+ url: string;
78
+ secureUrl?: string;
79
+ width?: number;
80
+ height?: number;
81
+ }
82
+
83
+ export interface OpenGraphProduct extends OpenGraphBase {
84
+ type: 'product';
85
+ price?: {
86
+ amount: number;
87
+ currency: string;
88
+ };
89
+ availability?: 'instock' | 'oos' | 'pending';
90
+ condition?: 'new' | 'refurbished' | 'used';
91
+ priceAmount?: number;
92
+ priceCurrency?: string;
93
+ retailerItemId?: string;
94
+ brand?: string;
95
+ category?: string;
96
+ color?: string;
97
+ material?: string;
98
+ pattern?: string;
99
+ size?: string;
100
+ }
101
+
102
+ export interface OpenGraphProductGroup extends OpenGraphBase {
103
+ type: 'product.group';
104
+ }
105
+
106
+ export interface OpenGraphPlace extends OpenGraphBase {
107
+ type: 'place';
108
+ latitude?: number;
109
+ longitude?: number;
110
+ streetAddress?: string;
111
+ locality?: string;
112
+ region?: string;
113
+ postalCode?: string;
114
+ countryName?: string;
115
+ }
116
+
117
+ export interface OpenGraphRestaurant extends Omit<OpenGraphPlace, 'type'> {
118
+ type: 'restaurant.restaurant';
119
+ contactData?: {
120
+ streetAddress?: string;
121
+ locality?: string;
122
+ region?: string;
123
+ postalCode?: string;
124
+ countryName?: string;
125
+ email?: string;
126
+ phoneNumber?: string;
127
+ faxNumber?: string;
128
+ };
129
+ menu?: string;
130
+ section?: string;
131
+ priceRange?: string;
132
+ }
133
+
134
+ export interface OpenGraphBusiness extends OpenGraphBase {
135
+ type: 'business.business';
136
+ contactData?: {
137
+ streetAddress?: string;
138
+ locality?: string;
139
+ region?: string;
140
+ postalCode?: string;
141
+ countryName?: string;
142
+ email?: string;
143
+ phoneNumber?: string;
144
+ faxNumber?: string;
145
+ website?: string;
146
+ };
147
+ hours?: {
148
+ day: string;
149
+ start: string;
150
+ end: string;
151
+ }[];
152
+ }
153
+
154
+ export type OpenGraphType =
155
+ | OpenGraphArticle
156
+ | OpenGraphBook
157
+ | OpenGraphBusiness
158
+ | OpenGraphMusic
159
+ | OpenGraphPlace
160
+ | OpenGraphProduct
161
+ | OpenGraphProductGroup
162
+ | OpenGraphProfile
163
+ | OpenGraphRestaurant
164
+ | OpenGraphVideo
165
+ | OpenGraphWebsite;
@@ -0,0 +1,78 @@
1
+ import { isArray, isObject } from '@websolutespa/bom-core';
2
+
3
+ export type IPaginationItem = any;
4
+
5
+ export type IPaginationOptions = {
6
+ mode?: 'pagination' | 'infinite'; // default to pagination
7
+ page?: number;
8
+ perPage?: number;
9
+ delayInMs?: number;
10
+ };
11
+
12
+ export type IPaginationInfo<T extends IPaginationItem = IPaginationItem> = {
13
+ items: T[];
14
+ hasMore: boolean;
15
+ nextPage: number | undefined;
16
+ page: number;
17
+ pages: number;
18
+ perPage: number;
19
+ prevPage: number | undefined;
20
+ total: number;
21
+ };
22
+
23
+ export function isPaginationInfo(item: unknown): item is IPaginationInfo {
24
+ return isObject(item) && (
25
+ typeof item.page === 'number' &&
26
+ typeof item.pages === 'number' &&
27
+ typeof item.perPage === 'number' &&
28
+ typeof item.total === 'number' &&
29
+ isArray(item.items)
30
+ );
31
+ }
32
+
33
+ export function getPagedItems<T extends IPaginationItem = IPaginationItem>(
34
+ items: T[],
35
+ page: number,
36
+ perPage: number
37
+ ): T[] {
38
+ const from = (page - 1) * perPage;
39
+ const to = Math.min(page * perPage, items.length);
40
+ return items.slice(from, to);
41
+ }
42
+
43
+ export function getMoreItems<T extends IPaginationItem = IPaginationItem>(
44
+ items: T[],
45
+ page: number,
46
+ perPage: number
47
+ ): T[] {
48
+ const total = items.length;
49
+ const position = Math.min(total, perPage * page);
50
+ return items.slice(0, position);
51
+ }
52
+
53
+ export function getPaginationInfo<T extends IPaginationItem = IPaginationItem>(
54
+ items: T[],
55
+ options?: IPaginationOptions
56
+ ): IPaginationInfo<T> {
57
+ const mode = options?.mode || 'pagination';
58
+ const page = options?.page || 1;
59
+ const perPage = options?.perPage || 12;
60
+ const total = items.length;
61
+ const pages = Math.ceil(total / perPage);
62
+ const hasMore = page < pages;
63
+ const prevPage = page > 1 ? page - 1 : undefined;
64
+ const nextPage = page < pages ? page + 1 : undefined;
65
+ const pagedItems = mode === 'pagination' ?
66
+ getPagedItems(items, page, perPage) :
67
+ getMoreItems(items, page, perPage);
68
+ return {
69
+ items: pagedItems,
70
+ hasMore,
71
+ nextPage,
72
+ page,
73
+ pages,
74
+ perPage,
75
+ prevPage,
76
+ total,
77
+ };
78
+ }
@@ -0,0 +1,12 @@
1
+ import { bowl } from '@websolutespa/payload-plugin-bowl';
2
+ import { Config } from 'payload';
3
+
4
+ export const config: Partial<Config> = {
5
+ collections: [],
6
+ globals: [],
7
+ plugins: [
8
+ bowl({
9
+ defaultMarket: 'ww',
10
+ }),
11
+ ],
12
+ };
@@ -0,0 +1,83 @@
1
+ import { asCategoryId, findRouteOfCategory, ICategory, IRoute, IRouteLink, localize } from '@websolutespa/bom-core';
2
+
3
+ export function getRouteLinkCategory(
4
+ locale: string,
5
+ routes: IRoute[],
6
+ categories: ICategory[],
7
+ rootCategory: ICategory,
8
+ category: ICategory
9
+ ): IRouteLink | IRouteLink[] | undefined {
10
+ const route = findRouteOfCategory(routes, category.id);
11
+ const otherCategories = categories.filter(x => x.id !== category.id);
12
+ const childCategories = otherCategories.filter(x => asCategoryId(x.category) === category.id);
13
+ // console.log('childCategories', category.id, childCategories.map(x => x.id));
14
+ const routeLink = newRouteLink(category, route, locale);
15
+ // console.log('getRouteLinkCategory', href, category.id, route);
16
+ const items: IRouteLink[] = [];
17
+ for (const childCategory of childCategories) {
18
+ const itemOrItems = getRouteLinkCategory(locale, routes, otherCategories, rootCategory, childCategory);
19
+ if (Array.isArray(itemOrItems)) {
20
+ items.push(...itemOrItems);
21
+ } else if (itemOrItems) {
22
+ items.push(itemOrItems);
23
+ }
24
+ }
25
+ if (
26
+ !route ||
27
+ (category.isHidden && category.id !== rootCategory.id)
28
+ ) {
29
+ return items;
30
+ }
31
+ routeLink.items = items;
32
+ return routeLink;
33
+ }
34
+
35
+ export function newRouteLink(category: ICategory, route: IRoute | undefined, locale: string): IRouteLink {
36
+ const href = (route && route.id) ? route.id.toString() : '/';
37
+ const id = route?.page || category.id;
38
+ const media = category.media || route?.media;
39
+ const schema = route?.template || route?.schema || category?.schema || 'unknown';
40
+ const title = localize(category.title || route?.title || 'untitled', locale);
41
+ const routeLink = {
42
+ category: category.id,
43
+ href,
44
+ id,
45
+ items: [],
46
+ media,
47
+ schema,
48
+ title,
49
+ };
50
+ return routeLink;
51
+ }
52
+
53
+ export function resolveRoute(route: IRoute) {
54
+ return `/${route.market}/${route.locale}/${route.schema}/${route.page}`;
55
+ }
56
+
57
+ export function routeToRouteLink(route: IRoute): IRouteLink {
58
+ const routeLink: IRouteLink = {
59
+ category: asCategoryId(route.category),
60
+ href: route.id,
61
+ id: route.page,
62
+ items: [],
63
+ media: route.media,
64
+ schema: route.schema,
65
+ title: route.title,
66
+ };
67
+ return routeLink;
68
+ }
69
+
70
+ /**
71
+ * Converts a route (and optional override title) into an IRouteLink.
72
+ * route.id is the pathname (href), route.page is the document ID.
73
+ */
74
+ export function routeToLink(route: IRoute, title?: string): IRouteLink {
75
+ return {
76
+ category: route.category,
77
+ href: route.id,
78
+ id: route.page,
79
+ schema: route.schema,
80
+ title: title ?? route.title,
81
+ media: route.media,
82
+ };
83
+ }
@@ -0,0 +1,63 @@
1
+ export enum NextPhase {
2
+ Export = 'phase-export',
3
+ ProductionBuild = 'phase-production-build',
4
+ ProductionServer = 'phase-production-server',
5
+ DevelopmentServer = 'phase-development-server',
6
+ Test = 'phase-test',
7
+ }
8
+
9
+ function getNextPhase(): NextPhase {
10
+ let nextPhase = NextPhase.Export;
11
+ if (typeof process !== 'undefined' && process.env.NEXT_PHASE) {
12
+ nextPhase = process.env.NEXT_PHASE as NextPhase;
13
+ }
14
+ return nextPhase;
15
+ }
16
+
17
+ export const nextPhase: NextPhase = getNextPhase();
18
+
19
+ // buildStrategy
20
+ export enum BuildStrategy {
21
+ Static = 'static',
22
+ Runtime = 'runtime',
23
+ }
24
+
25
+ function getBuildStrategy(): BuildStrategy {
26
+ let buildStrategy = BuildStrategy.Static;
27
+ if (typeof process !== 'undefined' && process.env.BUILD_STRATEGY) {
28
+ buildStrategy = process.env.BUILD_STRATEGY as BuildStrategy;
29
+ }
30
+ return buildStrategy;
31
+ }
32
+
33
+ export const buildStrategy: BuildStrategy = getBuildStrategy();
34
+
35
+ // storeStrategy
36
+ export enum StoreStrategy {
37
+ Local = 'local',
38
+ Api = 'api',
39
+ DecoratedApi = 'decoratedApi',
40
+ Data = 'data',
41
+ Mock = 'mock',
42
+ }
43
+
44
+ function getStoreStrategy(): StoreStrategy {
45
+ let storeStrategy = StoreStrategy.Mock;
46
+ if (typeof process !== 'undefined' && process.env.STORE_STRATEGY) {
47
+ storeStrategy = process.env.STORE_STRATEGY as StoreStrategy;
48
+ }
49
+ return storeStrategy;
50
+ }
51
+
52
+ export const storeStrategy: StoreStrategy = getStoreStrategy();
53
+
54
+ // storeCacheDuration
55
+ function getStoreCacheDuration(): number {
56
+ let storeCacheDuration = (1 * 60 * 1000);
57
+ if (typeof process !== 'undefined' && process.env.STORE_CACHE_DURATION) {
58
+ storeCacheDuration = parseInt(process.env.STORE_CACHE_DURATION);
59
+ }
60
+ return storeCacheDuration;
61
+ }
62
+
63
+ export const storeCacheDuration: number = getStoreCacheDuration();
@@ -0,0 +1,24 @@
1
+ import * as StructuredData from 'schema-dts';
2
+
3
+ export type StructuredDataType =
4
+ | StructuredData.Article
5
+ | StructuredData.AudioObject
6
+ | StructuredData.BlogPosting
7
+ | StructuredData.BreadcrumbList
8
+ | StructuredData.Course
9
+ | StructuredData.Event
10
+ | StructuredData.FAQPage
11
+ | StructuredData.HowTo
12
+ | StructuredData.ItemList
13
+ | StructuredData.JobPosting
14
+ | StructuredData.ListItem
15
+ | StructuredData.LocalBusiness
16
+ | StructuredData.NewsArticle
17
+ | StructuredData.Organization
18
+ | StructuredData.Person
19
+ | StructuredData.Place
20
+ | StructuredData.Product
21
+ | StructuredData.Recipe
22
+ | StructuredData.VideoObject
23
+ | StructuredData.WebPage
24
+ | StructuredData.WebSite;
@@ -0,0 +1,49 @@
1
+ // Twitter Card Types
2
+
3
+ export interface TwitterCardBase {
4
+ card: 'summary' | 'summary_large_image' | 'app' | 'player';
5
+ site?: string;
6
+ siteId?: string;
7
+ creator?: string;
8
+ creatorId?: string;
9
+ title?: string;
10
+ description?: string;
11
+ image?: string;
12
+ imageAlt?: string;
13
+ }
14
+
15
+ export interface TwitterCardSummary extends TwitterCardBase {
16
+ card: 'summary';
17
+ }
18
+
19
+ export interface TwitterCardSummaryLargeImage extends TwitterCardBase {
20
+ card: 'summary_large_image';
21
+ }
22
+
23
+ export interface TwitterCardApp extends TwitterCardBase {
24
+ card: 'app';
25
+ appNameIphone?: string;
26
+ appIdIphone?: string;
27
+ appUrlIphone?: string;
28
+ appNameIpad?: string;
29
+ appIdIpad?: string;
30
+ appUrlIpad?: string;
31
+ appNameGoogleplay?: string;
32
+ appIdGoogleplay?: string;
33
+ appUrlGoogleplay?: string;
34
+ appCountry?: string;
35
+ }
36
+
37
+ export interface TwitterCardPlayer extends TwitterCardBase {
38
+ card: 'player';
39
+ player: string;
40
+ playerWidth: number;
41
+ playerHeight: number;
42
+ playerStream?: string;
43
+ }
44
+
45
+ export type TwitterCardType =
46
+ | TwitterCardApp
47
+ | TwitterCardPlayer
48
+ | TwitterCardSummary
49
+ | TwitterCardSummaryLargeImage;
@@ -0,0 +1,45 @@
1
+
2
+ export function getPublicUrl() {
3
+ const publicUrl = process.env && process.env.NEXT_PUBLIC_URL ? process.env.NEXT_PUBLIC_URL : '';
4
+ return publicUrl;
5
+ }
6
+
7
+ export function resolveHref(href: string = '') {
8
+ return href.startsWith('http') ? href : `${getPublicUrl()}${href}`;
9
+ }
10
+
11
+ export function trimTerminalSlash(path: string) {
12
+ return path.replace(/\/+$/, '');
13
+ }
14
+
15
+ export function sanitizeHref(url: URL, urlBeforeRedirect: URL | null) {
16
+ if (urlBeforeRedirect) {
17
+ const sanitizedUrl = new URL(urlBeforeRedirect);
18
+ sanitizedUrl.protocol = url.protocol;
19
+ return trimTerminalSlash(sanitizedUrl.href);
20
+ } else {
21
+ return trimTerminalSlash(url.href);
22
+ }
23
+ }
24
+
25
+ export function sanitizeRedirectFrom(path: string, url: URL, urlBeforeRedirect: URL | null) {
26
+ path = trimTerminalSlash(path);
27
+ if (urlBeforeRedirect) {
28
+ return path.replace(/^(\^?)((https?:\/\/)+)/, `$1${url.protocol}//`);
29
+ } else {
30
+ return path.replace(/^(\^?)((https?:\/\/)+(\w|\.|:)+)/, `$1${url.origin}`);
31
+ }
32
+ }
33
+
34
+ export function sanitizeRedirectTo(path?: string) {
35
+ return path ? trimTerminalSlash(path) : undefined;
36
+ }
37
+
38
+ export function sanitizeRedirectUrl(path: string | undefined, url: URL) {
39
+ if (path != null) {
40
+ path = trimTerminalSlash(path);
41
+ return path.match(/^((https?:\/\/)+)/) ? path : `${url.origin}${path}`;
42
+ } else {
43
+ return path;
44
+ }
45
+ }