@byline/core 6.7.2 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@types/admin-types.d.ts +6 -6
- package/dist/@types/admin-types.js +3 -3
- package/dist/@types/form-admin-config.test.node.js +1 -1
- package/dist/@types/singleton-admin-config.test.node.js +8 -5
- package/dist/@types/site-config.d.ts +15 -1
- package/dist/config/config.d.ts +2 -2
- package/dist/config/config.js +14 -7
- package/dist/config/group-collections.js +1 -1
- package/dist/config/group-collections.test.node.js +6 -6
- package/dist/config/validate-admin-configs.d.ts +1 -1
- package/dist/config/validate-admin-configs.js +10 -10
- package/dist/config/validate-admin-configs.test.node.js +22 -22
- package/dist/config/validate-passkey-config.d.ts +4 -0
- package/dist/config/validate-passkey-config.js +89 -0
- package/dist/config/validate-passkey-config.test.node.d.ts +1 -0
- package/dist/config/validate-passkey-config.test.node.js +152 -0
- package/dist/core.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/paths/path-dialects.test.node.js +2 -2
- package/dist/paths/paths.test.node.js +1 -1
- package/dist/scheduler/validate-scheduler-config.d.ts +2 -1
- package/dist/scheduler/validate-scheduler-config.js +9 -4
- package/dist/scheduler/validate-scheduler-config.test.node.js +17 -0
- package/dist/services/document-lifecycle/copy-to-locale.js +2 -1
- package/dist/services/document-lifecycle/delete-locale.js +27 -0
- package/dist/services/document-lifecycle/internals.d.ts +3 -2
- package/dist/services/document-lifecycle/internals.js +3 -3
- package/dist/services/document-lifecycle/merge-write-locale.d.ts +12 -0
- package/dist/services/document-lifecycle/merge-write-locale.js +53 -0
- package/dist/services/document-lifecycle/persistence.d.ts +4 -1
- package/dist/services/document-lifecycle/persistence.js +23 -1
- package/dist/services/document-lifecycle/update.js +2 -2
- package/dist/services/document-lifecycle.test.node.js +4 -2
- package/dist/services/singleton-lifecycle/copy-to-locale.js +1 -1
- package/dist/services/singleton-lifecycle/update.js +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/storage-flatten.js +8 -2
- package/dist/storage/storage-restore.d.ts +15 -2
- package/dist/storage/storage-restore.js +66 -15
- package/dist/storage/unavailable-blocks.test.node.d.ts +8 -0
- package/dist/storage/unavailable-blocks.test.node.js +132 -0
- package/dist/validation/document-fields.js +7 -4
- package/package.json +3 -3
|
@@ -8,11 +8,16 @@
|
|
|
8
8
|
import { RESERVED_FIELD_NAMES } from '../config/validate-collections.js';
|
|
9
9
|
import { ERR_DATABASE } from '../lib/errors.js';
|
|
10
10
|
import { getLogger } from '../lib/logger.js';
|
|
11
|
+
// Shared across duplicate server module graphs, just like the logger registry.
|
|
12
|
+
const loggedVersionsKey = Symbol.for('byline.unavailable-block-versions');
|
|
13
|
+
const processState = globalThis;
|
|
14
|
+
processState[loggedVersionsKey] ??= new Set();
|
|
15
|
+
const loggedVersions = processState[loggedVersionsKey];
|
|
11
16
|
/**
|
|
12
17
|
* Main entrypoint for restoring a document's field data from flattened form
|
|
13
18
|
* back into the original nested structure.
|
|
14
19
|
*
|
|
15
|
-
* Always returns `{ data, warnings }`. Warnings carry per-row reasons (e.g.
|
|
20
|
+
* Always returns `{ data, warnings, unavailableBlocks }`. Warnings carry per-row reasons (e.g.
|
|
16
21
|
* "Invalid block index 'undefined'") that are typically the result of a
|
|
17
22
|
* collection schema change leaving orphan rows behind. Callers decide what
|
|
18
23
|
* to do with them — strict reads reject any non-empty `warnings`, lenient
|
|
@@ -21,9 +26,10 @@ import { getLogger } from '../lib/logger.js';
|
|
|
21
26
|
* @param fields - The field definitions for the collection.
|
|
22
27
|
* @param flattenedData - The flattened field data to restore.
|
|
23
28
|
*/
|
|
24
|
-
export const restoreFieldSetData = (fields, flattenedData, resolveLocale) => {
|
|
29
|
+
export const restoreFieldSetData = (fields, flattenedData, resolveLocale, identity) => {
|
|
25
30
|
const result = {};
|
|
26
31
|
const warnings = [];
|
|
32
|
+
const unavailableByArray = new Map();
|
|
27
33
|
for (const item of flattenedData) {
|
|
28
34
|
// biome-ignore lint/style/noNonNullAssertion: TODO: put in a proper check here?
|
|
29
35
|
const fieldName = item.field_path[0];
|
|
@@ -38,7 +44,7 @@ export const restoreFieldSetData = (fields, flattenedData, resolveLocale) => {
|
|
|
38
44
|
if (!field) {
|
|
39
45
|
}
|
|
40
46
|
else {
|
|
41
|
-
result[fieldName] = restoreFieldData(field, result[fieldName], item, 1, warnings, resolveLocale);
|
|
47
|
+
result[fieldName] = restoreFieldData(field, result[fieldName], item, 1, warnings, unavailableByArray, resolveLocale);
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
// Array/blocks/group/hasMany-relation fixup — a required list-shaped field
|
|
@@ -54,17 +60,52 @@ export const restoreFieldSetData = (fields, flattenedData, resolveLocale) => {
|
|
|
54
60
|
result[field.name] = result[field.name] || {};
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
|
-
|
|
63
|
+
const unavailableBlocks = [];
|
|
64
|
+
// Finish only after all rows (including identity metadata) have been read.
|
|
65
|
+
// Walk parents before children so nested report paths use compacted indices.
|
|
66
|
+
const finish = (fields, value, prefix = '') => {
|
|
67
|
+
for (const field of fields) {
|
|
68
|
+
const path = prefix ? `${prefix}.${field.name}` : field.name;
|
|
69
|
+
const child = value?.[field.name];
|
|
70
|
+
if (field.type === 'group' && child)
|
|
71
|
+
finish(field.fields, child, path);
|
|
72
|
+
if ((field.type === 'blocks' || field.type === 'array') && Array.isArray(child)) {
|
|
73
|
+
for (const issue of [...(unavailableByArray.get(child)?.values() ?? [])].sort((a, b) => a.storedIndex - b.storedIndex))
|
|
74
|
+
unavailableBlocks.push({ fieldPath: path, ...issue });
|
|
75
|
+
const compacted = child.filter((item) => item != null);
|
|
76
|
+
value[field.name] = compacted;
|
|
77
|
+
compacted.forEach((item, index) => {
|
|
78
|
+
const itemFields = field.type === 'array'
|
|
79
|
+
? field.fields
|
|
80
|
+
: field.blocks.find((block) => block.blockType === item._type)?.fields;
|
|
81
|
+
if (itemFields)
|
|
82
|
+
finish(itemFields, item, `${path}[${index}]`);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
finish(fields, result);
|
|
88
|
+
if (identity && unavailableBlocks.length > 0) {
|
|
89
|
+
const key = JSON.stringify([identity.collectionPath, identity.documentId, identity.versionId]);
|
|
90
|
+
if (!loggedVersions.has(key)) {
|
|
91
|
+
loggedVersions.add(key);
|
|
92
|
+
getLogger().warn({
|
|
93
|
+
...identity,
|
|
94
|
+
blockTypes: [...new Set(unavailableBlocks.map((issue) => issue.blockType))],
|
|
95
|
+
}, 'Document contains unavailable blocks; saving content will remove them');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { data: result, warnings, unavailableBlocks };
|
|
58
99
|
};
|
|
59
|
-
const restoreFieldData = (field, target, data, pathIndex, warnings, resolveLocale) => {
|
|
100
|
+
const restoreFieldData = (field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale) => {
|
|
60
101
|
if (field.type === 'group') {
|
|
61
|
-
return restoreGroupFieldData(field, target, data, pathIndex, warnings, resolveLocale);
|
|
102
|
+
return restoreGroupFieldData(field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale);
|
|
62
103
|
}
|
|
63
104
|
else if (field.type === 'array') {
|
|
64
|
-
return restoreArrayFieldData(field, target, data, pathIndex, warnings, resolveLocale);
|
|
105
|
+
return restoreArrayFieldData(field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale);
|
|
65
106
|
}
|
|
66
107
|
else if (field.type === 'blocks') {
|
|
67
|
-
return restoreBlocksFieldData(field, target, data, pathIndex, warnings, resolveLocale);
|
|
108
|
+
return restoreBlocksFieldData(field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale);
|
|
68
109
|
}
|
|
69
110
|
else if (field.type === 'relation' && field.hasMany) {
|
|
70
111
|
return restoreMultiRelationFieldData(target, data, pathIndex, warnings);
|
|
@@ -95,7 +136,7 @@ const restoreFieldData = (field, target, data, pathIndex, warnings, resolveLocal
|
|
|
95
136
|
}
|
|
96
137
|
return target;
|
|
97
138
|
};
|
|
98
|
-
const restoreGroupFieldData = (field, target, data, pathIndex, warnings, resolveLocale) => {
|
|
139
|
+
const restoreGroupFieldData = (field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale) => {
|
|
99
140
|
if (pathIndex >= data.field_path.length) {
|
|
100
141
|
warnings.push(`Path ended unexpectedly while restoring group: ${data.field_path.join('.')}`);
|
|
101
142
|
return target;
|
|
@@ -108,10 +149,10 @@ const restoreGroupFieldData = (field, target, data, pathIndex, warnings, resolve
|
|
|
108
149
|
return target;
|
|
109
150
|
}
|
|
110
151
|
target = target || {};
|
|
111
|
-
target[fieldName] = restoreFieldData(subField, target[fieldName], data, pathIndex + 1, warnings, resolveLocale);
|
|
152
|
+
target[fieldName] = restoreFieldData(subField, target[fieldName], data, pathIndex + 1, warnings, unavailableByArray, resolveLocale);
|
|
112
153
|
return target;
|
|
113
154
|
};
|
|
114
|
-
const restoreArrayFieldData = (field, target, data, pathIndex, warnings, resolveLocale) => {
|
|
155
|
+
const restoreArrayFieldData = (field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale) => {
|
|
115
156
|
if (pathIndex >= data.field_path.length) {
|
|
116
157
|
warnings.push(`Path ended unexpectedly while restoring array: ${data.field_path.join('.')}`);
|
|
117
158
|
return target;
|
|
@@ -140,10 +181,10 @@ const restoreArrayFieldData = (field, target, data, pathIndex, warnings, resolve
|
|
|
140
181
|
// Sub-field was removed from the schema; silently skip orphaned rows.
|
|
141
182
|
return target;
|
|
142
183
|
}
|
|
143
|
-
target[arrayIndex][fieldName] = restoreFieldData(subField, target[arrayIndex][fieldName], data, pathIndex + 2, warnings, resolveLocale);
|
|
184
|
+
target[arrayIndex][fieldName] = restoreFieldData(subField, target[arrayIndex][fieldName], data, pathIndex + 2, warnings, unavailableByArray, resolveLocale);
|
|
144
185
|
return target;
|
|
145
186
|
};
|
|
146
|
-
const restoreBlocksFieldData = (field, target, data, pathIndex, warnings, resolveLocale) => {
|
|
187
|
+
const restoreBlocksFieldData = (field, target, data, pathIndex, warnings, unavailableByArray, resolveLocale) => {
|
|
147
188
|
const arrayIndex = Number.parseInt(data.field_path[pathIndex] ?? '', 10);
|
|
148
189
|
if (Number.isNaN(arrayIndex) || arrayIndex < 0) {
|
|
149
190
|
warnings.push(`Invalid block index '${data.field_path[pathIndex]}' in path ${data.field_path.join('.')}`);
|
|
@@ -156,7 +197,17 @@ const restoreBlocksFieldData = (field, target, data, pathIndex, warnings, resolv
|
|
|
156
197
|
}
|
|
157
198
|
const block = field.blocks.find((f) => f.blockType === blockType);
|
|
158
199
|
if (block == null) {
|
|
159
|
-
|
|
200
|
+
target = target || [];
|
|
201
|
+
let issues = unavailableByArray.get(target);
|
|
202
|
+
if (!issues) {
|
|
203
|
+
issues = new Map();
|
|
204
|
+
unavailableByArray.set(target, issues);
|
|
205
|
+
}
|
|
206
|
+
const issue = issues.get(arrayIndex) ?? { storedIndex: arrayIndex, blockType };
|
|
207
|
+
if (data.field_type === 'meta' && pathIndex + 2 === data.field_path.length) {
|
|
208
|
+
issue.id = data.item_id;
|
|
209
|
+
}
|
|
210
|
+
issues.set(arrayIndex, issue);
|
|
160
211
|
return target;
|
|
161
212
|
}
|
|
162
213
|
target = target || [];
|
|
@@ -178,7 +229,7 @@ const restoreBlocksFieldData = (field, target, data, pathIndex, warnings, resolv
|
|
|
178
229
|
// Sub-field was removed from the schema; silently skip orphaned rows.
|
|
179
230
|
return target;
|
|
180
231
|
}
|
|
181
|
-
target[arrayIndex][fieldName] = restoreFieldData(subField, target[arrayIndex][fieldName], data, pathIndex + 3, warnings, resolveLocale);
|
|
232
|
+
target[arrayIndex][fieldName] = restoreFieldData(subField, target[arrayIndex][fieldName], data, pathIndex + 3, warnings, unavailableByArray, resolveLocale);
|
|
182
233
|
return target;
|
|
183
234
|
};
|
|
184
235
|
const restoreMultiRelationFieldData = (target, data, pathIndex, warnings) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import { getLogger } from '../lib/logger.js';
|
|
10
|
+
import { applyPatches } from '../patches/apply-patches.js';
|
|
11
|
+
import { validateDocumentFields } from '../validation/document-fields.js';
|
|
12
|
+
import { flattenFieldSetData } from './storage-flatten.js';
|
|
13
|
+
import { restoreFieldSetData } from './storage-restore.js';
|
|
14
|
+
const logger = vi.hoisted(() => ({ warn: vi.fn() }));
|
|
15
|
+
vi.mock('../lib/logger.js', () => ({ getLogger: () => logger }));
|
|
16
|
+
const live = { blockType: 'live', fields: [{ name: 'text', type: 'text' }] };
|
|
17
|
+
const retired = { ...live, blockType: 'retired' };
|
|
18
|
+
const blocks = (optional = false) => ({
|
|
19
|
+
name: 'content',
|
|
20
|
+
type: 'blocks',
|
|
21
|
+
optional,
|
|
22
|
+
blocks: [live, retired],
|
|
23
|
+
});
|
|
24
|
+
const item = (_type, i) => ({ _id: `item-${i}`, _type, text: `${_type}-${i}` });
|
|
25
|
+
describe('unavailable blocks', () => {
|
|
26
|
+
for (const optional of [false, true]) {
|
|
27
|
+
for (const types of [
|
|
28
|
+
['retired', 'live'],
|
|
29
|
+
['live', 'retired', 'live'],
|
|
30
|
+
['live', 'retired'],
|
|
31
|
+
['retired'],
|
|
32
|
+
['retired', 'retired', 'live'],
|
|
33
|
+
]) {
|
|
34
|
+
it(`compacts ${types.join(',')} (optional=${optional}) with an independent report`, () => {
|
|
35
|
+
const before = [blocks(optional)];
|
|
36
|
+
const after = [{ ...blocks(optional), blocks: [live] }];
|
|
37
|
+
const original = { content: types.map(item) };
|
|
38
|
+
const rows = flattenFieldSetData(before, original, 'en');
|
|
39
|
+
const restored = restoreFieldSetData(after, rows, 'en');
|
|
40
|
+
expect(restored.warnings).toEqual([]);
|
|
41
|
+
expect(restored.unavailableBlocks).toEqual(types.flatMap((type, storedIndex) => type === 'retired'
|
|
42
|
+
? [{ fieldPath: 'content', storedIndex, blockType: type, id: `item-${storedIndex}` }]
|
|
43
|
+
: []));
|
|
44
|
+
expect(restored.data.content).toEqual(original.content.filter((item) => item._type === 'live'));
|
|
45
|
+
for (const data of [restored.data, JSON.parse(JSON.stringify(restored.data))]) {
|
|
46
|
+
expect(validateDocumentFields(after, data)).toEqual([]);
|
|
47
|
+
const saved = restoreFieldSetData(after, flattenFieldSetData(after, data, 'en'), 'en');
|
|
48
|
+
expect(saved.unavailableBlocks).toEqual([]);
|
|
49
|
+
expect(saved.data.content ?? []).toEqual(restored.data.content);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
it('reports groups, arrays and nested blocks using compacted parent indices', () => {
|
|
55
|
+
const before = [
|
|
56
|
+
{ name: 'group', type: 'group', fields: [blocks()] },
|
|
57
|
+
{ name: 'sections', type: 'array', fields: [blocks()] },
|
|
58
|
+
{
|
|
59
|
+
name: 'outer',
|
|
60
|
+
type: 'blocks',
|
|
61
|
+
blocks: [retired, { blockType: 'container', fields: [blocks()] }],
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
const after = [
|
|
65
|
+
{ name: 'group', type: 'group', fields: [{ ...blocks(), blocks: [live] }] },
|
|
66
|
+
{ name: 'sections', type: 'array', fields: [{ ...blocks(), blocks: [live] }] },
|
|
67
|
+
{
|
|
68
|
+
name: 'outer',
|
|
69
|
+
type: 'blocks',
|
|
70
|
+
blocks: [{ blockType: 'container', fields: [{ ...blocks(), blocks: [live] }] }],
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
const content = [item('retired', 0), item('live', 1)];
|
|
74
|
+
const rows = flattenFieldSetData(before, {
|
|
75
|
+
group: { content },
|
|
76
|
+
sections: [{ content }],
|
|
77
|
+
outer: [item('retired', 0), { _type: 'container', content }],
|
|
78
|
+
}, 'en');
|
|
79
|
+
const result = restoreFieldSetData(after, rows.reverse(), 'en');
|
|
80
|
+
expect(result.unavailableBlocks.map((issue) => issue.fieldPath)).toEqual([
|
|
81
|
+
'group.content',
|
|
82
|
+
'sections[0].content',
|
|
83
|
+
'outer',
|
|
84
|
+
'outer[0].content',
|
|
85
|
+
]);
|
|
86
|
+
expect(result.warnings).toEqual([]);
|
|
87
|
+
expect(result.data.outer[0].content).toEqual([item('live', 1)]);
|
|
88
|
+
});
|
|
89
|
+
it('applies a positional patch to a surviving block after the missing card', () => {
|
|
90
|
+
const before = [blocks()];
|
|
91
|
+
const after = [{ ...blocks(), blocks: [live] }];
|
|
92
|
+
const source = { content: ['retired', 'live', 'live'].map(item) };
|
|
93
|
+
const { data } = restoreFieldSetData(after, flattenFieldSetData(before, source, 'en'));
|
|
94
|
+
const definition = {
|
|
95
|
+
path: 'pages',
|
|
96
|
+
labels: { singular: 'Page', plural: 'Pages' },
|
|
97
|
+
fields: after,
|
|
98
|
+
};
|
|
99
|
+
const patched = applyPatches(definition, data, [
|
|
100
|
+
{ kind: 'field.set', path: 'content[1].text', value: 'edited' },
|
|
101
|
+
]);
|
|
102
|
+
expect(patched.errors).toEqual([]);
|
|
103
|
+
const saved = restoreFieldSetData(after, flattenFieldSetData(after, patched.doc, 'en'));
|
|
104
|
+
expect(saved.data.content.map((item) => [item._id, item.text])).toEqual([
|
|
105
|
+
['item-1', 'live-1'],
|
|
106
|
+
['item-2', 'edited'],
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
it.each([new Array(1), [null], [undefined], [42], [[]]].map((content) => ({ content })))('rejects malformed items without a destructure crash: $content', ({ content }) => {
|
|
110
|
+
expect(validateDocumentFields([blocks()], { content })).not.toEqual([]);
|
|
111
|
+
expect(() => flattenFieldSetData([blocks()], { content }, 'en')).toThrow(/Block item is missing or must be an object/);
|
|
112
|
+
});
|
|
113
|
+
it('logs once per document version without adding a strict-read warning', () => {
|
|
114
|
+
const spy = vi.spyOn(getLogger(), 'warn').mockImplementation(() => { });
|
|
115
|
+
try {
|
|
116
|
+
const rows = flattenFieldSetData([blocks()], { content: [item('retired', 0)] }, 'en');
|
|
117
|
+
const identity = { collectionPath: 'pages', documentId: crypto.randomUUID(), versionId: 'v1' };
|
|
118
|
+
for (let i = 0; i < 2; i++)
|
|
119
|
+
expect(restoreFieldSetData([{ ...blocks(), blocks: [live] }], rows, 'en', identity).warnings).toEqual([]);
|
|
120
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(spy).toHaveBeenCalledWith({ ...identity, blockTypes: ['retired'] }, expect.any(String));
|
|
122
|
+
restoreFieldSetData([{ ...blocks(), blocks: [live] }], rows, 'en', {
|
|
123
|
+
...identity,
|
|
124
|
+
versionId: 'v2',
|
|
125
|
+
});
|
|
126
|
+
expect(spy).toHaveBeenCalledTimes(2);
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
spy.mockRestore();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -119,13 +119,16 @@ export function validateDocumentFields(fields, data, options = {}) {
|
|
|
119
119
|
if (field.validation?.maxLength != null && value.length > field.validation.maxLength)
|
|
120
120
|
add(path, `${label} allows at most ${field.validation.maxLength} items`);
|
|
121
121
|
}
|
|
122
|
-
value.
|
|
122
|
+
for (let index = 0; index < value.length; index++) {
|
|
123
|
+
const item = value[index];
|
|
123
124
|
const itemPath = record(item) && typeof item._id === 'string' && /^[\w-]+$/.test(item._id)
|
|
124
125
|
? `${path}[id=${item._id}]`
|
|
125
126
|
: `${path}[${index}]`;
|
|
126
127
|
if (!record(item)) {
|
|
127
|
-
add(itemPath,
|
|
128
|
-
|
|
128
|
+
add(itemPath, field.type === 'blocks'
|
|
129
|
+
? 'Block item is missing or must be an object'
|
|
130
|
+
: 'Item must be an object');
|
|
131
|
+
continue;
|
|
129
132
|
}
|
|
130
133
|
if (field.type === 'array')
|
|
131
134
|
visit(field.fields, item, itemPath);
|
|
@@ -136,7 +139,7 @@ export function validateDocumentFields(fields, data, options = {}) {
|
|
|
136
139
|
else
|
|
137
140
|
visit(block.fields, item, itemPath);
|
|
138
141
|
}
|
|
139
|
-
}
|
|
142
|
+
}
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
else {
|
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"name": "@byline/core",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "6.
|
|
5
|
+
"version": "6.9.0",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=22.22.2"
|
|
8
8
|
},
|
|
9
9
|
"description": "Byline CMS core package",
|
|
10
10
|
"keywords": [
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"dist"
|
|
83
83
|
],
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@byline/auth": "6.
|
|
85
|
+
"@byline/auth": "6.9.0",
|
|
86
86
|
"dotenv": "^18.0.8",
|
|
87
87
|
"pino": "^10.4.0",
|
|
88
88
|
"sharp": "^0.35.5",
|