sanity-plugin-seofields 1.7.0 → 1.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.
Files changed (111) hide show
  1. package/README.md +440 -29
  2. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.cts +78 -0
  3. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.ts +78 -0
  4. package/dist/SeoHealthTool-2COI27KI.cjs +8 -0
  5. package/dist/SeoHealthTool-2COI27KI.cjs.map +1 -0
  6. package/dist/SeoHealthTool-CWI3KB2V.js +8 -0
  7. package/dist/SeoHealthTool-CWI3KB2V.js.map +1 -0
  8. package/dist/{SeoPreview-WYH7NYNM.cjs → SeoPreview-LMZAWWOE.cjs} +35 -38
  9. package/dist/SeoPreview-LMZAWWOE.cjs.map +1 -0
  10. package/dist/{SeoPreview-KEGGQSPJ.js → SeoPreview-UNQBKTQO.js} +14 -11
  11. package/dist/SeoPreview-UNQBKTQO.js.map +1 -0
  12. package/dist/chunk-3OK3S2F7.cjs +673 -0
  13. package/dist/chunk-3OK3S2F7.cjs.map +1 -0
  14. package/dist/{chunk-GWPOJSGW.cjs → chunk-5XTQRILL.cjs} +1022 -307
  15. package/dist/chunk-5XTQRILL.cjs.map +1 -0
  16. package/dist/{chunk-2NMEKWO5.js → chunk-7N4MLTMR.js} +8 -15
  17. package/dist/chunk-7N4MLTMR.js.map +1 -0
  18. package/dist/{chunk-S367Y35J.cjs → chunk-A57XNQC7.cjs} +9 -20
  19. package/dist/chunk-A57XNQC7.cjs.map +1 -0
  20. package/dist/chunk-BWLJDK5J.js +624 -0
  21. package/dist/chunk-BWLJDK5J.js.map +1 -0
  22. package/dist/chunk-FUUWBQE2.cjs +195 -0
  23. package/dist/chunk-FUUWBQE2.cjs.map +1 -0
  24. package/dist/{chunk-ZBHLMQTS.cjs → chunk-HHO2AKAP.js} +44 -17
  25. package/dist/chunk-HHO2AKAP.js.map +1 -0
  26. package/dist/chunk-KIEO6QG3.js +195 -0
  27. package/dist/chunk-KIEO6QG3.js.map +1 -0
  28. package/dist/chunk-R2U7JF7U.cjs +624 -0
  29. package/dist/chunk-R2U7JF7U.cjs.map +1 -0
  30. package/dist/{chunk-HRKRV2X7.js → chunk-XZKQWV2H.js} +974 -148
  31. package/dist/chunk-XZKQWV2H.js.map +1 -0
  32. package/dist/{chunk-HDZZQCH7.js → chunk-Y46ACXM4.cjs} +45 -5
  33. package/dist/chunk-Y46ACXM4.cjs.map +1 -0
  34. package/dist/chunk-Z5AOUU4H.js +673 -0
  35. package/dist/chunk-Z5AOUU4H.js.map +1 -0
  36. package/dist/cli.js +27 -27
  37. package/dist/{component-DQ4KuQQD.d.ts → component-CjT1hvxh.d.ts} +3 -27
  38. package/dist/{component-D1sBm6ts.d.cts → component-WYh0z8as.d.cts} +3 -27
  39. package/dist/define-cli.cjs +2 -4
  40. package/dist/define-cli.cjs.map +1 -1
  41. package/dist/define-cli.js +4 -4
  42. package/dist/define-cli.js.map +1 -1
  43. package/dist/head.cjs +14 -0
  44. package/dist/head.cjs.map +1 -0
  45. package/dist/head.d.cts +254 -0
  46. package/dist/head.d.ts +254 -0
  47. package/dist/head.js +14 -0
  48. package/dist/head.js.map +1 -0
  49. package/dist/index.cjs +1797 -230
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +11 -598
  52. package/dist/index.d.ts +11 -598
  53. package/dist/index.js +1777 -197
  54. package/dist/index.js.map +1 -1
  55. package/dist/next.cjs +210 -447
  56. package/dist/next.cjs.map +1 -1
  57. package/dist/next.d.cts +8 -200
  58. package/dist/next.d.ts +8 -200
  59. package/dist/next.js +200 -124
  60. package/dist/next.js.map +1 -1
  61. package/dist/plugin-DQdThyxG.d.ts +449 -0
  62. package/dist/plugin-DoGeZP79.d.cts +449 -0
  63. package/dist/schema/next.cjs +173 -319
  64. package/dist/schema/next.cjs.map +1 -1
  65. package/dist/schema/next.d.cts +4 -4
  66. package/dist/schema/next.d.ts +4 -4
  67. package/dist/schema/next.js +172 -8
  68. package/dist/schema/next.js.map +1 -1
  69. package/dist/schema.cjs +390 -489
  70. package/dist/schema.cjs.map +1 -1
  71. package/dist/schema.d.cts +26 -16
  72. package/dist/schema.d.ts +26 -16
  73. package/dist/schema.js +236 -13
  74. package/dist/schema.js.map +1 -1
  75. package/dist/server.cjs +149 -0
  76. package/dist/server.cjs.map +1 -0
  77. package/dist/server.d.cts +102 -0
  78. package/dist/server.d.ts +102 -0
  79. package/dist/server.js +149 -0
  80. package/dist/server.js.map +1 -0
  81. package/dist/{types-CeBULNFF.d.ts → types-BAdN1RMu.d.ts} +2 -2
  82. package/dist/{types-_wBLg6RV.d.cts → types-C5i3KMgs.d.cts} +2 -2
  83. package/dist/{types-9_wbCIi9.d.ts → types-DP-DiW6f.d.cts} +29 -2
  84. package/dist/{types-Bb49oRBV.d.cts → types-DRLUGLtX.d.ts} +29 -2
  85. package/dist/{types-B91ena4g.d.cts → types-DxlPXihz.d.cts} +8 -1
  86. package/dist/{types-B91ena4g.d.ts → types-DxlPXihz.d.ts} +8 -1
  87. package/package.json +25 -3
  88. package/dist/SeoHealthDashboard-IP5BJ322.cjs +0 -10
  89. package/dist/SeoHealthDashboard-IP5BJ322.cjs.map +0 -1
  90. package/dist/SeoHealthDashboard-PTUEVYZL.js +0 -4
  91. package/dist/SeoHealthDashboard-PTUEVYZL.js.map +0 -1
  92. package/dist/SeoHealthTool-CZZRMWQK.cjs +0 -14
  93. package/dist/SeoHealthTool-CZZRMWQK.cjs.map +0 -1
  94. package/dist/SeoHealthTool-IULT4G25.js +0 -12
  95. package/dist/SeoHealthTool-IULT4G25.js.map +0 -1
  96. package/dist/SeoPreview-KEGGQSPJ.js.map +0 -1
  97. package/dist/SeoPreview-WYH7NYNM.cjs.map +0 -1
  98. package/dist/chunk-2NMEKWO5.js.map +0 -1
  99. package/dist/chunk-5YRH47VQ.cjs +0 -2167
  100. package/dist/chunk-5YRH47VQ.cjs.map +0 -1
  101. package/dist/chunk-ARELX3GK.cjs +0 -689
  102. package/dist/chunk-ARELX3GK.cjs.map +0 -1
  103. package/dist/chunk-AW7OKBQQ.js +0 -2161
  104. package/dist/chunk-AW7OKBQQ.js.map +0 -1
  105. package/dist/chunk-FONJYVF2.js +0 -610
  106. package/dist/chunk-FONJYVF2.js.map +0 -1
  107. package/dist/chunk-GWPOJSGW.cjs.map +0 -1
  108. package/dist/chunk-HDZZQCH7.js.map +0 -1
  109. package/dist/chunk-HRKRV2X7.js.map +0 -1
  110. package/dist/chunk-S367Y35J.cjs.map +0 -1
  111. package/dist/chunk-ZBHLMQTS.cjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,316 +1,12 @@
