@uniweb/semantic-parser 1.1.4 → 1.1.6
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/AGENTS.md +8 -11
- package/README.md +3 -160
- package/package.json +2 -5
- package/src/index.js +1 -2
- package/src/processors/groups.js +16 -15
- package/docs/api.md +0 -350
- package/docs/entity-consolidation.md +0 -470
- package/docs/file-structure.md +0 -50
- package/docs/guide.md +0 -206
- package/docs/mapping-patterns.md +0 -928
- package/docs/text-component-reference.md +0 -515
- package/reference/README.md +0 -195
- package/reference/Text.js +0 -188
- package/src/mappers/accessor.js +0 -312
- package/src/mappers/extractors.js +0 -416
- package/src/mappers/helpers.js +0 -234
- package/src/mappers/index.js +0 -28
- package/src/mappers/types.js +0 -495
- package/src/processors/groups_backup.js +0 -379
- package/src/processors/groups_doc.md +0 -179
- package/src/processors/sequence_backup.js +0 -402
- package/src/processors_old/byType.js +0 -129
- package/src/processors_old/groups.js +0 -240
- package/src/processors_old/sequence.js +0 -140
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-built extractors for common component patterns
|
|
3
|
-
*
|
|
4
|
-
* All extractors work with the flat content structure:
|
|
5
|
-
* - Root level: title, pretitle, subtitle, paragraphs, links, imgs, items, etc.
|
|
6
|
-
* - Items array: each item has flat structure (title, paragraphs, etc.)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { first, joinParagraphs } from "./helpers.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Extract hero component data
|
|
13
|
-
* Common pattern: Large header with title, subtitle, image, and CTA
|
|
14
|
-
*
|
|
15
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
16
|
-
* @returns {Object} Hero component data
|
|
17
|
-
*/
|
|
18
|
-
function hero(parsed) {
|
|
19
|
-
const links = parsed?.links || [];
|
|
20
|
-
const buttonLink = links.find(l => l.role?.startsWith('button'));
|
|
21
|
-
const plainLink = links.find(l => !l.role?.startsWith('button'));
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
title: parsed?.title || null,
|
|
25
|
-
subtitle: parsed?.subtitle || null,
|
|
26
|
-
kicker: parsed?.pretitle || null,
|
|
27
|
-
description: parsed?.paragraphs || [],
|
|
28
|
-
image: first(parsed?.imgs)?.url || null,
|
|
29
|
-
imageAlt: first(parsed?.imgs)?.alt || null,
|
|
30
|
-
banner: null, // Banner detection would need to be added separately
|
|
31
|
-
cta: buttonLink || plainLink || null,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract card component data
|
|
37
|
-
* Common pattern: Title, description, image, and link
|
|
38
|
-
*
|
|
39
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
40
|
-
* @param {Object} options - Extraction options
|
|
41
|
-
* @param {boolean} options.useItems - Extract from items instead of main
|
|
42
|
-
* @param {number} options.itemIndex - Specific item index to extract from
|
|
43
|
-
* @returns {Object|Array} Card data or array of cards if useItems=true
|
|
44
|
-
*/
|
|
45
|
-
function card(parsed, options = {}) {
|
|
46
|
-
const { useItems = false, itemIndex } = options;
|
|
47
|
-
|
|
48
|
-
const extractCard = (content) => {
|
|
49
|
-
if (!content) return null;
|
|
50
|
-
|
|
51
|
-
const links = content.links || [];
|
|
52
|
-
const buttonLink = links.find(l => l.role?.startsWith('button'));
|
|
53
|
-
const plainLink = links.find(l => !l.role?.startsWith('button'));
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
title: content.title || null,
|
|
57
|
-
subtitle: content.subtitle || null,
|
|
58
|
-
description: content.paragraphs || [],
|
|
59
|
-
image: first(content.imgs)?.url || null,
|
|
60
|
-
imageAlt: first(content.imgs)?.alt || null,
|
|
61
|
-
icon: first(content.icons) || null,
|
|
62
|
-
link: plainLink || null,
|
|
63
|
-
cta: buttonLink || plainLink || null,
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
if (useItems) {
|
|
68
|
-
const items = parsed?.items || [];
|
|
69
|
-
if (itemIndex !== undefined) {
|
|
70
|
-
return extractCard(items[itemIndex]);
|
|
71
|
-
}
|
|
72
|
-
return items.map(extractCard).filter(Boolean);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return extractCard(parsed);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Extract article/blog content
|
|
80
|
-
* Common pattern: Title, author info, content blocks, images
|
|
81
|
-
*
|
|
82
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
83
|
-
* @returns {Object} Article data
|
|
84
|
-
*/
|
|
85
|
-
function article(parsed) {
|
|
86
|
-
return {
|
|
87
|
-
title: parsed?.title || null,
|
|
88
|
-
subtitle: parsed?.subtitle || null,
|
|
89
|
-
kicker: parsed?.pretitle || null,
|
|
90
|
-
author: null, // Would need metadata support
|
|
91
|
-
date: null, // Would need metadata support
|
|
92
|
-
banner: null, // Banner detection would need to be added separately
|
|
93
|
-
content: parsed?.paragraphs || [],
|
|
94
|
-
images: parsed?.imgs || [],
|
|
95
|
-
videos: parsed?.videos || [],
|
|
96
|
-
links: parsed?.links || [],
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Extract statistics/metrics data
|
|
102
|
-
* Common pattern: Numeric value with label
|
|
103
|
-
*
|
|
104
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
105
|
-
* @returns {Array} Array of stat objects
|
|
106
|
-
*/
|
|
107
|
-
function stats(parsed) {
|
|
108
|
-
const items = parsed?.items || [];
|
|
109
|
-
|
|
110
|
-
return items
|
|
111
|
-
.map((item) => ({
|
|
112
|
-
value: item.title || null,
|
|
113
|
-
label: item.subtitle || first(item.paragraphs) || null,
|
|
114
|
-
description: item.paragraphs || [],
|
|
115
|
-
}))
|
|
116
|
-
.filter((stat) => stat.value);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extract navigation menu structure
|
|
121
|
-
* Common pattern: Hierarchical menu with labels, links, and optional children
|
|
122
|
-
*
|
|
123
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
124
|
-
* @returns {Array} Navigation items
|
|
125
|
-
*/
|
|
126
|
-
function navigation(parsed) {
|
|
127
|
-
const items = parsed?.items || [];
|
|
128
|
-
|
|
129
|
-
return items
|
|
130
|
-
.map((item) => {
|
|
131
|
-
const navItem = {
|
|
132
|
-
label: item.title || null,
|
|
133
|
-
href: first(item.links)?.href || null,
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// Extract children from nested lists
|
|
137
|
-
const firstList = first(item.lists);
|
|
138
|
-
if (firstList && firstList.length > 0) {
|
|
139
|
-
navItem.children = firstList
|
|
140
|
-
.map((listItem) => ({
|
|
141
|
-
label: joinParagraphs(listItem.paragraphs) || null,
|
|
142
|
-
href: first(listItem.links)?.href || null,
|
|
143
|
-
icon: first(listItem.icons) || null,
|
|
144
|
-
}))
|
|
145
|
-
.filter((child) => child.label);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return navItem;
|
|
149
|
-
})
|
|
150
|
-
.filter((item) => item.label);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Extract feature list
|
|
155
|
-
* Common pattern: Icon/image, title, description
|
|
156
|
-
*
|
|
157
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
158
|
-
* @returns {Array} Feature items
|
|
159
|
-
*/
|
|
160
|
-
function features(parsed) {
|
|
161
|
-
const items = parsed?.items || [];
|
|
162
|
-
|
|
163
|
-
return items
|
|
164
|
-
.map((item) => ({
|
|
165
|
-
title: item.title || null,
|
|
166
|
-
subtitle: item.subtitle || null,
|
|
167
|
-
description: item.paragraphs || [],
|
|
168
|
-
icon: first(item.icons) || null,
|
|
169
|
-
image: first(item.imgs)?.url || null,
|
|
170
|
-
link: first(item.links) || null,
|
|
171
|
-
}))
|
|
172
|
-
.filter((feature) => feature.title);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Extract testimonial data
|
|
177
|
-
* Common pattern: Quote, author name, role, image
|
|
178
|
-
*
|
|
179
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
180
|
-
* @param {Object} options - Extraction options
|
|
181
|
-
* @param {boolean} options.useItems - Extract from items instead of main
|
|
182
|
-
* @returns {Object|Array} Testimonial data
|
|
183
|
-
*/
|
|
184
|
-
function testimonial(parsed, options = {}) {
|
|
185
|
-
const { useItems = false } = options;
|
|
186
|
-
|
|
187
|
-
const extractTestimonial = (content) => {
|
|
188
|
-
if (!content) return null;
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
quote: content.paragraphs || [],
|
|
192
|
-
author: content.title || null,
|
|
193
|
-
role: content.subtitle || null,
|
|
194
|
-
company: content.pretitle || null,
|
|
195
|
-
image: first(content.imgs)?.url || null,
|
|
196
|
-
imageAlt: first(content.imgs)?.alt || null,
|
|
197
|
-
};
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
if (useItems) {
|
|
201
|
-
const items = parsed?.items || [];
|
|
202
|
-
return items.map(extractTestimonial).filter(Boolean);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return extractTestimonial(parsed);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Extract FAQ (question and answer pairs)
|
|
210
|
-
* Common pattern: Question as title, answer as content
|
|
211
|
-
*
|
|
212
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
213
|
-
* @returns {Array} FAQ items
|
|
214
|
-
*/
|
|
215
|
-
function faq(parsed) {
|
|
216
|
-
const items = parsed?.items || [];
|
|
217
|
-
|
|
218
|
-
return items
|
|
219
|
-
.map((item) => ({
|
|
220
|
-
question: item.title || null,
|
|
221
|
-
answer: item.paragraphs || [],
|
|
222
|
-
links: item.links || [],
|
|
223
|
-
}))
|
|
224
|
-
.filter((item) => item.question);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Extract pricing tier data
|
|
229
|
-
* Common pattern: Plan name, price, features list, CTA
|
|
230
|
-
*
|
|
231
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
232
|
-
* @returns {Array} Pricing tiers
|
|
233
|
-
*/
|
|
234
|
-
function pricing(parsed) {
|
|
235
|
-
const items = parsed?.items || [];
|
|
236
|
-
|
|
237
|
-
return items
|
|
238
|
-
.map((item) => {
|
|
239
|
-
const firstList = first(item.lists);
|
|
240
|
-
const links = item.links || [];
|
|
241
|
-
const buttonLink = links.find(l => l.role?.startsWith('button'));
|
|
242
|
-
|
|
243
|
-
return {
|
|
244
|
-
name: item.title || null,
|
|
245
|
-
price: item.subtitle || null,
|
|
246
|
-
description: first(item.paragraphs) || null,
|
|
247
|
-
features: firstList
|
|
248
|
-
? firstList
|
|
249
|
-
.map((listItem) =>
|
|
250
|
-
joinParagraphs(listItem.paragraphs)
|
|
251
|
-
)
|
|
252
|
-
.filter(Boolean)
|
|
253
|
-
: [],
|
|
254
|
-
cta: buttonLink || first(links) || null,
|
|
255
|
-
highlighted:
|
|
256
|
-
item.pretitle?.toLowerCase().includes("popular") || false,
|
|
257
|
-
};
|
|
258
|
-
})
|
|
259
|
-
.filter((tier) => tier.name);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Extract team member data
|
|
264
|
-
* Common pattern: Name, role, bio, image, social links
|
|
265
|
-
*
|
|
266
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
267
|
-
* @returns {Array} Team members
|
|
268
|
-
*/
|
|
269
|
-
function team(parsed) {
|
|
270
|
-
const items = parsed?.items || [];
|
|
271
|
-
|
|
272
|
-
return items
|
|
273
|
-
.map((item) => ({
|
|
274
|
-
name: item.title || null,
|
|
275
|
-
role: item.subtitle || null,
|
|
276
|
-
department: item.pretitle || null,
|
|
277
|
-
bio: item.paragraphs || [],
|
|
278
|
-
image: first(item.imgs)?.url || null,
|
|
279
|
-
imageAlt: first(item.imgs)?.alt || null,
|
|
280
|
-
links: item.links || [],
|
|
281
|
-
}))
|
|
282
|
-
.filter((member) => member.name);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Extract gallery images
|
|
287
|
-
* Common pattern: Collection of images with captions
|
|
288
|
-
*
|
|
289
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
290
|
-
* @param {Object} options - Extraction options
|
|
291
|
-
* @param {string} options.source - Source to extract from: 'main', 'items', 'all'
|
|
292
|
-
* @returns {Array} Gallery images
|
|
293
|
-
*/
|
|
294
|
-
function gallery(parsed, options = {}) {
|
|
295
|
-
const { source = "all" } = options;
|
|
296
|
-
const images = [];
|
|
297
|
-
|
|
298
|
-
if (source === "main" || source === "all") {
|
|
299
|
-
const mainImages = parsed?.imgs || [];
|
|
300
|
-
images.push(...mainImages);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (source === "items" || source === "all") {
|
|
304
|
-
const items = parsed?.items || [];
|
|
305
|
-
items.forEach((item) => {
|
|
306
|
-
const itemImages = item.imgs || [];
|
|
307
|
-
images.push(...itemImages);
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return images.map((img) => ({
|
|
312
|
-
url: img.url,
|
|
313
|
-
alt: img.alt || null,
|
|
314
|
-
caption: img.caption || null,
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Extract content in legacy Article class format
|
|
320
|
-
* Used for backward compatibility with existing components
|
|
321
|
-
*
|
|
322
|
-
* This extractor transforms the new flat parser output into the nested format
|
|
323
|
-
* used by the legacy Article class, enabling drop-in replacement without
|
|
324
|
-
* breaking existing components.
|
|
325
|
-
*
|
|
326
|
-
* NOTE: Reconstructs deprecated fields (buttons, cards, documents, forms, alignment)
|
|
327
|
-
* from the new consolidated structure for backwards compatibility.
|
|
328
|
-
*
|
|
329
|
-
* @param {Object} parsed - Parsed content from parseContent() (flat structure)
|
|
330
|
-
* @returns {Object} Legacy format { main, items } with nested header/body structure
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
* const { parseContent, mappers } = require('@uniweb/semantic-parser');
|
|
334
|
-
* const parsed = parseContent(doc);
|
|
335
|
-
* const legacy = mappers.extractors.legacy(parsed);
|
|
336
|
-
* // Returns: { main: { header: {...}, body: {...} }, items: [...] }
|
|
337
|
-
*/
|
|
338
|
-
function legacy(parsed) {
|
|
339
|
-
const transformToNested = (content) => {
|
|
340
|
-
if (!content) return null;
|
|
341
|
-
|
|
342
|
-
let imgs = content.imgs || [];
|
|
343
|
-
let banner = imgs.filter((item) => {
|
|
344
|
-
return (item.role = "banner");
|
|
345
|
-
})?.[0];
|
|
346
|
-
|
|
347
|
-
if (!banner) banner = imgs[0];
|
|
348
|
-
|
|
349
|
-
// Reconstruct deprecated fields from new structure
|
|
350
|
-
const links = content.links || [];
|
|
351
|
-
const buttons = links
|
|
352
|
-
.filter(l => l.role?.startsWith('button'))
|
|
353
|
-
.map(l => ({ attrs: l, content: l.label }));
|
|
354
|
-
const documents = links
|
|
355
|
-
.filter(l => l.role === 'document')
|
|
356
|
-
.map(l => ({ title: l.label, href: l.href, coverImg: l.preview }));
|
|
357
|
-
const plainLinks = links.filter(l => !l.role?.startsWith('button') && l.role !== 'document');
|
|
358
|
-
|
|
359
|
-
const cards = content.data?.cards || [];
|
|
360
|
-
const form = content.data?.form || null;
|
|
361
|
-
const forms = form ? [form] : [];
|
|
362
|
-
|
|
363
|
-
return {
|
|
364
|
-
header: {
|
|
365
|
-
title: content.title || "",
|
|
366
|
-
subtitle: content.subtitle || "",
|
|
367
|
-
subtitle2: content.subtitle2 || "",
|
|
368
|
-
pretitle: content.pretitle || "",
|
|
369
|
-
// Auto-fill description (legacy behavior)
|
|
370
|
-
description:
|
|
371
|
-
content.subtitle2 ||
|
|
372
|
-
first(content.paragraphs) ||
|
|
373
|
-
"",
|
|
374
|
-
alignment: "", // Deprecated: always empty
|
|
375
|
-
},
|
|
376
|
-
banner,
|
|
377
|
-
body: {
|
|
378
|
-
paragraphs: content.paragraphs || [],
|
|
379
|
-
headings: content.headings || [],
|
|
380
|
-
imgs,
|
|
381
|
-
videos: content.videos || [],
|
|
382
|
-
lists: content.lists || [],
|
|
383
|
-
links: plainLinks,
|
|
384
|
-
icons: content.icons || [],
|
|
385
|
-
buttons,
|
|
386
|
-
cards,
|
|
387
|
-
documents,
|
|
388
|
-
forms,
|
|
389
|
-
form,
|
|
390
|
-
quotes: content.quotes || [],
|
|
391
|
-
properties: content.data || {},
|
|
392
|
-
propertyBlocks: [],
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
main: transformToNested(parsed),
|
|
399
|
-
items: (parsed?.items || []).map(transformToNested),
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export {
|
|
404
|
-
hero,
|
|
405
|
-
card,
|
|
406
|
-
article,
|
|
407
|
-
stats,
|
|
408
|
-
navigation,
|
|
409
|
-
features,
|
|
410
|
-
testimonial,
|
|
411
|
-
faq,
|
|
412
|
-
pricing,
|
|
413
|
-
team,
|
|
414
|
-
gallery,
|
|
415
|
-
legacy,
|
|
416
|
-
};
|
package/src/mappers/helpers.js
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper utilities for content transformation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createExcerpt, stripMarkup } from './types.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get the first item from an array or return default value
|
|
9
|
-
* @param {Array} arr - Array to get first item from
|
|
10
|
-
* @param {*} defaultValue - Value to return if array is empty or undefined
|
|
11
|
-
* @returns {*} First item or default value
|
|
12
|
-
*/
|
|
13
|
-
function first(arr, defaultValue = null) {
|
|
14
|
-
return arr?.[0] ?? defaultValue;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Get the last item from an array or return default value
|
|
19
|
-
* @param {Array} arr - Array to get last item from
|
|
20
|
-
* @param {*} defaultValue - Value to return if array is empty or undefined
|
|
21
|
-
* @returns {*} Last item or default value
|
|
22
|
-
*/
|
|
23
|
-
function last(arr, defaultValue = null) {
|
|
24
|
-
if (!arr || arr.length === 0) return defaultValue;
|
|
25
|
-
return arr[arr.length - 1];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Transform an array using a mapping function
|
|
30
|
-
* @param {Array} arr - Array to transform
|
|
31
|
-
* @param {Function} transform - Transformation function
|
|
32
|
-
* @returns {Array} Transformed array
|
|
33
|
-
*/
|
|
34
|
-
function transformArray(arr, transform) {
|
|
35
|
-
if (!Array.isArray(arr)) return [];
|
|
36
|
-
return arr.map(transform);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Filter array and optionally transform
|
|
41
|
-
* @param {Array} arr - Array to filter
|
|
42
|
-
* @param {Function} predicate - Filter function
|
|
43
|
-
* @param {Function} transform - Optional transformation function
|
|
44
|
-
* @returns {Array} Filtered (and transformed) array
|
|
45
|
-
*/
|
|
46
|
-
function filterArray(arr, predicate, transform = null) {
|
|
47
|
-
if (!Array.isArray(arr)) return [];
|
|
48
|
-
const filtered = arr.filter(predicate);
|
|
49
|
-
return transform ? filtered.map(transform) : filtered;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Join array of strings with separator
|
|
54
|
-
* @param {Array} arr - Array to join
|
|
55
|
-
* @param {string} separator - Separator string
|
|
56
|
-
* @returns {string} Joined string
|
|
57
|
-
*/
|
|
58
|
-
function joinText(arr, separator = ' ') {
|
|
59
|
-
if (!Array.isArray(arr)) return '';
|
|
60
|
-
return arr.filter(Boolean).join(separator);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Flatten nested array structure
|
|
65
|
-
* @param {Array} arr - Array to flatten
|
|
66
|
-
* @param {number} depth - Depth to flatten (default: 1)
|
|
67
|
-
* @returns {Array} Flattened array
|
|
68
|
-
*/
|
|
69
|
-
function flatten(arr, depth = 1) {
|
|
70
|
-
if (!Array.isArray(arr)) return [];
|
|
71
|
-
return arr.flat(depth);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Check if value exists (not null, undefined, or empty string)
|
|
76
|
-
* @param {*} value - Value to check
|
|
77
|
-
* @returns {boolean} True if value exists
|
|
78
|
-
*/
|
|
79
|
-
function exists(value) {
|
|
80
|
-
return value !== null && value !== undefined && value !== '';
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get value with fallback
|
|
85
|
-
* @param {*} value - Primary value
|
|
86
|
-
* @param {*} fallback - Fallback value
|
|
87
|
-
* @returns {*} Value or fallback
|
|
88
|
-
*/
|
|
89
|
-
function withDefault(value, fallback) {
|
|
90
|
-
return exists(value) ? value : fallback;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Validate that required fields are present
|
|
95
|
-
* @param {Object} data - Data object to validate
|
|
96
|
-
* @param {Array<string>} required - Array of required field names
|
|
97
|
-
* @returns {Object} Validation result { valid: boolean, missing: Array<string> }
|
|
98
|
-
*/
|
|
99
|
-
function validateRequired(data, required) {
|
|
100
|
-
const missing = required.filter(field => !exists(data[field]));
|
|
101
|
-
return {
|
|
102
|
-
valid: missing.length === 0,
|
|
103
|
-
missing
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Pick specific properties from object
|
|
109
|
-
* @param {Object} obj - Source object
|
|
110
|
-
* @param {Array<string>} keys - Keys to pick
|
|
111
|
-
* @returns {Object} Object with only picked keys
|
|
112
|
-
*/
|
|
113
|
-
function pick(obj, keys) {
|
|
114
|
-
if (!obj) return {};
|
|
115
|
-
return keys.reduce((result, key) => {
|
|
116
|
-
if (key in obj) {
|
|
117
|
-
result[key] = obj[key];
|
|
118
|
-
}
|
|
119
|
-
return result;
|
|
120
|
-
}, {});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Omit specific properties from object
|
|
125
|
-
* @param {Object} obj - Source object
|
|
126
|
-
* @param {Array<string>} keys - Keys to omit
|
|
127
|
-
* @returns {Object} Object without omitted keys
|
|
128
|
-
*/
|
|
129
|
-
function omit(obj, keys) {
|
|
130
|
-
if (!obj) return {};
|
|
131
|
-
const result = { ...obj };
|
|
132
|
-
keys.forEach(key => delete result[key]);
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Safely access nested property
|
|
138
|
-
* @param {Object} obj - Object to access
|
|
139
|
-
* @param {string} path - Dot-notation path (e.g., 'a.b.c')
|
|
140
|
-
* @param {*} defaultValue - Default value if path doesn't exist
|
|
141
|
-
* @returns {*} Value at path or default
|
|
142
|
-
*/
|
|
143
|
-
function get(obj, path, defaultValue = undefined) {
|
|
144
|
-
if (!obj || !path) return defaultValue;
|
|
145
|
-
|
|
146
|
-
const keys = path.split('.');
|
|
147
|
-
let result = obj;
|
|
148
|
-
|
|
149
|
-
for (const key of keys) {
|
|
150
|
-
if (result === null || result === undefined) {
|
|
151
|
-
return defaultValue;
|
|
152
|
-
}
|
|
153
|
-
result = result[key];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return result !== undefined ? result : defaultValue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Compact an array (remove null, undefined, empty string)
|
|
161
|
-
* @param {Array} arr - Array to compact
|
|
162
|
-
* @returns {Array} Compacted array
|
|
163
|
-
*/
|
|
164
|
-
function compact(arr) {
|
|
165
|
-
if (!Array.isArray(arr)) return [];
|
|
166
|
-
return arr.filter(exists);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Create a safe extractor that won't throw on missing data
|
|
171
|
-
* @param {Function} extractFn - Extraction function that might throw
|
|
172
|
-
* @param {*} defaultValue - Default value if extraction fails
|
|
173
|
-
* @returns {Function} Safe extraction function
|
|
174
|
-
*/
|
|
175
|
-
function safe(extractFn, defaultValue = null) {
|
|
176
|
-
return (...args) => {
|
|
177
|
-
try {
|
|
178
|
-
return extractFn(...args) ?? defaultValue;
|
|
179
|
-
} catch (error) {
|
|
180
|
-
return defaultValue;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Join paragraphs into a single string
|
|
187
|
-
* @param {Array|string} paragraphs - Array of paragraphs or single paragraph
|
|
188
|
-
* @param {string} separator - Separator to use between paragraphs
|
|
189
|
-
* @returns {string} Joined text
|
|
190
|
-
*/
|
|
191
|
-
function joinParagraphs(paragraphs, separator = ' ') {
|
|
192
|
-
if (!Array.isArray(paragraphs)) return paragraphs || '';
|
|
193
|
-
return paragraphs.filter(Boolean).join(separator);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Create an excerpt from paragraphs
|
|
198
|
-
* @param {Array|string} paragraphs - Array of paragraphs or single paragraph
|
|
199
|
-
* @param {Object} options - Excerpt options (maxLength, boundary, ellipsis)
|
|
200
|
-
* @returns {string} Generated excerpt
|
|
201
|
-
*/
|
|
202
|
-
function excerptFromParagraphs(paragraphs, options = {}) {
|
|
203
|
-
return createExcerpt(paragraphs, options);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Count words in text or paragraphs
|
|
208
|
-
* @param {Array|string} text - Text or array of paragraphs
|
|
209
|
-
* @returns {number} Word count
|
|
210
|
-
*/
|
|
211
|
-
function countWords(text) {
|
|
212
|
-
const plain = Array.isArray(text) ? text.join(' ') : text;
|
|
213
|
-
return stripMarkup(plain).split(/\s+/).filter(Boolean).length;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export {
|
|
217
|
-
first,
|
|
218
|
-
last,
|
|
219
|
-
transformArray,
|
|
220
|
-
filterArray,
|
|
221
|
-
joinText,
|
|
222
|
-
flatten,
|
|
223
|
-
exists,
|
|
224
|
-
withDefault,
|
|
225
|
-
validateRequired,
|
|
226
|
-
pick,
|
|
227
|
-
omit,
|
|
228
|
-
get,
|
|
229
|
-
compact,
|
|
230
|
-
safe,
|
|
231
|
-
joinParagraphs,
|
|
232
|
-
excerptFromParagraphs,
|
|
233
|
-
countWords
|
|
234
|
-
};
|
package/src/mappers/index.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content mapping utilities for transforming parsed content
|
|
3
|
-
* into component-specific formats
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as helpers from './helpers.js';
|
|
7
|
-
import * as accessor from './accessor.js';
|
|
8
|
-
import * as extractors from './extractors.js';
|
|
9
|
-
import * as types from './types.js';
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
// Helper utilities
|
|
13
|
-
helpers,
|
|
14
|
-
|
|
15
|
-
// Path-based accessor
|
|
16
|
-
accessor,
|
|
17
|
-
|
|
18
|
-
// Common extractors
|
|
19
|
-
extractors,
|
|
20
|
-
|
|
21
|
-
// Type system
|
|
22
|
-
types
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Re-export all functions for direct access
|
|
26
|
-
export * from './helpers.js';
|
|
27
|
-
export * from './accessor.js';
|
|
28
|
-
export * from './extractors.js';
|