@nitrogenbuilder/connector-payload 0.1.38 → 0.1.40
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/components/NitrogenComponentInventoryClient.d.ts +2 -1
- package/dist/components/NitrogenComponentInventoryClient.js +370 -63
- package/dist/components/NitrogenComponentInventoryField.js +12 -2
- package/dist/components/NitrogenComponentInventoryView.js +12 -2
- package/dist/components/NitrogenPackageVersionsField.d.ts +3 -0
- package/dist/components/NitrogenPackageVersionsField.js +138 -0
- package/dist/components/NitrogenPackageVersionsHeaderField.d.ts +3 -0
- package/dist/components/NitrogenPackageVersionsHeaderField.js +5 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +10 -0
- package/dist/editor/NitrogenEditorPage.js +6 -2
- package/dist/endpoints/component-inventory.js +274 -1
- package/dist/globals/NitrogenSettings.js +37 -0
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ type InventoryClientProps = {
|
|
|
8
8
|
isUnknown: boolean;
|
|
9
9
|
usages: NitrogenComponentUsageDoc[];
|
|
10
10
|
}>;
|
|
11
|
+
previewUrl: string;
|
|
11
12
|
};
|
|
12
|
-
export default function NitrogenComponentInventoryClient({ adminRoute, components, }: InventoryClientProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default function NitrogenComponentInventoryClient({ adminRoute, components, previewUrl, }: InventoryClientProps): import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
export {};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { formatAdminURL } from 'payload/shared';
|
|
5
|
+
const EDITABLE_PROP_TYPES = new Set([
|
|
6
|
+
'boolean',
|
|
7
|
+
'color',
|
|
8
|
+
'color-select',
|
|
9
|
+
'color-swatch',
|
|
10
|
+
'enum',
|
|
11
|
+
'enum-inline',
|
|
12
|
+
'number',
|
|
13
|
+
'string',
|
|
14
|
+
'url',
|
|
15
|
+
'wysiwyg',
|
|
16
|
+
]);
|
|
5
17
|
function formatSourceLabel(slug) {
|
|
6
18
|
if (slug === 'nitrogen-templates') {
|
|
7
19
|
return 'Nitrogen Templates';
|
|
@@ -12,6 +24,86 @@ function formatSourceLabel(slug) {
|
|
|
12
24
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
13
25
|
.join(' ');
|
|
14
26
|
}
|
|
27
|
+
function getPreviewTargetOrigin(previewUrl) {
|
|
28
|
+
try {
|
|
29
|
+
return new URL(previewUrl).origin;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return '*';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function normalizeManifestOptions(options) {
|
|
36
|
+
if (!options?.length) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return options.map((option) => {
|
|
40
|
+
if (typeof option === 'string') {
|
|
41
|
+
return {
|
|
42
|
+
label: option,
|
|
43
|
+
value: option,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
label: option.label || option.value,
|
|
48
|
+
value: option.value,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function isEditableProp(prop) {
|
|
53
|
+
return EDITABLE_PROP_TYPES.has(prop.type);
|
|
54
|
+
}
|
|
55
|
+
function collectEditablePropOptions(definition) {
|
|
56
|
+
if (!definition) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const options = [];
|
|
60
|
+
Object.values(definition.categories).forEach((category) => {
|
|
61
|
+
Object.values(category.groups).forEach((group) => {
|
|
62
|
+
Object.entries(group.props).forEach(([propName, prop]) => {
|
|
63
|
+
if (!isEditableProp(prop)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
options.push({
|
|
67
|
+
defaultValue: prop.default,
|
|
68
|
+
label: `${category.label} / ${group.label} / ${prop.label || propName}`,
|
|
69
|
+
options: normalizeManifestOptions(prop.options),
|
|
70
|
+
path: `${group.key}.${propName}`,
|
|
71
|
+
type: prop.type,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return options;
|
|
77
|
+
}
|
|
78
|
+
function stringifyPropValue(value, propType) {
|
|
79
|
+
if (propType === 'boolean') {
|
|
80
|
+
return Boolean(value);
|
|
81
|
+
}
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === 'number') {
|
|
86
|
+
return String(value);
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
function coercePropValue(value, propType) {
|
|
91
|
+
if (propType === 'boolean') {
|
|
92
|
+
return Boolean(value);
|
|
93
|
+
}
|
|
94
|
+
if (propType === 'number') {
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
97
|
+
}
|
|
98
|
+
return String(value);
|
|
99
|
+
}
|
|
100
|
+
function getDefaultDraft(option) {
|
|
101
|
+
return {
|
|
102
|
+
propPath: option?.path || '',
|
|
103
|
+
status: 'idle',
|
|
104
|
+
value: option ? stringifyPropValue(option.defaultValue, option.type) : '',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
15
107
|
function renderProp(name, prop, depth = 0) {
|
|
16
108
|
return (_jsxs("div", { style: { paddingLeft: depth * 16 }, children: [_jsxs("div", { style: { fontFamily: 'monospace', fontSize: 12, lineHeight: 1.5 }, children: [_jsx("strong", { children: name }), " ", _jsxs("span", { style: { color: '#6b7280' }, children: ["(", prop.type, ")"] }), prop.label ? _jsxs("span", { style: { color: '#6b7280' }, children: [" - ", prop.label] }) : null] }), prop.props ? (_jsx("div", { style: { marginTop: 4 }, children: Object.entries(prop.props).map(([childName, childProp]) => renderProp(childName, childProp, depth + 1)) })) : null] }, `${name}-${depth}`));
|
|
17
109
|
}
|
|
@@ -34,51 +126,22 @@ function buttonStyle(kind) {
|
|
|
34
126
|
padding: '8px 12px',
|
|
35
127
|
};
|
|
36
128
|
}
|
|
37
|
-
function
|
|
38
|
-
const relationshipMap = new Map();
|
|
39
|
-
usages.forEach((usage) => {
|
|
40
|
-
const relationshipName = getRelationshipName(usage);
|
|
41
|
-
if (!relationshipName) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const entry = relationshipMap.get(relationshipName) ?? {
|
|
45
|
-
documentKeys: new Set(),
|
|
46
|
-
occurrenceCount: 0,
|
|
47
|
-
};
|
|
48
|
-
entry.occurrenceCount += 1;
|
|
49
|
-
entry.documentKeys.add(`${usage.sourceCollection}:${usage.sourceDocumentId}`);
|
|
50
|
-
relationshipMap.set(relationshipName, entry);
|
|
51
|
-
});
|
|
52
|
-
return Array.from(relationshipMap.entries())
|
|
53
|
-
.map(([componentName, entry]) => ({
|
|
54
|
-
componentName,
|
|
55
|
-
documentCount: entry.documentKeys.size,
|
|
56
|
-
occurrenceCount: entry.occurrenceCount,
|
|
57
|
-
}))
|
|
58
|
-
.sort((a, b) => {
|
|
59
|
-
if (b.documentCount !== a.documentCount) {
|
|
60
|
-
return b.documentCount - a.documentCount;
|
|
61
|
-
}
|
|
62
|
-
if (b.occurrenceCount !== a.occurrenceCount) {
|
|
63
|
-
return b.occurrenceCount - a.occurrenceCount;
|
|
64
|
-
}
|
|
65
|
-
return a.componentName.localeCompare(b.componentName);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function renderRelationshipList(title, summaries, emptyMessage) {
|
|
69
|
-
return (_jsxs("div", { style: { marginTop: 16 }, children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: title }), summaries.length ? (_jsx("div", { style: { display: 'grid', gap: 8 }, children: summaries.map((summary) => (_jsxs("div", { style: {
|
|
70
|
-
border: '1px solid #e5e7eb',
|
|
71
|
-
borderRadius: 6,
|
|
72
|
-
padding: 12,
|
|
73
|
-
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: summary.componentName }), _jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: [summary.documentCount, " doc", summary.documentCount === 1 ? '' : 's', " \u00B7", ' ', summary.occurrenceCount, " occurrence", summary.occurrenceCount === 1 ? '' : 's'] })] }, `${title}-${summary.componentName}`))) })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: emptyMessage }))] }));
|
|
74
|
-
}
|
|
75
|
-
export default function NitrogenComponentInventoryClient({ adminRoute, components, }) {
|
|
129
|
+
export default function NitrogenComponentInventoryClient({ adminRoute, components, previewUrl, }) {
|
|
76
130
|
const sourceFilterRef = useRef(null);
|
|
131
|
+
const previewFrameRefs = useRef({});
|
|
77
132
|
const allComponentNames = useMemo(() => components.map(({ componentName }) => componentName), [components]);
|
|
78
133
|
const [searchValue, setSearchValue] = useState('');
|
|
79
134
|
const [sourceSearchValue, setSourceSearchValue] = useState('');
|
|
80
135
|
const [selectedSources, setSelectedSources] = useState([]);
|
|
81
136
|
const [openNames, setOpenNames] = useState({});
|
|
137
|
+
const [previewStates, setPreviewStates] = useState({});
|
|
138
|
+
const [propUpdateDrafts, setPropUpdateDrafts] = useState({});
|
|
139
|
+
const editablePropOptionsByComponent = useMemo(() => {
|
|
140
|
+
return new Map(components.map((component) => [
|
|
141
|
+
component.componentName,
|
|
142
|
+
collectEditablePropOptions(component.definition),
|
|
143
|
+
]));
|
|
144
|
+
}, [components]);
|
|
82
145
|
const sourceOptions = useMemo(() => {
|
|
83
146
|
const collectionOptions = new Map();
|
|
84
147
|
const documentOptions = new Map();
|
|
@@ -140,15 +203,6 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
140
203
|
return !hasSourceFilter || component.usages.length > 0;
|
|
141
204
|
});
|
|
142
205
|
}, [components, selectedSources]);
|
|
143
|
-
const allSourceFilteredUsages = useMemo(() => sourceFilteredComponents.flatMap((component) => component.usages), [sourceFilteredComponents]);
|
|
144
|
-
const containsByComponent = useMemo(() => {
|
|
145
|
-
const componentMap = new Map();
|
|
146
|
-
sourceFilteredComponents.forEach(({ componentName }) => {
|
|
147
|
-
const summaries = buildRelationshipSummaries(allSourceFilteredUsages.filter((usage) => usage.parentComponentName === componentName), (usage) => usage.componentName);
|
|
148
|
-
componentMap.set(componentName, summaries);
|
|
149
|
-
});
|
|
150
|
-
return componentMap;
|
|
151
|
-
}, [allSourceFilteredUsages, sourceFilteredComponents]);
|
|
152
206
|
const filteredComponents = useMemo(() => {
|
|
153
207
|
const normalizedQuery = searchValue.trim().toLowerCase();
|
|
154
208
|
return sourceFilteredComponents.filter((component) => {
|
|
@@ -170,6 +224,22 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
170
224
|
document.removeEventListener('mousedown', handlePointerDown);
|
|
171
225
|
};
|
|
172
226
|
}, []);
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
setPropUpdateDrafts((current) => {
|
|
229
|
+
const next = { ...current };
|
|
230
|
+
Object.entries(next).forEach(([componentName, draft]) => {
|
|
231
|
+
if (!draft.result) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
next[componentName] = {
|
|
235
|
+
...draft,
|
|
236
|
+
result: undefined,
|
|
237
|
+
status: 'idle',
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
return next;
|
|
241
|
+
});
|
|
242
|
+
}, [selectedSources]);
|
|
173
243
|
const expandAll = () => {
|
|
174
244
|
setOpenNames((current) => ({
|
|
175
245
|
...current,
|
|
@@ -196,6 +266,154 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
196
266
|
: selectedSourceLabels.length === 1
|
|
197
267
|
? selectedSourceLabels[0]
|
|
198
268
|
: `${selectedSourceLabels.length} sources`;
|
|
269
|
+
const postPreviewDataToFrame = useCallback((componentName, data) => {
|
|
270
|
+
if (!previewUrl || !data) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const frameWindow = previewFrameRefs.current[componentName]?.contentWindow;
|
|
274
|
+
if (!frameWindow) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
frameWindow.postMessage({
|
|
278
|
+
componentName,
|
|
279
|
+
payload: data,
|
|
280
|
+
type: 'nitrogen-component-preview',
|
|
281
|
+
}, getPreviewTargetOrigin(previewUrl));
|
|
282
|
+
}, [previewUrl]);
|
|
283
|
+
const loadPreviewData = useCallback(async (componentName, usages) => {
|
|
284
|
+
if (!previewUrl) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
setPreviewStates((current) => {
|
|
288
|
+
if (current[componentName]?.status === 'loading' || current[componentName]?.data) {
|
|
289
|
+
return current;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
...current,
|
|
293
|
+
[componentName]: {
|
|
294
|
+
status: 'loading',
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
const previewUsage = usages[0];
|
|
299
|
+
const params = new URLSearchParams({
|
|
300
|
+
componentName,
|
|
301
|
+
});
|
|
302
|
+
if (previewUsage) {
|
|
303
|
+
params.set('sourceCollection', previewUsage.sourceCollection);
|
|
304
|
+
params.set('sourceDocumentId', previewUsage.sourceDocumentId);
|
|
305
|
+
if (previewUsage.moduleId) {
|
|
306
|
+
params.set('moduleId', previewUsage.moduleId);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const response = await fetch(`/api/nitrogen/v1/component-inventory/preview-data?${params}`);
|
|
311
|
+
const data = (await response.json());
|
|
312
|
+
if (!response.ok || !('success' in data)) {
|
|
313
|
+
throw new Error('error' in data && data.error ? data.error : 'Failed to load preview data.');
|
|
314
|
+
}
|
|
315
|
+
setPreviewStates((current) => ({
|
|
316
|
+
...current,
|
|
317
|
+
[componentName]: {
|
|
318
|
+
data,
|
|
319
|
+
status: 'loaded',
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
window.setTimeout(() => postPreviewDataToFrame(componentName, data), 0);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
setPreviewStates((current) => ({
|
|
326
|
+
...current,
|
|
327
|
+
[componentName]: {
|
|
328
|
+
error: error instanceof Error ? error.message : 'Failed to load preview data.',
|
|
329
|
+
status: 'error',
|
|
330
|
+
},
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
}, [postPreviewDataToFrame, previewUrl]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
filteredComponents.forEach((component) => {
|
|
336
|
+
if (openNames[component.componentName]) {
|
|
337
|
+
void loadPreviewData(component.componentName, component.usages);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}, [filteredComponents, loadPreviewData, openNames]);
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
function handlePreviewReady(event) {
|
|
343
|
+
if (!previewUrl || event.origin !== getPreviewTargetOrigin(previewUrl)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const data = event.data;
|
|
347
|
+
if (data.type !== 'nitrogen-component-preview-ready' || typeof data.componentName !== 'string') {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
postPreviewDataToFrame(data.componentName, previewStates[data.componentName]?.data);
|
|
351
|
+
}
|
|
352
|
+
window.addEventListener('message', handlePreviewReady);
|
|
353
|
+
return () => {
|
|
354
|
+
window.removeEventListener('message', handlePreviewReady);
|
|
355
|
+
};
|
|
356
|
+
}, [postPreviewDataToFrame, previewStates, previewUrl]);
|
|
357
|
+
function updatePropDraft(componentName, nextDraft) {
|
|
358
|
+
setPropUpdateDrafts((current) => ({
|
|
359
|
+
...current,
|
|
360
|
+
[componentName]: {
|
|
361
|
+
...(current[componentName] || getDefaultDraft()),
|
|
362
|
+
...nextDraft,
|
|
363
|
+
},
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
async function runPropUpdate(componentName, editableProps, mode, usages) {
|
|
367
|
+
const draft = propUpdateDrafts[componentName] || getDefaultDraft(editableProps[0]);
|
|
368
|
+
const selectedProp = editableProps.find((option) => option.path === draft.propPath);
|
|
369
|
+
if (!selectedProp) {
|
|
370
|
+
updatePropDraft(componentName, {
|
|
371
|
+
error: 'Select a supported prop first.',
|
|
372
|
+
status: 'error',
|
|
373
|
+
});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
updatePropDraft(componentName, {
|
|
377
|
+
error: undefined,
|
|
378
|
+
status: mode === 'apply' ? 'applying' : 'previewing',
|
|
379
|
+
});
|
|
380
|
+
try {
|
|
381
|
+
const response = await fetch(`/api/nitrogen/v1/component-inventory/prop-update/${mode}`, {
|
|
382
|
+
body: JSON.stringify({
|
|
383
|
+
componentName,
|
|
384
|
+
propPath: selectedProp.path,
|
|
385
|
+
sourceFilters: selectedSources,
|
|
386
|
+
value: coercePropValue(draft.value, selectedProp.type),
|
|
387
|
+
}),
|
|
388
|
+
headers: {
|
|
389
|
+
'Content-Type': 'application/json',
|
|
390
|
+
},
|
|
391
|
+
method: 'POST',
|
|
392
|
+
});
|
|
393
|
+
const result = (await response.json());
|
|
394
|
+
if (!response.ok || !('success' in result)) {
|
|
395
|
+
throw new Error('error' in result && result.error ? result.error : 'Prop update failed.');
|
|
396
|
+
}
|
|
397
|
+
updatePropDraft(componentName, {
|
|
398
|
+
result,
|
|
399
|
+
status: mode === 'apply' ? 'applied' : 'ready',
|
|
400
|
+
});
|
|
401
|
+
if (mode === 'apply') {
|
|
402
|
+
setPreviewStates((current) => {
|
|
403
|
+
const next = { ...current };
|
|
404
|
+
delete next[componentName];
|
|
405
|
+
return next;
|
|
406
|
+
});
|
|
407
|
+
void loadPreviewData(componentName, usages);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
updatePropDraft(componentName, {
|
|
412
|
+
error: error instanceof Error ? error.message : 'Prop update failed.',
|
|
413
|
+
status: 'error',
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
199
417
|
return (_jsxs("div", { style: { display: 'grid', gap: 16 }, children: [_jsxs("div", { style: {
|
|
200
418
|
alignItems: 'center',
|
|
201
419
|
display: 'flex',
|
|
@@ -267,8 +485,13 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
267
485
|
}, children: option.kind === 'collection' ? 'Collection' : 'Item' })] }), _jsx("span", { style: { color: '#6b7280', fontSize: 12 }, children: option.description })] })] }, option.key));
|
|
268
486
|
})) : (_jsx("p", { style: { color: '#6b7280', fontSize: 13, margin: 0 }, children: "No sources match." })) })] }) })] }), _jsx("button", { onClick: expandAll, style: buttonStyle('secondary'), type: "button", children: "Expand All" }), _jsx("button", { onClick: collapseAll, style: buttonStyle('secondary'), type: "button", children: "Collapse All" })] })] }), filteredComponents.map(({ componentName, definition, isUnknown, usages }) => {
|
|
269
487
|
const isOpen = Boolean(openNames[componentName]);
|
|
270
|
-
const
|
|
271
|
-
const
|
|
488
|
+
const editableProps = editablePropOptionsByComponent.get(componentName) || [];
|
|
489
|
+
const propDraft = propUpdateDrafts[componentName] || getDefaultDraft(editableProps[0]);
|
|
490
|
+
const selectedEditableProp = editableProps.find((option) => option.path === propDraft.propPath);
|
|
491
|
+
const previewState = previewStates[componentName] || { status: 'idle' };
|
|
492
|
+
const componentPreviewUrl = previewUrl
|
|
493
|
+
? `${previewUrl}${previewUrl.includes('?') ? '&' : '?'}componentName=${encodeURIComponent(componentName)}`
|
|
494
|
+
: '';
|
|
272
495
|
return (_jsxs("section", { style: {
|
|
273
496
|
background: '#fff',
|
|
274
497
|
border: '1px solid #e5e7eb',
|
|
@@ -289,21 +512,105 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
289
512
|
padding: 16,
|
|
290
513
|
textAlign: 'left',
|
|
291
514
|
width: '100%',
|
|
292
|
-
}, type: "button", children: [_jsxs("div", { style: { display: 'grid', gap: 4 }, children: [_jsxs("div", { style: { alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("span", { style: { fontSize: 16, fontWeight: 700 }, children: componentName }), _jsxs("span", { style: { color: '#6b7280', fontSize: 13, fontWeight: 500 }, children: [usages.length, " usage", usages.length === 1 ? '' : 's'] }),
|
|
515
|
+
}, type: "button", children: [_jsxs("div", { style: { display: 'grid', gap: 4 }, children: [_jsxs("div", { style: { alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("span", { style: { fontSize: 16, fontWeight: 700 }, children: componentName }), _jsxs("span", { style: { color: '#6b7280', fontSize: 13, fontWeight: 500 }, children: [usages.length, " usage", usages.length === 1 ? '' : 's'] }), isUnknown ? (_jsx("span", { style: { color: '#b45309', fontSize: 13, fontWeight: 600 }, children: "Missing from manifest" })) : null] }), definition ? (_jsxs("div", { style: { color: '#6b7280', fontSize: 13 }, children: [definition.sidebarCategory || 'Uncategorized', definition.scope ? ` - ${definition.scope}` : ''] })) : null] }), _jsx("span", { style: { color: '#6b7280', fontSize: 18, lineHeight: 1 }, children: isOpen ? '−' : '+' })] }), isOpen ? (_jsxs("div", { style: {
|
|
293
516
|
borderTop: '1px solid #e5e7eb',
|
|
294
517
|
display: 'grid',
|
|
295
518
|
gap: 0,
|
|
296
|
-
gridTemplateColumns: '
|
|
297
|
-
}, children: [_jsxs("div", { style: {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
519
|
+
gridTemplateColumns: 'minmax(0, 1.45fr) minmax(300px, 0.9fr)',
|
|
520
|
+
}, children: [_jsxs("div", { style: {
|
|
521
|
+
borderRight: '1px solid #e5e7eb',
|
|
522
|
+
display: 'grid',
|
|
523
|
+
gap: 18,
|
|
524
|
+
padding: 16,
|
|
525
|
+
}, children: [_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Used On" }), usages.length ? (_jsx("div", { style: { display: 'grid', gap: 8 }, children: usages.map((usage) => (_jsxs("a", { href: formatAdminURL({
|
|
526
|
+
adminRoute,
|
|
527
|
+
path: `/collections/${usage.sourceCollection}/${usage.sourceDocumentId}`,
|
|
528
|
+
}), style: {
|
|
529
|
+
border: '1px solid #e5e7eb',
|
|
530
|
+
borderRadius: 6,
|
|
531
|
+
color: 'inherit',
|
|
532
|
+
display: 'block',
|
|
533
|
+
padding: 12,
|
|
534
|
+
textDecoration: 'none',
|
|
535
|
+
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: usage.sourceTitle || usage.sourceSlug || usage.sourceDocumentId }), _jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: [usage.sourceCollection, " - ", usage.modulePath] })] }, usage.usageKey))) })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No indexed usages yet." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Props" }), definition ? (Object.values(definition.categories).map((category) => renderCategory(category))) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No manifest definition available." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 10px', fontSize: 15 }, children: "Update Prop Across Site" }), editableProps.length ? (_jsxs("div", { style: {
|
|
536
|
+
border: '1px solid #e5e7eb',
|
|
537
|
+
borderRadius: 8,
|
|
538
|
+
display: 'grid',
|
|
539
|
+
gap: 10,
|
|
540
|
+
padding: 12,
|
|
541
|
+
}, children: [_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Prop", _jsx("select", { onChange: (event) => {
|
|
542
|
+
const nextProp = editableProps.find((option) => option.path === event.target.value);
|
|
543
|
+
updatePropDraft(componentName, {
|
|
544
|
+
error: undefined,
|
|
545
|
+
propPath: event.target.value,
|
|
546
|
+
result: undefined,
|
|
547
|
+
status: 'idle',
|
|
548
|
+
value: nextProp
|
|
549
|
+
? stringifyPropValue(nextProp.defaultValue, nextProp.type)
|
|
550
|
+
: '',
|
|
551
|
+
});
|
|
552
|
+
}, style: {
|
|
553
|
+
border: '1px solid #d1d5db',
|
|
554
|
+
borderRadius: 6,
|
|
555
|
+
color: '#111827',
|
|
556
|
+
maxWidth: 520,
|
|
557
|
+
padding: '8px 10px',
|
|
558
|
+
width: '100%',
|
|
559
|
+
}, value: propDraft.propPath, children: editableProps.map((option) => (_jsx("option", { value: option.path, children: option.label }, option.path))) })] }), selectedEditableProp?.type === 'boolean' ? (_jsxs("label", { style: { alignItems: 'center', display: 'flex', gap: 8, fontSize: 13 }, children: [_jsx("input", { checked: Boolean(propDraft.value), onChange: (event) => updatePropDraft(componentName, {
|
|
560
|
+
result: undefined,
|
|
561
|
+
status: 'idle',
|
|
562
|
+
value: event.target.checked,
|
|
563
|
+
}), type: "checkbox" }), "Value"] })) : selectedEditableProp?.options?.length ? (_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Value", _jsx("select", { onChange: (event) => updatePropDraft(componentName, {
|
|
564
|
+
result: undefined,
|
|
565
|
+
status: 'idle',
|
|
566
|
+
value: event.target.value,
|
|
567
|
+
}), style: {
|
|
568
|
+
border: '1px solid #d1d5db',
|
|
569
|
+
borderRadius: 6,
|
|
570
|
+
color: '#111827',
|
|
571
|
+
maxWidth: 520,
|
|
572
|
+
padding: '8px 10px',
|
|
573
|
+
width: '100%',
|
|
574
|
+
}, value: String(propDraft.value), children: selectedEditableProp.options.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value))) })] })) : (_jsxs("label", { style: { display: 'grid', gap: 6, fontSize: 13, fontWeight: 600 }, children: ["Value", _jsx("input", { onChange: (event) => updatePropDraft(componentName, {
|
|
575
|
+
result: undefined,
|
|
576
|
+
status: 'idle',
|
|
577
|
+
value: event.target.value,
|
|
578
|
+
}), style: {
|
|
579
|
+
border: '1px solid #d1d5db',
|
|
580
|
+
borderRadius: 6,
|
|
581
|
+
color: '#111827',
|
|
582
|
+
maxWidth: 520,
|
|
583
|
+
padding: '8px 10px',
|
|
584
|
+
width: '100%',
|
|
585
|
+
}, type: selectedEditableProp?.type === 'number' ? 'number' : 'text', value: String(propDraft.value) })] })), _jsxs("div", { style: { color: '#6b7280', fontSize: 12 }, children: ["Scope: ", selectedSources.length ? sourceFilterLabel : 'all indexed usages'] }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 }, children: [_jsx("button", { disabled: propDraft.status === 'previewing' || propDraft.status === 'applying', onClick: () => void runPropUpdate(componentName, editableProps, 'preview', usages), style: buttonStyle('secondary'), type: "button", children: propDraft.status === 'previewing' ? 'Checking...' : 'Dry Run' }), _jsx("button", { disabled: propDraft.status === 'previewing' ||
|
|
586
|
+
propDraft.status === 'applying' ||
|
|
587
|
+
!propDraft.result, onClick: () => void runPropUpdate(componentName, editableProps, 'apply', usages), style: {
|
|
588
|
+
...buttonStyle('primary'),
|
|
589
|
+
opacity: propDraft.result ? 1 : 0.5,
|
|
590
|
+
}, type: "button", children: propDraft.status === 'applying' ? 'Applying...' : 'Apply Update' })] }), propDraft.error ? (_jsx("p", { style: { color: '#b91c1c', fontSize: 13, margin: 0 }, children: propDraft.error })) : null, propDraft.result ? (_jsxs("div", { style: { color: '#374151', fontSize: 13 }, children: [propDraft.result.mode === 'apply' ? 'Updated' : 'Would update', ' ', propDraft.result.occurrenceCount, " occurrence", propDraft.result.occurrenceCount === 1 ? '' : 's', " in", ' ', propDraft.result.documentCount, " document", propDraft.result.documentCount === 1 ? '' : 's', "."] })) : null] })) : (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "No scalar props are available for bulk updates." }))] })] }), _jsxs("div", { style: { padding: 16 }, children: [_jsxs("div", { style: {
|
|
591
|
+
alignItems: 'center',
|
|
592
|
+
display: 'flex',
|
|
593
|
+
gap: 12,
|
|
594
|
+
justifyContent: 'space-between',
|
|
595
|
+
marginBottom: 10,
|
|
596
|
+
}, children: [_jsxs("div", { children: [_jsx("h3", { style: { margin: 0, fontSize: 15 }, children: "Preview" }), previewState.data?.source ? (_jsxs("div", { style: { color: '#6b7280', fontSize: 12, marginTop: 2 }, children: ["Rendering saved usage from ", previewState.data.source.title || previewState.data.source.documentId] })) : null] }), previewState.status === 'loading' ? (_jsx("span", { style: { color: '#6b7280', fontSize: 12 }, children: "Loading preview..." })) : null] }), !previewUrl ? (_jsx("p", { style: { color: '#6b7280', margin: 0 }, children: "Configure a Nitrogen development or frontend URL to enable component previews." })) : previewState.status === 'error' ? (_jsx("p", { style: { color: '#b91c1c', margin: 0 }, children: previewState.error })) : (_jsx("div", { style: {
|
|
597
|
+
background: '#f9fafb',
|
|
598
|
+
border: '1px solid #e5e7eb',
|
|
599
|
+
borderRadius: 8,
|
|
600
|
+
overflow: 'hidden',
|
|
601
|
+
position: 'relative',
|
|
602
|
+
width: '100%',
|
|
603
|
+
aspectRatio: '4 / 3',
|
|
604
|
+
}, children: _jsx("iframe", { ref: (element) => {
|
|
605
|
+
previewFrameRefs.current[componentName] = element;
|
|
606
|
+
}, onLoad: () => postPreviewDataToFrame(componentName, previewState.data), src: componentPreviewUrl, style: {
|
|
607
|
+
border: 0,
|
|
304
608
|
display: 'block',
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
609
|
+
height: '100%',
|
|
610
|
+
left: 0,
|
|
611
|
+
position: 'absolute',
|
|
612
|
+
top: 0,
|
|
613
|
+
width: '100%',
|
|
614
|
+
}, title: `${componentName} preview` }) }))] })] })) : null] }, componentName));
|
|
308
615
|
})] }));
|
|
309
616
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing';
|
|
3
|
+
import { getNitrogenSettings } from '../endpoints/helpers';
|
|
3
4
|
import NitrogenComponentInventoryClient from './NitrogenComponentInventoryClient';
|
|
4
5
|
import NitrogenComponentInventoryReindexButton from './NitrogenComponentInventoryReindexButton';
|
|
6
|
+
function buildComponentPreviewUrl(settings) {
|
|
7
|
+
const frontendBaseUrl = String(settings.developmentUrl || settings.frontendUrl || '').replace(/\/$/, '');
|
|
8
|
+
if (!frontendBaseUrl) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
const url = new URL('/nitrogen-component-preview', frontendBaseUrl);
|
|
12
|
+
return url.toString();
|
|
13
|
+
}
|
|
5
14
|
const NitrogenComponentInventoryField = async ({ payload, req, }) => {
|
|
6
|
-
const [catalogDocs, usageDocs] = await Promise.all([
|
|
15
|
+
const [catalogDocs, usageDocs, nitrogenSettings] = await Promise.all([
|
|
7
16
|
findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION),
|
|
8
17
|
findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION),
|
|
18
|
+
getNitrogenSettings(payload).catch(() => null),
|
|
9
19
|
]);
|
|
10
20
|
const catalogByName = new Map(catalogDocs.map((doc) => [doc.componentName, doc.definition]));
|
|
11
21
|
const usageByName = new Map();
|
|
@@ -21,6 +31,6 @@ const NitrogenComponentInventoryField = async ({ payload, req, }) => {
|
|
|
21
31
|
isUnknown: !catalogByName.get(componentName),
|
|
22
32
|
usages: usageByName.get(componentName) || [],
|
|
23
33
|
}));
|
|
24
|
-
return (_jsxs("div", { style: { display: 'grid', gap: 24, paddingBottom: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12 }, children: [_jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: req.payload.config.routes.admin, components: components })] }));
|
|
34
|
+
return (_jsxs("div", { style: { display: 'grid', gap: 24, paddingBottom: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12 }, children: [_jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: req.payload.config.routes.admin, components: components, previewUrl: nitrogenSettings ? buildComponentPreviewUrl(nitrogenSettings) : '' })] }));
|
|
25
35
|
};
|
|
26
36
|
export default NitrogenComponentInventoryField;
|
|
@@ -2,13 +2,23 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
// @ts-expect-error The consuming Payload app provides @payloadcms/next at runtime.
|
|
3
3
|
import { DefaultTemplate } from '@payloadcms/next/templates';
|
|
4
4
|
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, } from '../inventory/indexing';
|
|
5
|
+
import { getNitrogenSettings } from '../endpoints/helpers';
|
|
5
6
|
import NitrogenComponentInventoryClient from './NitrogenComponentInventoryClient';
|
|
6
7
|
import NitrogenComponentInventoryReindexButton from './NitrogenComponentInventoryReindexButton';
|
|
8
|
+
function buildComponentPreviewUrl(settings) {
|
|
9
|
+
const frontendBaseUrl = String(settings.developmentUrl || settings.frontendUrl || '').replace(/\/$/, '');
|
|
10
|
+
if (!frontendBaseUrl) {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
const url = new URL('/nitrogen-component-preview', frontendBaseUrl);
|
|
14
|
+
return url.toString();
|
|
15
|
+
}
|
|
7
16
|
export default async function NitrogenComponentInventoryView({ clientConfig, initPageResult, i18n, locale, params, searchParams, viewActions, viewType, }) {
|
|
8
17
|
const payload = initPageResult.req.payload;
|
|
9
|
-
const [catalogDocs, usageDocs] = await Promise.all([
|
|
18
|
+
const [catalogDocs, usageDocs, nitrogenSettings] = await Promise.all([
|
|
10
19
|
findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION),
|
|
11
20
|
findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION),
|
|
21
|
+
getNitrogenSettings(payload).catch(() => null),
|
|
12
22
|
]);
|
|
13
23
|
const catalogByName = new Map(catalogDocs.map((doc) => [doc.componentName, doc.definition]));
|
|
14
24
|
const usageByName = new Map();
|
|
@@ -24,5 +34,5 @@ export default async function NitrogenComponentInventoryView({ clientConfig, ini
|
|
|
24
34
|
isUnknown: !catalogByName.get(componentName),
|
|
25
35
|
usages: usageByName.get(componentName) || [],
|
|
26
36
|
}));
|
|
27
|
-
return (_jsx(DefaultTemplate, { i18n: i18n, locale: locale, params: params, payload: payload, req: initPageResult.req, searchParams: searchParams, user: initPageResult.req.user, viewActions: viewActions, viewType: viewType, visibleEntities: initPageResult.visibleEntities, children: _jsxs("div", { style: { padding: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12, marginBottom: 24 }, children: [_jsx("h1", { style: { margin: 0, fontSize: 28 }, children: "Nitrogen Components" }), _jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: clientConfig.routes.admin, components: components })] }) }));
|
|
37
|
+
return (_jsx(DefaultTemplate, { i18n: i18n, locale: locale, params: params, payload: payload, req: initPageResult.req, searchParams: searchParams, user: initPageResult.req.user, viewActions: viewActions, viewType: viewType, visibleEntities: initPageResult.visibleEntities, children: _jsxs("div", { style: { padding: 24 }, children: [_jsxs("div", { style: { display: 'grid', gap: 12, marginBottom: 24 }, children: [_jsx("h1", { style: { margin: 0, fontSize: 28 }, children: "Nitrogen Components" }), _jsx("p", { style: { margin: 0, color: '#4b5563', maxWidth: 920 }, children: "Inventory view for the registered Nitrogen component manifest and all indexed usages across Nitrogen-enabled documents and templates." }), _jsx(NitrogenComponentInventoryReindexButton, {})] }), _jsx(NitrogenComponentInventoryClient, { adminRoute: clientConfig.routes.admin, components: components, previewUrl: nitrogenSettings ? buildComponentPreviewUrl(nitrogenSettings) : '' })] }) }));
|
|
28
38
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { NITROGEN_PACKAGE_NAMES, } from '../constants.js';
|
|
6
|
+
async function readJsonFile(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
const contents = await fs.readFile(filePath, 'utf8');
|
|
9
|
+
return JSON.parse(contents);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function fileExists(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
await fs.access(filePath);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function getDeclaredSpec(packageJson, packageName) {
|
|
25
|
+
return (packageJson.dependencies?.[packageName] ||
|
|
26
|
+
packageJson.devDependencies?.[packageName] ||
|
|
27
|
+
packageJson.optionalDependencies?.[packageName] ||
|
|
28
|
+
packageJson.peerDependencies?.[packageName]);
|
|
29
|
+
}
|
|
30
|
+
async function findNearestPackageJson(packageName, startPath) {
|
|
31
|
+
let directory = path.dirname(startPath);
|
|
32
|
+
while (directory !== path.dirname(directory)) {
|
|
33
|
+
const packageJsonPath = path.join(directory, 'package.json');
|
|
34
|
+
const packageJson = await readJsonFile(packageJsonPath);
|
|
35
|
+
if (packageJson?.name === packageName) {
|
|
36
|
+
return { packageJson, packageJsonPath };
|
|
37
|
+
}
|
|
38
|
+
directory = path.dirname(directory);
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
async function resolveInstalledPackageVersion(appRoot, packageName) {
|
|
43
|
+
try {
|
|
44
|
+
const requireFromApp = createRequire(path.join(appRoot, 'package.json'));
|
|
45
|
+
const entryPath = requireFromApp.resolve(packageName);
|
|
46
|
+
const resolvedPackage = await findNearestPackageJson(packageName, entryPath);
|
|
47
|
+
return {
|
|
48
|
+
installedVersion: resolvedPackage?.packageJson.version,
|
|
49
|
+
resolvedPackageJsonPath: resolvedPackage?.packageJsonPath,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function findPackageJsonPath(candidates) {
|
|
57
|
+
for (const candidate of candidates) {
|
|
58
|
+
if (!candidate)
|
|
59
|
+
continue;
|
|
60
|
+
const packageJsonPath = candidate.endsWith('package.json')
|
|
61
|
+
? candidate
|
|
62
|
+
: path.join(candidate, 'package.json');
|
|
63
|
+
if (await fileExists(packageJsonPath)) {
|
|
64
|
+
return packageJsonPath;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
function buildFrontendRootCandidates(cwd) {
|
|
70
|
+
return [
|
|
71
|
+
process.env.NITROGEN_FRONTEND_PACKAGE_JSON,
|
|
72
|
+
process.env.NITROGEN_FRONTEND_ROOT,
|
|
73
|
+
path.resolve(cwd, '../frontend'),
|
|
74
|
+
path.resolve(cwd, '../../apps/frontend'),
|
|
75
|
+
path.resolve(cwd, 'apps/frontend'),
|
|
76
|
+
path.resolve(cwd, '../apps/frontend'),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
async function getAppVersionSummary(app, label, packageJsonCandidates) {
|
|
80
|
+
const packageJsonPath = await findPackageJsonPath(packageJsonCandidates);
|
|
81
|
+
const packageJson = packageJsonPath
|
|
82
|
+
? await readJsonFile(packageJsonPath)
|
|
83
|
+
: undefined;
|
|
84
|
+
const rootPath = packageJsonPath ? path.dirname(packageJsonPath) : undefined;
|
|
85
|
+
const packages = rootPath && packageJson
|
|
86
|
+
? await Promise.all(NITROGEN_PACKAGE_NAMES.map(async (packageName) => {
|
|
87
|
+
const installed = await resolveInstalledPackageVersion(rootPath, packageName);
|
|
88
|
+
return {
|
|
89
|
+
declaredSpec: getDeclaredSpec(packageJson, packageName),
|
|
90
|
+
packageName,
|
|
91
|
+
...installed,
|
|
92
|
+
};
|
|
93
|
+
}))
|
|
94
|
+
: [];
|
|
95
|
+
return {
|
|
96
|
+
app,
|
|
97
|
+
label,
|
|
98
|
+
packageJsonPath,
|
|
99
|
+
packages: packages.filter((pkg) => pkg.declaredSpec || pkg.installedVersion),
|
|
100
|
+
rootPath,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function versionCell(version) {
|
|
104
|
+
return version ? (_jsx("code", { style: { color: '#111827', fontSize: 12 }, children: version })) : (_jsx("span", { style: { color: '#9ca3af' }, children: "Not resolved" }));
|
|
105
|
+
}
|
|
106
|
+
function renderAppSummary(summary) {
|
|
107
|
+
return (_jsxs("section", { style: {
|
|
108
|
+
border: '1px solid #e5e7eb',
|
|
109
|
+
borderRadius: 8,
|
|
110
|
+
overflow: 'hidden',
|
|
111
|
+
}, children: [_jsxs("div", { style: { background: '#f9fafb', padding: '12px 14px' }, children: [_jsx("h3", { style: { fontSize: 15, margin: 0 }, children: summary.label }), summary.packageJsonPath ? (_jsx("p", { style: { color: '#6b7280', fontSize: 12, margin: '4px 0 0' }, children: summary.packageJsonPath })) : (_jsx("p", { style: { color: '#991b1b', fontSize: 12, margin: '4px 0 0' }, children: "Could not find package.json." }))] }), summary.packages.length ? (_jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { borderCollapse: 'collapse', fontSize: 13, width: '100%' }, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableHeaderStyle, children: "Package" }), _jsx("th", { style: tableHeaderStyle, children: "Declared spec" }), _jsx("th", { style: tableHeaderStyle, children: "Installed version" })] }) }), _jsx("tbody", { children: summary.packages.map((pkg) => (_jsxs("tr", { children: [_jsx("td", { style: tableCellStyle, children: _jsx("code", { style: { fontSize: 12 }, children: pkg.packageName }) }), _jsx("td", { style: tableCellStyle, children: pkg.declaredSpec ? (_jsx("code", { style: { color: '#374151', fontSize: 12 }, children: pkg.declaredSpec })) : (_jsx("span", { style: { color: '#9ca3af' }, children: "Not declared" })) }), _jsx("td", { style: tableCellStyle, children: versionCell(pkg.installedVersion) })] }, pkg.packageName))) })] }) })) : (_jsx("p", { style: { color: '#6b7280', fontSize: 13, margin: 0, padding: 14 }, children: "No @nitrogenbuilder packages found for this app." }))] }, summary.app));
|
|
112
|
+
}
|
|
113
|
+
const tableHeaderStyle = {
|
|
114
|
+
borderTop: '1px solid #e5e7eb',
|
|
115
|
+
color: '#4b5563',
|
|
116
|
+
fontSize: 12,
|
|
117
|
+
fontWeight: 700,
|
|
118
|
+
padding: '10px 12px',
|
|
119
|
+
textAlign: 'left',
|
|
120
|
+
};
|
|
121
|
+
const tableCellStyle = {
|
|
122
|
+
borderTop: '1px solid #e5e7eb',
|
|
123
|
+
padding: '10px 12px',
|
|
124
|
+
verticalAlign: 'top',
|
|
125
|
+
};
|
|
126
|
+
const NitrogenPackageVersionsField = async () => {
|
|
127
|
+
const cwd = process.cwd();
|
|
128
|
+
const [backend, frontend] = await Promise.all([
|
|
129
|
+
getAppVersionSummary('backend', 'Backend', [
|
|
130
|
+
process.env.NITROGEN_BACKEND_PACKAGE_JSON,
|
|
131
|
+
process.env.NITROGEN_BACKEND_ROOT,
|
|
132
|
+
cwd,
|
|
133
|
+
]),
|
|
134
|
+
getAppVersionSummary('frontend', 'Frontend', buildFrontendRootCandidates(cwd)),
|
|
135
|
+
]);
|
|
136
|
+
return (_jsxs("div", { style: { display: 'grid', gap: 14, marginBottom: 24 }, children: [renderAppSummary(backend), renderAppSummary(frontend)] }));
|
|
137
|
+
};
|
|
138
|
+
export default NitrogenPackageVersionsField;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const NitrogenPackageVersionsHeaderField = () => {
|
|
3
|
+
return (_jsxs("div", { style: { marginBottom: 16 }, children: [_jsx("h2", { style: { fontSize: 18, margin: '0 0 6px' }, children: "Nitrogen Versions" }), _jsx("p", { style: { color: '#6b7280', fontSize: 13, margin: 0, maxWidth: 920 }, children: "Declared spec is read from package.json. Installed version is resolved from node_modules, so dev link/file dependencies and production installs are shown separately." })] }));
|
|
4
|
+
};
|
|
5
|
+
export default NitrogenPackageVersionsHeaderField;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DEFAULT_EDITOR_CDN_VERSION = "0.5.30";
|
|
2
|
+
export declare const NITROGEN_PACKAGE_NAMES: readonly ["@nitrogenbuilder/client", "@nitrogenbuilder/client-core", "@nitrogenbuilder/client-preact", "@nitrogenbuilder/client-react", "@nitrogenbuilder/connector-payload", "@nitrogenbuilder/editor", "@nitrogenbuilder/types"];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DEFAULT_EDITOR_CDN_VERSION = '0.5.30';
|
|
2
|
+
export const NITROGEN_PACKAGE_NAMES = [
|
|
3
|
+
'@nitrogenbuilder/client',
|
|
4
|
+
'@nitrogenbuilder/client-core',
|
|
5
|
+
'@nitrogenbuilder/client-preact',
|
|
6
|
+
'@nitrogenbuilder/client-react',
|
|
7
|
+
'@nitrogenbuilder/connector-payload',
|
|
8
|
+
'@nitrogenbuilder/editor',
|
|
9
|
+
'@nitrogenbuilder/types',
|
|
10
|
+
];
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { getPayload } from "payload";
|
|
3
3
|
import { headers } from "next/headers";
|
|
4
4
|
import { redirect } from "next/navigation";
|
|
5
|
-
|
|
5
|
+
import { DEFAULT_EDITOR_CDN_VERSION } from "../constants.js";
|
|
6
6
|
function getNitrogenApiUrl(isDevelopment) {
|
|
7
7
|
if (isDevelopment) {
|
|
8
8
|
return "/api/nitrogen/v1";
|
|
@@ -111,7 +111,11 @@ export function createNitrogenEditorPage(config) {
|
|
|
111
111
|
const editorAssetsBase = isEditorDev
|
|
112
112
|
? editorOrigin
|
|
113
113
|
: "/nitrogen-editor/assets";
|
|
114
|
-
const
|
|
114
|
+
const editorCdnVersion = typeof settings.editorCdnVersion === "string" &&
|
|
115
|
+
settings.editorCdnVersion.trim()
|
|
116
|
+
? settings.editorCdnVersion.trim()
|
|
117
|
+
: DEFAULT_EDITOR_CDN_VERSION;
|
|
118
|
+
const cdnBase = `https://cdn.jsdelivr.net/npm/@nitrogenbuilder/editor@${editorCdnVersion}`;
|
|
115
119
|
return (_jsxs(_Fragment, { children: [!isEditorDev && (_jsxs(_Fragment, { children: [_jsx("link", { rel: "preconnect", href: "https://cdn.jsdelivr.net" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" })] })), isEditorDev ? (_jsxs(_Fragment, { children: [_jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${editorAssetsBase}/fa620pro/css/all.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${editorAssetsBase}/fa620pro/css/sharp-solid.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${editorOrigin}/src/index.scss` })] })) : (_jsxs(_Fragment, { children: [_jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${editorAssetsBase}/fa620pro/css/all.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${editorAssetsBase}/fa620pro/css/sharp-solid.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${cdnBase}/index.css` })] })), _jsx("script", { dangerouslySetInnerHTML: {
|
|
116
120
|
__html: [
|
|
117
121
|
`window.nitrogenConfig = ${JSON.stringify(builderConfig)};`,
|
|
@@ -1,5 +1,199 @@
|
|
|
1
1
|
import { requireAuth } from './helpers';
|
|
2
|
-
import { reindexAllComponentInventory } from '../inventory/indexing';
|
|
2
|
+
import { findAllDocs, NITROGEN_COMPONENT_CATALOG_COLLECTION, NITROGEN_COMPONENT_USAGE_COLLECTION, reindexAllComponentInventory, } from '../inventory/indexing';
|
|
3
|
+
function moduleNameFromNode(node) {
|
|
4
|
+
const value = node.module;
|
|
5
|
+
if (typeof value === 'string')
|
|
6
|
+
return value;
|
|
7
|
+
if (value && typeof value === 'object' && typeof value.name === 'string') {
|
|
8
|
+
return String(value.name);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function cloneJson(value) {
|
|
13
|
+
return value === undefined ? value : JSON.parse(JSON.stringify(value));
|
|
14
|
+
}
|
|
15
|
+
function isSourceFilterMatch(usage, sourceFilters) {
|
|
16
|
+
if (!sourceFilters.length) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return (sourceFilters.includes(`collection:${usage.sourceCollection}`) ||
|
|
20
|
+
sourceFilters.includes(`document:${usage.sourceCollection}:${usage.sourceDocumentId}`));
|
|
21
|
+
}
|
|
22
|
+
async function findUsageDocsForComponent(payload, componentName, sourceFilters) {
|
|
23
|
+
const usageDocs = await findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION, {
|
|
24
|
+
limit: 200,
|
|
25
|
+
where: {
|
|
26
|
+
componentName: {
|
|
27
|
+
equals: componentName,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
return usageDocs.filter((usage) => isSourceFilterMatch(usage, sourceFilters));
|
|
32
|
+
}
|
|
33
|
+
function findModuleInTree(modules, componentName, moduleId) {
|
|
34
|
+
if (!Array.isArray(modules)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
for (const maybeNode of modules) {
|
|
38
|
+
if (!maybeNode || typeof maybeNode !== 'object') {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const node = maybeNode;
|
|
42
|
+
const nodeModuleId = typeof node.id === 'string' || typeof node.id === 'number' ? String(node.id) : undefined;
|
|
43
|
+
const nodeComponentName = moduleNameFromNode(node);
|
|
44
|
+
if (nodeComponentName === componentName &&
|
|
45
|
+
(!moduleId || !nodeModuleId || nodeModuleId === moduleId)) {
|
|
46
|
+
return node;
|
|
47
|
+
}
|
|
48
|
+
const props = node.props;
|
|
49
|
+
if (props && typeof props === 'object') {
|
|
50
|
+
const children = props.children;
|
|
51
|
+
if (Array.isArray(children)) {
|
|
52
|
+
const found = findModuleInTree(children, componentName, moduleId);
|
|
53
|
+
if (found)
|
|
54
|
+
return found;
|
|
55
|
+
}
|
|
56
|
+
else if (children && typeof children === 'object') {
|
|
57
|
+
for (const slotChildren of Object.values(children)) {
|
|
58
|
+
const found = findModuleInTree(slotChildren, componentName, moduleId);
|
|
59
|
+
if (found)
|
|
60
|
+
return found;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(node.children)) {
|
|
65
|
+
const found = findModuleInTree(node.children, componentName, moduleId);
|
|
66
|
+
if (found)
|
|
67
|
+
return found;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
function setValueAtPath(target, path, value) {
|
|
73
|
+
const parts = path.split('.').filter(Boolean);
|
|
74
|
+
let cursor = target;
|
|
75
|
+
parts.forEach((part, index) => {
|
|
76
|
+
if (index === parts.length - 1) {
|
|
77
|
+
cursor[part] = value;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const next = cursor[part];
|
|
81
|
+
if (!next || typeof next !== 'object' || Array.isArray(next)) {
|
|
82
|
+
cursor[part] = {};
|
|
83
|
+
}
|
|
84
|
+
cursor = cursor[part];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function updateModulesForComponent(modules, componentName, propPath, value, options) {
|
|
88
|
+
if (!Array.isArray(modules)) {
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
let occurrenceCount = 0;
|
|
92
|
+
modules.forEach((maybeNode) => {
|
|
93
|
+
if (!maybeNode || typeof maybeNode !== 'object') {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const node = maybeNode;
|
|
97
|
+
if (moduleNameFromNode(node) === componentName) {
|
|
98
|
+
occurrenceCount += 1;
|
|
99
|
+
if (options.apply) {
|
|
100
|
+
if (!node.props || typeof node.props !== 'object' || Array.isArray(node.props)) {
|
|
101
|
+
node.props = {};
|
|
102
|
+
}
|
|
103
|
+
setValueAtPath(node.props, propPath, value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const props = node.props;
|
|
107
|
+
if (props && typeof props === 'object') {
|
|
108
|
+
const children = props.children;
|
|
109
|
+
if (Array.isArray(children)) {
|
|
110
|
+
occurrenceCount += updateModulesForComponent(children, componentName, propPath, value, options);
|
|
111
|
+
}
|
|
112
|
+
else if (children && typeof children === 'object') {
|
|
113
|
+
Object.values(children).forEach((slotChildren) => {
|
|
114
|
+
occurrenceCount += updateModulesForComponent(slotChildren, componentName, propPath, value, options);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(node.children)) {
|
|
119
|
+
occurrenceCount += updateModulesForComponent(node.children, componentName, propPath, value, options);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return occurrenceCount;
|
|
123
|
+
}
|
|
124
|
+
async function getComponentCatalogDoc(payload, componentName) {
|
|
125
|
+
const docs = await findAllDocs(payload, NITROGEN_COMPONENT_CATALOG_COLLECTION, {
|
|
126
|
+
limit: 1,
|
|
127
|
+
where: {
|
|
128
|
+
componentName: {
|
|
129
|
+
equals: componentName,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return docs[0];
|
|
134
|
+
}
|
|
135
|
+
async function runComponentPropUpdate(payload, body, mode) {
|
|
136
|
+
const componentName = typeof body.componentName === 'string' ? body.componentName.trim() : '';
|
|
137
|
+
const propPath = typeof body.propPath === 'string' ? body.propPath.trim() : '';
|
|
138
|
+
const sourceFilters = Array.isArray(body.sourceFilters)
|
|
139
|
+
? body.sourceFilters.filter((value) => typeof value === 'string')
|
|
140
|
+
: [];
|
|
141
|
+
if (!componentName || !propPath) {
|
|
142
|
+
return Response.json({ error: 'componentName and propPath are required.' }, { status: 400 });
|
|
143
|
+
}
|
|
144
|
+
const usageDocs = await findUsageDocsForComponent(payload, componentName, sourceFilters);
|
|
145
|
+
const sourceKeys = Array.from(new Set(usageDocs.map((usage) => `${usage.sourceCollection}:${usage.sourceDocumentId}`)));
|
|
146
|
+
const documents = [];
|
|
147
|
+
let occurrenceCount = 0;
|
|
148
|
+
for (const sourceKey of sourceKeys) {
|
|
149
|
+
const [collection, ...idParts] = sourceKey.split(':');
|
|
150
|
+
const documentId = idParts.join(':');
|
|
151
|
+
try {
|
|
152
|
+
const doc = (await payload.findByID({
|
|
153
|
+
collection: collection,
|
|
154
|
+
id: documentId,
|
|
155
|
+
depth: 0,
|
|
156
|
+
overrideAccess: true,
|
|
157
|
+
}));
|
|
158
|
+
const nextNitrogenData = cloneJson(doc.nitrogenData ?? []);
|
|
159
|
+
const docOccurrenceCount = updateModulesForComponent(nextNitrogenData, componentName, propPath, body.value, { apply: mode === 'apply' });
|
|
160
|
+
if (docOccurrenceCount === 0) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
occurrenceCount += docOccurrenceCount;
|
|
164
|
+
documents.push({
|
|
165
|
+
collection,
|
|
166
|
+
documentId,
|
|
167
|
+
occurrenceCount: docOccurrenceCount,
|
|
168
|
+
title: doc.title || doc.slug || undefined,
|
|
169
|
+
});
|
|
170
|
+
if (mode === 'apply') {
|
|
171
|
+
await payload.update({
|
|
172
|
+
collection: collection,
|
|
173
|
+
id: documentId,
|
|
174
|
+
data: {
|
|
175
|
+
nitrogenData: nextNitrogenData,
|
|
176
|
+
},
|
|
177
|
+
depth: 0,
|
|
178
|
+
overrideAccess: true,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
payload.logger.warn(`[@nitrogenbuilder/connector-payload] Could not update "${componentName}" in ${sourceKey}: ${error instanceof Error ? error.message : String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
componentName,
|
|
188
|
+
documentCount: documents.length,
|
|
189
|
+
documents,
|
|
190
|
+
mode,
|
|
191
|
+
occurrenceCount,
|
|
192
|
+
propPath,
|
|
193
|
+
success: true,
|
|
194
|
+
value: body.value,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
3
197
|
export function createComponentInventoryEndpoints(args) {
|
|
4
198
|
return [
|
|
5
199
|
{
|
|
@@ -16,5 +210,84 @@ export function createComponentInventoryEndpoints(args) {
|
|
|
16
210
|
});
|
|
17
211
|
},
|
|
18
212
|
},
|
|
213
|
+
{
|
|
214
|
+
path: '/nitrogen/v1/component-inventory/preview-data',
|
|
215
|
+
method: 'get',
|
|
216
|
+
handler: async (req) => {
|
|
217
|
+
const authError = requireAuth(req);
|
|
218
|
+
if (authError)
|
|
219
|
+
return authError;
|
|
220
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
221
|
+
const componentName = url.searchParams.get('componentName')?.trim() || '';
|
|
222
|
+
const sourceCollection = url.searchParams.get('sourceCollection')?.trim() || '';
|
|
223
|
+
const sourceDocumentId = url.searchParams.get('sourceDocumentId')?.trim() || '';
|
|
224
|
+
const moduleId = url.searchParams.get('moduleId')?.trim() || undefined;
|
|
225
|
+
if (!componentName) {
|
|
226
|
+
return Response.json({ error: 'componentName is required.' }, { status: 400 });
|
|
227
|
+
}
|
|
228
|
+
if (sourceCollection && sourceDocumentId) {
|
|
229
|
+
try {
|
|
230
|
+
const doc = (await req.payload.findByID({
|
|
231
|
+
collection: sourceCollection,
|
|
232
|
+
id: sourceDocumentId,
|
|
233
|
+
depth: 0,
|
|
234
|
+
overrideAccess: true,
|
|
235
|
+
}));
|
|
236
|
+
const module = findModuleInTree(doc.nitrogenData, componentName, moduleId);
|
|
237
|
+
if (module) {
|
|
238
|
+
return Response.json({
|
|
239
|
+
componentName,
|
|
240
|
+
module: cloneJson(module),
|
|
241
|
+
source: {
|
|
242
|
+
collection: sourceCollection,
|
|
243
|
+
documentId: sourceDocumentId,
|
|
244
|
+
title: doc.title || doc.slug || '',
|
|
245
|
+
},
|
|
246
|
+
success: true,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Fall back to a default module below.
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const catalogDoc = await getComponentCatalogDoc(req.payload, componentName);
|
|
255
|
+
return Response.json({
|
|
256
|
+
componentName,
|
|
257
|
+
definition: catalogDoc?.definition || null,
|
|
258
|
+
module: {
|
|
259
|
+
id: `nitrogen-component-preview-${componentName}`,
|
|
260
|
+
module: componentName,
|
|
261
|
+
props: {},
|
|
262
|
+
},
|
|
263
|
+
source: null,
|
|
264
|
+
success: true,
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
path: '/nitrogen/v1/component-inventory/prop-update/preview',
|
|
270
|
+
method: 'post',
|
|
271
|
+
handler: async (req) => {
|
|
272
|
+
const authError = requireAuth(req);
|
|
273
|
+
if (authError)
|
|
274
|
+
return authError;
|
|
275
|
+
const body = (await req.json?.());
|
|
276
|
+
const result = await runComponentPropUpdate(req.payload, body || {}, 'preview');
|
|
277
|
+
return result instanceof Response ? result : Response.json(result);
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
path: '/nitrogen/v1/component-inventory/prop-update/apply',
|
|
282
|
+
method: 'post',
|
|
283
|
+
handler: async (req) => {
|
|
284
|
+
const authError = requireAuth(req);
|
|
285
|
+
if (authError)
|
|
286
|
+
return authError;
|
|
287
|
+
const body = (await req.json?.());
|
|
288
|
+
const result = await runComponentPropUpdate(req.payload, body || {}, 'apply');
|
|
289
|
+
return result instanceof Response ? result : Response.json(result);
|
|
290
|
+
},
|
|
291
|
+
},
|
|
19
292
|
];
|
|
20
293
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { DEFAULT_EDITOR_CDN_VERSION } from '../constants.js';
|
|
2
|
+
function validateEditorCdnVersion(value) {
|
|
3
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
return /^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(value.trim())
|
|
7
|
+
? true
|
|
8
|
+
: 'Use an npm version or dist tag, such as 0.5.30, latest, or beta.';
|
|
9
|
+
}
|
|
1
10
|
export const NitrogenSettings = {
|
|
2
11
|
slug: 'nitrogen-settings',
|
|
3
12
|
admin: {
|
|
@@ -43,5 +52,33 @@ export const NitrogenSettings = {
|
|
|
43
52
|
},
|
|
44
53
|
},
|
|
45
54
|
},
|
|
55
|
+
{
|
|
56
|
+
name: 'packageVersionsHeader',
|
|
57
|
+
type: 'ui',
|
|
58
|
+
admin: {
|
|
59
|
+
components: {
|
|
60
|
+
Field: '@nitrogenbuilder/connector-payload/components/NitrogenPackageVersionsHeaderField',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'editorCdnVersion',
|
|
66
|
+
label: 'Editor CDN Version',
|
|
67
|
+
type: 'text',
|
|
68
|
+
defaultValue: DEFAULT_EDITOR_CDN_VERSION,
|
|
69
|
+
validate: validateEditorCdnVersion,
|
|
70
|
+
admin: {
|
|
71
|
+
description: 'Version or npm dist tag used for @nitrogenbuilder/editor on jsDelivr when not using editor development mode.',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'packageVersions',
|
|
76
|
+
type: 'ui',
|
|
77
|
+
admin: {
|
|
78
|
+
components: {
|
|
79
|
+
Field: '@nitrogenbuilder/connector-payload/components/NitrogenPackageVersionsField',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
46
83
|
],
|
|
47
84
|
};
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED