@pagebridge/sanity 0.0.1

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 (38) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/dist/components/RefreshQueueTool.d.ts +2 -0
  5. package/dist/components/RefreshQueueTool.d.ts.map +1 -0
  6. package/dist/components/RefreshQueueTool.js +55 -0
  7. package/dist/components/SearchPerformancePane.d.ts +6 -0
  8. package/dist/components/SearchPerformancePane.d.ts.map +1 -0
  9. package/dist/components/SearchPerformancePane.js +80 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +4 -0
  13. package/dist/plugin.d.ts +40 -0
  14. package/dist/plugin.d.ts.map +1 -0
  15. package/dist/plugin.js +89 -0
  16. package/dist/schemas/gscRefreshTask.d.ts +24 -0
  17. package/dist/schemas/gscRefreshTask.d.ts.map +1 -0
  18. package/dist/schemas/gscRefreshTask.js +196 -0
  19. package/dist/schemas/gscSite.d.ts +7 -0
  20. package/dist/schemas/gscSite.d.ts.map +1 -0
  21. package/dist/schemas/gscSite.js +68 -0
  22. package/dist/schemas/gscSnapshot.d.ts +22 -0
  23. package/dist/schemas/gscSnapshot.d.ts.map +1 -0
  24. package/dist/schemas/gscSnapshot.js +124 -0
  25. package/dist/schemas/index.d.ts +4 -0
  26. package/dist/schemas/index.d.ts.map +1 -0
  27. package/dist/schemas/index.js +3 -0
  28. package/eslint.config.js +3 -0
  29. package/package.json +50 -0
  30. package/src/components/RefreshQueueTool.tsx +196 -0
  31. package/src/components/SearchPerformancePane.tsx +237 -0
  32. package/src/index.ts +18 -0
  33. package/src/plugin.ts +115 -0
  34. package/src/schemas/gscRefreshTask.ts +203 -0
  35. package/src/schemas/gscSite.ts +71 -0
  36. package/src/schemas/gscSnapshot.ts +131 -0
  37. package/src/schemas/index.ts +11 -0
  38. package/tsconfig.json +23 -0