1
+ import { S as SeoFieldsPluginConfig, s as seofields } from './plugin-DoGeZP79.cjs';
2
+ export { A as AiConfig, a as AiIndustry, b as AllFieldKeys, F as FieldVisibilityConfig, P as PublishGateRequiredField, c as SeoFieldConfig, d as SeoFieldGroup, e as SeoFieldKeys, f as SeoObjectFieldName, V as ValidHiddenFieldKeys, o as openGraphFieldKeys, t as twitterFieldKeys } from './plugin-DoGeZP79.cjs';
1
3
  import * as sanity from 'sanity';
2
4
  import { SchemaTypeDefinition } from 'sanity';
3
- import { ComponentType, ReactNode } from 'react';
4
- import { D as DocumentWithSeoHealth$1 } from './types-B91ena4g.cjs';
5
- export { S as SeoHealthMetrics, a as SeoHealthStatus } from './types-B91ena4g.cjs';
6
- import { StructureBuilder, ComponentBuilder } from 'sanity/structure';
7
-
8
- interface SeoFieldConfig {
9
- title?: string;
10
- description?: string;
11
- }
12
- type SeoFieldKeys = 'title' | 'description' | 'canonicalUrl' | 'metaImage' | 'keywords' | 'metaAttributes' | 'robots';
13
- type openGraphFieldKeys = 'openGraphUrl' | 'openGraphTitle' | 'openGraphDescription' | 'openGraphSiteName' | 'openGraphType' | 'openGraphImageType' | 'openGraphImage' | 'openGraphImageUrl';
14
- type twitterFieldKeys = 'twitterCard' | 'twitterSite' | 'twitterCreator' | 'twitterTitle' | 'twitterDescription' | 'twitterImageType' | 'twitterImage' | 'twitterImageUrl';
15
- type AllFieldKeys = SeoFieldKeys | openGraphFieldKeys | twitterFieldKeys;
16
- /**
17
- * Names of top-level fields inside the `seoFields` object type.
18
- * Use these when assigning fields to groups in `fieldGroups`.
19
- */
20
- type SeoObjectFieldName = 'robots' | 'preview' | 'title' | 'description' | 'metaImage' | 'metaAttributes' | 'keywords' | 'canonicalUrl' | 'openGraph' | 'twitter';
21
- /**
22
- * Defines a single tab/group within the `seoFields` object.
23
- */
24
- interface SeoFieldGroup {
25
- /** Unique key for this group (used internally by Sanity). */
26
- name: string;
27
- /** Human-readable label shown as the tab title. */
28
- title: string;
29
- /** Whether this tab is selected by default. Only one group should be `true`. */
30
- default?: boolean;
31
- /**
32
- * Field names to include in this group.
33
- * Use the top-level seoFields field names: `'title'`, `'description'`, `'metaImage'`,
34
- * `'keywords'`, `'canonicalUrl'`, `'metaAttributes'`, `'robots'`, `'preview'`,
35
- * `'openGraph'`, `'twitter'`.
36
- */
37
- fields: SeoObjectFieldName[];
38
- /** Optional icon displayed next to the tab title. Must be a React component. */
39
- icon?: ComponentType;
40
- }
41
- type ValidHiddenFieldKeys = Exclude<AllFieldKeys, 'openGraphImageUrl' | 'twitterImageUrl' | 'openGraphImageType' | 'twitterImageType'>;
42
- interface FieldVisibilityConfig {
43
- hiddenFields?: ValidHiddenFieldKeys[];
44
- }
45
- interface SeoFieldsPluginConfig {
46
- /**
47
- * Enable or configure the SEO preview feature.
48
- * If set to `true`, the SEO preview will be enabled with default settings.
49
- * If set to an object, you can provide a custom `prefix` function to modify the URL prefix
50
- * and/or a `titleSuffix` to append text (e.g. a brand name) after the meta title in the preview.
51
- * The plugin automatically adds a `|` separator — only provide the suffix text itself.
52
- *
53
- * Example:
54
- * ```
55
- * seoPreview: {
56
- * prefix: (doc) => `/${doc.slug?.current || 'untitled'}`,
57
- * titleSuffix: 'Acme Corp',
58
- * // or dynamically:
59
- * titleSuffix: (doc) => doc.brandName || 'Acme Corp',
60
- * }
61
- * ```
62
- */
63
- seoPreview?: boolean | {
64
- prefix?: (doc: {
65
- _type?: string;
66
- } & Record<string, unknown>) => string;
67
- /**
68
- * A static string or function appended to the meta title in the Live Preview.
69
- * Useful for showing a brand suffix (e.g. `'Acme Corp'`) that is added
70
- * via your Next.js title template but not stored in the Sanity field.
71
- * The plugin automatically prepends a `|` separator so only provide the
72
- * text itself. The suffix is rendered in a muted style so editors can
73
- * distinguish it from the typed title. The combined length (including the
74
- * separator) is checked against the 60-character SERP limit.
75
- */
76
- titleSuffix?: string | ((doc: {
77
- _type?: string;
78
- } & Record<string, unknown>) => string);
79
- /**
80
- * When `true`, the `titleSuffix` is rendered in the same color and weight
81
- * as the main title (`#1a0dab`, `fontWeight: 500`) instead of the default
82
- * muted style (`#70757a`, `fontWeight: 400`).
83
- *
84
- * @default false
85
- */
86
- titleSuffixInheritColor?: boolean;
87
- /**
88
- * A GROQ query string whose result is used as the title suffix in the Live Preview.
89
- * The query runs against your dataset at Studio load time, making it useful for
90
- * fetching a dynamic value from another document (e.g. a settings singleton).
91
- * Takes priority over `titleSuffix` when both are provided.
92
- *
93
- * @example
94
- * ```ts
95
- * titleSuffixQuery: '*[_type == "siteSettings"][0].siteName'
96
- * ```
97
- */
98
- titleSuffixQuery?: string;
99
- };
100
- /**
101
- * A mapping of field keys to their configuration settings.
102
- * This allows customization of field titles and descriptions.
103
- * For example, to change the title of the 'title' field:
104
- */
105
- fieldOverrides?: Partial<Record<AllFieldKeys, SeoFieldConfig>>;
106
- /**
107
- * A mapping of document types to field visibility configurations.
108
- * This allows you to specify which fields should be hidden for specific document types.
109
- */
110
- fieldVisibility?: Record<string, FieldVisibilityConfig>;
111
- /**
112
- * A list of fields that should be hidden by default in all document types.
113
- * This can be overridden by specific document type settings in `fieldVisibility`.
114
- */
115
- defaultHiddenFields?: ValidHiddenFieldKeys[];
116
- /**
117
- * Group the SEO fields into tabbed sections inside the `seoFields` object.
118
- * When configured, the Studio shows tabs (Sanity groups) so editors can
119
- * switch between e.g. "Meta", "Open Graph", and "Twitter Card" panels.
120
- *
121
- * @example
122
- * fieldGroups: [
123
- * { name: 'meta', title: 'Meta', default: true,
124
- * fields: ['title', 'description', 'metaImage', 'keywords', 'canonicalUrl', 'metaAttributes', 'robots', 'preview'] },
125
- * { name: 'openGraph', title: 'Open Graph', fields: ['openGraph'] },
126
- * { name: 'twitter', title: 'Twitter Card', fields: ['twitter'] },
127
- * ]
128
- */
129
- fieldGroups?: SeoFieldGroup[];
130
- /**
131
- * The base URL of your website, used for generating full URLs in the SEO preview.
132
- * Defaults to 'https://www.example.com' if not provided.
133
- */
134
- baseUrl?: string;
135
- /**
136
- * The Sanity API version to use for all plugin clients (SEO Preview, Health Dashboard).
137
- * Defaults to '2024-01-01'.
138
- * @example '2024-01-01'
139
- */
140
- apiVersion?: string;
141
- /**
142
- * Enable or configure the SEO Health Dashboard tool.
143
- * If set to `true`, the dashboard is enabled with all defaults.
144
- * If set to an object, you can customise the tool and dashboard settings.
145
- * Defaults to `true`.
146
- * Example:
147
- * ```
148
- * healthDashboard: {
149
- * toolTitle: 'SEO Overview', // Studio nav tab label
150
- * content: {
151
- * icon: '🔍', // Emoji icon shown before the page heading
152
- * title: 'My SEO Dashboard',// Page heading inside the tool (no emoji)
153
- * description: 'Track SEO across all documents', // Subtitle under the heading
154
- * },
155
- * display: {
156
- * typeColumn: false, // Hide the document type column (default: true)
157
- * documentId: false, // Hide the document ID under titles (default: true)
158
- * },
159
- * query: {
160
- * // Option 1 – filter by specific document types
161
- * types: ['post', 'page'],
162
- * // Option 2 – provide a full custom GROQ query (takes precedence over `types`)
163
- * // Must return documents with at least: _id, _type, title, seo, _updatedAt
164
- * groq: `*[seo != null && defined(slug.current)]{ _id, _type, title, slug, seo, _updatedAt }`,
165
- * },
166
- * }
167
- * ```
168
- */
169
- healthDashboard?: boolean | {
170
- tool?: {
171
- title?: string;
172
- name?: string;
173
- };
174
- toolTitle?: string;
175
- content?: {
176
- icon?: string;
177
- title?: string;
178
- description?: string;
179
- /** Text shown while the license key is being verified. Defaults to "Verifying license…" */
180
- loadingLicense?: string;
181
- /** Text shown while documents are being fetched. Defaults to "Loading documents…" */
182
- loadingDocuments?: string;
183
- /** Text shown when the query returns zero results. Defaults to "No documents found" */
184
- noDocuments?: string;
185
- };
186
- /**
187
- * Show or hide the document type column in the results table.
188
- * Defaults to `true`.
189
- */
190
- showTypeColumn?: boolean;
191
- /**
192
- * Show or hide the Sanity document `_id` under each title.
193
- * Defaults to `true`.
194
- */
195
- showDocumentId?: boolean;
196
- query?: {
197
- /**
198
- * Limit the dashboard to specific document types.
199
- * Example: `['post', 'page']`
200
- */
201
- types?: string[];
202
- /**
203
- * When using `types`, also require the `seo` field to be non-null.
204
- * Set to `false` to include documents of those types even if `seo` is missing.
205
- * Defaults to `true`.
206
- */
207
- requireSeo?: boolean;
208
- /**
209
- * Provide a fully custom GROQ query. Takes precedence over `types`.
210
- * The query must return documents with at least: _id, _type, title, seo, _updatedAt
211
- */
212
- groq?: string;
213
- };
214
- /**
215
- * The Sanity API version to use for the client (e.g. '2023-01-01').
216
- * Defaults to '2023-01-01'.
217
- * @deprecated Use the root-level `apiVersion` option instead.
218
- * @example
219
- * // Before (deprecated):
220
- * healthDashboard: { apiVersion: '2024-01-01' }
221
- * // After:
222
- * apiVersion: '2024-01-01'
223
- */
224
- apiVersion?: string;
225
- /**
226
- * License key for the SEO Health Dashboard pro feature.
227
- * Obtain a license at https://sanity-plugin-seofields.thehardik.in
228
- */
229
- licenseKey?: string;
230
- /**
231
- * Map raw `_type` values to human-readable display labels.
232
- * Used in both the Type column and the Type filter dropdown.
233
- * Any type without an entry falls back to the raw `_type` string.
234
- *
235
- * @example
236
- * typeDisplayLabels: { productDrug: 'Products', singleCondition: 'Condition' }
237
- */
238
- typeDisplayLabels?: Record<string, string>;
239
- /**
240
- * Controls how the document type is rendered in the Type column.
241
- * - `'badge'` (default) — coloured pill
242
- * - `'text'` — plain text, useful for dense layouts
243
- */
244
- typeColumnMode?: 'badge' | 'text';
245
- /**
246
- * The document field to use as the display title in the dashboard.
247
- *
248
- * - `string` — use this field for every document type (e.g. `'name'`)
249
- * - `Record<string, string>` — per-type mapping; unmapped types fall back to `title`
250
- *
251
- * @example
252
- * titleField: 'name'
253
- *
254
- * @example
255
- * titleField: { post: 'title', product: 'name', category: 'label' }
256
- */
257
- titleField?: string | Record<string, string>;
258
- /**
259
- * Callback function to render a custom badge next to the document title.
260
- * Receives the full document and should return badge data or undefined.
261
- *
262
- * @example
263
- * getDocumentBadge: (doc) => {
264
- * if (doc.services === 'NHS')
265
- * return { label: 'NHS', bgColor: '#e0f2fe', textColor: '#0369a1' }
266
- * if (doc.services === 'Private')
267
- * return { label: 'Private', bgColor: '#fef3c7', textColor: '#92400e' }
268
- * }
269
- */
270
- getDocumentBadge?: (doc: DocumentWithSeoHealth$1 & Record<string, unknown>) => {
271
- label: string;
272
- bgColor?: string;
273
- textColor?: string;
274
- fontSize?: string;
275
- } | undefined;
276
- /**
277
- * The `name` of the Sanity structure tool that contains the monitored documents.
278
- * Required when you have multiple structure tools and the documents live in a
279
- * non-default one. Clicking a title will navigate to
280
- * `/{basePath}/{structureTool}/intent/edit/…` directly.
281
- *
282
- * @example
283
- * structureTool: 'common'
284
- */
285
- structureTool?: string;
286
- /**
287
- * Enable preview/demo mode to show dummy data.
288
- * Useful for testing, documentation, or showcasing the dashboard.
289
- * When enabled, displays realistic sample documents with various SEO scores.
290
- * Defaults to `false`.
291
- *
292
- * @example
293
- * previewMode: true
294
- */
295
- previewMode?: boolean;
296
- /**
297
- * Export options for the SEO Health Dashboard.
298
- * Set to `true` (default) to enable both CSV and JSON export,
299
- * or configure per-format.
300
- */
301
- export?: boolean | {
302
- enabled?: boolean;
303
- formats?: Array<'csv' | 'json'>;
304
- };
305
- /**
306
- * Show compact inline stat pills in the header row instead of the
307
- * full 6-card stats grid. Useful for saving vertical space.
308
- * Defaults to `false`.
309
- */
310
- compactStats?: boolean;
311
- };
312
- }
313
- declare const seofields: sanity.Plugin<void | SeoFieldsPluginConfig>;
5
+ export { SeoHealthPaneOptions, createSeoHealthPane } from 'seofields-pro';
6
+ export { S as SchemaOrgDocumentJsonLdPreview, S as SchemaOrgJsonLdPreview, a as SchemaOrgJsonLdPreviewOptions, b as SchemaOrgJsonLdPreviewProps } from './SchemaOrgJsonLdPreview-DJLkIPq4.cjs';
7
+ import { c as SeoHealthMetrics } from './types-DxlPXihz.cjs';
8
+ export { D as DocumentWithSeoHealth, d as SeoHealthStatus } from './types-DxlPXihz.cjs';
9
+ import 'react';
314
10
 
