@nitrogenbuilder/connector-payload 0.1.42 → 0.1.44
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/dist/collection-registry.d.ts +6 -0
- package/dist/collection-registry.js +10 -0
- package/dist/components/NitrogenEditButtonRuntime.js +21 -1
- package/dist/components/NitrogenViewButtonRuntime.js +21 -1
- package/dist/endpoints/all.js +13 -10
- package/dist/endpoints/batch.js +4 -4
- package/dist/endpoints/collection-endpoints.js +8 -5
- package/dist/endpoints/ctrlLinkResolver.d.ts +3 -0
- package/dist/endpoints/ctrlLinkResolver.js +228 -0
- package/dist/endpoints/helpers.d.ts +45 -0
- package/dist/endpoints/helpers.js +46 -6
- package/dist/endpoints/templates.js +14 -11
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The plugin registers collections at init time via `registerCollection()`.
|
|
5
5
|
*/
|
|
6
|
+
type RegisteredCollection = {
|
|
7
|
+
collectionSlug: string;
|
|
8
|
+
routePattern?: string;
|
|
9
|
+
};
|
|
6
10
|
export declare function registerCollection(alias: string, collectionSlug: string, routePattern?: string): void;
|
|
7
11
|
export declare function resolveCollection(postType: string): string;
|
|
8
12
|
export declare function resolveCollectionRoutePattern(postType: string): string | undefined;
|
|
13
|
+
export declare function getRegisteredCollections(): RegisteredCollection[];
|
|
9
14
|
export declare function buildRelativePermalink(postType: string, slug: string): string;
|
|
10
15
|
export declare function buildPermalink(postType: string, slug: string, frontendUrl?: string): string;
|
|
16
|
+
export {};
|
|
@@ -33,6 +33,16 @@ export function resolveCollection(postType) {
|
|
|
33
33
|
export function resolveCollectionRoutePattern(postType) {
|
|
34
34
|
return registeredCollections.get(postType)?.routePattern;
|
|
35
35
|
}
|
|
36
|
+
export function getRegisteredCollections() {
|
|
37
|
+
const collectionsBySlug = new Map();
|
|
38
|
+
for (const collection of registeredCollections.values()) {
|
|
39
|
+
const existing = collectionsBySlug.get(collection.collectionSlug);
|
|
40
|
+
if (!existing || (!existing.routePattern && collection.routePattern)) {
|
|
41
|
+
collectionsBySlug.set(collection.collectionSlug, collection);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return Array.from(collectionsBySlug.values());
|
|
45
|
+
}
|
|
36
46
|
export function buildRelativePermalink(postType, slug) {
|
|
37
47
|
return buildRelativePath(resolveCollectionRoutePattern(postType), slug);
|
|
38
48
|
}
|
|
@@ -38,6 +38,23 @@ function normalizeRoutePattern(routePattern) {
|
|
|
38
38
|
function trimSlashes(value) {
|
|
39
39
|
return value.replace(/^\/+|\/+$/g, '');
|
|
40
40
|
}
|
|
41
|
+
function normalizeRelativePath(value) {
|
|
42
|
+
const trimmed = value.trim();
|
|
43
|
+
if (!trimmed || trimmed === '/home')
|
|
44
|
+
return '/';
|
|
45
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
46
|
+
}
|
|
47
|
+
function getBreadcrumbRelativePath(data) {
|
|
48
|
+
if (!data || typeof data !== 'object')
|
|
49
|
+
return undefined;
|
|
50
|
+
const breadcrumbs = data.breadcrumbs;
|
|
51
|
+
const lastUrl = Array.isArray(breadcrumbs)
|
|
52
|
+
? breadcrumbs.at(-1)?.url
|
|
53
|
+
: undefined;
|
|
54
|
+
return typeof lastUrl === 'string' && lastUrl.trim()
|
|
55
|
+
? normalizeRelativePath(lastUrl)
|
|
56
|
+
: undefined;
|
|
57
|
+
}
|
|
41
58
|
function buildRelativePath(slug, routePattern, templateSlug) {
|
|
42
59
|
const normalizedRoutePattern = normalizeRoutePattern(routePattern);
|
|
43
60
|
if (normalizedRoutePattern) {
|
|
@@ -60,6 +77,7 @@ export const NitrogenEditButtonRuntime = ({ collection, routePattern }) => {
|
|
|
60
77
|
const [id, setId] = useState(undefined);
|
|
61
78
|
const [token, setToken] = useState(undefined);
|
|
62
79
|
const [slug, setSlug] = useState(undefined);
|
|
80
|
+
const [documentRelativePath, setDocumentRelativePath] = useState(undefined);
|
|
63
81
|
const [frontendUrl, setFrontendUrl] = useState(undefined);
|
|
64
82
|
const [instanceUrl, setInstanceUrl] = useState(undefined);
|
|
65
83
|
const [authorId, setAuthorId] = useState(undefined);
|
|
@@ -73,6 +91,7 @@ export const NitrogenEditButtonRuntime = ({ collection, routePattern }) => {
|
|
|
73
91
|
fetch(`/api/${collection}/${docId}`)
|
|
74
92
|
.then((res) => res.json())
|
|
75
93
|
.then((data) => {
|
|
94
|
+
setDocumentRelativePath(getBreadcrumbRelativePath(data));
|
|
76
95
|
if (data.slug) {
|
|
77
96
|
setSlug(data.slug);
|
|
78
97
|
}
|
|
@@ -142,7 +161,8 @@ export const NitrogenEditButtonRuntime = ({ collection, routePattern }) => {
|
|
|
142
161
|
const editHref = `${editorBase}/nitrogen-editor?token=${encodeURIComponent(token)}&collection=${encodeURIComponent(collection)}&pageId=${id}&authorId=${encodedAuthorId}&author=${encodedAuthorId}${developmentFlags}`;
|
|
143
162
|
const viewRelativePath = collection === 'nitrogen-templates'
|
|
144
163
|
? `/nitrogen-templates/${trimSlashes(slug || '')}`
|
|
145
|
-
:
|
|
164
|
+
: documentRelativePath ||
|
|
165
|
+
buildRelativePath(slug || '', routePattern, templateSlug ?? undefined);
|
|
146
166
|
const viewHref = `${frontendUrl}${viewRelativePath}`;
|
|
147
167
|
const editButtonVars = isDevelopmentMode
|
|
148
168
|
? nitrogenEditDevButtonVars
|
|
@@ -24,6 +24,23 @@ function normalizeRoutePattern(routePattern) {
|
|
|
24
24
|
function trimSlashes(value) {
|
|
25
25
|
return value.replace(/^\/+|\/+$/g, '');
|
|
26
26
|
}
|
|
27
|
+
function normalizeRelativePath(value) {
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (!trimmed || trimmed === '/home')
|
|
30
|
+
return '/';
|
|
31
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
32
|
+
}
|
|
33
|
+
function getBreadcrumbRelativePath(data) {
|
|
34
|
+
if (!data || typeof data !== 'object')
|
|
35
|
+
return undefined;
|
|
36
|
+
const breadcrumbs = data.breadcrumbs;
|
|
37
|
+
const lastUrl = Array.isArray(breadcrumbs)
|
|
38
|
+
? breadcrumbs.at(-1)?.url
|
|
39
|
+
: undefined;
|
|
40
|
+
return typeof lastUrl === 'string' && lastUrl.trim()
|
|
41
|
+
? normalizeRelativePath(lastUrl)
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
27
44
|
function buildRelativePath(slug, routePattern, templateSlug) {
|
|
28
45
|
const normalizedRoutePattern = normalizeRoutePattern(routePattern);
|
|
29
46
|
if (normalizedRoutePattern) {
|
|
@@ -44,6 +61,7 @@ function buildRelativePath(slug, routePattern, templateSlug) {
|
|
|
44
61
|
}
|
|
45
62
|
export const NitrogenViewButtonRuntime = ({ collection, routePattern }) => {
|
|
46
63
|
const [slug, setSlug] = useState(undefined);
|
|
64
|
+
const [documentRelativePath, setDocumentRelativePath] = useState(undefined);
|
|
47
65
|
const [siteUrl, setSiteUrl] = useState(undefined);
|
|
48
66
|
const [templateSlug, setTemplateSlug] = useState(undefined);
|
|
49
67
|
useEffect(() => {
|
|
@@ -55,6 +73,7 @@ export const NitrogenViewButtonRuntime = ({ collection, routePattern }) => {
|
|
|
55
73
|
fetch(`/api/${collection}/${docId}`)
|
|
56
74
|
.then((res) => res.json())
|
|
57
75
|
.then((data) => {
|
|
76
|
+
setDocumentRelativePath(getBreadcrumbRelativePath(data));
|
|
58
77
|
if (data.slug) {
|
|
59
78
|
setSlug(data.slug);
|
|
60
79
|
}
|
|
@@ -105,7 +124,8 @@ export const NitrogenViewButtonRuntime = ({ collection, routePattern }) => {
|
|
|
105
124
|
}
|
|
106
125
|
const relativePath = collection === 'nitrogen-templates'
|
|
107
126
|
? `/nitrogen-templates/${trimSlashes(slug)}`
|
|
108
|
-
:
|
|
127
|
+
: documentRelativePath ||
|
|
128
|
+
buildRelativePath(slug, routePattern, templateSlug ?? undefined);
|
|
109
129
|
const href = `${siteUrl}${relativePath}`;
|
|
110
130
|
return (_jsx(Button, { buttonStyle: "primary", el: "anchor", margin: false, newTab: true, url: href, extraButtonProps: {
|
|
111
131
|
style: nitrogenViewButtonVars,
|
package/dist/endpoints/all.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getNitrogenSettings } from './helpers.js';
|
|
2
|
-
import {
|
|
1
|
+
import { getDocumentPermalink, getDocumentRelativePermalink, getNitrogenSettings, } from './helpers.js';
|
|
2
|
+
import { resolveCollection } from '../collection-registry.js';
|
|
3
3
|
export const allEndpoints = [
|
|
4
4
|
// GET /api/nitrogen/v1/all — List all items with filtering
|
|
5
5
|
{
|
|
@@ -30,14 +30,17 @@ export const allEndpoints = [
|
|
|
30
30
|
limit: perPage,
|
|
31
31
|
depth: 0,
|
|
32
32
|
});
|
|
33
|
-
const items = result.docs.map((doc) =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
const items = result.docs.map((doc) => {
|
|
34
|
+
const document = doc;
|
|
35
|
+
return {
|
|
36
|
+
id: document.id,
|
|
37
|
+
title: String(document.title || ''),
|
|
38
|
+
slug: String(document.slug || ''),
|
|
39
|
+
permalink: getDocumentPermalink(document, settings, postType),
|
|
40
|
+
relative_permalink: getDocumentRelativePermalink(document, postType),
|
|
41
|
+
type: collection,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
41
44
|
return Response.json(items);
|
|
42
45
|
}
|
|
43
46
|
catch {
|
package/dist/endpoints/batch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getNitrogenSettings, buildDynamicData, buildPageResponse, } from "./helpers.js";
|
|
1
|
+
import { resolveCollection } from "../collection-registry.js";
|
|
2
|
+
import { getNitrogenSettings, buildDynamicData, buildPageResponse, getDocumentPermalink, getDocumentRelativePermalink, } from "./helpers.js";
|
|
3
3
|
function toArray(value) {
|
|
4
4
|
if (Array.isArray(value)) {
|
|
5
5
|
return value.map((item) => String(item).trim()).filter(Boolean);
|
|
@@ -286,8 +286,8 @@ export const batchEndpoints = [
|
|
|
286
286
|
id: d.id,
|
|
287
287
|
title: String(d.title || ""),
|
|
288
288
|
slug,
|
|
289
|
-
permalink:
|
|
290
|
-
relative_permalink:
|
|
289
|
+
permalink: getDocumentPermalink(d, settings, endpoint),
|
|
290
|
+
relative_permalink: getDocumentRelativePermalink(d, endpoint),
|
|
291
291
|
};
|
|
292
292
|
});
|
|
293
293
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getNitrogenSettings, buildDynamicData,
|
|
1
|
+
import { getNitrogenSettings, buildDynamicData, buildResolvedPageResponse, buildResolvedListItemResponse, getTemplateForType, requireAuth, } from './helpers.js';
|
|
2
2
|
/**
|
|
3
3
|
* Creates a complete set of CRUD endpoints for a given Payload collection.
|
|
4
4
|
*
|
|
@@ -28,7 +28,7 @@ export function createCollectionEndpoints(collectionSlug, endpointPrefix) {
|
|
|
28
28
|
limit: 0,
|
|
29
29
|
depth: 1,
|
|
30
30
|
});
|
|
31
|
-
const items = result.docs.map((doc) =>
|
|
31
|
+
const items = await Promise.all(result.docs.map((doc) => buildResolvedListItemResponse(payload, doc, settings, collectionSlug)));
|
|
32
32
|
return Response.json(items);
|
|
33
33
|
},
|
|
34
34
|
},
|
|
@@ -75,8 +75,9 @@ export function createCollectionEndpoints(collectionSlug, endpointPrefix) {
|
|
|
75
75
|
getTemplateForType(payload, collectionSlug),
|
|
76
76
|
]);
|
|
77
77
|
const dynamicData = buildDynamicData(doc, settings);
|
|
78
|
+
const response = await buildResolvedPageResponse(payload, doc, settings, dynamicData, collectionSlug);
|
|
78
79
|
return Response.json({
|
|
79
|
-
...
|
|
80
|
+
...response,
|
|
80
81
|
template: pageTemplate,
|
|
81
82
|
headerTemplate,
|
|
82
83
|
footerTemplate,
|
|
@@ -190,8 +191,9 @@ export function createCollectionEndpoints(collectionSlug, endpointPrefix) {
|
|
|
190
191
|
}
|
|
191
192
|
const doc = result.docs[0];
|
|
192
193
|
const dynamicData = buildDynamicData(doc, settings);
|
|
194
|
+
const response = await buildResolvedPageResponse(payload, doc, settings, dynamicData, collectionSlug);
|
|
193
195
|
return Response.json({
|
|
194
|
-
...
|
|
196
|
+
...response,
|
|
195
197
|
template: pageTemplate,
|
|
196
198
|
headerTemplate,
|
|
197
199
|
footerTemplate,
|
|
@@ -217,8 +219,9 @@ export function createCollectionEndpoints(collectionSlug, endpointPrefix) {
|
|
|
217
219
|
}
|
|
218
220
|
const doc = result.docs[0];
|
|
219
221
|
const dynamicData = buildDynamicData(doc, settings);
|
|
222
|
+
const response = await buildResolvedPageResponse(payload, doc, settings, dynamicData, collectionSlug);
|
|
220
223
|
return Response.json({
|
|
221
|
-
...
|
|
224
|
+
...response,
|
|
222
225
|
template: pageTemplate,
|
|
223
226
|
headerTemplate,
|
|
224
227
|
footerTemplate,
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { buildRelativePermalink, getRegisteredCollections, } from '../collection-registry.js';
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
4
|
+
}
|
|
5
|
+
function getString(value) {
|
|
6
|
+
return typeof value === 'string' ? value : '';
|
|
7
|
+
}
|
|
8
|
+
function getId(value) {
|
|
9
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
10
|
+
return value;
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function cloneJson(value) {
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return value;
|
|
16
|
+
return JSON.parse(JSON.stringify(value));
|
|
17
|
+
}
|
|
18
|
+
function normalizeRelativePath(value) {
|
|
19
|
+
const trimmed = value.trim();
|
|
20
|
+
if (!trimmed || trimmed === '/home')
|
|
21
|
+
return '/';
|
|
22
|
+
try {
|
|
23
|
+
const parsed = new URL(trimmed);
|
|
24
|
+
return normalizeRelativePath(`${parsed.pathname}${parsed.search}${parsed.hash}`);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Treat as already relative.
|
|
28
|
+
}
|
|
29
|
+
const [path = '', suffix = ''] = trimmed.split(/([?#].*)/, 2);
|
|
30
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
31
|
+
const withoutTrailingSlash = normalizedPath.length > 1 ? normalizedPath.replace(/\/+$/, '') : normalizedPath;
|
|
32
|
+
return `${withoutTrailingSlash}${suffix}`;
|
|
33
|
+
}
|
|
34
|
+
function getPathname(value) {
|
|
35
|
+
const normalized = normalizeRelativePath(value);
|
|
36
|
+
return normalized.split(/[?#]/)[0] || '/';
|
|
37
|
+
}
|
|
38
|
+
function matchRoutePattern(pathname, routePattern) {
|
|
39
|
+
if (!routePattern)
|
|
40
|
+
return 1;
|
|
41
|
+
const pattern = getPathname(routePattern);
|
|
42
|
+
const placeholderMatch = pattern.match(/\[(?:\.\.\.)?slug\]/);
|
|
43
|
+
if (placeholderMatch) {
|
|
44
|
+
const base = pattern.slice(0, placeholderMatch.index).replace(/\/+$/, '');
|
|
45
|
+
if (!base) {
|
|
46
|
+
return pathname === '/' ? 0 : 20;
|
|
47
|
+
}
|
|
48
|
+
return pathname.startsWith(`${base}/`) ? 100 + base.length : 0;
|
|
49
|
+
}
|
|
50
|
+
if (pathname === pattern)
|
|
51
|
+
return 90 + pattern.length;
|
|
52
|
+
return pathname.startsWith(`${pattern}/`) ? 50 + pattern.length : 0;
|
|
53
|
+
}
|
|
54
|
+
function getCollectionCandidates(link, internalRef) {
|
|
55
|
+
const explicitCollection = getString(internalRef.collection) ||
|
|
56
|
+
getString(internalRef.collectionSlug) ||
|
|
57
|
+
getString(link.collection) ||
|
|
58
|
+
getString(link.collectionSlug);
|
|
59
|
+
const savedPath = getString(internalRef.relativePermalink) ||
|
|
60
|
+
getString(link.url) ||
|
|
61
|
+
getString(internalRef.slug);
|
|
62
|
+
const pathname = getPathname(savedPath);
|
|
63
|
+
const candidates = new Map();
|
|
64
|
+
if (explicitCollection) {
|
|
65
|
+
candidates.set(explicitCollection, Number.MAX_SAFE_INTEGER);
|
|
66
|
+
}
|
|
67
|
+
for (const collection of getRegisteredCollections()) {
|
|
68
|
+
const score = matchRoutePattern(pathname, collection.routePattern);
|
|
69
|
+
if (score <= 0)
|
|
70
|
+
continue;
|
|
71
|
+
candidates.set(collection.collectionSlug, Math.max(candidates.get(collection.collectionSlug) || 0, score));
|
|
72
|
+
}
|
|
73
|
+
if (!candidates.has('pages')) {
|
|
74
|
+
candidates.set('pages', 1);
|
|
75
|
+
}
|
|
76
|
+
return Array.from(candidates.entries())
|
|
77
|
+
.sort((a, b) => b[1] - a[1])
|
|
78
|
+
.map(([collection]) => collection);
|
|
79
|
+
}
|
|
80
|
+
function getDocumentRelativePermalink(doc, collectionSlug) {
|
|
81
|
+
const breadcrumbs = doc.breadcrumbs;
|
|
82
|
+
const lastUrl = Array.isArray(breadcrumbs)
|
|
83
|
+
? breadcrumbs.at(-1)?.url
|
|
84
|
+
: undefined;
|
|
85
|
+
if (typeof lastUrl === 'string' && lastUrl.trim()) {
|
|
86
|
+
return normalizeRelativePath(lastUrl);
|
|
87
|
+
}
|
|
88
|
+
return buildRelativePermalink(collectionSlug, String(doc.slug || ''));
|
|
89
|
+
}
|
|
90
|
+
function getDocumentPermalink(doc, settings, collectionSlug) {
|
|
91
|
+
const relativePermalink = getDocumentRelativePermalink(doc, collectionSlug);
|
|
92
|
+
const baseUrl = (settings.frontendUrl || '').replace(/\/$/, '');
|
|
93
|
+
return baseUrl ? `${baseUrl}${relativePermalink}` : relativePermalink;
|
|
94
|
+
}
|
|
95
|
+
function getPayloadBaseUrl(settings) {
|
|
96
|
+
return (process.env.NEXT_PUBLIC_PAYLOAD_API_URL ||
|
|
97
|
+
process.env.NEXT_PUBLIC_SERVER_URL ||
|
|
98
|
+
settings.nitrogenEditorUrl ||
|
|
99
|
+
settings.instanceUrl ||
|
|
100
|
+
'').replace(/\/$/, '');
|
|
101
|
+
}
|
|
102
|
+
function resolveMediaUrl(url, settings) {
|
|
103
|
+
if (!url || /^https?:\/\//i.test(url))
|
|
104
|
+
return url;
|
|
105
|
+
const baseUrl = getPayloadBaseUrl(settings);
|
|
106
|
+
const relativeUrl = url.startsWith('/') ? url : `/${url}`;
|
|
107
|
+
return baseUrl ? `${baseUrl}${relativeUrl}` : relativeUrl;
|
|
108
|
+
}
|
|
109
|
+
async function findInternalTarget(context, link, internalRef) {
|
|
110
|
+
const entryId = getId(internalRef.entryId);
|
|
111
|
+
if (entryId === null)
|
|
112
|
+
return null;
|
|
113
|
+
const candidates = getCollectionCandidates(link, internalRef);
|
|
114
|
+
for (const collection of candidates) {
|
|
115
|
+
try {
|
|
116
|
+
const doc = (await context.payload.findByID({
|
|
117
|
+
collection: collection,
|
|
118
|
+
id: entryId,
|
|
119
|
+
depth: 1,
|
|
120
|
+
disableErrors: true,
|
|
121
|
+
}));
|
|
122
|
+
if (!doc)
|
|
123
|
+
continue;
|
|
124
|
+
return {
|
|
125
|
+
slug: String(doc.slug || internalRef.slug || ''),
|
|
126
|
+
relativePermalink: getDocumentRelativePermalink(doc, collection),
|
|
127
|
+
url: getDocumentPermalink(doc, context.settings, collection),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Try the next candidate collection.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
async function resolveInternalLink(context, link, internalRef) {
|
|
137
|
+
const entryId = getId(internalRef.entryId);
|
|
138
|
+
if (entryId === null)
|
|
139
|
+
return null;
|
|
140
|
+
const key = [
|
|
141
|
+
entryId,
|
|
142
|
+
getString(internalRef.relativePermalink),
|
|
143
|
+
getString(link.url),
|
|
144
|
+
getString(internalRef.collection),
|
|
145
|
+
getString(link.collection),
|
|
146
|
+
].join(':');
|
|
147
|
+
if (!context.internalLinks.has(key)) {
|
|
148
|
+
context.internalLinks.set(key, findInternalTarget(context, link, internalRef));
|
|
149
|
+
}
|
|
150
|
+
return context.internalLinks.get(key);
|
|
151
|
+
}
|
|
152
|
+
async function findMediaTarget(context, mediaRef) {
|
|
153
|
+
const mediaId = getId(mediaRef.mediaId);
|
|
154
|
+
if (mediaId === null)
|
|
155
|
+
return null;
|
|
156
|
+
try {
|
|
157
|
+
const doc = (await context.payload.findByID({
|
|
158
|
+
collection: 'media',
|
|
159
|
+
id: mediaId,
|
|
160
|
+
depth: 0,
|
|
161
|
+
disableErrors: true,
|
|
162
|
+
}));
|
|
163
|
+
const url = doc?.url || '';
|
|
164
|
+
if (!doc || !url)
|
|
165
|
+
return null;
|
|
166
|
+
return {
|
|
167
|
+
filename: doc.filename || getString(mediaRef.filename),
|
|
168
|
+
mimeType: doc.mimeType || getString(mediaRef.mimeType),
|
|
169
|
+
url: resolveMediaUrl(url, context.settings),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function resolveMediaLink(context, mediaRef) {
|
|
177
|
+
const mediaId = getId(mediaRef.mediaId);
|
|
178
|
+
if (mediaId === null)
|
|
179
|
+
return null;
|
|
180
|
+
const key = String(mediaId);
|
|
181
|
+
if (!context.mediaLinks.has(key)) {
|
|
182
|
+
context.mediaLinks.set(key, findMediaTarget(context, mediaRef));
|
|
183
|
+
}
|
|
184
|
+
return context.mediaLinks.get(key);
|
|
185
|
+
}
|
|
186
|
+
async function resolveLinkRecord(context, link) {
|
|
187
|
+
if (link.type === 'internal' && isRecord(link.internalRef)) {
|
|
188
|
+
const resolved = await resolveInternalLink(context, link, link.internalRef);
|
|
189
|
+
if (!resolved)
|
|
190
|
+
return;
|
|
191
|
+
link.url = resolved.url;
|
|
192
|
+
link.internalRef.slug = resolved.slug;
|
|
193
|
+
link.internalRef.relativePermalink = resolved.relativePermalink;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (link.type === 'media' && isRecord(link.mediaRef)) {
|
|
197
|
+
const resolved = await resolveMediaLink(context, link.mediaRef);
|
|
198
|
+
if (!resolved)
|
|
199
|
+
return;
|
|
200
|
+
link.url = resolved.url;
|
|
201
|
+
link.mediaRef.url = resolved.url;
|
|
202
|
+
link.mediaRef.filename = resolved.filename;
|
|
203
|
+
link.mediaRef.mimeType = resolved.mimeType;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function walk(context, value) {
|
|
207
|
+
if (Array.isArray(value)) {
|
|
208
|
+
await Promise.all(value.map((item) => walk(context, item)));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (!isRecord(value))
|
|
212
|
+
return;
|
|
213
|
+
await resolveLinkRecord(context, value);
|
|
214
|
+
await Promise.all(Object.values(value).map((item) => walk(context, item)));
|
|
215
|
+
}
|
|
216
|
+
export async function resolveCtrlLinksInNitrogenData(payload, value, settings) {
|
|
217
|
+
if (!value)
|
|
218
|
+
return value;
|
|
219
|
+
const cloned = cloneJson(value);
|
|
220
|
+
const context = {
|
|
221
|
+
internalLinks: new Map(),
|
|
222
|
+
mediaLinks: new Map(),
|
|
223
|
+
payload,
|
|
224
|
+
settings,
|
|
225
|
+
};
|
|
226
|
+
await walk(context, cloned);
|
|
227
|
+
return cloned;
|
|
228
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Payload, PayloadRequest } from 'payload';
|
|
2
2
|
import type { JsonObject, NitrogenPageDoc, NitrogenTemplateDoc, NitrogenSettingsGlobal, MediaDoc } from '../types.js';
|
|
3
|
+
export declare function getDocumentRelativePermalink(doc: NitrogenPageDoc | NitrogenTemplateDoc | Record<string, unknown>, collectionSlug?: string): string;
|
|
4
|
+
export declare function getDocumentPermalink(doc: NitrogenPageDoc | NitrogenTemplateDoc | Record<string, unknown>, settings: NitrogenSettingsGlobal, collectionSlug?: string): string;
|
|
3
5
|
export interface TemplateRef {
|
|
4
6
|
ID: string | number;
|
|
5
7
|
content: string;
|
|
@@ -29,6 +31,29 @@ export declare function buildPageResponse(doc: NitrogenPageDoc | NitrogenTemplat
|
|
|
29
31
|
wysiwygColors: {};
|
|
30
32
|
};
|
|
31
33
|
};
|
|
34
|
+
export declare function buildResolvedPageResponse(payload: Payload, doc: NitrogenPageDoc | NitrogenTemplateDoc, settings: NitrogenSettingsGlobal, dynamicData: JsonObject, collectionSlug?: string): Promise<{
|
|
35
|
+
id: string | number;
|
|
36
|
+
token: string;
|
|
37
|
+
title: string;
|
|
38
|
+
author: string | number;
|
|
39
|
+
slug: string;
|
|
40
|
+
permalink: string;
|
|
41
|
+
relative_permalink: string;
|
|
42
|
+
content: string;
|
|
43
|
+
dynamic_data: JsonObject;
|
|
44
|
+
settings: JsonObject;
|
|
45
|
+
nitrogen_settings: {
|
|
46
|
+
variables?: Record<string, {
|
|
47
|
+
type?: string;
|
|
48
|
+
value?: string | boolean;
|
|
49
|
+
}> | Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
value?: string;
|
|
52
|
+
}>;
|
|
53
|
+
urlMaps: never[];
|
|
54
|
+
wysiwygColors: {};
|
|
55
|
+
};
|
|
56
|
+
}>;
|
|
32
57
|
export declare function buildListItemResponse(doc: NitrogenPageDoc | NitrogenTemplateDoc, settings: NitrogenSettingsGlobal, collectionSlug?: string): {
|
|
33
58
|
id: string | number;
|
|
34
59
|
title: string;
|
|
@@ -49,6 +74,26 @@ export declare function buildListItemResponse(doc: NitrogenPageDoc | NitrogenTem
|
|
|
49
74
|
wysiwygColors: {};
|
|
50
75
|
};
|
|
51
76
|
};
|
|
77
|
+
export declare function buildResolvedListItemResponse(payload: Payload, doc: NitrogenPageDoc | NitrogenTemplateDoc, settings: NitrogenSettingsGlobal, collectionSlug?: string): Promise<{
|
|
78
|
+
id: string | number;
|
|
79
|
+
title: string;
|
|
80
|
+
slug: string;
|
|
81
|
+
permalink: string;
|
|
82
|
+
relative_permalink: string;
|
|
83
|
+
content: string;
|
|
84
|
+
settings: JsonObject;
|
|
85
|
+
nitrogen_settings: {
|
|
86
|
+
variables?: Record<string, {
|
|
87
|
+
type?: string;
|
|
88
|
+
value?: string | boolean;
|
|
89
|
+
}> | Array<{
|
|
90
|
+
name: string;
|
|
91
|
+
value?: string;
|
|
92
|
+
}>;
|
|
93
|
+
urlMaps: never[];
|
|
94
|
+
wysiwygColors: {};
|
|
95
|
+
};
|
|
96
|
+
}>;
|
|
52
97
|
export declare function buildMediaItemResponse(doc: MediaDoc): {
|
|
53
98
|
id: string | number;
|
|
54
99
|
filename: string;
|
|
@@ -1,4 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildRelativePermalink } from '../collection-registry.js';
|
|
2
|
+
import { resolveCtrlLinksInNitrogenData } from './ctrlLinkResolver.js';
|
|
3
|
+
function normalizeRelativePath(value) {
|
|
4
|
+
const trimmed = value.trim();
|
|
5
|
+
if (!trimmed || trimmed === '/home')
|
|
6
|
+
return '/';
|
|
7
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
8
|
+
}
|
|
9
|
+
export function getDocumentRelativePermalink(doc, collectionSlug = 'pages') {
|
|
10
|
+
const breadcrumbs = doc.breadcrumbs;
|
|
11
|
+
const lastUrl = Array.isArray(breadcrumbs)
|
|
12
|
+
? breadcrumbs.at(-1)?.url
|
|
13
|
+
: undefined;
|
|
14
|
+
if (typeof lastUrl === 'string' && lastUrl.trim()) {
|
|
15
|
+
return normalizeRelativePath(lastUrl);
|
|
16
|
+
}
|
|
17
|
+
return buildRelativePermalink(collectionSlug, String(doc.slug || ''));
|
|
18
|
+
}
|
|
19
|
+
export function getDocumentPermalink(doc, settings, collectionSlug = 'pages') {
|
|
20
|
+
const relativePermalink = getDocumentRelativePermalink(doc, collectionSlug);
|
|
21
|
+
const baseUrl = (settings.frontendUrl || '').replace(/\/$/, '');
|
|
22
|
+
return baseUrl ? `${baseUrl}${relativePermalink}` : relativePermalink;
|
|
23
|
+
}
|
|
2
24
|
export function formatDate(date) {
|
|
3
25
|
const d = new Date(date);
|
|
4
26
|
const pad = (n) => String(n).padStart(2, '0');
|
|
@@ -60,8 +82,8 @@ export function buildPageResponse(doc, settings, dynamicData, collectionSlug = '
|
|
|
60
82
|
title: doc.title,
|
|
61
83
|
author: doc.author && typeof doc.author === 'object' ? doc.author.id : (doc.author || ''),
|
|
62
84
|
slug,
|
|
63
|
-
permalink:
|
|
64
|
-
relative_permalink:
|
|
85
|
+
permalink: getDocumentPermalink(doc, settings, collectionSlug),
|
|
86
|
+
relative_permalink: getDocumentRelativePermalink(doc, collectionSlug),
|
|
65
87
|
content: doc.nitrogenData ? JSON.stringify(doc.nitrogenData) : '[]',
|
|
66
88
|
dynamic_data: dynamicData,
|
|
67
89
|
settings: pageSettings || templateSettings || {
|
|
@@ -74,6 +96,13 @@ export function buildPageResponse(doc, settings, dynamicData, collectionSlug = '
|
|
|
74
96
|
},
|
|
75
97
|
};
|
|
76
98
|
}
|
|
99
|
+
export async function buildResolvedPageResponse(payload, doc, settings, dynamicData, collectionSlug = 'pages') {
|
|
100
|
+
const response = buildPageResponse(doc, settings, dynamicData, collectionSlug);
|
|
101
|
+
if (doc.nitrogenData) {
|
|
102
|
+
response.content = JSON.stringify(await resolveCtrlLinksInNitrogenData(payload, doc.nitrogenData, settings));
|
|
103
|
+
}
|
|
104
|
+
return response;
|
|
105
|
+
}
|
|
77
106
|
export function buildListItemResponse(doc, settings, collectionSlug = 'pages') {
|
|
78
107
|
const pageSettings = 'pageSettings' in doc ? doc.pageSettings : undefined;
|
|
79
108
|
const templateSettings = 'templateSettings' in doc ? doc.templateSettings : undefined;
|
|
@@ -82,8 +111,8 @@ export function buildListItemResponse(doc, settings, collectionSlug = 'pages') {
|
|
|
82
111
|
id: doc.id,
|
|
83
112
|
title: doc.title,
|
|
84
113
|
slug,
|
|
85
|
-
permalink:
|
|
86
|
-
relative_permalink:
|
|
114
|
+
permalink: getDocumentPermalink(doc, settings, collectionSlug),
|
|
115
|
+
relative_permalink: getDocumentRelativePermalink(doc, collectionSlug),
|
|
87
116
|
content: doc.nitrogenData ? JSON.stringify(doc.nitrogenData) : '[]',
|
|
88
117
|
settings: pageSettings || templateSettings || {},
|
|
89
118
|
nitrogen_settings: {
|
|
@@ -93,6 +122,13 @@ export function buildListItemResponse(doc, settings, collectionSlug = 'pages') {
|
|
|
93
122
|
},
|
|
94
123
|
};
|
|
95
124
|
}
|
|
125
|
+
export async function buildResolvedListItemResponse(payload, doc, settings, collectionSlug = 'pages') {
|
|
126
|
+
const response = buildListItemResponse(doc, settings, collectionSlug);
|
|
127
|
+
if (doc.nitrogenData) {
|
|
128
|
+
response.content = JSON.stringify(await resolveCtrlLinksInNitrogenData(payload, doc.nitrogenData, settings));
|
|
129
|
+
}
|
|
130
|
+
return response;
|
|
131
|
+
}
|
|
96
132
|
export function buildMediaItemResponse(doc) {
|
|
97
133
|
const filename = doc.filename || '';
|
|
98
134
|
const ext = filename.includes('.') ? `.${filename.split('.').pop()}` : '';
|
|
@@ -176,9 +212,13 @@ export async function getTemplateForType(payload, type) {
|
|
|
176
212
|
const doc = result.docs[0];
|
|
177
213
|
if (!doc)
|
|
178
214
|
return null;
|
|
215
|
+
const settings = await getNitrogenSettings(payload);
|
|
216
|
+
const resolvedNitrogenData = doc.nitrogenData
|
|
217
|
+
? await resolveCtrlLinksInNitrogenData(payload, doc.nitrogenData, settings)
|
|
218
|
+
: null;
|
|
179
219
|
return {
|
|
180
220
|
ID: doc.id,
|
|
181
|
-
content:
|
|
221
|
+
content: resolvedNitrogenData ? JSON.stringify(resolvedNitrogenData) : '[]',
|
|
182
222
|
};
|
|
183
223
|
}
|
|
184
224
|
catch {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { getNitrogenSettings, buildDynamicData,
|
|
2
|
-
import { buildPermalink, buildRelativePermalink } from '../collection-registry.js';
|
|
1
|
+
import { getNitrogenSettings, buildDynamicData, buildResolvedPageResponse, buildResolvedListItemResponse, getDocumentPermalink, getDocumentRelativePermalink, getTemplateForType, requireAuth, } from './helpers.js';
|
|
3
2
|
function isContentTemplateCollection(collection) {
|
|
4
3
|
return !!collection && collection !== 'header' && collection !== 'footer';
|
|
5
4
|
}
|
|
@@ -11,10 +10,11 @@ function getTemplatePreviewTarget(doc, settings, associatedCollection, associate
|
|
|
11
10
|
? associatedCollection
|
|
12
11
|
: 'nitrogen-templates';
|
|
13
12
|
const previewSlug = String(associatedDoc?.slug || doc.slug || '');
|
|
13
|
+
const previewDoc = associatedDoc || doc;
|
|
14
14
|
return {
|
|
15
15
|
slug: previewSlug,
|
|
16
|
-
permalink:
|
|
17
|
-
relative_permalink:
|
|
16
|
+
permalink: getDocumentPermalink(previewDoc, settings, previewCollection),
|
|
17
|
+
relative_permalink: getDocumentRelativePermalink(previewDoc, previewCollection),
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
export const templatesEndpoints = [
|
|
@@ -60,7 +60,7 @@ export const templatesEndpoints = [
|
|
|
60
60
|
// Collection may not exist — use template's own data
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
const response =
|
|
63
|
+
const response = await buildResolvedPageResponse(payload, doc, settings, dynamicData);
|
|
64
64
|
const previewTarget = getTemplatePreviewTarget(doc, settings, associatedCollection, associatedDoc);
|
|
65
65
|
return Response.json({
|
|
66
66
|
...response,
|
|
@@ -99,11 +99,14 @@ export const templatesEndpoints = [
|
|
|
99
99
|
limit: 0,
|
|
100
100
|
depth: 1,
|
|
101
101
|
});
|
|
102
|
-
const items = result.docs.map((doc) =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
const items = await Promise.all(result.docs.map(async (doc) => {
|
|
103
|
+
const response = await buildResolvedListItemResponse(payload, doc, settings);
|
|
104
|
+
return {
|
|
105
|
+
...response,
|
|
106
|
+
permalink: `${(settings.frontendUrl || '').replace(/\/$/, '')}/nitrogen-templates/${doc.slug}`,
|
|
107
|
+
relative_permalink: `/nitrogen-templates/${doc.slug}`,
|
|
108
|
+
postType: doc.associatedCollection || '',
|
|
109
|
+
};
|
|
107
110
|
}));
|
|
108
111
|
return Response.json(items);
|
|
109
112
|
},
|
|
@@ -147,7 +150,7 @@ export const templatesEndpoints = [
|
|
|
147
150
|
// Collection may not exist — use template's own data
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
|
-
const response =
|
|
153
|
+
const response = await buildResolvedPageResponse(payload, doc, settings, dynamicData);
|
|
151
154
|
const previewTarget = getTemplatePreviewTarget(doc, settings, associatedCollection, associatedDoc);
|
|
152
155
|
return Response.json({
|
|
153
156
|
...response,
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ interface NitrogenDocBase {
|
|
|
11
11
|
id: string | number;
|
|
12
12
|
title: string;
|
|
13
13
|
slug: string;
|
|
14
|
+
breadcrumbs?: Array<{
|
|
15
|
+
url?: string | null;
|
|
16
|
+
label?: string | null;
|
|
17
|
+
doc?: string | number | null;
|
|
18
|
+
}>;
|
|
14
19
|
status?: 'draft' | 'published';
|
|
15
20
|
author?: string | number | {
|
|
16
21
|
id: string | number;
|
package/package.json
CHANGED