@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,159 @@
1
+ import { getCollectionDescription, ICollectionDescription, IEntity, IMemoryStore, IMockStore, isArray, ISerializedCollection, ISerializedStore, isProduction, isValidCollectionDescription } from '@websolutespa/bom-core';
2
+ import { toCategory } from './category';
3
+ import { toCollection } from './collection';
4
+ import { toPage } from './page';
5
+ import { createRouteService } from './route';
6
+
7
+ // storeStrategy
8
+ export enum StoreStrategy {
9
+ Local = 'local',
10
+ Api = 'api',
11
+ DecoratedApi = 'decoratedApi',
12
+ Data = 'data',
13
+ Mock = 'mock',
14
+ }
15
+
16
+ function getStoreStrategy(): StoreStrategy {
17
+ let storeStrategy = StoreStrategy.Mock;
18
+ if (typeof process !== 'undefined' && process.env.STORE_STRATEGY) {
19
+ storeStrategy = process.env.STORE_STRATEGY as StoreStrategy;
20
+ }
21
+ return storeStrategy;
22
+ }
23
+
24
+ export const storeStrategy: StoreStrategy = getStoreStrategy();
25
+
26
+ export function resolveMockStore(data: IMockStore): IMemoryStore {
27
+ const mocks = { ...data.collections, ...data.pages };
28
+ const collectionKeys = Object.keys(data.collections);
29
+ const pageKeys = Object.keys(data.pages);
30
+ const keys = collectionKeys.concat(pageKeys);
31
+ const store: ISerializedStore = {};
32
+ const createdAt = new Date();
33
+ const updatedAt = new Date();
34
+ const collections: ICollectionDescription[] = keys.map((key) => getCollectionDescription(key));
35
+ collections.forEach((description) => {
36
+ if (!isValidCollectionDescription(description)) {
37
+ throw `collection error: invalid singular key ${description.singularName}`;
38
+ }
39
+ const mockItems = (mocks[description.singularName as keyof typeof mocks] || []) as IEntity[];
40
+ const items = mockData(mockItems);
41
+ let collectionService: ISerializedCollection;
42
+ if (description.singularName === 'category') {
43
+ // console.log('store.buildStore', 'categories');
44
+ collectionService = toCategory(description, items);
45
+ } else if (pageKeys.includes(description.singularName)) {
46
+ // console.log('store.buildStore', 'pages');
47
+ collectionService = toPage(description, items, createdAt, updatedAt);
48
+ } else {
49
+ // console.log('store.buildStore', 'data');
50
+ collectionService = toCollection(description, items, createdAt, updatedAt);
51
+ }
52
+ store[description.singularName] = collectionService;
53
+ });
54
+ if (!store.category) {
55
+ store.category = toCategory(getCollectionDescription('category'), []);
56
+ }
57
+ if (!store.menu) {
58
+ store.menu = toCollection(getCollectionDescription('menu'), []);
59
+ }
60
+ if (!store.redirect) {
61
+ store.redirect = toCollection(getCollectionDescription('redirect'), []);
62
+ }
63
+ const routeService = createRouteService(store, pageKeys);
64
+ store.route = routeService;
65
+ // const serializedCollections: ISerializedCollection[] = Object.keys(store).map((key) => store[key]!);
66
+ // console.log('serializedCollections', serializedCollections);
67
+ const mockStore: IMemoryStore = {};
68
+ Object.keys(store).forEach((key) => {
69
+ mockStore[key] = store[key]!.items;
70
+ });
71
+ console.log('[mock] resolveMockStore');
72
+ return mockStore;
73
+ }
74
+
75
+ export function serializeMockStore(mockStore: IMockStore) {
76
+ if (
77
+ !isProduction &&
78
+ storeStrategy === StoreStrategy.Mock
79
+ ) {
80
+ const store = resolveMockStore(mockStore);
81
+ return JSON.stringify(store);
82
+ } else {
83
+ return JSON.stringify({});
84
+ }
85
+ }
86
+
87
+ export function mockEnv(mockStore: IMockStore) {
88
+ if (storeStrategy === StoreStrategy.Mock) {
89
+ return {
90
+ COLLECTIONS: [
91
+ ...Object.keys(mockStore.collections),
92
+ ...Object.keys(mockStore.pages),
93
+ ].join(','),
94
+ PAGES: [
95
+ ...Object.keys(mockStore.pages),
96
+ ].join(','),
97
+ STORE: serializeMockStore(mockStore),
98
+ };
99
+ } else {
100
+ return {
101
+ COLLECTIONS: [
102
+ 'category',
103
+ 'label',
104
+ 'locale',
105
+ 'market',
106
+ 'menu',
107
+ 'redirect',
108
+ 'route',
109
+ 'template',
110
+ ].join(','),
111
+ PAGES: [].join(','),
112
+ STORE: JSON.stringify({
113
+ category: [],
114
+ label: [],
115
+ locale: [],
116
+ market: [],
117
+ menu: [],
118
+ redirect: [],
119
+ route: [],
120
+ template: [],
121
+ }),
122
+ };
123
+ }
124
+ }
125
+
126
+ export function mockData(items: IEntity[]): IEntity[] {
127
+ items.forEach((item, i) => {
128
+ if (item.id == null) {
129
+ item.id = (i + 1);
130
+ }
131
+ });
132
+ if (items.length > 0) {
133
+ const inherit: boolean = items.find(x => x.$inherit && (x.$inherit === true || isArray(x.$inherit))) !== undefined;
134
+ if (inherit) {
135
+ const forbiddenProperties = ['slug', 'meta', 'order', 'isDefault', 'isHidden', 'useSplat'];
136
+ let lastItem: IEntity | undefined = undefined;
137
+ const parsedItems = items.map(({ $inherit, ...item }) => {
138
+ let inheritedItem = item;
139
+ if (lastItem) {
140
+ if ($inherit === true) {
141
+ inheritedItem = { ...lastItem, ...item };
142
+ } else if (isArray($inherit)) {
143
+ inheritedItem = { ...pickProps(lastItem, ...($inherit as string[])), ...item };
144
+ }
145
+ }
146
+ lastItem = Object.fromEntries(
147
+ Object.entries(inheritedItem).filter(([k, v]) => !forbiddenProperties.includes(k))
148
+ ) as IEntity;
149
+ return inheritedItem;
150
+ });
151
+ return parsedItems;
152
+ }
153
+ }
154
+ return items;
155
+ }
156
+
157
+ export function pickProps(item: Record<string, unknown>, ...props: string[]): Record<string, unknown> {
158
+ return Object.fromEntries(Object.entries(item).filter(([k, v]) => props.includes(k)));
159
+ }
@@ -0,0 +1,86 @@
1
+ 'use server';
2
+
3
+ import { getCategorySegments, ICategorized, ICategory, IRoute, IRouteLink } from '@websolutespa/bom-core';
4
+ import type { User } from '@websolutespa/payload-plugin-bowl';
5
+ import type { CollectionSlug } from 'payload';
6
+ import { buildBreadcrumb } from '../lib/breadcrumb';
7
+ import type { MixerContext, TypedPage } from '../types';
8
+ import { buildBaseOptions } from './mixer';
9
+ import type { MixerPayload } from './mixer-payload';
10
+
11
+ /**
12
+ * Fetches a single page document by ID using Payload local API and assembles
13
+ * the IPage envelope (routes, alternates, breadcrumb, parentRoute).
14
+ *
15
+ * All data required for the envelope comes from mixerContext — no store calls.
16
+ * - routes/alternates : mixerContext.routes filtered by schema+id
17
+ * - segments : getCategorySegments() from bom-core (pure)
18
+ * - breadcrumb : findRouteOfCategory() from bom-core + mixerContext.currentRoutes
19
+ */
20
+ export async function getPage<TSlug extends CollectionSlug>(
21
+ slug: TSlug,
22
+ id: string,
23
+ market: string,
24
+ locale: string,
25
+ payload: MixerPayload,
26
+ user: User,
27
+ mixerContext: MixerContext,
28
+ draftMode?: boolean
29
+ ): Promise<TypedPage<TSlug> | undefined> {
30
+ try {
31
+ const base = buildBaseOptions(user, locale, draftMode);
32
+ const page = await payload.findByID({
33
+ collection: slug,
34
+ id,
35
+ locale: base.locale,
36
+ fallbackLocale: base.fallbackLocale,
37
+ draft: base.draft,
38
+ depth: base.depth,
39
+ user: base.user,
40
+ overrideAccess: base.overrideAccess,
41
+ disableErrors: true,
42
+ context: mixerContext,
43
+ });
44
+ if (page) {
45
+ // All routes for this schema+document across every market/locale (for alternates)
46
+ const routes = (mixerContext.routes as IRoute[]).filter(
47
+ r => r.schema === slug && String(r.page) === String(id)
48
+ );
49
+ const currentRoute = routes.find(r => r.market === market && r.locale === locale);
50
+ const alternates = routes;
51
+
52
+ // Segments derived from categories already present in mixerContext (pure, no I/O)
53
+ const categories = mixerContext.categories as unknown as ICategory[];
54
+ const segments = getCategorySegments(categories, page as unknown as ICategorized);
55
+
56
+ // Breadcrumb built from currentRoutes (pre-filtered to market+locale by mixer)
57
+ const currentRoutes = mixerContext.currentRoutes as IRoute[];
58
+ const breadcrumb: IRouteLink[] = currentRoute
59
+ ? buildBreadcrumb(segments, currentRoute, currentRoutes)
60
+ : [];
61
+
62
+ const parentRoute: IRouteLink | undefined = breadcrumb.length > 1
63
+ ? breadcrumb[breadcrumb.length - 2]
64
+ : undefined;
65
+
66
+ return {
67
+ ...page,
68
+ href: currentRoute?.id,
69
+ alternates,
70
+ breadcrumb,
71
+ parentRoute,
72
+ } as unknown as TypedPage<TSlug>;
73
+ } else {
74
+ console.log('PageService.getPage.notfound', slug, id, locale);
75
+ return undefined;
76
+ }
77
+ } catch {
78
+ return undefined;
79
+ }
80
+ }
81
+
82
+ /*
83
+ export function asServerProps(props: any): any {
84
+ return JSON.parse(JSON.stringify(props));
85
+ }
86
+ */
@@ -0,0 +1,101 @@
1
+ 'server-only';
2
+
3
+ import { SignJWT, jwtVerify } from 'jose';
4
+ import { cookies as getCookies } from 'next/headers';
5
+ import { cache } from 'react';
6
+
7
+ type ISession = {
8
+ token: string;
9
+ expiresAt: Date;
10
+ }
11
+
12
+ function getSessionSecret() {
13
+ const secret = process.env.MIXER_SECRET || '';
14
+ if (!secret) {
15
+ throw 'getSessionSecret MIXER_SECRET not defined';
16
+ }
17
+ return secret;
18
+ }
19
+
20
+ function getSessionKey() {
21
+ const secret = getSessionSecret();
22
+ const sessionKey = new TextEncoder().encode(secret);
23
+ return sessionKey;
24
+ }
25
+
26
+ export async function encrypt(session: ISession) {
27
+ try {
28
+ const sessionKey = getSessionKey();
29
+ return new SignJWT(session)
30
+ .setProtectedHeader({ alg: 'HS256' })
31
+ .setIssuedAt()
32
+ .setExpirationTime('7d')
33
+ .sign(sessionKey);
34
+ } catch (error) {
35
+ console.log('encrypt.error', 'Failed to encrypt session');
36
+ }
37
+ }
38
+
39
+ export async function decrypt(session: string | undefined = '') {
40
+ try {
41
+ const sessionKey = getSessionKey();
42
+ const { payload } = await jwtVerify(session, sessionKey, {
43
+ algorithms: ['HS256'],
44
+ });
45
+ return payload;
46
+ } catch (error) {
47
+ console.log('decrypt.error', 'Failed to decrypt session');
48
+ }
49
+ }
50
+
51
+ export async function createSession(token: string) {
52
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
53
+ const session = await encrypt({ token, expiresAt });
54
+ if (session) {
55
+ const cookies = await getCookies();
56
+ cookies.set('session', session, {
57
+ httpOnly: true,
58
+ secure: true,
59
+ expires: expiresAt,
60
+ sameSite: 'lax',
61
+ path: '/',
62
+ });
63
+ }
64
+ }
65
+
66
+ export const getSession = cache(async () => {
67
+ const cookies = await getCookies();
68
+ const encriptedSession = cookies.get('session')?.value;
69
+ const session = await decrypt(encriptedSession);
70
+ return session as ISession | undefined;
71
+ });
72
+
73
+ export const hasValidSession = cache(async () => {
74
+ const session = await getSession();
75
+ return session?.token && new Date(session?.expiresAt) > new Date();
76
+ });
77
+
78
+ export async function updateSession() {
79
+ const cookies = await getCookies();
80
+ const session = cookies.get('session')?.value;
81
+ if (!session) {
82
+ return null;
83
+ }
84
+ const payload = await decrypt(session);
85
+ if (!payload) {
86
+ return null;
87
+ }
88
+ const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
89
+ cookies.set('session', session, {
90
+ httpOnly: true,
91
+ secure: true,
92
+ expires: expires,
93
+ sameSite: 'lax',
94
+ path: '/',
95
+ });
96
+ }
97
+
98
+ export async function deleteSession() {
99
+ const cookies = await getCookies();
100
+ cookies.delete('session');
101
+ }
@@ -0,0 +1,23 @@
1
+ import { getHost, IRoute } from '@websolutespa/bom-core';
2
+ import { NextRequest } from 'next/server';
3
+ import { resolveRoute } from '../lib/route';
4
+
5
+ export function getNextUrl(request: NextRequest, route: IRoute) {
6
+ const nextUrl = request.nextUrl.clone();
7
+ const resolvedPathname = resolveRoute(route);
8
+ // console.log('getNextUrl.resolvedPathname', resolvedPathname);
9
+ nextUrl.pathname = resolvedPathname;
10
+ return nextUrl;
11
+ }
12
+
13
+ export function getHrefBeforeRedirect(request: NextRequest): string | null {
14
+ let hrefBeforeRedirect: string | null = null;
15
+ const urlBeforeRedirect = request.nextUrl.clone();
16
+ const host = getHost(request.headers);
17
+ if (host) {
18
+ urlBeforeRedirect.host = host;
19
+ urlBeforeRedirect.port = host.split(':')[1] || '';
20
+ hrefBeforeRedirect = urlBeforeRedirect.href;
21
+ }
22
+ return hrefBeforeRedirect;
23
+ }
@@ -0,0 +1,11 @@
1
+ import { NextApiRequest } from 'next';
2
+
3
+ export function getUtmz(request: NextApiRequest): string {
4
+ let utmz = 'N/A';
5
+ if (typeof request.cookies.__utmz === 'string') {
6
+ utmz = request.cookies.__utmz;
7
+ const i = utmz.indexOf('utmcsr');
8
+ utmz = utmz.substring(i === -1 ? 0 : i);
9
+ }
10
+ return utmz;
11
+ }
package/src/types.ts ADDED
@@ -0,0 +1,211 @@
1
+ import type { ICategorized, IComponent, ILayout, ILocale, IMarket, IPage, IPrettify, IRoute, PageProps } from '@websolutespa/bom-core';
2
+ import type { Category, EndUser, User } from '@websolutespa/payload-plugin-bowl';
3
+ import type { DynamicOptions, Loader } from 'next/dynamic';
4
+ import type { BasePayload, CollectionSlug, DataFromCollectionSlug, SanitizedConfig, TypedFallbackLocale, TypedLocale } from 'payload';
5
+ import type { ComponentType, ReactNode } from 'react';
6
+ import type { BowlDecorator, MixerPayload } from './server/mixer-payload';
7
+
8
+ export * from './instrumentation/types';
9
+
10
+ export type ReadonlyHeaders = Headers & {
11
+ /** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
12
+ append(...args: any[]): void;
13
+ /** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
14
+ set(...args: any[]): void;
15
+ /** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
16
+ delete(...args: any[]): void;
17
+ };
18
+
19
+ export type BaseOptions = {
20
+ currentDepth?: number;
21
+ depth?: number;
22
+ disableErrors?: boolean;
23
+ fallbackLocale?: TypedFallbackLocale;
24
+ includeLockStatus?: boolean;
25
+ limit?: number;
26
+ locale?: 'all' | TypedLocale;
27
+ overrideAccess?: boolean;
28
+ page?: number;
29
+ pagination?: boolean;
30
+ showHiddenFields?: boolean;
31
+ trash?: boolean;
32
+ user?: any;
33
+ draft?: boolean;
34
+ };
35
+
36
+ export type LocalProvider = {
37
+ config: Promise<SanitizedConfig> | undefined;
38
+ sanitizedConfig: SanitizedConfig | undefined;
39
+ payload: BasePayload | undefined;
40
+ };
41
+
42
+ export type SearchParams = {
43
+ [key: string]: string | string[] | undefined;
44
+ }
45
+
46
+ export type MarketLocaleParams = {
47
+ market: string;
48
+ locale: string;
49
+ }
50
+
51
+ export type NotFoundParams = IPrettify<MarketLocaleParams & {
52
+ slug: string;
53
+ }>;
54
+
55
+ export type NotFoundContext = Readonly<{
56
+ params: Promise<NotFoundParams>;
57
+ }>;
58
+
59
+ export type LayoutContext = Readonly<{
60
+ children: ReactNode;
61
+ params: Promise<MarketLocaleParams>;
62
+ }>;
63
+
64
+ export type PageParams = IPrettify<MarketLocaleParams & {
65
+ id: string;
66
+ }>;
67
+
68
+ export type PageContext = Readonly<{
69
+ params: Promise<PageParams>;
70
+ searchParams?: Promise<SearchParams>;
71
+ }>;
72
+
73
+ export type PageLayoutContext = Readonly<{
74
+ params: Promise<PageParams>;
75
+ children: ReactNode;
76
+ }>;
77
+
78
+ export type PartialPageProps<T extends ICategorized, B> = Omit<PageProps<T>, 'page'> & Partial<Pick<PageProps<T>, 'page'>> & B;
79
+
80
+ export type ComponentProps<T extends Record<string, any> = Record<string, any>> = { item: IComponent & T };
81
+
82
+ export type ComponentContext<T extends Record<string, any> = Record<string, any>> = PageContext & { page: IPage } & ComponentProps<T>;
83
+
84
+ export type IUser = User;
85
+
86
+ export type IEndUser = EndUser;
87
+
88
+ /*
89
+ export type IUser = {
90
+ id: number;
91
+ firstName: string;
92
+ lastName: string;
93
+ email: string;
94
+ };
95
+ */
96
+
97
+ // export type ILocale = (Locale['items'][number] & { title: string });
98
+
99
+ export type MixerContext = {
100
+ market: string;
101
+ marketIds: string[];
102
+ markets: IMarket[];
103
+ locale: string;
104
+ localeIds: string[];
105
+ locales: ILocale[];
106
+ routes: IRoute[];
107
+ currentRoutes: IRoute[];
108
+ categories: Category[];
109
+ richText: boolean;
110
+ };
111
+
112
+ export type IContext = {
113
+ payload: MixerPayload;
114
+ context: MixerContext;
115
+ }
116
+
117
+ export type PageDocument<TSlug extends CollectionSlug> = DataFromCollectionSlug<TSlug>;
118
+
119
+ /** A generated page collection document enriched with Bowl's page envelope. */
120
+ export type TypedPage<TSlug extends CollectionSlug> = BowlDecorator<PageDocument<TSlug>> & IPage<ICategorized>;
121
+
122
+ export type TypedPageContext<TSlug extends CollectionSlug = CollectionSlug> = {
123
+ layout: ILayout;
124
+ page: TypedPage<TSlug> | undefined;
125
+ user: IUser | IEndUser | null;
126
+ label: (key: string, params?: Record<string, any>) => string;
127
+ link: (category: string) => string | undefined;
128
+ };
129
+
130
+ /** Broad page context used where the page collection is only known at runtime. */
131
+ export type IPageContext = TypedPageContext;
132
+
133
+ export type ILazyComponent = IComponent;
134
+
135
+ export type ILazyProps<T> = {
136
+ item: T & ILazyComponent;
137
+ index: number;
138
+ children?: ReactNode;
139
+ };
140
+
141
+ export type ILazyableProps<T> = {
142
+ item: T & Partial<ILazyComponent>;
143
+ index?: number;
144
+ children?: ReactNode;
145
+ };
146
+
147
+ export type INullableLazyableProps<T> = {
148
+ item?: T & Partial<ILazyComponent>;
149
+ index?: number;
150
+ children?: ReactNode;
151
+ };
152
+
153
+ export type ILazyedProps<T> = Omit<ILazyProps<T>, 'children'>;
154
+
155
+ export type ILazyComponentProps = {
156
+ item: ILazyComponent;
157
+ index: number;
158
+ };
159
+
160
+ export type ILazyModules = Record<string, ComponentType<ILazyProps<any>>>;
161
+
162
+ export type ILazyComponentFunc<P = {}> = DynamicOptions<P> | Loader<P>;
163
+
164
+ export type ILazyFuncProps<T> = IContext & IPageContext & {
165
+ component: T & ILazyComponent;
166
+ };
167
+
168
+ export type ILazyStaticPropsFunc<T = any> = (props: ILazyFuncProps<T>) => Promise<T & ILazyComponent>;
169
+
170
+ export type ILazyStaticProps = Record<string, ILazyStaticPropsFunc>;
171
+
172
+ /*
173
+ export const LAZY_MODULES: ILazyModules = {};
174
+
175
+ export function withLazyModule<T>(key: string, callback: ILazyComponentFunc<ILazyProps<T>>) {
176
+ const module = dynamic<ILazyProps<T>>(callback);
177
+ LAZY_MODULES[key] = module;
178
+ return module;
179
+ }
180
+
181
+ export function withLazyModules(modules: Record<string, ILazyComponentFunc<ILazyProps<any>>>) {
182
+ Object.entries(modules).forEach(([key, callback]) => {
183
+ withLazyModule(key, callback);
184
+ });
185
+ return LAZY_MODULES;
186
+ }
187
+ */
188
+
189
+
190
+
191
+
192
+
193
+ /*
194
+
195
+ export type NextAppPage<T = undefined> = T extends undefined
196
+ ? FC<{
197
+ searchParams?: Promise<SearchParams>;
198
+ }>
199
+ : FC<{
200
+ params: Promise<T>;
201
+ searchParams?: Promise<SearchParams>;
202
+ }>;
203
+
204
+ export type NextAppLayout<T = undefined> = T extends undefined
205
+ ? FC<PropsWithChildren>
206
+ : FC<
207
+ PropsWithChildren<{
208
+ params: Promise<T>;
209
+ }>
210
+ >;
211
+ */