315
11
  declare function seoFieldsSchema(config?: SeoFieldsPluginConfig): SchemaTypeDefinition;
316
12
 
@@ -347,292 +43,9 @@ declare const _default: {
347
43
 
348
44
  declare function twitter(config?: SeoFieldsPluginConfig): SchemaTypeDefinition;
349
45
 
350
- interface SanityImage {
351
- _type: 'image';
352
- asset: {
353
- _ref: string;
354
- _type: 'reference';
355
- };
356
- hotspot?: {
357
- x: number;
358
- y: number;
359
- height: number;
360
- width: number;
361
- };
362
- crop?: {
363
- top: number;
364
- bottom: number;
365
- left: number;
366
- right: number;
367
- };
368
- alt?: string;
369
- }
370
- interface SanityImageWithAlt extends SanityImage {
371
- alt: string;
372
- }
373
- interface RobotsSettings {
374
- noIndex?: boolean;
375
- noFollow?: boolean;
376
- }
377
- interface MetaAttribute {
378
- _type: 'metaAttribute';
379
- key?: string;
380
- type?: 'string' | 'image';
381
- value?: string;
382
- image?: SanityImage;
383
- }
384
- interface OpenGraphSettings {
385
- _type: 'openGraph';
386
- url?: string;
387
- title?: string;
388
- description?: string;
389
- siteName?: string;
390
- type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product';
391
- imageType?: 'upload' | 'url';
392
- image?: SanityImageWithAlt;
393
- imageUrl?: string;
394
- }
395
- interface TwitterCardSettings {
396
- _type: 'twitter';
397
- card?: 'summary' | 'summary_large_image' | 'app' | 'player';
398
- site?: string;
399
- creator?: string;
400
- title?: string;
401
- description?: string;
402
- imageType?: 'upload' | 'url';
403
- image?: SanityImageWithAlt;
404
- imageUrl?: string;
405
- }
406
- interface SeoFields {
407
- _type: 'seoFields';
408
- robots?: RobotsSettings;
409
- preview?: string;
410
- title?: string;
411
- description?: string;
412
- metaImage?: SanityImage;
413
- metaAttributes?: MetaAttribute[];
414
- keywords?: string[];
415
- canonicalUrl?: string;
416
- openGraph?: OpenGraphSettings;
417
- twitter?: TwitterCardSettings;
418
- }
419
- type SeoHealthStatus = 'excellent' | 'good' | 'fair' | 'poor' | 'missing';
420
- interface SeoHealthMetrics {
421
- score: number;
422
- status: SeoHealthStatus;
423
- issues: string[];
424
- }
425
- interface DocumentWithSeoHealth {
426
- _id: string;
427
- _type: string;
428
- title?: string;
429
- slug?: {
430
- current: string;
431
- };
432
- seo?: SeoFields;
433
- _updatedAt?: string;
434
- health: SeoHealthMetrics;
435
- }
436
- interface DeprecationWarning {
437
- key: string;
438
- version: string;
439
- changelogUrl: string;
440
- }
441
-
442
- interface SeoHealthDashboardProps {
443
- icon?: string;
444
- title?: string;
445
- description?: string;
446
- showTypeColumn?: boolean;
447
- showDocumentId?: boolean;
448
- /**
449
- * Limit the dashboard to specific document type names.
450
- * If both queryTypes and customQuery are provided, customQuery takes precedence.
451
- */
452
- queryTypes?: string[];
453
- /**
454
- * When using `queryTypes`, also filter by `seo != null`.
455
- * Set to `false` to include documents of those types even without an seo field.
456
- * Defaults to `true`.
457
- */
458
- queryRequireSeo?: boolean;
459
- /**
460
- * A fully custom GROQ query used to fetch documents.
461
- * Must return objects with at least: _id, _type, title, seo, _updatedAt
462
- * Takes precedence over queryTypes.
463
- */
464
- customQuery?: string;
465
- /**
466
- * The Sanity API version to use for the client (e.g. '2023-01-01').
467
- * Defaults to '2023-01-01'.
468
- */
469
- apiVersion?: string;
470
- /**
471
- * License key for the SEO Health Dashboard.
472
- * Obtain a key at https://sanity-plugin-seofields.thehardik.in
473
- */
474
- licenseKey?: string;
475
- /**
476
- * Map raw `_type` values to human-readable display labels used in the
477
- * Type column and the Type filter dropdown.
478
- * Any type without an entry falls back to the raw `_type` string.
479
- *
480
- * @example
481
- * typeDisplayLabels={{ productDrug: 'Products', singleCondition: 'Condition' }}
482
- */
483
- typeDisplayLabels?: Record<string, string>;
484
- /**
485
- * Controls how the type is rendered in the Type column.
486
- * - `'badge'` (default) — coloured pill, consistent with score badges
487
- * - `'text'` — plain text, useful for dense layouts
488
- */
489
- typeColumnMode?: 'badge' | 'text';
490
- /**
491
- * The document field to use as the display title.
492
- *
493
- * - `string` — use this field for every document type (e.g. `'name'`)
494
- * - `Record<string, string>` — per-type mapping; unmapped types fall back to `title`
495
- *
496
- * @example
497
- * // Same field for all types
498
- * titleField: 'name'
499
- *
500
- * @example
501
- * // Different field per type
502
- * titleField: { post: 'title', product: 'name', category: 'label' }
503
- */
504
- titleField?: string | Record<string, string>;
505
- /**
506
- * Callback function to render a custom badge next to the document title.
507
- * Receives the full document and should return badge data or undefined.
508
- *
509
- * @example
510
- * getDocumentBadge: (doc) => {
511
- * if (doc.services === 'NHS')
512
- * return { label: 'NHS', bgColor: '#e0f2fe', textColor: '#0369a1' }
513
- * if (doc.services === 'Private')
514
- * return { label: 'Private', bgColor: '#fef3c7', textColor: '#92400e' }
515
- * }
516
- */
517
- getDocumentBadge?: (doc: DocumentWithSeoHealth & Record<string, unknown>) => {
518
- label: string;
519
- bgColor?: string;
520
- textColor?: string;
521
- fontSize?: string;
522
- } | undefined;
523
- /**
524
- * Custom text shown while the license key is being verified.
525
- * Defaults to `"Verifying license…"`.
526
- */
527
- loadingLicense?: ReactNode;
528
- /**
529
- * Custom text shown while documents are being fetched.
530
- * Defaults to `"Loading documents…"`.
531
- */
532
- loadingDocuments?: ReactNode;
533
- /**
534
- * Custom text shown when the query returns zero results.
535
- * Defaults to `"No documents found"`.
536
- */
537
- noDocuments?: ReactNode;
538
- /**
539
- * Enable preview/demo mode to show dummy data.
540
- * Useful for testing, documentation, or showcasing the dashboard.
541
- * When enabled, displays realistic sample documents with various SEO scores.
542
- * Defaults to `false`.
543
- */
544
- previewMode?: boolean;
545
- /**
546
- * When `true`, clicking a document title opens the document editor as a split
547
- * pane to the right, keeping the SEO Health pane visible on the left.
548
- * This uses Sanity's pane router and requires the component to be rendered
549
- * inside a desk-structure pane context (i.e. via `createSeoHealthPane`).
550
- *
551
- * When `false` (default), clicking navigates to the document via the standard
552
- * intent-link system (full navigation).
553
- *
554
- * This is set to `true` automatically by `createSeoHealthPane`.
555
- */
556
- openInPane?: boolean;
557
- /**
558
- * The `name` of the Sanity structure tool that contains the monitored documents.
559
- * When provided, clicking a document title navigates directly to that tool's
560
- * intent URL (`/{basePath}/{structureTool}/intent/edit/id=…;type=…/`) instead of
561
- * using the generic intent resolver, which always picks the first registered tool.
562
- *
563
- * Required when you have multiple structure tools and the documents live in a
564
- * non-default one (e.g. `name: 'common'`).
565
- *
566
- * @example
567
- * structureTool: 'common'
568
- */
569
- structureTool?: string;
570
- /**
571
- * @internal — populated by the plugin when deprecated config keys are detected.
572
- * Each entry carries the migration hint, the version it was deprecated in, and
573
- * the matching changelog URL so the banner can group warnings by release.
574
- */
575
- _deprecationWarnings?: DeprecationWarning[];
576
- /**
577
- * Whether the export (CSV / JSON) feature is enabled.
578
- * Defaults to `true`.
579
- */
580
- exportEnabled?: boolean;
581
- /**
582
- * Which export formats to offer.
583
- * Defaults to `['csv', 'json']`.
584
- */
585
- exportFormats?: Array<'csv' | 'json'>;
586
- /**
587
- * Render compact inline stat pills in the header instead of the full stats grid.
588
- * Defaults to `false`.
589
- */
590
- compactStats?: boolean;
591
- }
592
-
593
- /**
594
- * Options accepted by `createSeoHealthPane`.
595
- * All props from `SeoHealthDashboardProps` are supported.
596
- *
597
- * `licenseKey` is **required** — the dashboard will not render without it.
598
- */
599
- interface SeoHealthPaneOptions extends Omit<SeoHealthDashboardProps, 'customQuery'> {
600
- /** Required license key (format: `SEOF-XXXX-XXXX-XXXX`). */
601
- licenseKey: string;
602
- /**
603
- * A fully custom GROQ query used to fetch documents for the dashboard.
604
- * The query must return documents with at least: `_id`, `_type`, `title`, `seo`, `_updatedAt`.
605
- *
606
- * Takes precedence over `queryTypes` when both are provided.
607
- *
608
- * @example
609
- * query: `*[_type in ["post","page"] && defined(seo)]{ _id, _type, title, slug, seo, _updatedAt }`
610
- */
611
- query?: string;
46
+ interface SeoDocument {
47
+ seo?: Record<string, unknown>;
612
48
  }
613
- /**
614
- * Creates a desk-structure pane for the SEO Health Dashboard.
615
- *
616
- * Returns a **`ComponentBuilder`** with a built-in `.child()` resolver so that
617
- * clicking any document row opens the document editor as a split pane to the right.
618
- *
619
- * Use it **directly** as the `.child()` value — do **not** wrap it in `S.component()`.
620
- *
621
- * ```ts
622
- * // sanity.config.ts
623
- * structure: (S) =>
624
- * S.list().items([
625
- * S.listItem()
626
- * .title('SEO Health')
627
- * .child(
628
- * createSeoHealthPane(S, {
629
- * licenseKey: 'SEOF-XXXX-XXXX-XXXX',
630
- * query: `*[_type == "post" && defined(seo)]{ _id, _type, title, slug, seo, _updatedAt }`,
631
- * })
632
- * ),
633
- * ])
634
- * ```
635
- */
636
- declare function createSeoHealthPane(optionsOrS: StructureBuilder, optionsWhenS: SeoHealthPaneOptions): ComponentBuilder;
49
+ declare const calculateHealthScore: (doc: SeoDocument) => SeoHealthMetrics;
637
50
 
638
- export { type AllFieldKeys, DocumentWithSeoHealth$1 as DocumentWithSeoHealth, type FieldVisibilityConfig, type SeoFieldConfig, type SeoFieldGroup, type SeoFieldKeys, type SeoFieldsPluginConfig, type SeoHealthPaneOptions, type SeoObjectFieldName, type ValidHiddenFieldKeys, types as allSchemas, baseMeta as baseMetaSchema, createSeoHealthPane, seofields as default, _default$2 as metaAttributeSchema, _default$1 as metaTagSchema, type openGraphFieldKeys, openGraph as openGraphSchema, _default as robotsSchema, seoFieldsSchema, type twitterFieldKeys, twitter as twitterSchema };
51
+ export { SeoFieldsPluginConfig, SeoHealthMetrics, types as allSchemas, baseMeta as baseMetaSchema, calculateHealthScore, seofields as default, _default$2 as metaAttributeSchema, _default$1 as metaTagSchema, openGraph as openGraphSchema, _default as robotsSchema, seoFieldsSchema, twitter as twitterSchema };