@@ -0,0 +1,196 @@
1
+ import { defineType, defineField } from "sanity";
2
+ export const createGscRefreshTask = (options = {}) => {
3
+ const contentTypes = options.contentTypes ?? [];
4
+ return defineType({
5
+ name: "gscRefreshTask",
6
+ title: "Refresh Task",
7
+ type: "document",
8
+ fields: [
9
+ defineField({
10
+ name: "site",
11
+ type: "reference",
12
+ to: [{ type: "gscSite" }],
13
+ validation: (Rule) => Rule.required(),
14
+ }),
15
+ ...(contentTypes.length > 0
16
+ ? [
17
+ defineField({
18
+ name: "linkedDocument",
19
+ title: "Content to Refresh",
20
+ type: "reference",
21
+ to: contentTypes.map((type) => ({ type })),
22
+ validation: (Rule) => Rule.required(),
23
+ }),
24
+ ]
25
+ : []),
26
+ defineField({
27
+ name: "reason",
28
+ title: "Reason",
29
+ type: "string",
30
+ options: {
31
+ list: [
32
+ { title: "Position Decay", value: "position_decay" },
33
+ { title: "Low CTR", value: "low_ctr" },
34
+ { title: "Impressions Drop", value: "impressions_drop" },
35
+ { title: "Manual", value: "manual" },
36
+ ],
37
+ },
38
+ }),
39
+ defineField({
40
+ name: "severity",
41
+ title: "Severity",
42
+ type: "string",
43
+ options: {
44
+ list: [
45
+ { title: "Low", value: "low" },
46
+ { title: "Medium", value: "medium" },
47
+ { title: "High", value: "high" },
48
+ ],
49
+ },
50
+ }),
51
+ defineField({
52
+ name: "status",
53
+ title: "Status",
54
+ type: "string",
55
+ options: {
56
+ list: [
57
+ { title: "Open", value: "open" },
58
+ { title: "Snoozed", value: "snoozed" },
59
+ { title: "In Progress", value: "in_progress" },
60
+ { title: "Done", value: "done" },
61
+ { title: "Dismissed", value: "dismissed" },
62
+ ],
63
+ },
64
+ initialValue: "open",
65
+ }),
66
+ defineField({
67
+ name: "snoozedUntil",
68
+ title: "Snoozed Until",
69
+ type: "datetime",
70
+ hidden: ({ parent }) => parent?.status !== "snoozed",
71
+ }),
72
+ defineField({
73
+ name: "metrics",
74
+ title: "Metrics at Detection",
75
+ type: "object",
76
+ fields: [
77
+ defineField({
78
+ name: "positionBefore",
79
+ type: "number",
80
+ title: "Position (28 days ago)",
81
+ }),
82
+ defineField({
83
+ name: "positionNow",
84
+ type: "number",
85
+ title: "Position (current)",
86
+ }),
87
+ defineField({
88
+ name: "positionDelta",
89
+ type: "number",
90
+ title: "Position Change",
91
+ }),
92
+ defineField({
93
+ name: "ctrBefore",
94
+ type: "number",
95
+ title: "CTR (28 days ago)",
96
+ }),
97
+ defineField({
98
+ name: "ctrNow",
99
+ type: "number",
100
+ title: "CTR (current)",
101
+ }),
102
+ defineField({
103
+ name: "impressions",
104
+ type: "number",
105
+ title: "Impressions (last 28d)",
106
+ }),
107
+ ],
108
+ }),
109
+ defineField({
110
+ name: "queryContext",
111
+ title: "Top Queries",
112
+ type: "array",
113
+ description: "What people searched to find this page",
114
+ of: [
115
+ {
116
+ type: "object",
117
+ fields: [
118
+ defineField({
119
+ name: "query",
120
+ type: "string",
121
+ title: "Query",
122
+ }),
123
+ defineField({
124
+ name: "impressions",
125
+ type: "number",
126
+ title: "Impressions",
127
+ }),
128
+ defineField({
129
+ name: "clicks",
130
+ type: "number",
131
+ title: "Clicks",
132
+ }),
133
+ defineField({
134
+ name: "position",
135
+ type: "number",
136
+ title: "Avg Position",
137
+ }),
138
+ ],
139
+ preview: {
140
+ select: {
141
+ query: "query",
142
+ impressions: "impressions",
143
+ position: "position",
144
+ },
145
+ prepare({ query, impressions, position }) {
146
+ return {
147
+ title: query,
148
+ subtitle: `Pos ${position?.toFixed(1)} · ${impressions} impr`,
149
+ };
150
+ },
151
+ },
152
+ },
153
+ ],
154
+ }),
155
+ defineField({
156
+ name: "notes",
157
+ title: "Notes",
158
+ type: "text",
159
+ description: "Why was this snoozed/dismissed?",
160
+ }),
161
+ defineField({
162
+ name: "createdAt",
163
+ type: "datetime",
164
+ }),
165
+ defineField({
166
+ name: "resolvedAt",
167
+ type: "datetime",
168
+ }),
169
+ ],
170
+ orderings: [
171
+ {
172
+ title: "Severity",
173
+ name: "severityDesc",
174
+ by: [
175
+ { field: "severity", direction: "desc" },
176
+ { field: "createdAt", direction: "desc" },
177
+ ],
178
+ },
179
+ ],
180
+ preview: {
181
+ select: {
182
+ title: "linkedDocument.title",
183
+ reason: "reason",
184
+ severity: "severity",
185
+ },
186
+ prepare({ title, reason, severity }) {
187
+ return {
188
+ title: title || "Unknown document",
189
+ subtitle: `${severity} · ${reason.replace(/_/g, " ")}`,
190
+ };
191
+ },
192
+ },
193
+ });
194
+ };
195
+ // Default export for backward compatibility (without linkedDocument)
196
+ export const gscRefreshTask = createGscRefreshTask();
@@ -0,0 +1,7 @@
1
+ export declare const gscSite: {
2
+ type: "document";
3
+ name: "gscSite";
4
+ } & Omit<import("sanity").DocumentDefinition, "preview"> & {
5
+ preview?: import("sanity").PreviewConfig<Record<string, string>, Record<never, any>> | undefined;
6
+ };
7
+ //# sourceMappingURL=gscSite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gscSite.d.ts","sourceRoot":"","sources":["../../src/schemas/gscSite.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO;;;;;CAoElB,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { defineType, defineField } from "sanity";
2
+ export const gscSite = defineType({
3
+ name: "gscSite",
4
+ title: "GSC Site",
5
+ type: "document",
6
+ fields: [
7
+ defineField({
8
+ name: "siteUrl",
9
+ title: "Site URL",
10
+ type: "string",
11
+ description: "Exactly as it appears in GSC (e.g., sc-domain:example.com)",
12
+ validation: (Rule) => Rule.required(),
13
+ }),
14
+ defineField({
15
+ name: "slug",
16
+ title: "Slug",
17
+ type: "slug",
18
+ options: { source: "siteUrl" },
19
+ description: "Used for filtering in multi-site setups",
20
+ }),
21
+ defineField({
22
+ name: "defaultLocale",
23
+ title: "Default Locale",
24
+ type: "string",
25
+ initialValue: "en",
26
+ }),
27
+ defineField({
28
+ name: "pathPrefix",
29
+ title: "Path Prefix",
30
+ type: "string",
31
+ description: 'If your blog lives at /blog, enter "/blog"',
32
+ }),
33
+ defineField({
34
+ name: "contentTypes",
35
+ title: "Content Types",
36
+ type: "array",
37
+ of: [{ type: "string" }],
38
+ initialValue: ["post", "page"],
39
+ description: "Sanity document types to match URLs against (e.g., post, page, article)",
40
+ }),
41
+ defineField({
42
+ name: "slugField",
43
+ title: "Slug Field",
44
+ type: "string",
45
+ initialValue: "slug",
46
+ description: "The field name containing the URL slug (default: slug)",
47
+ }),
48
+ defineField({
49
+ name: "lastSyncedAt",
50
+ title: "Last Synced",
51
+ type: "datetime",
52
+ readOnly: true,
53
+ }),
54
+ defineField({
55
+ name: "lastDiagnosticsAt",
56
+ title: "Last Diagnostics Run",
57
+ type: "datetime",
58
+ readOnly: true,
59
+ }),
60
+ defineField({
61
+ name: "unmatchedCount",
62
+ title: "Unmatched URLs",
63
+ type: "number",
64
+ readOnly: true,
65
+ description: "Number of GSC URLs that could not be matched to Sanity documents",
66
+ }),
67
+ ],
68
+ });
@@ -0,0 +1,22 @@
1
+ export interface GscSnapshotOptions {
2
+ contentTypes?: string[];
3
+ }
4
+ export declare const createGscSnapshot: (options?: GscSnapshotOptions) => {
5
+ type: "document";
6
+ name: "gscSnapshot";
7
+ } & Omit<import("sanity").DocumentDefinition, "preview"> & {
8
+ preview?: import("sanity").PreviewConfig<{
9
+ title: string;
10
+ subtitle: string;
11
+ }, Record<"title" | "subtitle", any>> | undefined;
12
+ };
13
+ export declare const gscSnapshot: {
14
+ type: "document";
15
+ name: "gscSnapshot";
16
+ } & Omit<import("sanity").DocumentDefinition, "preview"> & {
17
+ preview?: import("sanity").PreviewConfig<{
18
+ title: string;
19
+ subtitle: string;
20
+ }, Record<"title" | "subtitle", any>> | undefined;
21
+ };
22
+ //# sourceMappingURL=gscSnapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gscSnapshot.d.ts","sourceRoot":"","sources":["../../src/schemas/gscSnapshot.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,iBAAiB,GAAI,UAAS,kBAAuB;;;;;;;;CAyHjE,CAAC;AAGF,eAAO,MAAM,WAAW;;;;;;;;CAAsB,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { defineType, defineField } from "sanity";
2
+ export const createGscSnapshot = (options = {}) => {
3
+ const contentTypes = options.contentTypes ?? [];
4
+ return defineType({
5
+ name: "gscSnapshot",
6
+ title: "GSC Snapshot",
7
+ type: "document",
8
+ fields: [
9
+ defineField({
10
+ name: "site",
11
+ title: "Site",
12
+ type: "reference",
13
+ to: [{ type: "gscSite" }],
14
+ validation: (Rule) => Rule.required(),
15
+ }),
16
+ defineField({
17
+ name: "page",
18
+ title: "Page URL",
19
+ type: "string",
20
+ validation: (Rule) => Rule.required(),
21
+ }),
22
+ ...(contentTypes.length > 0
23
+ ? [
24
+ defineField({
25
+ name: "linkedDocument",
26
+ title: "Linked Document",
27
+ type: "reference",
28
+ to: contentTypes.map((type) => ({ type })),
29
+ description: "Auto-matched Sanity document",
30
+ }),
31
+ ]
32
+ : []),
33
+ defineField({
34
+ name: "period",
35
+ title: "Period",
36
+ type: "string",
37
+ options: {
38
+ list: ["last7", "last28", "last90"],
39
+ },
40
+ }),
41
+ defineField({
42
+ name: "clicks",
43
+ type: "number",
44
+ }),
45
+ defineField({
46
+ name: "impressions",
47
+ type: "number",
48
+ }),
49
+ defineField({
50
+ name: "ctr",
51
+ title: "CTR",
52
+ type: "number",
53
+ description: "Stored as decimal (0.05 = 5%)",
54
+ }),
55
+ defineField({
56
+ name: "position",
57
+ title: "Average Position",
58
+ type: "number",
59
+ }),
60
+ defineField({
61
+ name: "topQueries",
62
+ title: "Top Queries",
63
+ type: "array",
64
+ of: [
65
+ {
66
+ type: "object",
67
+ fields: [
68
+ defineField({ name: "query", type: "string" }),
69
+ defineField({ name: "clicks", type: "number" }),
70
+ defineField({ name: "impressions", type: "number" }),
71
+ defineField({ name: "position", type: "number" }),
72
+ ],
73
+ },
74
+ ],
75
+ }),
76
+ defineField({
77
+ name: "fetchedAt",
78
+ title: "Fetched At",
79
+ type: "datetime",
80
+ }),
81
+ defineField({
82
+ name: "indexStatus",
83
+ title: "Index Status",
84
+ type: "object",
85
+ fields: [
86
+ defineField({
87
+ name: "verdict",
88
+ title: "Verdict",
89
+ type: "string",
90
+ options: {
91
+ list: ["indexed", "not_indexed", "excluded"],
92
+ },
93
+ }),
94
+ defineField({
95
+ name: "coverageState",
96
+ title: "Coverage State",
97
+ type: "string",
98
+ description: "Human-readable index status from Google",
99
+ }),
100
+ defineField({
101
+ name: "lastCrawlTime",
102
+ title: "Last Crawl Time",
103
+ type: "datetime",
104
+ }),
105
+ defineField({
106
+ name: "robotsTxtState",
107
+ title: "Robots.txt State",
108
+ type: "string",
109
+ }),
110
+ defineField({
111
+ name: "pageFetchState",
112
+ title: "Page Fetch State",
113
+ type: "string",
114
+ }),
115
+ ],
116
+ }),
117
+ ],
118
+ preview: {
119
+ select: { title: "page", subtitle: "period" },
120
+ },
121
+ });
122
+ };
123
+ // Default export for backward compatibility (without linkedDocument)
124
+ export const gscSnapshot = createGscSnapshot();
@@ -0,0 +1,4 @@
1
+ export { gscSite } from "./gscSite";
2
+ export { gscSnapshot, createGscSnapshot, type GscSnapshotOptions, } from "./gscSnapshot";
3
+ export { gscRefreshTask, createGscRefreshTask, type GscRefreshTaskOptions, } from "./gscRefreshTask";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { gscSite } from "./gscSite";
2
+ export { gscSnapshot, createGscSnapshot, } from "./gscSnapshot";
3
+ export { gscRefreshTask, createGscRefreshTask, } from "./gscRefreshTask";
@@ -0,0 +1,3 @@
1
+ import { config } from "@pagebridge/eslint-config/react-internal";
2
+
3
+ export default [...config];
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@pagebridge/sanity",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./schemas": {
14
+ "types": "./dist/schemas/index.d.ts",
15
+ "import": "./dist/schemas/index.js",
16
+ "require": "./dist/schemas/index.js",
17
+ "default": "./dist/schemas/index.js"
18
+ }
19
+ },
20
+ "peerDependencies": {
21
+ "sanity": "^5.0.0",
22
+ "@sanity/ui": "^3.0.0",
23
+ "@sanity/icons": "^3.0.0",
24
+ "react": "^18 || ^19",
25
+ "react-dom": "^18 || ^19"
26
+ },
27
+ "dependencies": {
28
+ "@swc/helpers": "^0.5.18"
29
+ },
30
+ "devDependencies": {
31
+ "sanity": "^5.7.0",
32
+ "@sanity/ui": "^3.1.11",
33
+ "@sanity/icons": "^3.7.4",
34
+ "react": "^19.2.3",
35
+ "react-dom": "^19.2.3",
36
+ "@types/node": "^22.15.3",
37
+ "@types/react": "19.2.3",
38
+ "@types/react-dom": "19.2.3",
39
+ "eslint": "^9.39.1",
40
+ "typescript": "^5.9.3",
41
+ "@pagebridge/eslint-config": "^0.0.0",
42
+ "@pagebridge/typescript-config": "^0.0.0"
43
+ },
44
+ "scripts": {
45
+ "build": "pnpm exec tsc",
46
+ "dev": "pnpm exec tsc --watch",
47
+ "check-types": "pnpm exec tsc --noEmit",
48
+ "lint": "eslint ."
49
+ }
50
+ }