@payloadcms/plugin-import-export 4.0.0-internal.5b1e7cd → 4.0.0-internal.5d5a2b2
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/assets.d.js +2 -0
- package/dist/@types/assets.d.js.map +1 -0
- package/dist/components/FieldsToExport/reduceFields.d.ts.map +1 -1
- package/dist/export/batchProcessor.d.ts +9 -1
- package/dist/export/batchProcessor.d.ts.map +1 -1
- package/dist/export/batchProcessor.js +57 -15
- package/dist/export/batchProcessor.js.map +1 -1
- package/dist/export/createExport.d.ts.map +1 -1
- package/dist/export/createExport.js +98 -20
- package/dist/export/createExport.js.map +1 -1
- package/dist/export/getCreateExportCollectionTask.d.ts.map +1 -1
- package/dist/export/getExportCollection.d.ts.map +1 -1
- package/dist/export/getFields.d.ts.map +1 -1
- package/dist/export/handleDownload.d.ts.map +1 -1
- package/dist/export/handlePreview.d.ts.map +1 -1
- package/dist/export/handlePreview.js +39 -14
- package/dist/export/handlePreview.js.map +1 -1
- package/dist/exports/types.d.ts +1 -1
- package/dist/exports/types.d.ts.map +1 -1
- package/dist/exports/types.js.map +1 -1
- package/dist/import/batchProcessor.d.ts +14 -2
- package/dist/import/batchProcessor.d.ts.map +1 -1
- package/dist/import/batchProcessor.js +49 -27
- package/dist/import/batchProcessor.js.map +1 -1
- package/dist/import/createImport.d.ts +1 -10
- package/dist/import/createImport.d.ts.map +1 -1
- package/dist/import/createImport.js +34 -53
- package/dist/import/createImport.js.map +1 -1
- package/dist/import/getCreateImportCollectionTask.d.ts.map +1 -1
- package/dist/import/getFields.d.ts.map +1 -1
- package/dist/import/getImportCollection.d.ts.map +1 -1
- package/dist/import/handlePreview.d.ts.map +1 -1
- package/dist/import/handlePreview.js +32 -6
- package/dist/import/handlePreview.js.map +1 -1
- package/dist/index.d.ts +50 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +189 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/applyFieldHooks.d.ts +22 -0
- package/dist/utilities/applyFieldHooks.d.ts.map +1 -0
- package/dist/utilities/applyFieldHooks.js +117 -0
- package/dist/utilities/applyFieldHooks.js.map +1 -0
- package/dist/utilities/applyFieldHooks.spec.js +205 -0
- package/dist/utilities/applyFieldHooks.spec.js.map +1 -0
- package/dist/utilities/buildDisabledFieldRegex.d.ts.map +1 -1
- package/dist/utilities/collectDisabledFieldPaths.d.ts.map +1 -1
- package/dist/utilities/collectDisabledFieldPaths.js +1 -1
- package/dist/utilities/collectDisabledFieldPaths.js.map +1 -1
- package/dist/utilities/fieldToRegex.d.ts.map +1 -1
- package/dist/utilities/flattenObject.d.ts +8 -6
- package/dist/utilities/flattenObject.d.ts.map +1 -1
- package/dist/utilities/flattenObject.js +85 -75
- package/dist/utilities/flattenObject.js.map +1 -1
- package/dist/utilities/flattenObject.spec.js +158 -0
- package/dist/utilities/flattenObject.spec.js.map +1 -0
- package/dist/utilities/flattenedFields.d.ts +21 -0
- package/dist/utilities/flattenedFields.d.ts.map +1 -0
- package/dist/utilities/flattenedFields.js +34 -0
- package/dist/utilities/flattenedFields.js.map +1 -0
- package/dist/utilities/getExportFieldFunctions.d.ts +5 -5
- package/dist/utilities/getExportFieldFunctions.d.ts.map +1 -1
- package/dist/utilities/getExportFieldFunctions.js +84 -98
- package/dist/utilities/getExportFieldFunctions.js.map +1 -1
- package/dist/utilities/getExportFieldFunctions.spec.js +50 -0
- package/dist/utilities/getExportFieldFunctions.spec.js.map +1 -0
- package/dist/utilities/getFileFromDoc.d.ts.map +1 -1
- package/dist/utilities/getFlattenedFieldKeys.d.ts.map +1 -1
- package/dist/utilities/getImportFieldFunctions.d.ts +5 -5
- package/dist/utilities/getImportFieldFunctions.d.ts.map +1 -1
- package/dist/utilities/getImportFieldFunctions.js +95 -103
- package/dist/utilities/getImportFieldFunctions.js.map +1 -1
- package/dist/utilities/getImportFieldFunctions.spec.js +167 -0
- package/dist/utilities/getImportFieldFunctions.spec.js.map +1 -0
- package/dist/utilities/getPluginCollections.d.ts.map +1 -1
- package/dist/utilities/getSchemaColumns.d.ts +1 -1
- package/dist/utilities/getSchemaColumns.d.ts.map +1 -1
- package/dist/utilities/getSchemaColumns.js +2 -2
- package/dist/utilities/getSchemaColumns.js.map +1 -1
- package/dist/utilities/getSelect.d.ts.map +1 -1
- package/dist/utilities/isPlainObject.d.ts +2 -0
- package/dist/utilities/isPlainObject.d.ts.map +1 -0
- package/dist/utilities/isPlainObject.js +3 -0
- package/dist/utilities/isPlainObject.js.map +1 -0
- package/dist/utilities/parseCSV.d.ts.map +1 -1
- package/dist/utilities/parseJSON.d.ts.map +1 -1
- package/dist/utilities/polymorphicRel.d.ts +14 -0
- package/dist/utilities/polymorphicRel.d.ts.map +1 -0
- package/dist/utilities/polymorphicRel.js +17 -0
- package/dist/utilities/polymorphicRel.js.map +1 -0
- package/dist/utilities/processRichTextField.d.ts.map +1 -1
- package/dist/utilities/processRichTextField.js +7 -0
- package/dist/utilities/processRichTextField.js.map +1 -1
- package/dist/utilities/processRichTextField.spec.js +308 -0
- package/dist/utilities/processRichTextField.spec.js.map +1 -0
- package/dist/utilities/removeDisabledFields.d.ts.map +1 -1
- package/dist/utilities/removeDisabledFields.js.map +1 -1
- package/dist/utilities/setNestedValue.d.ts.map +1 -1
- package/dist/utilities/setNestedValue.js +10 -8
- package/dist/utilities/setNestedValue.js.map +1 -1
- package/dist/utilities/siblingDoc.spec.js +278 -0
- package/dist/utilities/siblingDoc.spec.js.map +1 -0
- package/dist/utilities/sortHelpers.d.ts.map +1 -1
- package/dist/utilities/unflattenObject.d.ts +4 -3
- package/dist/utilities/unflattenObject.d.ts.map +1 -1
- package/dist/utilities/unflattenObject.js +69 -170
- package/dist/utilities/unflattenObject.js.map +1 -1
- package/dist/utilities/unflattenObject.spec.js +139 -0
- package/dist/utilities/unflattenObject.spec.js.map +1 -1
- package/dist/utilities/unflattenPostProcess.d.ts +11 -0
- package/dist/utilities/unflattenPostProcess.d.ts.map +1 -0
- package/dist/utilities/unflattenPostProcess.js +174 -0
- package/dist/utilities/unflattenPostProcess.js.map +1 -0
- package/dist/utilities/validateLimitValue.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { processRichTextField } from './processRichTextField.js';
|
|
3
|
+
// Minimal helpers to keep assertions readable
|
|
4
|
+
const asRecord = (v)=>v;
|
|
5
|
+
const asArray = (v)=>v;
|
|
6
|
+
// ─── Fixtures ────────────────────────────────────────────────────────────────
|
|
7
|
+
const textNode = (overrides = {})=>({
|
|
8
|
+
detail: 0,
|
|
9
|
+
format: 0,
|
|
10
|
+
mode: 'normal',
|
|
11
|
+
text: 'Hello',
|
|
12
|
+
type: 'text',
|
|
13
|
+
version: 1,
|
|
14
|
+
...overrides
|
|
15
|
+
});
|
|
16
|
+
const paragraphNode = (children = [
|
|
17
|
+
textNode()
|
|
18
|
+
])=>({
|
|
19
|
+
children,
|
|
20
|
+
direction: 'ltr',
|
|
21
|
+
format: '',
|
|
22
|
+
indent: 0,
|
|
23
|
+
type: 'paragraph',
|
|
24
|
+
version: 1
|
|
25
|
+
});
|
|
26
|
+
const lexicalDoc = (children = [
|
|
27
|
+
paragraphNode()
|
|
28
|
+
])=>({
|
|
29
|
+
children,
|
|
30
|
+
direction: 'ltr',
|
|
31
|
+
format: '',
|
|
32
|
+
indent: 0,
|
|
33
|
+
type: 'root',
|
|
34
|
+
version: 1
|
|
35
|
+
});
|
|
36
|
+
// ─── Primitive pass-through ──────────────────────────────────────────────────
|
|
37
|
+
describe('processRichTextField — primitive pass-through', ()=>{
|
|
38
|
+
it('should return null unchanged', ()=>{
|
|
39
|
+
expect(processRichTextField(null)).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
it('should return undefined unchanged', ()=>{
|
|
42
|
+
expect(processRichTextField(undefined)).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
it('should return a number unchanged', ()=>{
|
|
45
|
+
expect(processRichTextField(42)).toBe(42);
|
|
46
|
+
});
|
|
47
|
+
it('should return false unchanged', ()=>{
|
|
48
|
+
expect(processRichTextField(false)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
// ─── String input (the recovered-from-missed-hook path) ─────────────────────
|
|
52
|
+
describe('processRichTextField — JSON string input', ()=>{
|
|
53
|
+
it('should parse a valid JSON string into an object', ()=>{
|
|
54
|
+
const doc = lexicalDoc();
|
|
55
|
+
expect(processRichTextField(JSON.stringify(doc))).toEqual(doc);
|
|
56
|
+
});
|
|
57
|
+
it('should return an unparseable string unchanged', ()=>{
|
|
58
|
+
expect(processRichTextField('not json')).toBe('not json');
|
|
59
|
+
});
|
|
60
|
+
it('should return an empty string unchanged', ()=>{
|
|
61
|
+
expect(processRichTextField('')).toBe('');
|
|
62
|
+
});
|
|
63
|
+
it('should parse a string and still convert numeric string properties', ()=>{
|
|
64
|
+
const node = {
|
|
65
|
+
type: 'text',
|
|
66
|
+
detail: '0',
|
|
67
|
+
format: '3',
|
|
68
|
+
indent: '0',
|
|
69
|
+
version: '1',
|
|
70
|
+
text: 'hi'
|
|
71
|
+
};
|
|
72
|
+
const result = asRecord(processRichTextField(JSON.stringify(node)));
|
|
73
|
+
expect(result.detail).toBe(0);
|
|
74
|
+
expect(result.format).toBe(3);
|
|
75
|
+
expect(result.indent).toBe(0);
|
|
76
|
+
expect(result.version).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
// ─── Numeric property coercion ───────────────────────────────────────────────
|
|
80
|
+
describe('processRichTextField — numeric property coercion', ()=>{
|
|
81
|
+
it('should convert detail, format, indent, version from strings to numbers', ()=>{
|
|
82
|
+
const input = textNode({
|
|
83
|
+
detail: '0',
|
|
84
|
+
format: '0',
|
|
85
|
+
indent: '0',
|
|
86
|
+
version: '1'
|
|
87
|
+
});
|
|
88
|
+
const result = asRecord(processRichTextField(input));
|
|
89
|
+
expect(result.detail).toBe(0);
|
|
90
|
+
expect(result.format).toBe(0);
|
|
91
|
+
expect(result.indent).toBe(0);
|
|
92
|
+
expect(result.version).toBe(1);
|
|
93
|
+
});
|
|
94
|
+
it('should convert start and value from strings to numbers (list/listitem nodes)', ()=>{
|
|
95
|
+
const listItemNode = {
|
|
96
|
+
type: 'listitem',
|
|
97
|
+
start: '1',
|
|
98
|
+
value: '2',
|
|
99
|
+
version: '1'
|
|
100
|
+
};
|
|
101
|
+
const result = asRecord(processRichTextField(listItemNode));
|
|
102
|
+
expect(result.start).toBe(1);
|
|
103
|
+
expect(result.value).toBe(2);
|
|
104
|
+
});
|
|
105
|
+
it('should convert textFormat and textStyle from strings to numbers', ()=>{
|
|
106
|
+
const input = {
|
|
107
|
+
type: 'text',
|
|
108
|
+
textFormat: '4',
|
|
109
|
+
textStyle: '0',
|
|
110
|
+
version: '1',
|
|
111
|
+
text: 'x'
|
|
112
|
+
};
|
|
113
|
+
const result = asRecord(processRichTextField(input));
|
|
114
|
+
expect(result.textFormat).toBe(4);
|
|
115
|
+
expect(result.textStyle).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
it('should leave a non-numeric string in a numeric-property slot unchanged', ()=>{
|
|
118
|
+
// Lexical paragraph format can be "" (empty) or alignment strings
|
|
119
|
+
const input = paragraphNode();
|
|
120
|
+
const result = asRecord(processRichTextField(input));
|
|
121
|
+
expect(result.format).toBe('');
|
|
122
|
+
});
|
|
123
|
+
it('should leave already-numeric values unchanged', ()=>{
|
|
124
|
+
const input = textNode({
|
|
125
|
+
detail: 0,
|
|
126
|
+
format: 3,
|
|
127
|
+
version: 1
|
|
128
|
+
});
|
|
129
|
+
const result = asRecord(processRichTextField(input));
|
|
130
|
+
expect(result.detail).toBe(0);
|
|
131
|
+
expect(result.format).toBe(3);
|
|
132
|
+
expect(result.version).toBe(1);
|
|
133
|
+
});
|
|
134
|
+
it('should not convert boolean values on numeric-property keys', ()=>{
|
|
135
|
+
const input = {
|
|
136
|
+
type: 'custom',
|
|
137
|
+
format: true,
|
|
138
|
+
version: '1'
|
|
139
|
+
};
|
|
140
|
+
const result = asRecord(processRichTextField(input));
|
|
141
|
+
expect(result.format).toBe(true);
|
|
142
|
+
expect(result.version).toBe(1);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// ─── Recursive children processing ──────────────────────────────────────────
|
|
146
|
+
describe('processRichTextField — recursive children', ()=>{
|
|
147
|
+
it('should recursively convert numeric strings inside children arrays', ()=>{
|
|
148
|
+
const input = paragraphNode([
|
|
149
|
+
textNode({
|
|
150
|
+
detail: '0',
|
|
151
|
+
format: '0',
|
|
152
|
+
version: '1'
|
|
153
|
+
})
|
|
154
|
+
]);
|
|
155
|
+
const result = asRecord(processRichTextField(input));
|
|
156
|
+
const child = asRecord(asArray(result.children)[0]);
|
|
157
|
+
expect(child.detail).toBe(0);
|
|
158
|
+
expect(child.format).toBe(0);
|
|
159
|
+
expect(child.version).toBe(1);
|
|
160
|
+
});
|
|
161
|
+
it('should handle three levels of nesting (root → paragraph → text)', ()=>{
|
|
162
|
+
const input = lexicalDoc([
|
|
163
|
+
paragraphNode([
|
|
164
|
+
textNode({
|
|
165
|
+
version: '1',
|
|
166
|
+
format: '2'
|
|
167
|
+
})
|
|
168
|
+
])
|
|
169
|
+
]);
|
|
170
|
+
const result = asRecord(processRichTextField(input));
|
|
171
|
+
const paragraph = asRecord(asArray(result.children)[0]);
|
|
172
|
+
const text = asRecord(asArray(paragraph.children)[0]);
|
|
173
|
+
expect(text.version).toBe(1);
|
|
174
|
+
expect(text.format).toBe(2);
|
|
175
|
+
});
|
|
176
|
+
it('should handle multiple children at the same level', ()=>{
|
|
177
|
+
const input = lexicalDoc([
|
|
178
|
+
paragraphNode([
|
|
179
|
+
textNode({
|
|
180
|
+
text: 'Bold',
|
|
181
|
+
format: '1'
|
|
182
|
+
}),
|
|
183
|
+
textNode({
|
|
184
|
+
text: ' normal',
|
|
185
|
+
format: '0'
|
|
186
|
+
})
|
|
187
|
+
]),
|
|
188
|
+
paragraphNode([
|
|
189
|
+
textNode({
|
|
190
|
+
text: 'Second para',
|
|
191
|
+
version: '1'
|
|
192
|
+
})
|
|
193
|
+
])
|
|
194
|
+
]);
|
|
195
|
+
const result = asRecord(processRichTextField(input));
|
|
196
|
+
const firstPara = asRecord(asArray(result.children)[0]);
|
|
197
|
+
const secondPara = asRecord(asArray(result.children)[1]);
|
|
198
|
+
const bold = asRecord(asArray(firstPara.children)[0]);
|
|
199
|
+
const normal = asRecord(asArray(firstPara.children)[1]);
|
|
200
|
+
const secondText = asRecord(asArray(secondPara.children)[0]);
|
|
201
|
+
expect(bold.format).toBe(1);
|
|
202
|
+
expect(normal.format).toBe(0);
|
|
203
|
+
expect(secondText.version).toBe(1);
|
|
204
|
+
});
|
|
205
|
+
it('should skip null and non-object entries in children without throwing', ()=>{
|
|
206
|
+
const input = {
|
|
207
|
+
type: 'root',
|
|
208
|
+
version: 1,
|
|
209
|
+
children: [
|
|
210
|
+
null,
|
|
211
|
+
undefined,
|
|
212
|
+
'stray',
|
|
213
|
+
textNode()
|
|
214
|
+
]
|
|
215
|
+
};
|
|
216
|
+
const result = asRecord(processRichTextField(input));
|
|
217
|
+
const children = asArray(result.children);
|
|
218
|
+
expect(children[0]).toBeNull();
|
|
219
|
+
expect(children[1]).toBeUndefined();
|
|
220
|
+
expect(children[2]).toBe('stray');
|
|
221
|
+
expect(asRecord(children[3]).type).toBe('text');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
// ─── Nested non-children objects ─────────────────────────────────────────────
|
|
225
|
+
describe('processRichTextField — nested non-children objects', ()=>{
|
|
226
|
+
it('should recurse into plain nested objects (e.g. decorator node data)', ()=>{
|
|
227
|
+
const input = {
|
|
228
|
+
type: 'decorator',
|
|
229
|
+
version: '1',
|
|
230
|
+
data: {
|
|
231
|
+
detail: '2',
|
|
232
|
+
label: 'keep me'
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const result = asRecord(processRichTextField(input));
|
|
236
|
+
const data = asRecord(result.data);
|
|
237
|
+
expect(result.version).toBe(1);
|
|
238
|
+
expect(data.detail).toBe(2);
|
|
239
|
+
expect(data.label).toBe('keep me');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
// ─── Full Lexical document round-trips ───────────────────────────────────────
|
|
243
|
+
describe('processRichTextField — full document', ()=>{
|
|
244
|
+
it('should produce a stable result when called on an already-processed object', ()=>{
|
|
245
|
+
const doc = lexicalDoc();
|
|
246
|
+
const firstPass = processRichTextField(doc);
|
|
247
|
+
const secondPass = processRichTextField(firstPass);
|
|
248
|
+
expect(secondPass).toEqual(firstPass);
|
|
249
|
+
});
|
|
250
|
+
it('should fully round-trip a complex Lexical document serialised to JSON', ()=>{
|
|
251
|
+
const doc = lexicalDoc([
|
|
252
|
+
paragraphNode([
|
|
253
|
+
textNode({
|
|
254
|
+
text: 'Bold',
|
|
255
|
+
format: '1',
|
|
256
|
+
detail: '0',
|
|
257
|
+
indent: '0',
|
|
258
|
+
version: '1'
|
|
259
|
+
}),
|
|
260
|
+
textNode({
|
|
261
|
+
text: ' normal',
|
|
262
|
+
format: '0',
|
|
263
|
+
version: '1'
|
|
264
|
+
})
|
|
265
|
+
]),
|
|
266
|
+
{
|
|
267
|
+
type: 'list',
|
|
268
|
+
listType: 'number',
|
|
269
|
+
start: '1',
|
|
270
|
+
version: '1',
|
|
271
|
+
indent: '0',
|
|
272
|
+
direction: 'ltr',
|
|
273
|
+
children: [
|
|
274
|
+
{
|
|
275
|
+
type: 'listitem',
|
|
276
|
+
value: '1',
|
|
277
|
+
detail: '0',
|
|
278
|
+
format: '0',
|
|
279
|
+
version: '1',
|
|
280
|
+
text: 'Item A'
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
type: 'listitem',
|
|
284
|
+
value: '2',
|
|
285
|
+
detail: '0',
|
|
286
|
+
format: '0',
|
|
287
|
+
version: '1',
|
|
288
|
+
text: 'Item B'
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
]);
|
|
293
|
+
const result = asRecord(processRichTextField(JSON.stringify(doc)));
|
|
294
|
+
const para = asRecord(asArray(result.children)[0]);
|
|
295
|
+
const boldText = asRecord(asArray(para.children)[0]);
|
|
296
|
+
expect(boldText.format).toBe(1);
|
|
297
|
+
expect(boldText.detail).toBe(0);
|
|
298
|
+
const list = asRecord(asArray(result.children)[1]);
|
|
299
|
+
expect(list.start).toBe(1);
|
|
300
|
+
expect(list.indent).toBe(0);
|
|
301
|
+
const itemA = asRecord(asArray(list.children)[0]);
|
|
302
|
+
const itemB = asRecord(asArray(list.children)[1]);
|
|
303
|
+
expect(itemA.value).toBe(1);
|
|
304
|
+
expect(itemB.value).toBe(2);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
//# sourceMappingURL=processRichTextField.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/processRichTextField.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { processRichTextField } from './processRichTextField.js'\n\n// Minimal helpers to keep assertions readable\nconst asRecord = (v: unknown) => v as Record<string, unknown>\nconst asArray = (v: unknown) => v as unknown[]\n\n// ─── Fixtures ────────────────────────────────────────────────────────────────\n\nconst textNode = (overrides: Record<string, unknown> = {}) => ({\n detail: 0,\n format: 0,\n mode: 'normal',\n text: 'Hello',\n type: 'text',\n version: 1,\n ...overrides,\n})\n\nconst paragraphNode = (children: unknown[] = [textNode()]) => ({\n children,\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'paragraph',\n version: 1,\n})\n\nconst lexicalDoc = (children: unknown[] = [paragraphNode()]) => ({\n children,\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'root',\n version: 1,\n})\n\n// ─── Primitive pass-through ──────────────────────────────────────────────────\n\ndescribe('processRichTextField — primitive pass-through', () => {\n it('should return null unchanged', () => {\n expect(processRichTextField(null)).toBeNull()\n })\n\n it('should return undefined unchanged', () => {\n expect(processRichTextField(undefined)).toBeUndefined()\n })\n\n it('should return a number unchanged', () => {\n expect(processRichTextField(42)).toBe(42)\n })\n\n it('should return false unchanged', () => {\n expect(processRichTextField(false)).toBe(false)\n })\n})\n\n// ─── String input (the recovered-from-missed-hook path) ─────────────────────\n\ndescribe('processRichTextField — JSON string input', () => {\n it('should parse a valid JSON string into an object', () => {\n const doc = lexicalDoc()\n expect(processRichTextField(JSON.stringify(doc))).toEqual(doc)\n })\n\n it('should return an unparseable string unchanged', () => {\n expect(processRichTextField('not json')).toBe('not json')\n })\n\n it('should return an empty string unchanged', () => {\n expect(processRichTextField('')).toBe('')\n })\n\n it('should parse a string and still convert numeric string properties', () => {\n const node = { type: 'text', detail: '0', format: '3', indent: '0', version: '1', text: 'hi' }\n const result = asRecord(processRichTextField(JSON.stringify(node)))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(3)\n expect(result.indent).toBe(0)\n expect(result.version).toBe(1)\n })\n})\n\n// ─── Numeric property coercion ───────────────────────────────────────────────\n\ndescribe('processRichTextField — numeric property coercion', () => {\n it('should convert detail, format, indent, version from strings to numbers', () => {\n const input = textNode({ detail: '0', format: '0', indent: '0', version: '1' })\n const result = asRecord(processRichTextField(input))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(0)\n expect(result.indent).toBe(0)\n expect(result.version).toBe(1)\n })\n\n it('should convert start and value from strings to numbers (list/listitem nodes)', () => {\n const listItemNode = { type: 'listitem', start: '1', value: '2', version: '1' }\n const result = asRecord(processRichTextField(listItemNode))\n expect(result.start).toBe(1)\n expect(result.value).toBe(2)\n })\n\n it('should convert textFormat and textStyle from strings to numbers', () => {\n const input = { type: 'text', textFormat: '4', textStyle: '0', version: '1', text: 'x' }\n const result = asRecord(processRichTextField(input))\n expect(result.textFormat).toBe(4)\n expect(result.textStyle).toBe(0)\n })\n\n it('should leave a non-numeric string in a numeric-property slot unchanged', () => {\n // Lexical paragraph format can be \"\" (empty) or alignment strings\n const input = paragraphNode()\n const result = asRecord(processRichTextField(input))\n expect(result.format).toBe('')\n })\n\n it('should leave already-numeric values unchanged', () => {\n const input = textNode({ detail: 0, format: 3, version: 1 })\n const result = asRecord(processRichTextField(input))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(3)\n expect(result.version).toBe(1)\n })\n\n it('should not convert boolean values on numeric-property keys', () => {\n const input = { type: 'custom', format: true, version: '1' }\n const result = asRecord(processRichTextField(input))\n expect(result.format).toBe(true)\n expect(result.version).toBe(1)\n })\n})\n\n// ─── Recursive children processing ──────────────────────────────────────────\n\ndescribe('processRichTextField — recursive children', () => {\n it('should recursively convert numeric strings inside children arrays', () => {\n const input = paragraphNode([textNode({ detail: '0', format: '0', version: '1' })])\n const result = asRecord(processRichTextField(input))\n const child = asRecord(asArray(result.children)[0])\n expect(child.detail).toBe(0)\n expect(child.format).toBe(0)\n expect(child.version).toBe(1)\n })\n\n it('should handle three levels of nesting (root → paragraph → text)', () => {\n const input = lexicalDoc([paragraphNode([textNode({ version: '1', format: '2' })])])\n const result = asRecord(processRichTextField(input))\n const paragraph = asRecord(asArray(result.children)[0])\n const text = asRecord(asArray(paragraph.children)[0])\n expect(text.version).toBe(1)\n expect(text.format).toBe(2)\n })\n\n it('should handle multiple children at the same level', () => {\n const input = lexicalDoc([\n paragraphNode([\n textNode({ text: 'Bold', format: '1' }),\n textNode({ text: ' normal', format: '0' }),\n ]),\n paragraphNode([textNode({ text: 'Second para', version: '1' })]),\n ])\n const result = asRecord(processRichTextField(input))\n const firstPara = asRecord(asArray(result.children)[0])\n const secondPara = asRecord(asArray(result.children)[1])\n const bold = asRecord(asArray(firstPara.children)[0])\n const normal = asRecord(asArray(firstPara.children)[1])\n const secondText = asRecord(asArray(secondPara.children)[0])\n expect(bold.format).toBe(1)\n expect(normal.format).toBe(0)\n expect(secondText.version).toBe(1)\n })\n\n it('should skip null and non-object entries in children without throwing', () => {\n const input = { type: 'root', version: 1, children: [null, undefined, 'stray', textNode()] }\n const result = asRecord(processRichTextField(input))\n const children = asArray(result.children)\n expect(children[0]).toBeNull()\n expect(children[1]).toBeUndefined()\n expect(children[2]).toBe('stray')\n expect(asRecord(children[3]).type).toBe('text')\n })\n})\n\n// ─── Nested non-children objects ─────────────────────────────────────────────\n\ndescribe('processRichTextField — nested non-children objects', () => {\n it('should recurse into plain nested objects (e.g. decorator node data)', () => {\n const input = {\n type: 'decorator',\n version: '1',\n data: { detail: '2', label: 'keep me' },\n }\n const result = asRecord(processRichTextField(input))\n const data = asRecord(result.data)\n expect(result.version).toBe(1)\n expect(data.detail).toBe(2)\n expect(data.label).toBe('keep me')\n })\n})\n\n// ─── Full Lexical document round-trips ───────────────────────────────────────\n\ndescribe('processRichTextField — full document', () => {\n it('should produce a stable result when called on an already-processed object', () => {\n const doc = lexicalDoc()\n const firstPass = processRichTextField(doc)\n const secondPass = processRichTextField(firstPass)\n expect(secondPass).toEqual(firstPass)\n })\n\n it('should fully round-trip a complex Lexical document serialised to JSON', () => {\n const doc = lexicalDoc([\n paragraphNode([\n textNode({ text: 'Bold', format: '1', detail: '0', indent: '0', version: '1' }),\n textNode({ text: ' normal', format: '0', version: '1' }),\n ]),\n {\n type: 'list',\n listType: 'number',\n start: '1',\n version: '1',\n indent: '0',\n direction: 'ltr',\n children: [\n { type: 'listitem', value: '1', detail: '0', format: '0', version: '1', text: 'Item A' },\n { type: 'listitem', value: '2', detail: '0', format: '0', version: '1', text: 'Item B' },\n ],\n },\n ])\n\n const result = asRecord(processRichTextField(JSON.stringify(doc)))\n\n const para = asRecord(asArray(result.children)[0])\n const boldText = asRecord(asArray(para.children)[0])\n expect(boldText.format).toBe(1)\n expect(boldText.detail).toBe(0)\n\n const list = asRecord(asArray(result.children)[1])\n expect(list.start).toBe(1)\n expect(list.indent).toBe(0)\n\n const itemA = asRecord(asArray(list.children)[0])\n const itemB = asRecord(asArray(list.children)[1])\n expect(itemA.value).toBe(1)\n expect(itemB.value).toBe(2)\n })\n})\n"],"names":["describe","expect","it","processRichTextField","asRecord","v","asArray","textNode","overrides","detail","format","mode","text","type","version","paragraphNode","children","direction","indent","lexicalDoc","toBeNull","undefined","toBeUndefined","toBe","doc","JSON","stringify","toEqual","node","result","input","listItemNode","start","value","textFormat","textStyle","child","paragraph","firstPara","secondPara","bold","normal","secondText","data","label","firstPass","secondPass","listType","para","boldText","list","itemA","itemB"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"removeDisabledFields.d.ts","sourceRoot":"","sources":["../../src/utilities/removeDisabledFields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"removeDisabledFields.d.ts","sourceRoot":"","sources":["../../src/utilities/removeDisabledFields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,oBAAoB,GAC/B,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,WAAU,MAAM,EAAO,KACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CA8DxB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/removeDisabledFields.ts"],"sourcesContent":["/**\n * Recursively removes fields from a deeply nested object based on dot-notation paths.\n *\n * This utility supports removing:\n * - Nested fields in plain objects (e.g., \"group.value\")\n * - Fields inside arrays of objects (e.g., \"group.array.field1\")\n *\n * It safely traverses both object and array structures and avoids mutating the original input.\n *\n * @param obj - The original object to clean.\n * @param disabled - An array of dot-separated paths indicating which fields to remove.\n * @returns A deep clone of the original object with specified fields removed.\n */\n\nexport const removeDisabledFields = (\n obj: Record<string, unknown>,\n disabled: string[] = [],\n): Record<string, unknown> => {\n if (!disabled.length) {\n return obj\n }\n\n const clone = structuredClone(obj)\n\n // Process each disabled path independently\n for (const path of disabled) {\n const parts = path.split('.')\n\n /**\n * Recursively walks the object tree according to the dot path,\n * and deletes the field once the full path is reached.\n *\n * @param target - The current object or array being traversed\n * @param i - The index of the current path part\n */\n const removeRecursively = (target:
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/removeDisabledFields.ts"],"sourcesContent":["/**\n * Recursively removes fields from a deeply nested object based on dot-notation paths.\n *\n * This utility supports removing:\n * - Nested fields in plain objects (e.g., \"group.value\")\n * - Fields inside arrays of objects (e.g., \"group.array.field1\")\n *\n * It safely traverses both object and array structures and avoids mutating the original input.\n *\n * @param obj - The original object to clean.\n * @param disabled - An array of dot-separated paths indicating which fields to remove.\n * @returns A deep clone of the original object with specified fields removed.\n */\n\nexport const removeDisabledFields = (\n obj: Record<string, unknown>,\n disabled: string[] = [],\n): Record<string, unknown> => {\n if (!disabled.length) {\n return obj\n }\n\n const clone = structuredClone(obj)\n\n // Process each disabled path independently\n for (const path of disabled) {\n const parts = path.split('.')\n\n /**\n * Recursively walks the object tree according to the dot path,\n * and deletes the field once the full path is reached.\n *\n * @param target - The current object or array being traversed\n * @param i - The index of the current path part\n */\n const removeRecursively = (target: unknown, i = 0): void => {\n if (target == null) {\n return\n }\n\n const key = parts[i]\n\n // If at the final part of the path, perform the deletion\n if (i === parts.length - 1) {\n // If the current level is an array, delete the key from each item\n if (Array.isArray(target)) {\n for (const item of target) {\n if (item && typeof item === 'object' && key !== undefined) {\n delete (item as Record<string, unknown>)[key]\n }\n }\n } else if (typeof target === 'object' && key !== undefined) {\n delete (target as Record<string, unknown>)[key]\n }\n return\n }\n\n if (key === undefined) {\n return\n }\n\n // Traverse to the next level in the path\n const next = (target as Record<string, unknown>)[key]\n\n if (Array.isArray(next)) {\n // If the next value is an array, recurse into each item\n for (const item of next) {\n removeRecursively(item, i + 1)\n }\n } else {\n // Otherwise, continue down the object path\n removeRecursively(next, i + 1)\n }\n }\n\n removeRecursively(clone)\n }\n\n return clone\n}\n"],"names":["removeDisabledFields","obj","disabled","length","clone","structuredClone","path","parts","split","removeRecursively","target","i","key","Array","isArray","item","undefined","next"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAED,OAAO,MAAMA,uBAAuB,CAClCC,KACAC,WAAqB,EAAE;IAEvB,IAAI,CAACA,SAASC,MAAM,EAAE;QACpB,OAAOF;IACT;IAEA,MAAMG,QAAQC,gBAAgBJ;IAE9B,2CAA2C;IAC3C,KAAK,MAAMK,QAAQJ,SAAU;QAC3B,MAAMK,QAAQD,KAAKE,KAAK,CAAC;QAEzB;;;;;;KAMC,GACD,MAAMC,oBAAoB,CAACC,QAAiBC,IAAI,CAAC;YAC/C,IAAID,UAAU,MAAM;gBAClB;YACF;YAEA,MAAME,MAAML,KAAK,CAACI,EAAE;YAEpB,yDAAyD;YACzD,IAAIA,MAAMJ,MAAMJ,MAAM,GAAG,GAAG;gBAC1B,kEAAkE;gBAClE,IAAIU,MAAMC,OAAO,CAACJ,SAAS;oBACzB,KAAK,MAAMK,QAAQL,OAAQ;wBACzB,IAAIK,QAAQ,OAAOA,SAAS,YAAYH,QAAQI,WAAW;4BACzD,OAAO,AAACD,IAAgC,CAACH,IAAI;wBAC/C;oBACF;gBACF,OAAO,IAAI,OAAOF,WAAW,YAAYE,QAAQI,WAAW;oBAC1D,OAAO,AAACN,MAAkC,CAACE,IAAI;gBACjD;gBACA;YACF;YAEA,IAAIA,QAAQI,WAAW;gBACrB;YACF;YAEA,yCAAyC;YACzC,MAAMC,OAAO,AAACP,MAAkC,CAACE,IAAI;YAErD,IAAIC,MAAMC,OAAO,CAACG,OAAO;gBACvB,wDAAwD;gBACxD,KAAK,MAAMF,QAAQE,KAAM;oBACvBR,kBAAkBM,MAAMJ,IAAI;gBAC9B;YACF,OAAO;gBACL,2CAA2C;gBAC3CF,kBAAkBQ,MAAMN,IAAI;YAC9B;QACF;QAEAF,kBAAkBL;IACpB;IAEA,OAAOA;AACT,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setNestedValue.d.ts","sourceRoot":"","sources":["../../src/utilities/setNestedValue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"setNestedValue.d.ts","sourceRoot":"","sources":["../../src/utilities/setNestedValue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,cAAc,GACzB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,MAAM,EACZ,OAAO,OAAO,KACb,IA8CF,CAAA"}
|
|
@@ -27,26 +27,28 @@
|
|
|
27
27
|
if (!Array.isArray(current)) {
|
|
28
28
|
current = [];
|
|
29
29
|
}
|
|
30
|
+
const currentArray = current;
|
|
30
31
|
// Ensure the array slot is initialized
|
|
31
|
-
if (!
|
|
32
|
-
|
|
32
|
+
if (!currentArray[index]) {
|
|
33
|
+
currentArray[index] = {};
|
|
33
34
|
}
|
|
34
35
|
if (isLast) {
|
|
35
|
-
|
|
36
|
+
currentArray[index] = value;
|
|
36
37
|
} else {
|
|
37
|
-
current =
|
|
38
|
+
current = currentArray[index];
|
|
38
39
|
}
|
|
39
40
|
} else {
|
|
41
|
+
const currentObj = current;
|
|
40
42
|
// Ensure the object key exists
|
|
41
43
|
if (isLast) {
|
|
42
44
|
if (typeof part === 'string') {
|
|
43
|
-
|
|
45
|
+
currentObj[part] = value;
|
|
44
46
|
}
|
|
45
47
|
} else {
|
|
46
|
-
if (typeof
|
|
47
|
-
|
|
48
|
+
if (typeof currentObj[part] !== 'object' || currentObj[part] === null) {
|
|
49
|
+
currentObj[part] = {};
|
|
48
50
|
}
|
|
49
|
-
current =
|
|
51
|
+
current = currentObj[part];
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/setNestedValue.ts"],"sourcesContent":["/**\n * Sets a value deeply into a nested object or array, based on a dot-notation path.\n *\n * This function:\n * - Supports array indexing (e.g., \"array.0.field1\")\n * - Creates intermediate arrays/objects as needed\n * - Mutates the target object directly\n *\n * @example\n * const obj = {}\n * setNestedValue(obj, 'group.array.0.field1', 'hello')\n * // Result: { group: { array: [ { field1: 'hello' } ] } }\n *\n * @param obj - The target object to mutate.\n * @param path - A dot-separated string path indicating where to assign the value.\n * @param value - The value to set at the specified path.\n */\n\nexport const setNestedValue = (\n obj: Record<string, unknown>,\n path: string,\n value: unknown,\n): void => {\n const parts = path.split('.')\n let current:
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/setNestedValue.ts"],"sourcesContent":["/**\n * Sets a value deeply into a nested object or array, based on a dot-notation path.\n *\n * This function:\n * - Supports array indexing (e.g., \"array.0.field1\")\n * - Creates intermediate arrays/objects as needed\n * - Mutates the target object directly\n *\n * @example\n * const obj = {}\n * setNestedValue(obj, 'group.array.0.field1', 'hello')\n * // Result: { group: { array: [ { field1: 'hello' } ] } }\n *\n * @param obj - The target object to mutate.\n * @param path - A dot-separated string path indicating where to assign the value.\n * @param value - The value to set at the specified path.\n */\n\nexport const setNestedValue = (\n obj: Record<string, unknown>,\n path: string,\n value: unknown,\n): void => {\n const parts = path.split('.')\n let current: unknown = obj\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const isLast = i === parts.length - 1\n const isIndex = !Number.isNaN(Number(part))\n\n if (isIndex) {\n const index = Number(part)\n\n // Ensure the current target is an array\n if (!Array.isArray(current)) {\n current = []\n }\n\n const currentArray = current as unknown[]\n\n // Ensure the array slot is initialized\n if (!currentArray[index]) {\n currentArray[index] = {}\n }\n\n if (isLast) {\n currentArray[index] = value\n } else {\n current = currentArray[index]\n }\n } else {\n const currentObj = current as Record<string, unknown>\n\n // Ensure the object key exists\n if (isLast) {\n if (typeof part === 'string') {\n currentObj[part] = value\n }\n } else {\n if (typeof currentObj[part as string] !== 'object' || currentObj[part as string] === null) {\n currentObj[part as string] = {}\n }\n\n current = currentObj[part as string]\n }\n }\n }\n}\n"],"names":["setNestedValue","obj","path","value","parts","split","current","i","length","part","isLast","isIndex","Number","isNaN","index","Array","isArray","currentArray","currentObj"],"mappings":"AAAA;;;;;;;;;;;;;;;;CAgBC,GAED,OAAO,MAAMA,iBAAiB,CAC5BC,KACAC,MACAC;IAEA,MAAMC,QAAQF,KAAKG,KAAK,CAAC;IACzB,IAAIC,UAAmBL;IAEvB,IAAK,IAAIM,IAAI,GAAGA,IAAIH,MAAMI,MAAM,EAAED,IAAK;QACrC,MAAME,OAAOL,KAAK,CAACG,EAAE;QACrB,MAAMG,SAASH,MAAMH,MAAMI,MAAM,GAAG;QACpC,MAAMG,UAAU,CAACC,OAAOC,KAAK,CAACD,OAAOH;QAErC,IAAIE,SAAS;YACX,MAAMG,QAAQF,OAAOH;YAErB,wCAAwC;YACxC,IAAI,CAACM,MAAMC,OAAO,CAACV,UAAU;gBAC3BA,UAAU,EAAE;YACd;YAEA,MAAMW,eAAeX;YAErB,uCAAuC;YACvC,IAAI,CAACW,YAAY,CAACH,MAAM,EAAE;gBACxBG,YAAY,CAACH,MAAM,GAAG,CAAC;YACzB;YAEA,IAAIJ,QAAQ;gBACVO,YAAY,CAACH,MAAM,GAAGX;YACxB,OAAO;gBACLG,UAAUW,YAAY,CAACH,MAAM;YAC/B;QACF,OAAO;YACL,MAAMI,aAAaZ;YAEnB,+BAA+B;YAC/B,IAAII,QAAQ;gBACV,IAAI,OAAOD,SAAS,UAAU;oBAC5BS,UAAU,CAACT,KAAK,GAAGN;gBACrB;YACF,OAAO;gBACL,IAAI,OAAOe,UAAU,CAACT,KAAe,KAAK,YAAYS,UAAU,CAACT,KAAe,KAAK,MAAM;oBACzFS,UAAU,CAACT,KAAe,GAAG,CAAC;gBAChC;gBAEAH,UAAUY,UAAU,CAACT,KAAe;YACtC;QACF;IACF;AACF,EAAC"}
|