@websolutespa/bom-next 3.0.2 → 4.0.0-next.1
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/CHANGELOG.md +37 -7
- package/README.md +28 -0
- package/dist/_virtual/{_rolldown/runtime.js → rolldown_runtime.js} +7 -4
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +17 -6
- package/dist/client/client.js.map +1 -1
- package/dist/client/extra.d.ts +3 -3
- package/dist/client/extra.d.ts.map +1 -1
- package/dist/client/extra.js +10 -5
- package/dist/client/extra.js.map +1 -1
- package/dist/client/label.d.ts +3 -3
- package/dist/client/label.d.ts.map +1 -1
- package/dist/client/label.js +10 -5
- package/dist/client/label.js.map +1 -1
- package/dist/client/layout.d.ts +3 -3
- package/dist/client/layout.d.ts.map +1 -1
- package/dist/client/layout.js +10 -5
- package/dist/client/layout.js.map +1 -1
- package/dist/client/local.collection.d.ts.map +1 -1
- package/dist/client/local.collection.js +4 -2
- package/dist/client/local.collection.js.map +1 -1
- package/dist/client/metadata.d.ts +3 -3
- package/dist/client/metadata.d.ts.map +1 -1
- package/dist/client/metadata.js +48 -40
- package/dist/client/metadata.js.map +1 -1
- package/dist/client/page.d.ts +3 -3
- package/dist/client/page.d.ts.map +1 -1
- package/dist/client/page.js +10 -5
- package/dist/client/page.js.map +1 -1
- package/dist/client/useApi.d.ts +7 -7
- package/dist/client/useApi.d.ts.map +1 -1
- package/dist/client/useApi.js +2 -1
- package/dist/client/useApi.js.map +1 -1
- package/dist/client/useCollectionApi.d.ts +6 -6
- package/dist/client/useCollectionApi.d.ts.map +1 -1
- package/dist/client/useCollectionApi.js +4 -2
- package/dist/client/useCollectionApi.js.map +1 -1
- package/dist/client/useExtra.d.ts.map +1 -1
- package/dist/client/useExtra.js +5 -2
- package/dist/client/useExtra.js.map +1 -1
- package/dist/client/useHttp.d.ts.map +1 -1
- package/dist/client/useHttp.js +9 -6
- package/dist/client/useHttp.js.map +1 -1
- package/dist/client/useLabel.d.ts.map +1 -1
- package/dist/client/useLabel.js +5 -2
- package/dist/client/useLabel.js.map +1 -1
- package/dist/client/useLayout.d.ts.map +1 -1
- package/dist/client/useLayout.js +5 -2
- package/dist/client/useLayout.js.map +1 -1
- package/dist/client/useLink.d.ts.map +1 -1
- package/dist/client/useLink.js +7 -3
- package/dist/client/useLink.js.map +1 -1
- package/dist/client/useMetaData.d.ts.map +1 -1
- package/dist/client/useMetaData.js +5 -2
- package/dist/client/useMetaData.js.map +1 -1
- package/dist/client/usePage.d.ts.map +1 -1
- package/dist/client/usePage.js +5 -2
- package/dist/client/usePage.js.map +1 -1
- package/dist/client/useUser.d.ts.map +1 -1
- package/dist/client/useUser.js +5 -2
- package/dist/client/useUser.js.map +1 -1
- package/dist/client/user.d.ts.map +1 -1
- package/dist/client/user.js +17 -11
- package/dist/client/user.js.map +1 -1
- package/dist/exports/client.js +2 -1
- package/dist/exports/instrumentation.js +2 -1
- package/dist/exports/proxy.js +2 -1
- package/dist/exports/server.d.ts +5 -4
- package/dist/exports/server.js +7 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/instrumentation/client.d.ts.map +1 -1
- package/dist/instrumentation/client.js +2 -1
- package/dist/instrumentation/client.js.map +1 -1
- package/dist/instrumentation/sentry/capture.requestError.js +4 -1
- package/dist/instrumentation/sentry/capture.requestError.js.map +1 -1
- package/dist/instrumentation/sentry/capture.routerTransitionStart.js +2 -1
- package/dist/instrumentation/sentry/capture.routerTransitionStart.js.map +1 -1
- package/dist/instrumentation/sentry/config.client.js +10 -2
- package/dist/instrumentation/sentry/config.client.js.map +1 -0
- package/dist/instrumentation/sentry/config.edge.js +10 -2
- package/dist/instrumentation/sentry/config.edge.js.map +1 -0
- package/dist/instrumentation/sentry/config.server.js +10 -2
- package/dist/instrumentation/sentry/config.server.js.map +1 -0
- package/dist/instrumentation/server.d.ts.map +1 -1
- package/dist/instrumentation/server.js +8 -5
- package/dist/instrumentation/server.js.map +1 -1
- package/dist/instrumentation/types.d.ts.map +1 -1
- package/dist/lib/breadcrumb.d.ts.map +1 -1
- package/dist/lib/breadcrumb.js +6 -1
- package/dist/lib/breadcrumb.js.map +1 -1
- package/dist/lib/const.d.ts +14 -2
- package/dist/lib/const.d.ts.map +1 -1
- package/dist/lib/const.js +24 -2
- package/dist/lib/const.js.map +1 -1
- package/dist/lib/http.d.ts +9 -0
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +27 -4
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/label.d.ts.map +1 -1
- package/dist/lib/label.js +4 -3
- package/dist/lib/label.js.map +1 -1
- package/dist/lib/layout.defaults.d.ts.map +1 -1
- package/dist/lib/layout.defaults.js +1 -1
- package/dist/lib/layout.defaults.js.map +1 -1
- package/dist/lib/link.d.ts.map +1 -1
- package/dist/lib/link.js +2 -1
- package/dist/lib/link.js.map +1 -1
- package/dist/lib/meta-data.d.ts.map +1 -1
- package/dist/lib/open-graph.d.ts.map +1 -1
- package/dist/lib/pagination.d.ts.map +1 -1
- package/dist/lib/pagination.js +4 -2
- package/dist/lib/pagination.js.map +1 -1
- package/dist/lib/route.d.ts.map +1 -1
- package/dist/lib/route.js +9 -3
- package/dist/lib/route.js.map +1 -1
- package/dist/lib/store.d.ts.map +1 -1
- package/dist/lib/store.js +31 -31
- package/dist/lib/store.js.map +1 -1
- package/dist/lib/structured-data.d.ts.map +1 -1
- package/dist/lib/twitter-card.d.ts.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +3 -2
- package/dist/lib/utils.js.map +1 -1
- package/dist/mock.d.ts.map +1 -1
- package/dist/node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.js +3 -2
- package/dist/node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.js.map +1 -1
- package/dist/node_modules/deepmerge/dist/cjs.js +17 -9
- package/dist/node_modules/deepmerge/dist/cjs.js.map +1 -1
- package/dist/node_modules/dom-serializer/lib/esm/foreignNames.js +2 -2
- package/dist/node_modules/dom-serializer/lib/esm/foreignNames.js.map +1 -1
- package/dist/node_modules/dom-serializer/lib/esm/index.js +10 -15
- package/dist/node_modules/dom-serializer/lib/esm/index.js.map +1 -1
- package/dist/node_modules/domelementtype/lib/esm/index.js +12 -12
- package/dist/node_modules/domelementtype/lib/esm/index.js.map +1 -1
- package/dist/node_modules/domhandler/lib/esm/index.js +4 -3
- package/dist/node_modules/domhandler/lib/esm/index.js.map +1 -1
- package/dist/node_modules/domhandler/lib/esm/node.js +8 -7
- package/dist/node_modules/domhandler/lib/esm/node.js.map +1 -1
- package/dist/node_modules/entities/lib/esm/decode.js +33 -32
- package/dist/node_modules/entities/lib/esm/decode.js.map +1 -1
- package/dist/node_modules/entities/lib/esm/decode_codepoint.js +5 -5
- package/dist/node_modules/entities/lib/esm/decode_codepoint.js.map +1 -1
- package/dist/node_modules/entities/lib/esm/escape.js +14 -6
- package/dist/node_modules/entities/lib/esm/escape.js.map +1 -1
- package/dist/node_modules/entities/lib/esm/generated/decode-data-html.js +2 -2
- package/dist/node_modules/entities/lib/esm/generated/decode-data-html.js.map +1 -1
- package/dist/node_modules/entities/lib/esm/generated/decode-data-xml.js +2 -2
- package/dist/node_modules/entities/lib/esm/generated/decode-data-xml.js.map +1 -1
- package/dist/node_modules/html-to-text/lib/html-to-text.js +45 -33
- package/dist/node_modules/html-to-text/lib/html-to-text.js.map +1 -1
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Parser.js +25 -25
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Parser.js.map +1 -1
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Tokenizer.js +76 -73
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Tokenizer.js.map +1 -1
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/index.js +4 -3
- package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/index.js.map +1 -1
- package/dist/node_modules/leac/lib/leac.js +72 -72
- package/dist/node_modules/leac/lib/leac.js.map +1 -1
- package/dist/node_modules/parseley/lib/parseley.js +37 -33
- package/dist/node_modules/parseley/lib/parseley.js.map +1 -1
- package/dist/node_modules/peberminta/lib/core.js +4 -4
- package/dist/node_modules/peberminta/lib/core.js.map +1 -1
- package/dist/node_modules/selderee/lib/selderee.js +16 -10
- package/dist/node_modules/selderee/lib/selderee.js.map +1 -1
- package/dist/proxy/protected-route.d.ts.map +1 -1
- package/dist/proxy/protected-route.js +11 -6
- package/dist/proxy/protected-route.js.map +1 -1
- package/dist/proxy/proxy.d.ts +5 -4
- package/dist/proxy/proxy.d.ts.map +1 -1
- package/dist/proxy/proxy.js +26 -6
- package/dist/proxy/proxy.js.map +1 -1
- package/dist/proxy/route-auto-detection.d.ts.map +1 -1
- package/dist/proxy/route-auto-detection.js +26 -14
- package/dist/proxy/route-auto-detection.js.map +1 -1
- package/dist/proxy/route-proxy.d.ts.map +1 -1
- package/dist/proxy/route-proxy.js +22 -5
- package/dist/proxy/route-proxy.js.map +1 -1
- package/dist/server/context.d.ts +31 -15
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +156 -69
- package/dist/server/context.js.map +1 -1
- package/dist/server/handlers/auth.handler.d.ts.map +1 -1
- package/dist/server/handlers/auth.handler.js +4 -2
- package/dist/server/handlers/auth.handler.js.map +1 -1
- package/dist/server/handlers/captions.handler.d.ts.map +1 -1
- package/dist/server/handlers/captions.handler.js +4 -2
- package/dist/server/handlers/captions.handler.js.map +1 -1
- package/dist/server/handlers/collection.handler.d.ts.map +1 -1
- package/dist/server/handlers/collection.handler.js +2 -1
- package/dist/server/handlers/collection.handler.js.map +1 -1
- package/dist/server/handlers/error-response.js +6 -2
- package/dist/server/handlers/error-response.js.map +1 -1
- package/dist/server/handlers/index.handler.d.ts.map +1 -1
- package/dist/server/handlers/index.handler.js +2 -1
- package/dist/server/handlers/index.handler.js.map +1 -1
- package/dist/server/handlers/preview.handler.d.ts.map +1 -1
- package/dist/server/handlers/preview.handler.js +6 -3
- package/dist/server/handlers/preview.handler.js.map +1 -1
- package/dist/server/handlers/registry.handler.d.ts.map +1 -1
- package/dist/server/handlers/registry.handler.js +14 -9
- package/dist/server/handlers/registry.handler.js.map +1 -1
- package/dist/server/handlers/route.handler.d.ts.map +1 -1
- package/dist/server/handlers/route.handler.js +12 -3
- package/dist/server/handlers/route.handler.js.map +1 -1
- package/dist/server/handlers/sitemap.handler.d.ts.map +1 -1
- package/dist/server/handlers/sitemap.handler.js +12 -9
- package/dist/server/handlers/sitemap.handler.js.map +1 -1
- package/dist/server/handlers/static-params.handler.d.ts.map +1 -1
- package/dist/server/handlers/static-params.handler.js +13 -8
- package/dist/server/handlers/static-params.handler.js.map +1 -1
- package/dist/server/handlers/vars.handler.d.ts.map +1 -1
- package/dist/server/handlers/vars.handler.js +5 -4
- package/dist/server/handlers/vars.handler.js.map +1 -1
- package/dist/server/helpers.d.ts +50 -0
- package/dist/server/helpers.d.ts.map +1 -0
- package/dist/server/helpers.js +60 -0
- package/dist/server/helpers.js.map +1 -0
- package/dist/server/layout.js +69 -34
- package/dist/server/layout.js.map +1 -1
- package/dist/server/lazy.d.ts +1 -0
- package/dist/server/lazy.d.ts.map +1 -1
- package/dist/server/lazy.js +3 -2
- package/dist/server/lazy.js.map +1 -1
- package/dist/server/local-api.d.ts.map +1 -1
- package/dist/server/local-api.js +12 -6
- package/dist/server/local-api.js.map +1 -1
- package/dist/server/metadata.d.ts +8 -5
- package/dist/server/metadata.d.ts.map +1 -1
- package/dist/server/metadata.js +21 -7
- package/dist/server/metadata.js.map +1 -1
- package/dist/server/mixer-payload.d.ts +1 -1
- package/dist/server/mixer-payload.d.ts.map +1 -1
- package/dist/server/mixer.d.ts +48 -6
- package/dist/server/mixer.d.ts.map +1 -1
- package/dist/server/mixer.js +235 -68
- package/dist/server/mixer.js.map +1 -1
- package/dist/server/page.d.ts +21 -5
- package/dist/server/page.d.ts.map +1 -1
- package/dist/server/page.js +74 -15
- package/dist/server/page.js.map +1 -1
- package/dist/server/utils.d.ts.map +1 -1
- package/dist/server/utils.js +4 -2
- package/dist/server/utils.js.map +1 -1
- package/dist/server/utmz.d.ts.map +1 -1
- package/dist/server/utmz.js +1 -1
- package/dist/server/utmz.js.map +1 -1
- package/dist/types.d.ts +79 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +16 -15
- package/src/exports/server.ts +1 -0
- package/src/lib/const.ts +15 -0
- package/src/lib/http.ts +15 -4
- package/src/proxy/proxy.ts +11 -4
- package/src/server/context.test.ts +467 -0
- package/src/server/context.ts +237 -78
- package/src/server/context.type-test.ts +106 -0
- package/src/server/helpers.ts +58 -0
- package/src/server/layout.ts +45 -39
- package/src/server/lazy.ts +1 -1
- package/src/server/metadata.ts +15 -7
- package/src/server/mixer.ts +57 -9
- package/src/server/page.ts +62 -17
- package/src/types.ts +109 -7
package/src/server/context.ts
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
'
|
|
1
|
+
import 'server-only';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getIsDevelopment, type ILayout } from '@websolutespa/bom-core';
|
|
4
|
+
import type { User } from '@websolutespa/payload-plugin-bowl';
|
|
4
5
|
import type { CollectionSlug, SanitizedConfig } from 'payload';
|
|
5
6
|
import { cache } from 'react';
|
|
7
|
+
import { DEFAULT_LAYOUT_DEPTH, DEFAULT_PAGE_DEPTH } from '../lib/const';
|
|
6
8
|
import { getLabelFromLabels } from '../lib/label';
|
|
7
9
|
import { getLink } from '../lib/link';
|
|
8
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
IPageContext,
|
|
12
|
+
LayoutContextOptions,
|
|
13
|
+
LayoutOption,
|
|
14
|
+
LayoutOptions,
|
|
15
|
+
MixerContext,
|
|
16
|
+
PageContextOptions,
|
|
17
|
+
PageSelect,
|
|
18
|
+
ReadOptions,
|
|
19
|
+
ReadonlyHeaders,
|
|
20
|
+
TypedPage,
|
|
21
|
+
TypedPageContext
|
|
22
|
+
} from '../types';
|
|
9
23
|
import { getLayout } from './layout';
|
|
10
|
-
import { getPayload } from './mixer';
|
|
24
|
+
import { getPayload, resolveDraft } from './mixer';
|
|
25
|
+
import type { MixerPayload } from './mixer-payload';
|
|
11
26
|
import { getPage } from './page';
|
|
12
27
|
|
|
13
28
|
/**
|
|
@@ -18,6 +33,104 @@ const PageContext = cache(() => {
|
|
|
18
33
|
return new Map<string, any>();
|
|
19
34
|
});
|
|
20
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Per-request memo of page and layout loads, keyed by a serialized string.
|
|
38
|
+
* React `cache` compares object arguments by identity, so options objects
|
|
39
|
+
* built inline by each caller would never hit: the key is serialized instead.
|
|
40
|
+
*/
|
|
41
|
+
const RequestMemo = cache(() => {
|
|
42
|
+
return new Map<string, Promise<unknown>>();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** Payload instance + authenticated user, resolved once per request. */
|
|
46
|
+
const getRequestPayload = cache((config: Promise<SanitizedConfig>, headers: ReadonlyHeaders | undefined, draft: boolean) => {
|
|
47
|
+
return getPayload({ config, headers, draft });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function memoize<T>(key: string, factory: () => Promise<T>): Promise<T> {
|
|
51
|
+
const memo = RequestMemo();
|
|
52
|
+
let promise = memo.get(key) as Promise<T> | undefined;
|
|
53
|
+
if (!promise) {
|
|
54
|
+
promise = factory();
|
|
55
|
+
memo.set(key, promise);
|
|
56
|
+
promise.catch(() => memo.delete(key));
|
|
57
|
+
}
|
|
58
|
+
return promise;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** JSON serialization with sorted keys, so equivalent options produce the same key. */
|
|
62
|
+
export function stableStringify(value: unknown): string {
|
|
63
|
+
return JSON.stringify(value, (_, item) => {
|
|
64
|
+
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
65
|
+
return Object.keys(item).sort().reduce((sorted, key) => {
|
|
66
|
+
sorted[key] = item[key];
|
|
67
|
+
return sorted;
|
|
68
|
+
}, {} as Record<string, unknown>);
|
|
69
|
+
}
|
|
70
|
+
return item;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getUserKey(user: User | null | undefined): string {
|
|
75
|
+
if (user && 'id' in user && user.id !== undefined && user.id !== null) {
|
|
76
|
+
return `${(user as { collection?: string }).collection || 'user'}:${user.id}`;
|
|
77
|
+
}
|
|
78
|
+
return 'guest';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isPromiseLike(value: unknown): value is Promise<SanitizedConfig> {
|
|
82
|
+
return !!value && typeof (value as PromiseLike<unknown>).then === 'function';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const warnedLegacySignatures = new Set<string>();
|
|
86
|
+
|
|
87
|
+
function warnLegacySignature(name: string, signature: string) {
|
|
88
|
+
if (getIsDevelopment() && !warnedLegacySignatures.has(name)) {
|
|
89
|
+
warnedLegacySignatures.add(name);
|
|
90
|
+
console.warn(`[bom-next] ${name}(..., config, headers) is deprecated. Use ${name}(${signature}, { config, headers }) instead.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function toPageOptions<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug>, TLayout extends LayoutOption>(
|
|
95
|
+
name: string,
|
|
96
|
+
optionsOrConfig: PageContextOptions<TSlug, TSelect, TLayout> | Promise<SanitizedConfig>,
|
|
97
|
+
headers?: ReadonlyHeaders
|
|
98
|
+
): PageContextOptions<TSlug, TSelect, TLayout> {
|
|
99
|
+
if (isPromiseLike(optionsOrConfig)) {
|
|
100
|
+
warnLegacySignature(name, 'market, locale, slug, id');
|
|
101
|
+
return { config: optionsOrConfig, headers };
|
|
102
|
+
}
|
|
103
|
+
return optionsOrConfig;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function loadLayout(
|
|
107
|
+
market: string,
|
|
108
|
+
locale: string,
|
|
109
|
+
payload: MixerPayload,
|
|
110
|
+
user: User,
|
|
111
|
+
mixerContext: MixerContext,
|
|
112
|
+
readOptions: { depth?: number; draft: boolean }
|
|
113
|
+
): Promise<ILayout> {
|
|
114
|
+
const read = { depth: readOptions.depth ?? DEFAULT_LAYOUT_DEPTH, draft: readOptions.draft };
|
|
115
|
+
const key = stableStringify(['layout', market, locale, getUserKey(user), read]);
|
|
116
|
+
return memoize(key, () => getLayout(market, locale, payload, user, mixerContext, read));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function toPageContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug>, TLayout extends LayoutOption>(
|
|
120
|
+
layout: ILayout | undefined,
|
|
121
|
+
page: TypedPage<TSlug, TSelect> | undefined,
|
|
122
|
+
user: User
|
|
123
|
+
): TypedPageContext<TSlug, TSelect, TLayout> {
|
|
124
|
+
return {
|
|
125
|
+
layout,
|
|
126
|
+
// shallow copy: the memoized document is shared by every caller in the request
|
|
127
|
+
page: page ? { ...page } : undefined,
|
|
128
|
+
user,
|
|
129
|
+
label: getLabelFromLabels(layout?.labels ?? []),
|
|
130
|
+
link: (category: string) => layout ? getLink(category, layout) : undefined,
|
|
131
|
+
} as TypedPageContext<TSlug, TSelect, TLayout>;
|
|
132
|
+
}
|
|
133
|
+
|
|
21
134
|
export async function getPageContext<TSlug extends CollectionSlug = CollectionSlug>(): Promise<TypedPageContext<TSlug>> {
|
|
22
135
|
return {
|
|
23
136
|
layout: PageContext().get('layout'),
|
|
@@ -28,7 +141,9 @@ export async function getPageContext<TSlug extends CollectionSlug = CollectionSl
|
|
|
28
141
|
} as TypedPageContext<TSlug>;
|
|
29
142
|
}
|
|
30
143
|
|
|
31
|
-
export async function setPageContext<TSlug extends CollectionSlug
|
|
144
|
+
export async function setPageContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
145
|
+
data: Partial<TypedPageContext<TSlug, TSelect, TLayout>>
|
|
146
|
+
) {
|
|
32
147
|
const context = PageContext();
|
|
33
148
|
for (const key in data) {
|
|
34
149
|
context.set(key, data[key as keyof IPageContext]);
|
|
@@ -36,112 +151,156 @@ export async function setPageContext<TSlug extends CollectionSlug>(data: TypedPa
|
|
|
36
151
|
}
|
|
37
152
|
|
|
38
153
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
154
|
+
* Initialises the full page context using Payload local API.
|
|
155
|
+
*
|
|
156
|
+
* Page and layout are fetched in parallel and memoized per request, so
|
|
157
|
+
* `getMetadata` and the page component share the same reads. The mixer
|
|
158
|
+
* context (routes + categories) is passed to every payload read so that
|
|
159
|
+
* payload-plugin-bowl can enrich responses, including populated documents.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* const { page, label, link } = await createPageContext(market, locale, 'post_detail', id, {
|
|
163
|
+
* config,
|
|
164
|
+
* depth: 2,
|
|
165
|
+
* select: { title: true, abstract: true, media: true, components: true },
|
|
166
|
+
* populate: { media: { url: true, alt: true, width: true, height: true, type: true } },
|
|
167
|
+
* });
|
|
41
168
|
*/
|
|
42
|
-
export
|
|
169
|
+
export function createPageContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
170
|
+
market: string,
|
|
171
|
+
locale: string,
|
|
172
|
+
slug: TSlug,
|
|
173
|
+
id: string,
|
|
174
|
+
options: PageContextOptions<TSlug, TSelect, TLayout>
|
|
175
|
+
): Promise<TypedPageContext<TSlug, TSelect, TLayout>>;
|
|
176
|
+
/** @deprecated Use `createPageContext(market, locale, slug, id, { config, headers })`. */
|
|
177
|
+
export function createPageContext<TSlug extends CollectionSlug>(
|
|
43
178
|
market: string,
|
|
44
179
|
locale: string,
|
|
45
180
|
slug: TSlug,
|
|
46
181
|
id: string,
|
|
47
182
|
config: Promise<SanitizedConfig>,
|
|
48
|
-
headers?:
|
|
49
|
-
): Promise<TypedPageContext<TSlug
|
|
50
|
-
|
|
183
|
+
headers?: ReadonlyHeaders
|
|
184
|
+
): Promise<TypedPageContext<TSlug>>;
|
|
185
|
+
export async function createPageContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
186
|
+
market: string,
|
|
187
|
+
locale: string,
|
|
188
|
+
slug: TSlug,
|
|
189
|
+
id: string,
|
|
190
|
+
optionsOrConfig: PageContextOptions<TSlug, TSelect, TLayout> | Promise<SanitizedConfig>,
|
|
191
|
+
headers?: ReadonlyHeaders
|
|
192
|
+
): Promise<TypedPageContext<TSlug, TSelect, TLayout>> {
|
|
193
|
+
const options = toPageOptions<TSlug, TSelect, TLayout>('createPageContext', optionsOrConfig, headers);
|
|
194
|
+
|
|
195
|
+
const draft = await resolveDraft(options.draft);
|
|
196
|
+
const { payload, user, getContext } = await getRequestPayload(options.config, options.headers, draft);
|
|
51
197
|
const context = await getContext(market, locale);
|
|
52
198
|
|
|
53
|
-
const
|
|
54
|
-
|
|
199
|
+
const pageRead: ReadOptions = {
|
|
200
|
+
draft,
|
|
201
|
+
depth: options.depth ?? DEFAULT_PAGE_DEPTH,
|
|
202
|
+
select: options.select as ReadOptions['select'],
|
|
203
|
+
populate: options.populate,
|
|
204
|
+
joins: options.joins as ReadOptions['joins'],
|
|
205
|
+
};
|
|
206
|
+
const pageKey = stableStringify(['page', market, locale, slug, id, getUserKey(user), pageRead]);
|
|
207
|
+
const pagePromise = memoize(pageKey, () =>
|
|
208
|
+
getPage<TSlug, TSelect>(slug, id, market, locale, payload, user, context, pageRead)
|
|
209
|
+
);
|
|
55
210
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
211
|
+
const layoutOptions = options.layout;
|
|
212
|
+
const layoutPromise = layoutOptions === false
|
|
213
|
+
? Promise.resolve(undefined)
|
|
214
|
+
: (async () => loadLayout(market, locale, payload, user, context, {
|
|
215
|
+
depth: layoutOptions?.depth,
|
|
216
|
+
draft: layoutOptions?.draft === undefined ? draft : await resolveDraft(layoutOptions.draft),
|
|
217
|
+
}))();
|
|
58
218
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
user,
|
|
63
|
-
label,
|
|
64
|
-
link,
|
|
65
|
-
} as TypedPageContext<TSlug>;
|
|
219
|
+
const [layout, page] = await Promise.all([layoutPromise, pagePromise]);
|
|
220
|
+
|
|
221
|
+
const pageContext = toPageContext<TSlug, TSelect, TLayout>(layout, page, user);
|
|
66
222
|
|
|
67
|
-
|
|
223
|
+
if (layout) {
|
|
224
|
+
await setPageContext(pageContext);
|
|
225
|
+
} else {
|
|
226
|
+
// layout skipped: keep any layout already stored for this request
|
|
227
|
+
await setPageContext({ page: pageContext.page, user: pageContext.user });
|
|
228
|
+
}
|
|
68
229
|
|
|
69
230
|
return pageContext;
|
|
70
231
|
}
|
|
71
232
|
|
|
72
233
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Replaces the @websolutespa/bom-next/server createPageContext that goes through the store
|
|
76
|
-
* abstraction. All data is fetched directly from Payload; the mixer context
|
|
77
|
-
* (routes + categories) is passed to every payload.find call so that
|
|
78
|
-
* payload-plugin-bowl can enrich responses.
|
|
234
|
+
* Same as createPageContext but without draft mode — used for error/not-found pages
|
|
235
|
+
* that should always show published content regardless of draft mode state.
|
|
79
236
|
*/
|
|
80
|
-
export
|
|
237
|
+
export function createNotFoundContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
238
|
+
market: string,
|
|
239
|
+
locale: string,
|
|
240
|
+
slug: TSlug,
|
|
241
|
+
id: string,
|
|
242
|
+
options: PageContextOptions<TSlug, TSelect, TLayout>
|
|
243
|
+
): Promise<TypedPageContext<TSlug, TSelect, TLayout>>;
|
|
244
|
+
/** @deprecated Use `createNotFoundContext(market, locale, slug, id, { config, headers })`. */
|
|
245
|
+
export function createNotFoundContext<TSlug extends CollectionSlug>(
|
|
81
246
|
market: string,
|
|
82
247
|
locale: string,
|
|
83
248
|
slug: TSlug,
|
|
84
249
|
id: string,
|
|
85
250
|
config: Promise<SanitizedConfig>,
|
|
86
|
-
headers?:
|
|
87
|
-
): Promise<TypedPageContext<TSlug
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const link = (category: string) => getLink(category, layout);
|
|
100
|
-
|
|
101
|
-
const pageContext = {
|
|
102
|
-
layout,
|
|
103
|
-
page,
|
|
104
|
-
user,
|
|
105
|
-
label,
|
|
106
|
-
link,
|
|
107
|
-
} as TypedPageContext<TSlug>;
|
|
108
|
-
|
|
109
|
-
setPageContext(pageContext);
|
|
110
|
-
|
|
111
|
-
/*
|
|
112
|
-
if (draft.isEnabled) {
|
|
113
|
-
draft.disable();
|
|
114
|
-
}
|
|
115
|
-
*/
|
|
116
|
-
|
|
117
|
-
return pageContext;
|
|
251
|
+
headers?: ReadonlyHeaders
|
|
252
|
+
): Promise<TypedPageContext<TSlug>>;
|
|
253
|
+
export async function createNotFoundContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
254
|
+
market: string,
|
|
255
|
+
locale: string,
|
|
256
|
+
slug: TSlug,
|
|
257
|
+
id: string,
|
|
258
|
+
optionsOrConfig: PageContextOptions<TSlug, TSelect, TLayout> | Promise<SanitizedConfig>,
|
|
259
|
+
headers?: ReadonlyHeaders
|
|
260
|
+
): Promise<TypedPageContext<TSlug, TSelect, TLayout>> {
|
|
261
|
+
const options = toPageOptions<TSlug, TSelect, TLayout>('createNotFoundContext', optionsOrConfig, headers);
|
|
262
|
+
const layout = (options.layout === false ? false : { ...options.layout, draft: false }) as TLayout;
|
|
263
|
+
return createPageContext<TSlug, TSelect, TLayout>(market, locale, slug, id, { ...options, draft: false, layout });
|
|
118
264
|
}
|
|
119
265
|
|
|
120
266
|
/**
|
|
121
267
|
* Layout-only context — no page fetch. Used by RootLayout server components.
|
|
122
268
|
*/
|
|
123
|
-
export
|
|
269
|
+
export function createLayoutContext(
|
|
270
|
+
market: string,
|
|
271
|
+
locale: string,
|
|
272
|
+
options: LayoutContextOptions
|
|
273
|
+
): Promise<IPageContext>;
|
|
274
|
+
/** @deprecated Use `createLayoutContext(market, locale, { config, headers })`. */
|
|
275
|
+
export function createLayoutContext(
|
|
124
276
|
market: string,
|
|
125
277
|
locale: string,
|
|
126
278
|
config: Promise<SanitizedConfig>,
|
|
127
|
-
headers?:
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
279
|
+
headers?: ReadonlyHeaders
|
|
280
|
+
): Promise<IPageContext>;
|
|
281
|
+
export async function createLayoutContext(
|
|
282
|
+
market: string,
|
|
283
|
+
locale: string,
|
|
284
|
+
optionsOrConfig: LayoutContextOptions | Promise<SanitizedConfig>,
|
|
285
|
+
headers?: ReadonlyHeaders
|
|
286
|
+
): Promise<IPageContext> {
|
|
287
|
+
let options: LayoutContextOptions;
|
|
288
|
+
if (isPromiseLike(optionsOrConfig)) {
|
|
289
|
+
warnLegacySignature('createLayoutContext', 'market, locale');
|
|
290
|
+
options = { config: optionsOrConfig, headers };
|
|
291
|
+
} else {
|
|
292
|
+
options = optionsOrConfig;
|
|
293
|
+
}
|
|
131
294
|
|
|
132
|
-
const
|
|
295
|
+
const draft = await resolveDraft(options.draft);
|
|
296
|
+
const { payload, user, getContext } = await getRequestPayload(options.config, options.headers, draft);
|
|
297
|
+
const context = await getContext(market, locale);
|
|
133
298
|
|
|
134
|
-
const
|
|
135
|
-
const link = (category: string) => getLink(category, layout);
|
|
299
|
+
const layout = await loadLayout(market, locale, payload, user, context, { depth: options.depth, draft });
|
|
136
300
|
|
|
137
|
-
const pageContext =
|
|
138
|
-
layout,
|
|
139
|
-
user,
|
|
140
|
-
label,
|
|
141
|
-
link,
|
|
142
|
-
} as unknown as IPageContext;
|
|
301
|
+
const { page: _page, ...pageContext } = toPageContext<CollectionSlug, undefined, LayoutOptions>(layout, undefined, user);
|
|
143
302
|
|
|
144
|
-
setPageContext(pageContext);
|
|
303
|
+
await setPageContext(pageContext);
|
|
145
304
|
|
|
146
|
-
return pageContext;
|
|
305
|
+
return pageContext as IPageContext;
|
|
147
306
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { SanitizedConfig } from 'payload';
|
|
2
|
+
import type { ILayout } from '@websolutespa/bom-core';
|
|
3
|
+
import type { LayoutOption, Populated, ReadonlyHeaders, TypedPageContext } from '../types';
|
|
4
|
+
import { createLayoutContext, createNotFoundContext, createPageContext } from './context';
|
|
5
|
+
import { createContextHelpers } from './helpers';
|
|
6
|
+
|
|
7
|
+
declare const config: Promise<SanitizedConfig>;
|
|
8
|
+
|
|
9
|
+
type Expect<T extends true> = T;
|
|
10
|
+
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
|
|
11
|
+
|
|
12
|
+
// options form, full document
|
|
13
|
+
const full = createPageContext('ww', 'it', 'page', 'id', { config, depth: 1, draft: 'auto' });
|
|
14
|
+
full.then(({ page, layout, label, link }) => {
|
|
15
|
+
page?.title;
|
|
16
|
+
page?.href;
|
|
17
|
+
page?.breadcrumb;
|
|
18
|
+
layout.tree;
|
|
19
|
+
label('key');
|
|
20
|
+
link('category');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// select narrows the page document type
|
|
24
|
+
const narrowed = createPageContext('ww', 'it', 'page', 'id', {
|
|
25
|
+
config,
|
|
26
|
+
select: { title: true, slug: true },
|
|
27
|
+
populate: { media: { url: true } },
|
|
28
|
+
layout: false,
|
|
29
|
+
});
|
|
30
|
+
narrowed.then(({ page }) => {
|
|
31
|
+
page?.title;
|
|
32
|
+
// envelope fields are always selected
|
|
33
|
+
page?.category;
|
|
34
|
+
page?.template;
|
|
35
|
+
page?.href;
|
|
36
|
+
page?.breadcrumb;
|
|
37
|
+
// @ts-expect-error not selected
|
|
38
|
+
page?.order;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// exclude mode keeps the envelope fields
|
|
42
|
+
createPageContext('ww', 'it', 'page', 'id', { config, select: { title: false, order: false } }).then(({ page }) => {
|
|
43
|
+
page?.title;
|
|
44
|
+
page?.media;
|
|
45
|
+
// @ts-expect-error excluded
|
|
46
|
+
page?.order;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// legacy positional form still compiles
|
|
50
|
+
const legacy = createPageContext('ww', 'it', 'page', 'id', config);
|
|
51
|
+
legacy.then((context) => {
|
|
52
|
+
type _check = Expect<Equal<typeof context, TypedPageContext<'page'>>>;
|
|
53
|
+
});
|
|
54
|
+
createNotFoundContext('ww', 'it', 'page', 'id', config);
|
|
55
|
+
createNotFoundContext('ww', 'it', 'page', 'id', { config, depth: 0 });
|
|
56
|
+
createLayoutContext('ww', 'it', config);
|
|
57
|
+
createLayoutContext('ww', 'it', { config, depth: 1 });
|
|
58
|
+
|
|
59
|
+
// @ts-expect-error config is required
|
|
60
|
+
createPageContext('ww', 'it', 'page', 'id', { depth: 1 });
|
|
61
|
+
|
|
62
|
+
const helpers = createContextHelpers({ config, defaults: { depth: 2 } });
|
|
63
|
+
helpers.createPageContext('ww', 'it', 'page', 'id').then(({ page }) => page?.title);
|
|
64
|
+
helpers.createPageContext('ww', 'it', 'page', 'id', { select: { title: true } }).then(({ page }) => {
|
|
65
|
+
// @ts-expect-error not selected
|
|
66
|
+
page?.order;
|
|
67
|
+
});
|
|
68
|
+
helpers.createLayoutContext('ww', 'it');
|
|
69
|
+
helpers.getMetadata('page');
|
|
70
|
+
|
|
71
|
+
// Populated narrows ids out of relation unions
|
|
72
|
+
type _populated = Expect<Equal<Populated<string | { url: string }>, { url: string }>>;
|
|
73
|
+
type _populatedArray = Expect<Equal<Populated<(number | { url: string })[]>, { url: string }[]>>;
|
|
74
|
+
|
|
75
|
+
// layout follows the layout option
|
|
76
|
+
declare const layoutOption: LayoutOption;
|
|
77
|
+
createPageContext('ww', 'it', 'page', 'id', { config }).then(({ layout }) => {
|
|
78
|
+
type _check = Expect<Equal<typeof layout, ILayout>>;
|
|
79
|
+
});
|
|
80
|
+
createPageContext('ww', 'it', 'page', 'id', { config, layout: { depth: 0 } }).then(({ layout }) => {
|
|
81
|
+
type _check = Expect<Equal<typeof layout, ILayout>>;
|
|
82
|
+
});
|
|
83
|
+
createPageContext('ww', 'it', 'page', 'id', { config, layout: false }).then(({ layout }) => {
|
|
84
|
+
type _check = Expect<Equal<typeof layout, undefined>>;
|
|
85
|
+
});
|
|
86
|
+
createPageContext('ww', 'it', 'page', 'id', { config, layout: layoutOption }).then(({ layout }) => {
|
|
87
|
+
type _check = Expect<Equal<typeof layout, ILayout | undefined>>;
|
|
88
|
+
});
|
|
89
|
+
createNotFoundContext('ww', 'it', 'page', 'id', { config, layout: false }).then(({ layout }) => {
|
|
90
|
+
type _check = Expect<Equal<typeof layout, undefined>>;
|
|
91
|
+
});
|
|
92
|
+
helpers.createPageContext('ww', 'it', 'page', 'id', { layout: false }).then(({ layout }) => {
|
|
93
|
+
type _check = Expect<Equal<typeof layout, undefined>>;
|
|
94
|
+
});
|
|
95
|
+
helpers.createPageContext('ww', 'it', 'page', 'id').then(({ layout }) => {
|
|
96
|
+
type _check = Expect<Equal<typeof layout, ILayout>>;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// headers are request scoped: per call only, never a bound default
|
|
100
|
+
declare const headers: ReadonlyHeaders;
|
|
101
|
+
// @ts-expect-error headers is not a default
|
|
102
|
+
createContextHelpers({ config, defaults: { headers } });
|
|
103
|
+
// @ts-expect-error skipping the layout is a per-call choice
|
|
104
|
+
createContextHelpers({ config, defaults: { layout: false } });
|
|
105
|
+
helpers.createPageContext('ww', 'it', 'page', 'id', { headers });
|
|
106
|
+
helpers.createLayoutContext('ww', 'it', { headers });
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { CollectionSlug, SanitizedConfig } from 'payload';
|
|
2
|
+
import type { LayoutContextOptions, LayoutOption, LayoutOptions, PageContextOptions, PageSelect } from '../types';
|
|
3
|
+
import { createLayoutContext, createNotFoundContext, createPageContext } from './context';
|
|
4
|
+
import { getMetadata } from './metadata';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options bound once for every call. Request-scoped values (`headers`) and
|
|
8
|
+
* per-page values (`select`, `populate`, `joins`) are passed per call;
|
|
9
|
+
* skipping the layout (`layout: false`) is a per-call choice too.
|
|
10
|
+
*/
|
|
11
|
+
type ContextDefaults = Omit<PageContextOptions, 'config' | 'headers' | 'select' | 'populate' | 'joins' | 'layout'> & {
|
|
12
|
+
layout?: LayoutOptions;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type PageOptions<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug>, TLayout extends LayoutOption> =
|
|
16
|
+
Omit<PageContextOptions<TSlug, TSelect, TLayout>, 'config'> & { config?: Promise<SanitizedConfig> };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Binds `config` and default options once, so templates don't pass them every time.
|
|
20
|
+
* Per-call options override the defaults.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // src/lib/context.ts in the app
|
|
24
|
+
* export const { createPageContext, createLayoutContext, getMetadata } =
|
|
25
|
+
* createContextHelpers({ config, defaults: { depth: 2 } });
|
|
26
|
+
*/
|
|
27
|
+
export function createContextHelpers({
|
|
28
|
+
config,
|
|
29
|
+
defaults = {},
|
|
30
|
+
}: {
|
|
31
|
+
config: Promise<SanitizedConfig>;
|
|
32
|
+
defaults?: ContextDefaults;
|
|
33
|
+
}) {
|
|
34
|
+
const layoutDefaults: Omit<LayoutContextOptions, 'config'> = {
|
|
35
|
+
draft: defaults.draft,
|
|
36
|
+
...defaults.layout,
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
createPageContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
40
|
+
market: string, locale: string, slug: TSlug, id: string, options?: PageOptions<TSlug, TSelect, TLayout>
|
|
41
|
+
) {
|
|
42
|
+
return createPageContext<TSlug, TSelect, TLayout>(market, locale, slug, id, { config, ...defaults as PageOptions<TSlug, TSelect, TLayout>, ...options });
|
|
43
|
+
},
|
|
44
|
+
createNotFoundContext<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined, TLayout extends LayoutOption = LayoutOptions>(
|
|
45
|
+
market: string, locale: string, slug: TSlug, id: string, options?: PageOptions<TSlug, TSelect, TLayout>
|
|
46
|
+
) {
|
|
47
|
+
return createNotFoundContext<TSlug, TSelect, TLayout>(market, locale, slug, id, { config, ...defaults as PageOptions<TSlug, TSelect, TLayout>, ...options });
|
|
48
|
+
},
|
|
49
|
+
createLayoutContext(market: string, locale: string, options?: Partial<LayoutContextOptions>) {
|
|
50
|
+
return createLayoutContext(market, locale, { config, ...layoutDefaults, ...options });
|
|
51
|
+
},
|
|
52
|
+
getMetadata<TSlug extends CollectionSlug, TSelect extends PageSelect<TSlug> = undefined>(
|
|
53
|
+
slug: TSlug, options?: Omit<PageContextOptions<TSlug, TSelect, LayoutOption>, 'config'>
|
|
54
|
+
) {
|
|
55
|
+
return getMetadata<TSlug, TSelect>(slug, config, { ...defaults, ...options });
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
package/src/server/layout.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
'
|
|
1
|
+
import 'server-only';
|
|
2
2
|
|
|
3
3
|
import { IApp, IAppExtended, ICategory, ILabel, ILayout, ILocale, ILocalizedMenu, IMarket, IRoute, IRouteLink, getRootCategory } from '@websolutespa/bom-core';
|
|
4
4
|
import type { User } from '@websolutespa/payload-plugin-bowl';
|
|
5
5
|
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
6
6
|
import { getRouteLinkCategory } from '../lib/route';
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
7
|
+
import { DEFAULT_LAYOUT_DEPTH } from '../lib/const';
|
|
8
|
+
import type { MixerContext, ReadOptions } from '../types';
|
|
9
|
+
import { buildReadOptions } from './mixer';
|
|
9
10
|
import type { MixerPayload } from './mixer-payload';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -23,6 +24,9 @@ import type { MixerPayload } from './mixer-payload';
|
|
|
23
24
|
*
|
|
24
25
|
* The route link tree is built from mixerContext.currentRoutes + mixerContext.categories
|
|
25
26
|
* using getRouteLinkCategory (pure fn, no store calls) from bom-mixer-models/server.
|
|
27
|
+
*
|
|
28
|
+
* The last argument accepts read options (depth, draft); depth defaults to
|
|
29
|
+
* DEFAULT_LAYOUT_DEPTH. A boolean is still accepted as the legacy draft flag.
|
|
26
30
|
*/
|
|
27
31
|
export async function getLayout<T extends IAppExtended = IAppExtended>(
|
|
28
32
|
market: string,
|
|
@@ -30,49 +34,51 @@ export async function getLayout<T extends IAppExtended = IAppExtended>(
|
|
|
30
34
|
payload: MixerPayload,
|
|
31
35
|
user: User,
|
|
32
36
|
mixerContext: MixerContext,
|
|
33
|
-
|
|
37
|
+
readOptions?: Pick<ReadOptions, 'depth' | 'draft'> | boolean
|
|
34
38
|
): Promise<ILayout<T>> {
|
|
35
|
-
const
|
|
39
|
+
const options = typeof readOptions === 'object' ? readOptions : { draft: readOptions };
|
|
40
|
+
const base = buildReadOptions({
|
|
41
|
+
user,
|
|
42
|
+
locale,
|
|
43
|
+
draft: options.draft,
|
|
44
|
+
depth: options.depth ?? DEFAULT_LAYOUT_DEPTH,
|
|
45
|
+
});
|
|
36
46
|
const findOpts = { ...base, pagination: false, limit: 10000 } as const;
|
|
37
47
|
|
|
38
|
-
// markets
|
|
39
|
-
const marketsResult = await
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
// independent reads: markets, locales (global with an `items` array), labels, app global, menus
|
|
49
|
+
const [marketsResult, localeGlobal, labelsResult, app, menusResult] = await Promise.all([
|
|
50
|
+
payload.find({
|
|
51
|
+
...findOpts,
|
|
52
|
+
collection: 'market' as CollectionSlug,
|
|
53
|
+
context: mixerContext,
|
|
54
|
+
}),
|
|
55
|
+
payload.findGlobal({
|
|
56
|
+
...base,
|
|
57
|
+
slug: 'locale' as GlobalSlug,
|
|
58
|
+
context: mixerContext,
|
|
59
|
+
}) as unknown as Promise<{ items: ILocale[] }>,
|
|
60
|
+
payload.find({
|
|
61
|
+
...findOpts,
|
|
62
|
+
collection: 'label' as CollectionSlug,
|
|
63
|
+
context: mixerContext,
|
|
64
|
+
}),
|
|
65
|
+
// app global (slug 'app', registered by payload-plugin-bowl)
|
|
66
|
+
payload.findGlobal({
|
|
67
|
+
...base,
|
|
68
|
+
slug: 'app' as GlobalSlug,
|
|
69
|
+
context: mixerContext,
|
|
70
|
+
}) as unknown as Promise<IApp<T>>,
|
|
71
|
+
payload.find({
|
|
72
|
+
...findOpts,
|
|
73
|
+
collection: 'menu' as CollectionSlug,
|
|
74
|
+
context: mixerContext,
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
45
77
|
|
|
46
|
-
|
|
47
|
-
const localeGlobal = await payload.findGlobal({
|
|
48
|
-
...base,
|
|
49
|
-
slug: 'locale' as GlobalSlug,
|
|
50
|
-
context: mixerContext,
|
|
51
|
-
}) as unknown as { items: ILocale[] };
|
|
78
|
+
const markets = marketsResult.docs as unknown as IMarket[];
|
|
52
79
|
const locales = (localeGlobal?.items ?? []).filter(x => x.isActive);
|
|
53
|
-
|
|
54
|
-
// labels
|
|
55
|
-
const labelsResult = await payload.find({
|
|
56
|
-
...findOpts,
|
|
57
|
-
collection: 'label' as CollectionSlug,
|
|
58
|
-
context: mixerContext,
|
|
59
|
-
});
|
|
60
80
|
const labels = labelsResult.docs as unknown as ILabel[];
|
|
61
81
|
|
|
62
|
-
// app global (slug 'app', registered by payload-plugin-bowl)
|
|
63
|
-
const app = await payload.findGlobal({
|
|
64
|
-
...base,
|
|
65
|
-
slug: 'app' as GlobalSlug,
|
|
66
|
-
context: mixerContext,
|
|
67
|
-
}) as unknown as IApp<T>;
|
|
68
|
-
|
|
69
|
-
// menus
|
|
70
|
-
const menusResult = await payload.find({
|
|
71
|
-
...findOpts,
|
|
72
|
-
collection: 'menu' as CollectionSlug,
|
|
73
|
-
context: mixerContext,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
82
|
const menu: Record<string, ILocalizedMenu> = {};
|
|
77
83
|
(menusResult.docs as unknown as ILocalizedMenu[]).forEach(x => {
|
|
78
84
|
menu[String(x.id)] = x;
|
package/src/server/lazy.ts
CHANGED