@nitrogenbuilder/connector-payload 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -25,10 +25,6 @@ export const NitrogenComponentUsage = {
|
|
|
25
25
|
type: 'text',
|
|
26
26
|
required: true,
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
name: 'depth',
|
|
30
|
-
type: 'number',
|
|
31
|
-
},
|
|
32
28
|
{
|
|
33
29
|
name: 'sourceCollection',
|
|
34
30
|
type: 'text',
|
|
@@ -56,26 +52,12 @@ export const NitrogenComponentUsage = {
|
|
|
56
52
|
name: 'sourceStatus',
|
|
57
53
|
type: 'text',
|
|
58
54
|
},
|
|
55
|
+
// Usage answers "which components appear in which documents". A module's
|
|
56
|
+
// position in the tree is deliberately not stored: nothing read it, and
|
|
57
|
+
// keeping it meant every reorder rewrote every row of the document.
|
|
59
58
|
{
|
|
60
59
|
name: 'moduleId',
|
|
61
60
|
type: 'text',
|
|
62
61
|
},
|
|
63
|
-
{
|
|
64
|
-
name: 'modulePath',
|
|
65
|
-
type: 'text',
|
|
66
|
-
required: true,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: 'parentComponentName',
|
|
70
|
-
type: 'text',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'parentModuleId',
|
|
74
|
-
type: 'text',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: 'parentModulePath',
|
|
78
|
-
type: 'text',
|
|
79
|
-
},
|
|
80
62
|
],
|
|
81
63
|
};
|
|
@@ -548,7 +548,7 @@ export default function NitrogenComponentInventoryClient({ adminRoute, component
|
|
|
548
548
|
display: 'block',
|
|
549
549
|
padding: 12,
|
|
550
550
|
textDecoration: 'none',
|
|
551
|
-
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: usage.sourceTitle || usage.sourceSlug || usage.sourceDocumentId }), _jsxs("div", { style: { color: payloadTheme.mutedText, fontSize: 12, marginTop: 2 }, children: [usage.sourceCollection,
|
|
551
|
+
}, children: [_jsx("div", { style: { fontWeight: 600 }, children: usage.sourceTitle || usage.sourceSlug || usage.sourceDocumentId }), _jsxs("div", { style: { color: payloadTheme.mutedText, fontSize: 12, marginTop: 2 }, children: [usage.sourceCollection, usage.sourceSlug ? ` - ${usage.sourceSlug}` : ''] })] }, usage.usageKey))) })) : (_jsx("p", { style: { color: payloadTheme.mutedText, margin: 0 }, children: "No indexed usages yet." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { color: payloadTheme.text, margin: '0 0 10px', fontSize: 15 }, children: "Props" }), definition ? (Object.values(definition.categories).map((category) => renderCategory(category))) : (_jsx("p", { style: { color: payloadTheme.mutedText, margin: 0 }, children: "No manifest definition available." }))] }), _jsxs("div", { children: [_jsx("h3", { style: { color: payloadTheme.text, margin: '0 0 10px', fontSize: 15 }, children: "Update Prop Across Site" }), editableProps.length ? (_jsxs("div", { style: {
|
|
552
552
|
border: `1px solid ${payloadTheme.border}`,
|
|
553
553
|
borderRadius: 8,
|
|
554
554
|
display: 'grid',
|
|
@@ -6,12 +6,7 @@ export declare const NITROGEN_COMPONENT_CATALOG_COLLECTION = "nitrogen-component
|
|
|
6
6
|
export declare const NITROGEN_COMPONENT_USAGE_COLLECTION = "nitrogen-component-usage";
|
|
7
7
|
type ComponentUsageRecord = {
|
|
8
8
|
componentName: string;
|
|
9
|
-
depth: number;
|
|
10
9
|
moduleId: string;
|
|
11
|
-
modulePath: string;
|
|
12
|
-
parentComponentName?: string;
|
|
13
|
-
parentModuleId?: string;
|
|
14
|
-
parentModulePath?: string;
|
|
15
10
|
};
|
|
16
11
|
type ReindexResult = {
|
|
17
12
|
catalogCount: number;
|
|
@@ -10,50 +10,41 @@ function moduleNameFromNode(node) {
|
|
|
10
10
|
}
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
|
-
function collectUsageFromTree(modules, pathPrefix, results
|
|
13
|
+
function collectUsageFromTree(modules, pathPrefix, results) {
|
|
14
14
|
if (!Array.isArray(modules))
|
|
15
15
|
return;
|
|
16
16
|
modules.forEach((maybeNode, index) => {
|
|
17
17
|
if (!maybeNode || typeof maybeNode !== 'object')
|
|
18
18
|
return;
|
|
19
19
|
const node = maybeNode;
|
|
20
|
-
const modulePath = `${pathPrefix}[${index}]`;
|
|
21
20
|
const componentName = moduleNameFromNode(node);
|
|
21
|
+
// The tree path is no longer stored, but it is still the identity of last
|
|
22
|
+
// resort for a node that carries no id of its own. Such a node reverts to
|
|
23
|
+
// the old positional behaviour and churns when the tree is reordered.
|
|
24
|
+
const modulePath = `${pathPrefix}[${index}]`;
|
|
22
25
|
const moduleId = typeof node.id === 'string' || typeof node.id === 'number'
|
|
23
26
|
? String(node.id)
|
|
24
27
|
: modulePath;
|
|
25
|
-
const currentContext = componentName
|
|
26
|
-
? {
|
|
27
|
-
componentName,
|
|
28
|
-
moduleId,
|
|
29
|
-
modulePath,
|
|
30
|
-
}
|
|
31
|
-
: parentContext;
|
|
32
28
|
if (componentName) {
|
|
33
29
|
results.push({
|
|
34
30
|
componentName,
|
|
35
|
-
depth,
|
|
36
31
|
moduleId,
|
|
37
|
-
modulePath,
|
|
38
|
-
parentComponentName: parentContext?.componentName,
|
|
39
|
-
parentModuleId: parentContext?.moduleId,
|
|
40
|
-
parentModulePath: parentContext?.modulePath,
|
|
41
32
|
});
|
|
42
33
|
}
|
|
43
34
|
const props = node.props;
|
|
44
35
|
if (props && typeof props === 'object') {
|
|
45
36
|
const children = props.children;
|
|
46
37
|
if (Array.isArray(children)) {
|
|
47
|
-
collectUsageFromTree(children, `${modulePath}.props.children`, results
|
|
38
|
+
collectUsageFromTree(children, `${modulePath}.props.children`, results);
|
|
48
39
|
}
|
|
49
40
|
else if (children && typeof children === 'object') {
|
|
50
41
|
for (const [slotName, slotChildren] of Object.entries(children)) {
|
|
51
|
-
collectUsageFromTree(slotChildren, `${modulePath}.props.children.${slotName}`, results
|
|
42
|
+
collectUsageFromTree(slotChildren, `${modulePath}.props.children.${slotName}`, results);
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
}
|
|
55
46
|
if (Array.isArray(node.children)) {
|
|
56
|
-
collectUsageFromTree(node.children, `${modulePath}.children`, results
|
|
47
|
+
collectUsageFromTree(node.children, `${modulePath}.children`, results);
|
|
57
48
|
}
|
|
58
49
|
});
|
|
59
50
|
}
|
|
@@ -84,62 +75,77 @@ export async function findAllDocs(payload, collection, options, req) {
|
|
|
84
75
|
}
|
|
85
76
|
return docs;
|
|
86
77
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Delete usage rows in one bulk statement rather than paginating the ids and
|
|
80
|
+
* issuing a `payload.delete` per row. Usage is rebuilt wholesale on every save,
|
|
81
|
+
* so a document with N modules previously cost N deletes (plus the finds to
|
|
82
|
+
* discover them) on the critical path of the save transaction.
|
|
83
|
+
*/
|
|
84
|
+
async function deleteUsageDocsWhere(payload, where, req) {
|
|
85
|
+
await payload.delete({
|
|
86
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
87
|
+
overrideAccess: true,
|
|
88
|
+
req,
|
|
89
|
+
where,
|
|
90
|
+
});
|
|
96
91
|
}
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
where: {
|
|
104
|
-
and: [
|
|
105
|
-
{
|
|
106
|
-
sourceCollection: {
|
|
107
|
-
equals: sourceCollection,
|
|
108
|
-
},
|
|
92
|
+
function usageDocsForSourceWhere(sourceCollection, sourceDocumentId) {
|
|
93
|
+
return {
|
|
94
|
+
and: [
|
|
95
|
+
{
|
|
96
|
+
sourceCollection: {
|
|
97
|
+
equals: sourceCollection,
|
|
109
98
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
sourceDocumentId: {
|
|
102
|
+
equals: sourceDocumentId,
|
|
114
103
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function usageKeyFor(sourceCollection, sourceDocumentId, record) {
|
|
109
|
+
return `${sourceCollection}:${sourceDocumentId}:${record.moduleId}:${record.componentName}`;
|
|
110
|
+
}
|
|
111
|
+
function usageSourceFields(doc) {
|
|
112
|
+
return {
|
|
113
|
+
sourceTitle: doc.title || '',
|
|
114
|
+
sourceSlug: doc.slug || '',
|
|
115
|
+
sourceStatus: doc.status || doc._status || '',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* `componentName`, `moduleId`, `sourceCollection` and `sourceDocumentId` are all
|
|
120
|
+
* encoded in the usage key, so a key match already implies they agree; the
|
|
121
|
+
* source fields are reconciled separately by a single bulk update. That leaves
|
|
122
|
+
* only `sourceType`, which is checked defensively so a row written with the
|
|
123
|
+
* wrong value can still heal.
|
|
124
|
+
*/
|
|
125
|
+
function usageRowIsCurrent(row, sourceType) {
|
|
126
|
+
return row.sourceType === sourceType;
|
|
127
|
+
}
|
|
128
|
+
function usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record) {
|
|
129
|
+
return {
|
|
130
|
+
usageKey: usageKeyFor(sourceCollection, sourceDocumentId, record),
|
|
131
|
+
componentName: record.componentName,
|
|
132
|
+
sourceCollection,
|
|
133
|
+
sourceType,
|
|
134
|
+
sourceDocumentId,
|
|
135
|
+
...sourceFields,
|
|
136
|
+
moduleId: record.moduleId,
|
|
137
|
+
};
|
|
118
138
|
}
|
|
119
139
|
async function createUsageDocs(payload, sourceCollection, sourceType, doc, req) {
|
|
120
140
|
const sourceDocumentId = String(doc.id);
|
|
141
|
+
const sourceFields = usageSourceFields(doc);
|
|
121
142
|
const usageRecords = extractComponentUsageRecords(doc.nitrogenData);
|
|
122
143
|
for (const record of usageRecords) {
|
|
123
144
|
await payload.create({
|
|
124
145
|
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
125
146
|
overrideAccess: true,
|
|
126
147
|
req,
|
|
127
|
-
data:
|
|
128
|
-
usageKey: `${sourceCollection}:${sourceDocumentId}:${record.moduleId}:${record.modulePath}:${record.componentName}`,
|
|
129
|
-
componentName: record.componentName,
|
|
130
|
-
depth: record.depth,
|
|
131
|
-
sourceCollection,
|
|
132
|
-
sourceType,
|
|
133
|
-
sourceDocumentId,
|
|
134
|
-
sourceTitle: doc.title || '',
|
|
135
|
-
sourceSlug: doc.slug || '',
|
|
136
|
-
sourceStatus: doc.status || doc._status || '',
|
|
137
|
-
moduleId: record.moduleId,
|
|
138
|
-
modulePath: record.modulePath,
|
|
139
|
-
parentComponentName: record.parentComponentName || '',
|
|
140
|
-
parentModuleId: record.parentModuleId || '',
|
|
141
|
-
parentModulePath: record.parentModulePath || '',
|
|
142
|
-
},
|
|
148
|
+
data: usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record),
|
|
143
149
|
});
|
|
144
150
|
}
|
|
145
151
|
return usageRecords.length;
|
|
@@ -193,23 +199,72 @@ export async function syncComponentCatalog(payload, manifestInput, req) {
|
|
|
193
199
|
}
|
|
194
200
|
export async function reindexDocumentUsage(payload, sourceCollection, doc, sourceType = 'document', req) {
|
|
195
201
|
const sourceDocumentId = String(doc.id);
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
const sourceWhere = usageDocsForSourceWhere(sourceCollection, sourceDocumentId);
|
|
203
|
+
const sourceFields = usageSourceFields(doc);
|
|
204
|
+
const usageRecords = extractComponentUsageRecords(doc.nitrogenData);
|
|
205
|
+
// Usage rows describe tree *structure* only — nothing derived from module
|
|
206
|
+
// props or content. Editing copy therefore leaves every row identical, so
|
|
207
|
+
// diffing turns the common autosave case into zero writes instead of a full
|
|
208
|
+
// delete-and-recreate of every row on the save's critical path.
|
|
209
|
+
const existingRows = await findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION, { limit: 200, where: sourceWhere }, req);
|
|
210
|
+
const desiredByKey = new Map();
|
|
211
|
+
for (const record of usageRecords) {
|
|
212
|
+
desiredByKey.set(usageKeyFor(sourceCollection, sourceDocumentId, record), record);
|
|
213
|
+
}
|
|
214
|
+
const existingByKey = new Map();
|
|
215
|
+
const staleKeys = [];
|
|
216
|
+
for (const row of existingRows) {
|
|
217
|
+
if (desiredByKey.has(row.usageKey))
|
|
218
|
+
existingByKey.set(row.usageKey, row);
|
|
219
|
+
else
|
|
220
|
+
staleKeys.push(row.usageKey);
|
|
221
|
+
}
|
|
222
|
+
if (staleKeys.length > 0) {
|
|
223
|
+
await deleteUsageDocsWhere(payload, { usageKey: { in: staleKeys } }, req);
|
|
224
|
+
}
|
|
225
|
+
// Title/slug/status are identical across every row of the document, so when
|
|
226
|
+
// they change one bulk update fixes the whole set rather than N row updates.
|
|
227
|
+
const sourceFieldsStale = existingRows.some((row) => (row.sourceTitle || '') !== sourceFields.sourceTitle ||
|
|
228
|
+
(row.sourceSlug || '') !== sourceFields.sourceSlug ||
|
|
229
|
+
(row.sourceStatus || '') !== sourceFields.sourceStatus);
|
|
230
|
+
if (sourceFieldsStale && existingByKey.size > 0) {
|
|
231
|
+
await payload.update({
|
|
232
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
233
|
+
overrideAccess: true,
|
|
234
|
+
req,
|
|
235
|
+
where: sourceWhere,
|
|
236
|
+
data: sourceFields,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
for (const [usageKey, record] of desiredByKey) {
|
|
240
|
+
const row = existingByKey.get(usageKey);
|
|
241
|
+
const data = usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record);
|
|
242
|
+
if (!row) {
|
|
243
|
+
await payload.create({
|
|
244
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
245
|
+
overrideAccess: true,
|
|
246
|
+
req,
|
|
247
|
+
data,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else if (!usageRowIsCurrent(row, sourceType)) {
|
|
251
|
+
await payload.update({
|
|
252
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
253
|
+
id: row.id,
|
|
254
|
+
overrideAccess: true,
|
|
255
|
+
req,
|
|
256
|
+
data,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return usageRecords.length;
|
|
199
261
|
}
|
|
200
262
|
export async function deleteDocumentUsage(payload, sourceCollection, sourceDocumentId, req) {
|
|
201
|
-
|
|
202
|
-
await deleteUsageDocs(payload, docsForSource, req);
|
|
263
|
+
await deleteUsageDocsWhere(payload, usageDocsForSourceWhere(sourceCollection, String(sourceDocumentId)), req);
|
|
203
264
|
}
|
|
204
265
|
export async function reindexAllComponentInventory(payload, collections, manifestInput, req) {
|
|
205
266
|
const entries = await syncComponentCatalog(payload, manifestInput, req);
|
|
206
|
-
|
|
207
|
-
limit: 200,
|
|
208
|
-
select: {
|
|
209
|
-
id: true,
|
|
210
|
-
},
|
|
211
|
-
}, req);
|
|
212
|
-
await deleteUsageDocs(payload, usageDocs, req);
|
|
267
|
+
await deleteUsageDocsWhere(payload, { id: { exists: true } }, req);
|
|
213
268
|
let documentCount = 0;
|
|
214
269
|
let usageCount = 0;
|
|
215
270
|
for (const collection of collections) {
|
package/dist/types.d.ts
CHANGED
|
@@ -152,7 +152,6 @@ export interface NitrogenComponentUsageDoc {
|
|
|
152
152
|
id: string | number;
|
|
153
153
|
usageKey: string;
|
|
154
154
|
componentName: string;
|
|
155
|
-
depth?: number;
|
|
156
155
|
sourceCollection: string;
|
|
157
156
|
sourceType: string;
|
|
158
157
|
sourceDocumentId: string;
|
|
@@ -160,10 +159,6 @@ export interface NitrogenComponentUsageDoc {
|
|
|
160
159
|
sourceSlug?: string;
|
|
161
160
|
sourceStatus?: string;
|
|
162
161
|
moduleId?: string;
|
|
163
|
-
modulePath: string;
|
|
164
|
-
parentComponentName?: string;
|
|
165
|
-
parentModuleId?: string;
|
|
166
|
-
parentModulePath?: string;
|
|
167
162
|
}
|
|
168
163
|
export interface NitrogenInventorySourceDoc {
|
|
169
164
|
id: string | number;
|
package/package.json
CHANGED