@coherent.js/seo 1.0.0-beta.5 → 1.0.0-beta.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/package.json +2 -2
- package/types/index.d.ts +337 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coherent.js/seo",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "SEO utilities for Coherent.js applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"author": "Coherent.js Team",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@coherent.js/core": "1.0.0-beta.
|
|
23
|
+
"@coherent.js/core": "1.0.0-beta.6"
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
package/types/index.d.ts
CHANGED
|
@@ -3,8 +3,164 @@
|
|
|
3
3
|
* @module @coherent.js/seo
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import type { CoherentNode } from '@coherent.js/core';
|
|
7
7
|
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Meta Tag Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generic meta tag definition
|
|
14
|
+
*/
|
|
15
|
+
export interface MetaTag {
|
|
16
|
+
/** Meta name attribute (for standard meta tags) */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Meta property attribute (for Open Graph, etc.) */
|
|
19
|
+
property?: string;
|
|
20
|
+
/** Meta content value */
|
|
21
|
+
content: string;
|
|
22
|
+
/** HTTP-equiv attribute */
|
|
23
|
+
httpEquiv?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Open Graph image configuration
|
|
28
|
+
*/
|
|
29
|
+
export interface OpenGraphImage {
|
|
30
|
+
/** Image URL */
|
|
31
|
+
url: string;
|
|
32
|
+
/** Image width in pixels */
|
|
33
|
+
width?: number;
|
|
34
|
+
/** Image height in pixels */
|
|
35
|
+
height?: number;
|
|
36
|
+
/** Image alt text */
|
|
37
|
+
alt?: string;
|
|
38
|
+
/** Image MIME type */
|
|
39
|
+
type?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Open Graph meta configuration
|
|
44
|
+
*/
|
|
45
|
+
export interface OpenGraphMeta {
|
|
46
|
+
/** Page title */
|
|
47
|
+
title: string;
|
|
48
|
+
/** Page description */
|
|
49
|
+
description?: string;
|
|
50
|
+
/** Content type */
|
|
51
|
+
type?: 'website' | 'article' | 'book' | 'profile' | 'video.movie' | 'video.episode' | 'music.song' | 'music.album';
|
|
52
|
+
/** Page URL */
|
|
53
|
+
url?: string;
|
|
54
|
+
/** Image(s) for sharing */
|
|
55
|
+
image?: string | OpenGraphImage | (string | OpenGraphImage)[];
|
|
56
|
+
/** Site name */
|
|
57
|
+
siteName?: string;
|
|
58
|
+
/** Locale (e.g., 'en_US') */
|
|
59
|
+
locale?: string;
|
|
60
|
+
/** Alternate locales */
|
|
61
|
+
alternateLocales?: string[];
|
|
62
|
+
/** Audio URL */
|
|
63
|
+
audio?: string;
|
|
64
|
+
/** Video URL */
|
|
65
|
+
video?: string;
|
|
66
|
+
/** Determiner (a, an, the, auto, or empty) */
|
|
67
|
+
determiner?: 'a' | 'an' | 'the' | 'auto' | '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Twitter Card meta configuration
|
|
72
|
+
*/
|
|
73
|
+
export interface TwitterMeta {
|
|
74
|
+
/** Card type */
|
|
75
|
+
card?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
76
|
+
/** Twitter @username of website */
|
|
77
|
+
site?: string;
|
|
78
|
+
/** Twitter @username of content creator */
|
|
79
|
+
creator?: string;
|
|
80
|
+
/** Card title (defaults to og:title) */
|
|
81
|
+
title?: string;
|
|
82
|
+
/** Card description (defaults to og:description) */
|
|
83
|
+
description?: string;
|
|
84
|
+
/** Card image (defaults to og:image) */
|
|
85
|
+
image?: string;
|
|
86
|
+
/** Image alt text */
|
|
87
|
+
imageAlt?: string;
|
|
88
|
+
/** App ID for app cards */
|
|
89
|
+
appIdIphone?: string;
|
|
90
|
+
/** App ID for iPad */
|
|
91
|
+
appIdIpad?: string;
|
|
92
|
+
/** App ID for Google Play */
|
|
93
|
+
appIdGooglePlay?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Comprehensive SEO configuration
|
|
98
|
+
*/
|
|
99
|
+
export interface SEOConfig {
|
|
100
|
+
/** Page title */
|
|
101
|
+
title: string;
|
|
102
|
+
/** Title template (e.g., '%s | My Site') */
|
|
103
|
+
titleTemplate?: string;
|
|
104
|
+
/** Page description */
|
|
105
|
+
description?: string;
|
|
106
|
+
/** Canonical URL */
|
|
107
|
+
canonical?: string;
|
|
108
|
+
/** Robots directives (e.g., 'index, follow') */
|
|
109
|
+
robots?: string;
|
|
110
|
+
/** Additional meta tags */
|
|
111
|
+
meta?: MetaTag[];
|
|
112
|
+
/** Open Graph configuration */
|
|
113
|
+
openGraph?: OpenGraphMeta;
|
|
114
|
+
/** Twitter Card configuration */
|
|
115
|
+
twitter?: TwitterMeta;
|
|
116
|
+
/** JSON-LD structured data */
|
|
117
|
+
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
|
|
118
|
+
/** Keywords (comma-separated or array) */
|
|
119
|
+
keywords?: string | string[];
|
|
120
|
+
/** Author name */
|
|
121
|
+
author?: string;
|
|
122
|
+
/** Viewport settings */
|
|
123
|
+
viewport?: string;
|
|
124
|
+
/** Character set */
|
|
125
|
+
charset?: string;
|
|
126
|
+
/** Language code */
|
|
127
|
+
language?: string;
|
|
128
|
+
/** Theme color */
|
|
129
|
+
themeColor?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// SEO Generation Functions
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generate meta tags from SEO configuration
|
|
138
|
+
* @returns Array of meta element nodes
|
|
139
|
+
*/
|
|
140
|
+
export function generateMeta(config: SEOConfig): CoherentNode[];
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Generate JSON-LD script tag from data
|
|
144
|
+
*/
|
|
145
|
+
export function generateJsonLd(data: Record<string, unknown>): CoherentNode;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a reusable SEO component from configuration
|
|
149
|
+
*/
|
|
150
|
+
export function createSEOComponent(config: SEOConfig): () => CoherentNode;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Merge multiple SEO configurations (later configs override earlier)
|
|
154
|
+
*/
|
|
155
|
+
export function mergeSEOConfig(...configs: Partial<SEOConfig>[]): SEOConfig;
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Meta Builder
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* All meta tags in a flat structure
|
|
163
|
+
*/
|
|
8
164
|
export interface MetaTags {
|
|
9
165
|
title?: string;
|
|
10
166
|
description?: string;
|
|
@@ -32,41 +188,88 @@ export interface MetaTags {
|
|
|
32
188
|
twitterDescription?: string;
|
|
33
189
|
twitterImage?: string;
|
|
34
190
|
// Custom meta tags
|
|
35
|
-
[key: string]:
|
|
191
|
+
[key: string]: unknown;
|
|
36
192
|
}
|
|
37
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Fluent builder for meta tags
|
|
196
|
+
*/
|
|
38
197
|
export class MetaBuilder {
|
|
39
198
|
constructor(tags?: MetaTags);
|
|
199
|
+
|
|
200
|
+
/** Set page title */
|
|
40
201
|
setTitle(title: string): this;
|
|
202
|
+
|
|
203
|
+
/** Set page description */
|
|
41
204
|
setDescription(description: string): this;
|
|
205
|
+
|
|
206
|
+
/** Set keywords */
|
|
42
207
|
setKeywords(keywords: string | string[]): this;
|
|
208
|
+
|
|
209
|
+
/** Set author */
|
|
43
210
|
setAuthor(author: string): this;
|
|
211
|
+
|
|
212
|
+
/** Set robots directive */
|
|
44
213
|
setRobots(robots: string): this;
|
|
214
|
+
|
|
215
|
+
/** Set canonical URL */
|
|
45
216
|
setCanonical(url: string): this;
|
|
217
|
+
|
|
218
|
+
/** Set viewport */
|
|
46
219
|
setViewport(viewport: string): this;
|
|
47
|
-
|
|
48
|
-
|
|
220
|
+
|
|
221
|
+
/** Set Open Graph properties */
|
|
222
|
+
setOpenGraph(tags: Partial<OpenGraphMeta>): this;
|
|
223
|
+
|
|
224
|
+
/** Set Twitter Card properties */
|
|
225
|
+
setTwitterCard(tags: Partial<TwitterMeta>): this;
|
|
226
|
+
|
|
227
|
+
/** Add a custom meta tag */
|
|
49
228
|
addCustomTag(name: string, content: string, type?: 'name' | 'property'): this;
|
|
50
|
-
|
|
51
|
-
|
|
229
|
+
|
|
230
|
+
/** Build as CoherentNode */
|
|
231
|
+
build(): CoherentNode;
|
|
232
|
+
|
|
233
|
+
/** Alias for build */
|
|
234
|
+
render(): CoherentNode;
|
|
235
|
+
|
|
236
|
+
/** Convert to HTML string */
|
|
52
237
|
toHTML(): string;
|
|
53
238
|
}
|
|
54
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Create a MetaBuilder instance
|
|
242
|
+
*/
|
|
55
243
|
export function createMetaBuilder(tags?: MetaTags): MetaBuilder;
|
|
56
|
-
export function generateMeta(tags: MetaTags): object;
|
|
57
244
|
|
|
58
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Generate meta nodes from flat tags
|
|
247
|
+
*/
|
|
248
|
+
export function generateMeta(tags: MetaTags): CoherentNode;
|
|
59
249
|
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// Sitemap Generator
|
|
252
|
+
// ============================================================================
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Sitemap entry configuration
|
|
256
|
+
*/
|
|
60
257
|
export interface SitemapEntry {
|
|
258
|
+
/** Page URL */
|
|
61
259
|
url: string;
|
|
260
|
+
/** Last modification date */
|
|
62
261
|
lastmod?: string | Date;
|
|
262
|
+
/** Change frequency */
|
|
63
263
|
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
264
|
+
/** Priority (0.0 to 1.0) */
|
|
64
265
|
priority?: number;
|
|
266
|
+
/** Image entries */
|
|
65
267
|
images?: Array<{
|
|
66
268
|
loc: string;
|
|
67
269
|
title?: string;
|
|
68
270
|
caption?: string;
|
|
69
271
|
}>;
|
|
272
|
+
/** Video entries */
|
|
70
273
|
videos?: Array<{
|
|
71
274
|
thumbnail_loc: string;
|
|
72
275
|
title: string;
|
|
@@ -74,30 +277,69 @@ export interface SitemapEntry {
|
|
|
74
277
|
content_loc?: string;
|
|
75
278
|
player_loc?: string;
|
|
76
279
|
}>;
|
|
280
|
+
/** Alternate language versions */
|
|
281
|
+
alternates?: Array<{
|
|
282
|
+
hreflang: string;
|
|
283
|
+
href: string;
|
|
284
|
+
}>;
|
|
77
285
|
}
|
|
78
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Sitemap generator options
|
|
289
|
+
*/
|
|
79
290
|
export interface SitemapOptions {
|
|
291
|
+
/** Site hostname (e.g., 'https://example.com') */
|
|
80
292
|
hostname: string;
|
|
293
|
+
/** Cache time in milliseconds */
|
|
81
294
|
cacheTime?: number;
|
|
295
|
+
/** XML namespaces */
|
|
82
296
|
xmlNs?: Record<string, string>;
|
|
297
|
+
/** XSL stylesheet URL */
|
|
83
298
|
xslUrl?: string;
|
|
84
299
|
}
|
|
85
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Sitemap generator class
|
|
303
|
+
*/
|
|
86
304
|
export class SitemapGenerator {
|
|
87
305
|
constructor(options: SitemapOptions);
|
|
306
|
+
|
|
307
|
+
/** Add a URL entry */
|
|
88
308
|
addUrl(entry: SitemapEntry): this;
|
|
309
|
+
|
|
310
|
+
/** Add multiple URL entries */
|
|
89
311
|
addUrls(entries: SitemapEntry[]): this;
|
|
312
|
+
|
|
313
|
+
/** Remove a URL entry */
|
|
90
314
|
removeUrl(url: string): this;
|
|
315
|
+
|
|
316
|
+
/** Generate sitemap XML string */
|
|
91
317
|
generate(): string;
|
|
318
|
+
|
|
319
|
+
/** Alias for generate */
|
|
92
320
|
toXML(): string;
|
|
321
|
+
|
|
322
|
+
/** Export entries as JSON */
|
|
93
323
|
toJSON(): SitemapEntry[];
|
|
94
324
|
}
|
|
95
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Create a sitemap generator
|
|
328
|
+
*/
|
|
96
329
|
export function createSitemapGenerator(options: SitemapOptions): SitemapGenerator;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Generate sitemap XML from entries
|
|
333
|
+
*/
|
|
97
334
|
export function generateSitemap(entries: SitemapEntry[], options: SitemapOptions): string;
|
|
98
335
|
|
|
99
|
-
//
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Structured Data (JSON-LD)
|
|
338
|
+
// ============================================================================
|
|
100
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Supported structured data types
|
|
342
|
+
*/
|
|
101
343
|
export type StructuredDataType =
|
|
102
344
|
| 'Article'
|
|
103
345
|
| 'BlogPosting'
|
|
@@ -115,30 +357,71 @@ export type StructuredDataType =
|
|
|
115
357
|
| 'FAQPage'
|
|
116
358
|
| 'HowTo'
|
|
117
359
|
| 'VideoObject'
|
|
118
|
-
| 'ImageObject'
|
|
360
|
+
| 'ImageObject'
|
|
361
|
+
| 'SoftwareApplication'
|
|
362
|
+
| 'Course'
|
|
363
|
+
| 'JobPosting';
|
|
119
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Structured data object
|
|
367
|
+
*/
|
|
120
368
|
export interface StructuredData {
|
|
121
369
|
'@context': string;
|
|
122
370
|
'@type': StructuredDataType | StructuredDataType[];
|
|
123
|
-
[key: string]:
|
|
371
|
+
[key: string]: unknown;
|
|
124
372
|
}
|
|
125
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Fluent builder for structured data
|
|
376
|
+
*/
|
|
126
377
|
export class StructuredDataBuilder {
|
|
127
378
|
constructor(type: StructuredDataType);
|
|
379
|
+
|
|
380
|
+
/** Change the schema type */
|
|
128
381
|
setType(type: StructuredDataType): this;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
382
|
+
|
|
383
|
+
/** Set a single property */
|
|
384
|
+
setProperty(key: string, value: unknown): this;
|
|
385
|
+
|
|
386
|
+
/** Set multiple properties */
|
|
387
|
+
setProperties(properties: Record<string, unknown>): this;
|
|
388
|
+
|
|
389
|
+
/** Add value to an array property */
|
|
390
|
+
addToArray(key: string, value: unknown): this;
|
|
391
|
+
|
|
392
|
+
/** Build the structured data object */
|
|
132
393
|
build(): StructuredData;
|
|
394
|
+
|
|
395
|
+
/** Convert to JSON string */
|
|
133
396
|
toJSON(): string;
|
|
134
|
-
|
|
397
|
+
|
|
398
|
+
/** Build as JSON-LD script node */
|
|
399
|
+
toJSONLD(): CoherentNode;
|
|
135
400
|
}
|
|
136
401
|
|
|
137
|
-
|
|
138
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Create a structured data builder
|
|
404
|
+
*/
|
|
405
|
+
export function createStructuredData(
|
|
406
|
+
type: StructuredDataType,
|
|
407
|
+
properties?: Record<string, unknown>
|
|
408
|
+
): StructuredDataBuilder;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Generate structured data object
|
|
412
|
+
*/
|
|
413
|
+
export function generateStructuredData(
|
|
414
|
+
type: StructuredDataType,
|
|
415
|
+
properties: Record<string, unknown>
|
|
416
|
+
): StructuredData;
|
|
139
417
|
|
|
140
|
-
//
|
|
418
|
+
// ============================================================================
|
|
419
|
+
// Common Structured Data Types
|
|
420
|
+
// ============================================================================
|
|
141
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Article structured data
|
|
424
|
+
*/
|
|
142
425
|
export interface ArticleData {
|
|
143
426
|
headline: string;
|
|
144
427
|
image: string | string[];
|
|
@@ -160,6 +443,9 @@ export interface ArticleData {
|
|
|
160
443
|
mainEntityOfPage?: string;
|
|
161
444
|
}
|
|
162
445
|
|
|
446
|
+
/**
|
|
447
|
+
* Product structured data
|
|
448
|
+
*/
|
|
163
449
|
export interface ProductData {
|
|
164
450
|
name: string;
|
|
165
451
|
image: string | string[];
|
|
@@ -182,6 +468,9 @@ export interface ProductData {
|
|
|
182
468
|
};
|
|
183
469
|
}
|
|
184
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Breadcrumb structured data
|
|
473
|
+
*/
|
|
185
474
|
export interface BreadcrumbData {
|
|
186
475
|
itemListElement: Array<{
|
|
187
476
|
'@type': 'ListItem';
|
|
@@ -190,3 +479,33 @@ export interface BreadcrumbData {
|
|
|
190
479
|
item?: string;
|
|
191
480
|
}>;
|
|
192
481
|
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Organization structured data
|
|
485
|
+
*/
|
|
486
|
+
export interface OrganizationData {
|
|
487
|
+
name: string;
|
|
488
|
+
url?: string;
|
|
489
|
+
logo?: string;
|
|
490
|
+
description?: string;
|
|
491
|
+
sameAs?: string[];
|
|
492
|
+
contactPoint?: {
|
|
493
|
+
'@type': 'ContactPoint';
|
|
494
|
+
telephone: string;
|
|
495
|
+
contactType: string;
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* FAQ Page structured data
|
|
501
|
+
*/
|
|
502
|
+
export interface FAQPageData {
|
|
503
|
+
mainEntity: Array<{
|
|
504
|
+
'@type': 'Question';
|
|
505
|
+
name: string;
|
|
506
|
+
acceptedAnswer: {
|
|
507
|
+
'@type': 'Answer';
|
|
508
|
+
text: string;
|
|
509
|
+
};
|
|
510
|
+
}>;
|
|
511
|
+
}
|