@sanity/ailf 4.1.0 → 4.3.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.
- package/config/package-surface.ts +37 -0
- package/config/preflight-scoring.ts +26 -0
- package/dist/_vendor/ailf-core/artifact-registry.d.ts +1 -1
- package/dist/_vendor/ailf-core/artifact-registry.js +47 -0
- package/dist/_vendor/ailf-core/config-helpers.d.ts +35 -0
- package/dist/_vendor/ailf-core/config-helpers.js +67 -0
- package/dist/_vendor/ailf-core/index.d.ts +1 -1
- package/dist/_vendor/ailf-core/index.js +1 -1
- package/dist/_vendor/ailf-core/ports/context.d.ts +18 -0
- package/dist/_vendor/ailf-core/ports/doc-fetcher.d.ts +30 -0
- package/dist/_vendor/ailf-core/ports/index.d.ts +3 -1
- package/dist/_vendor/ailf-core/ports/index.js +1 -0
- package/dist/_vendor/ailf-core/ports/mode-handler.d.ts +23 -0
- package/dist/_vendor/ailf-core/ports/package-surface-resolver.d.ts +71 -0
- package/dist/_vendor/ailf-core/ports/package-surface-resolver.js +36 -0
- package/dist/_vendor/ailf-core/schemas/eval-config.d.ts +6 -0
- package/dist/_vendor/ailf-core/schemas/eval-config.js +14 -0
- package/dist/_vendor/ailf-core/schemas/index.d.ts +1 -0
- package/dist/_vendor/ailf-core/schemas/index.js +1 -0
- package/dist/_vendor/ailf-core/schemas/symbol-preflight-report.d.ts +51 -0
- package/dist/_vendor/ailf-core/schemas/symbol-preflight-report.js +57 -0
- package/dist/_vendor/ailf-core/types/generalized-task.d.ts +20 -3
- package/dist/_vendor/ailf-core/types/index.d.ts +13 -1
- package/dist/_vendor/ailf-core/types/index.js +1 -0
- package/dist/_vendor/ailf-core/types/package-surface.d.ts +36 -0
- package/dist/_vendor/ailf-core/types/package-surface.js +13 -0
- package/dist/_vendor/ailf-core/types/preflight-scoring.d.ts +52 -0
- package/dist/_vendor/ailf-core/types/preflight-scoring.js +18 -0
- package/dist/_vendor/ailf-core/types/repo-config.d.ts +14 -0
- package/dist/_vendor/ailf-core/types/symbol-preflight-report.d.ts +66 -0
- package/dist/_vendor/ailf-core/types/symbol-preflight-report.js +25 -0
- package/dist/adapters/config-sources/file-config-adapter.js +1 -0
- package/dist/adapters/doc-fetchers/sanity-doc-fetcher.d.ts +25 -5
- package/dist/adapters/doc-fetchers/sanity-doc-fetcher.js +276 -95
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/package-surface/dts-package-surface.d.ts +46 -0
- package/dist/adapters/package-surface/dts-package-surface.js +173 -0
- package/dist/adapters/package-surface/in-memory-package-surface.d.ts +15 -0
- package/dist/adapters/package-surface/in-memory-package-surface.js +28 -0
- package/dist/adapters/package-surface/index.d.ts +9 -0
- package/dist/adapters/package-surface/index.js +8 -0
- package/dist/adapters/package-surface/parse-dts-exports.d.ts +31 -0
- package/dist/adapters/package-surface/parse-dts-exports.js +54 -0
- package/dist/adapters/task-sources/repo-schemas.d.ts +22 -0
- package/dist/adapters/task-sources/repo-schemas.js +93 -1
- package/dist/adapters/task-sources/repo-task-source.js +11 -2
- package/dist/commands/pipeline-action.d.ts +2 -0
- package/dist/commands/pipeline-action.js +12 -0
- package/dist/commands/remote-pipeline.js +9 -2
- package/dist/commands/remote-results.d.ts +12 -1
- package/dist/commands/remote-results.js +25 -5
- package/dist/commands/validate-tasks.js +8 -2
- package/dist/composition-root.js +9 -0
- package/dist/config/package-surface.ts +37 -0
- package/dist/config/preflight-scoring.ts +26 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/orchestration/build-app-context.js +1 -0
- package/dist/orchestration/pipeline-orchestrator.d.ts +19 -1
- package/dist/orchestration/pipeline-orchestrator.js +38 -0
- package/dist/orchestration/steps/calculate-scores-step.js +11 -0
- package/dist/orchestration/steps/generate-configs-step.js +16 -1
- package/dist/orchestration/steps/run-eval-step.js +27 -0
- package/dist/pipeline/calculate-scores.d.ts +66 -5
- package/dist/pipeline/calculate-scores.js +141 -27
- package/dist/pipeline/compiler/index.d.ts +1 -1
- package/dist/pipeline/compiler/index.js +1 -1
- package/dist/pipeline/compiler/literacy-bridge.d.ts +9 -0
- package/dist/pipeline/compiler/literacy-bridge.js +2 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/agent-harness-example-tasks.js +0 -12
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/knowledge-probe-example-tasks.js +0 -12
- package/dist/pipeline/compiler/mode-handlers/literacy/assertions.d.ts +1 -1
- package/dist/pipeline/compiler/mode-handlers/literacy/assertions.js +31 -4
- package/dist/pipeline/compiler/mode-handlers/literacy/compiler.js +190 -6
- package/dist/pipeline/compiler/mode-handlers/literacy/index.js +2 -0
- package/dist/pipeline/compiler/mode-handlers/literacy/types.d.ts +17 -2
- package/dist/pipeline/compiler/rubric-resolution.d.ts +17 -1
- package/dist/pipeline/compiler/rubric-resolution.js +78 -2
- package/dist/pipeline/compiler/scoring-bridge.d.ts +49 -2
- package/dist/pipeline/compiler/scoring-bridge.js +104 -10
- package/dist/pipeline/eval-fingerprint.d.ts +9 -0
- package/dist/pipeline/eval-fingerprint.js +7 -1
- package/dist/pipeline/preflight/compute-preflight.d.ts +67 -0
- package/dist/pipeline/preflight/compute-preflight.js +118 -0
- package/dist/pipeline/preflight/emit-symbol-preflight.d.ts +51 -0
- package/dist/pipeline/preflight/emit-symbol-preflight.js +102 -0
- package/dist/pipeline/preflight/load-package-surface.d.ts +14 -0
- package/dist/pipeline/preflight/load-package-surface.js +19 -0
- package/dist/pipeline/preflight/load-preflight-context.d.ts +13 -0
- package/dist/pipeline/preflight/load-preflight-context.js +25 -0
- package/dist/pipeline/preflight/load-preflight-scoring.d.ts +12 -0
- package/dist/pipeline/preflight/load-preflight-scoring.js +17 -0
- package/dist/pipeline/preflight/parse-imports.d.ts +62 -0
- package/dist/pipeline/preflight/parse-imports.js +125 -0
- package/dist/report-store.d.ts +8 -0
- package/dist/report-store.js +55 -6
- package/dist/sanity/document-renderers.d.ts +106 -0
- package/dist/sanity/document-renderers.js +307 -0
- package/dist/sanity/queries.d.ts +32 -11
- package/dist/sanity/queries.js +78 -0
- package/dist/sanity/symbol-index.d.ts +98 -0
- package/dist/sanity/symbol-index.js +615 -0
- package/dist/tasks/knowledge-probe/define-type-api.task.ts +2 -6
- package/dist/tasks/knowledge-probe/groq-projections.task.ts +0 -5
- package/dist/tasks/literacy/content-lake.task.ts +4 -10
- package/dist/tasks/literacy/frameworks.task.ts +2 -8
- package/dist/tasks/literacy/functions.task.ts +1 -4
- package/dist/tasks/literacy/groq.task.ts +3 -12
- package/dist/tasks/literacy/image-handling.task.ts +1 -4
- package/dist/tasks/literacy/nextjs-live.task.ts +1 -4
- package/dist/tasks/literacy/portable-text.task.ts +2 -8
- package/dist/tasks/literacy/studio-setup.task.ts +2 -8
- package/dist/tasks/literacy/visual-editing.task.ts +2 -8
- package/package.json +2 -1
- package/tasks/knowledge-probe/define-type-api.task.ts +2 -6
- package/tasks/knowledge-probe/groq-projections.task.ts +0 -5
- package/tasks/literacy/content-lake.task.ts +4 -10
- package/tasks/literacy/frameworks.task.ts +2 -8
- package/tasks/literacy/functions.task.ts +1 -4
- package/tasks/literacy/groq.task.ts +3 -12
- package/tasks/literacy/image-handling.task.ts +1 -4
- package/tasks/literacy/nextjs-live.task.ts +1 -4
- package/tasks/literacy/portable-text.task.ts +2 -8
- package/tasks/literacy/studio-setup.task.ts +2 -8
- package/tasks/literacy/visual-editing.task.ts +2 -8
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* document-renderers.ts
|
|
3
|
+
*
|
|
4
|
+
* Renderer registry that turns a Sanity document into both:
|
|
5
|
+
*
|
|
6
|
+
* - Markdown content for inclusion in a literacy task's grader/candidate
|
|
7
|
+
* context (existing surface, used by the doc fetcher).
|
|
8
|
+
* - A symbol-reference index for the W0197 grader-context pathway —
|
|
9
|
+
* a flat list of identifiers the doc legitimizes, with provenance.
|
|
10
|
+
* The grader prefers this over the rendered markdown when available
|
|
11
|
+
* (smaller, deterministic, harder for the grader's prior to override).
|
|
12
|
+
*
|
|
13
|
+
* Both surfaces dispatch through the same registry. Articles and
|
|
14
|
+
* typesReference docs have hand-written renderers; everything else falls
|
|
15
|
+
* through to the default walker. This keeps "what to do with a document
|
|
16
|
+
* of type X" a single decision point regardless of whether the doc was
|
|
17
|
+
* looked up by slug, path, perspective, or id.
|
|
18
|
+
*
|
|
19
|
+
* Two tiers of fidelity (rendered output):
|
|
20
|
+
*
|
|
21
|
+
* 1. Registered renderers (high fidelity) — `article`, `typesReference`.
|
|
22
|
+
* Hand-written for the document shapes we care about most.
|
|
23
|
+
* 2. Default renderer (best effort) — walks the doc, flattens portable-text
|
|
24
|
+
* fields, surfaces top-level scalars, follows references one level deep,
|
|
25
|
+
* skips framework-internal fields. Lets pinning a `marketingPage`,
|
|
26
|
+
* `glossaryEntry`, etc. work without AILF code changes.
|
|
27
|
+
*
|
|
28
|
+
* Adding a new high-fidelity renderer: implement a `DocumentRenderer`
|
|
29
|
+
* (both `render` and `extractSymbols`) and register it in
|
|
30
|
+
* `BUILT_IN_RENDERERS` keyed by `_type`.
|
|
31
|
+
*/
|
|
32
|
+
import { toMarkdown } from "./portable-text.js";
|
|
33
|
+
import { extractSymbolIndex, extractSymbolsFromTypedoc, mergeSymbolIndexes, } from "./symbol-index.js";
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Helpers
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
const DEFAULT_MAX_BYTES = 30_000;
|
|
38
|
+
const SKIP_FIELDS = new Set([
|
|
39
|
+
"_createdAt",
|
|
40
|
+
"_id",
|
|
41
|
+
"_rev",
|
|
42
|
+
"_system",
|
|
43
|
+
"_type",
|
|
44
|
+
"_updatedAt",
|
|
45
|
+
"_key",
|
|
46
|
+
]);
|
|
47
|
+
function isPortableTextArray(value) {
|
|
48
|
+
return (Array.isArray(value) &&
|
|
49
|
+
value.length > 0 &&
|
|
50
|
+
typeof value[0] === "object" &&
|
|
51
|
+
value[0] !== null &&
|
|
52
|
+
"_type" in value[0]);
|
|
53
|
+
}
|
|
54
|
+
function truncate(content, max) {
|
|
55
|
+
if (content.length <= max)
|
|
56
|
+
return content;
|
|
57
|
+
return (content.slice(0, max) +
|
|
58
|
+
`\n\n*[content truncated — ${content.length - max} more bytes omitted]*`);
|
|
59
|
+
}
|
|
60
|
+
function slugForDoc(doc) {
|
|
61
|
+
const slugField = doc.slug;
|
|
62
|
+
if (typeof slugField === "object" &&
|
|
63
|
+
slugField !== null &&
|
|
64
|
+
"current" in slugField &&
|
|
65
|
+
typeof slugField.current === "string") {
|
|
66
|
+
return slugField.current;
|
|
67
|
+
}
|
|
68
|
+
if (typeof slugField === "string")
|
|
69
|
+
return slugField;
|
|
70
|
+
return `${doc._type}:${doc._id}`;
|
|
71
|
+
}
|
|
72
|
+
function renderArticle(doc) {
|
|
73
|
+
const title = doc.title ?? "(untitled)";
|
|
74
|
+
const description = doc.description;
|
|
75
|
+
const section = doc.section;
|
|
76
|
+
const content = doc.content;
|
|
77
|
+
const sectionLabel = section?.title ? `Section: ${section.title}\n` : "";
|
|
78
|
+
const desc = description ? `${description}\n\n` : "";
|
|
79
|
+
const markdown = toMarkdown(content ?? []);
|
|
80
|
+
return {
|
|
81
|
+
content: `## ${title}\n\n${sectionLabel}${desc}${markdown}`,
|
|
82
|
+
fidelity: "high",
|
|
83
|
+
slug: slugForDoc(doc),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const articleRenderer = {
|
|
87
|
+
render: renderArticle,
|
|
88
|
+
extractSymbols(doc) {
|
|
89
|
+
const content = doc.content;
|
|
90
|
+
if (!Array.isArray(content))
|
|
91
|
+
return { symbols: [] };
|
|
92
|
+
return extractSymbolIndex(content);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
async function renderTypesReference(doc, ctx) {
|
|
96
|
+
const title = doc.title ?? "(untitled)";
|
|
97
|
+
const slug = slugForDoc(doc);
|
|
98
|
+
const library = doc.library;
|
|
99
|
+
const latestVersion = doc.latestVersion;
|
|
100
|
+
const asset = latestVersion?.attachment?.asset;
|
|
101
|
+
const max = ctx.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
102
|
+
const lines = [];
|
|
103
|
+
lines.push(`## ${title}`, "");
|
|
104
|
+
lines.push(`Slug: \`${slug}\``);
|
|
105
|
+
if (library?.npmName)
|
|
106
|
+
lines.push(`Package: \`${library.npmName}\``);
|
|
107
|
+
if (latestVersion?.semver)
|
|
108
|
+
lines.push(`Version: \`${latestVersion.semver}\``);
|
|
109
|
+
if (latestVersion?.date)
|
|
110
|
+
lines.push(`Released: ${latestVersion.date}`);
|
|
111
|
+
lines.push("");
|
|
112
|
+
if (!asset?.url) {
|
|
113
|
+
lines.push("*[no attached type definitions found — resolver could not locate `latestVersion.attachment.asset.url`]*");
|
|
114
|
+
return { content: lines.join("\n"), fidelity: "high", slug };
|
|
115
|
+
}
|
|
116
|
+
if (!ctx.fetchUrl) {
|
|
117
|
+
lines.push(`*[type definitions live at ${asset.url} — fetcher not configured to retrieve attachments]*`);
|
|
118
|
+
return { content: lines.join("\n"), fidelity: "high", slug };
|
|
119
|
+
}
|
|
120
|
+
const body = await ctx.fetchUrl(asset.url);
|
|
121
|
+
if (body === null) {
|
|
122
|
+
lines.push(`*[failed to fetch type definitions from ${asset.url}]*`);
|
|
123
|
+
return { content: lines.join("\n"), fidelity: "high", slug };
|
|
124
|
+
}
|
|
125
|
+
const filename = asset.originalFilename ?? "types.json";
|
|
126
|
+
const lang = filename.endsWith(".json") ? "json" : "";
|
|
127
|
+
lines.push(`### Type definitions (${filename})`, "");
|
|
128
|
+
lines.push("```" + lang);
|
|
129
|
+
// Reserve ~200 bytes for the fence + trailing notice
|
|
130
|
+
lines.push(truncate(body, Math.max(0, max - 200)));
|
|
131
|
+
lines.push("```");
|
|
132
|
+
return { content: lines.join("\n"), fidelity: "high", slug };
|
|
133
|
+
}
|
|
134
|
+
async function extractTypesReferenceSymbols(doc, ctx) {
|
|
135
|
+
const library = doc.library;
|
|
136
|
+
const latestVersion = doc.latestVersion;
|
|
137
|
+
const asset = latestVersion?.attachment?.asset;
|
|
138
|
+
// Re-fetches the same URL the renderer would; bounded to ≤ a handful of
|
|
139
|
+
// typesReference docs per eval run, so the duplicate I/O is acceptable.
|
|
140
|
+
// Fold into a single registry method later if profiling shows it bites.
|
|
141
|
+
if (!asset?.url || !ctx.fetchUrl)
|
|
142
|
+
return { symbols: [] };
|
|
143
|
+
const body = await ctx.fetchUrl(asset.url);
|
|
144
|
+
if (body === null)
|
|
145
|
+
return { symbols: [] };
|
|
146
|
+
return extractSymbolsFromTypedoc(body, library?.npmName);
|
|
147
|
+
}
|
|
148
|
+
const typesReferenceRenderer = {
|
|
149
|
+
render: renderTypesReference,
|
|
150
|
+
extractSymbols: extractTypesReferenceSymbols,
|
|
151
|
+
};
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// formatDefault — generic walker for any unknown `_type`.
|
|
154
|
+
//
|
|
155
|
+
// Walks top-level fields, renders portable-text arrays as Markdown, surfaces
|
|
156
|
+
// scalars as labeled key/value lines, and follows resolved references one
|
|
157
|
+
// level deep. The output is "best effort" — not as tight as a hand-written
|
|
158
|
+
// renderer, but feeds the LLM grader real content for any pinned doc type.
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
function renderScalar(value) {
|
|
161
|
+
if (typeof value === "string")
|
|
162
|
+
return value;
|
|
163
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
164
|
+
return String(value);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
function renderField(key, value, depth = 0) {
|
|
169
|
+
if (value === null || value === undefined)
|
|
170
|
+
return null;
|
|
171
|
+
if (SKIP_FIELDS.has(key))
|
|
172
|
+
return null;
|
|
173
|
+
const indent = " ".repeat(depth);
|
|
174
|
+
if (isPortableTextArray(value)) {
|
|
175
|
+
const md = toMarkdown(value);
|
|
176
|
+
if (!md.trim())
|
|
177
|
+
return null;
|
|
178
|
+
return `${indent}**${key}:**\n\n${md}`;
|
|
179
|
+
}
|
|
180
|
+
// Sanity slug field: { _type: "slug", current: "…" }
|
|
181
|
+
if (typeof value === "object" &&
|
|
182
|
+
value !== null &&
|
|
183
|
+
!Array.isArray(value) &&
|
|
184
|
+
value._type === "slug") {
|
|
185
|
+
const current = value.current;
|
|
186
|
+
if (typeof current === "string")
|
|
187
|
+
return `${indent}**${key}:** \`${current}\``;
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const scalar = renderScalar(value);
|
|
191
|
+
if (scalar !== null)
|
|
192
|
+
return `${indent}**${key}:** ${scalar}`;
|
|
193
|
+
// Single-level deref: a previously-resolved reference shows up as an
|
|
194
|
+
// object with its own `_id`/`_type` (the projection joined it in). Walk
|
|
195
|
+
// its fields one level deep.
|
|
196
|
+
if (typeof value === "object" &&
|
|
197
|
+
!Array.isArray(value) &&
|
|
198
|
+
depth === 0 &&
|
|
199
|
+
value._id !== undefined) {
|
|
200
|
+
const inner = Object.entries(value)
|
|
201
|
+
.map(([k, v]) => renderField(k, v, depth + 1))
|
|
202
|
+
.filter((line) => line !== null);
|
|
203
|
+
if (inner.length === 0)
|
|
204
|
+
return null;
|
|
205
|
+
return `${indent}**${key}:**\n${inner.join("\n")}`;
|
|
206
|
+
}
|
|
207
|
+
if (Array.isArray(value)) {
|
|
208
|
+
const items = value
|
|
209
|
+
.map((item, i) => renderField(String(i), item, depth + 1))
|
|
210
|
+
.filter((line) => line !== null);
|
|
211
|
+
if (items.length === 0)
|
|
212
|
+
return null;
|
|
213
|
+
return `${indent}**${key}:**\n${items.join("\n")}`;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
function renderDefault(doc) {
|
|
218
|
+
const title = doc.title ?? `(${doc._type})`;
|
|
219
|
+
const slug = slugForDoc(doc);
|
|
220
|
+
const lines = [`## ${title}`, "", `Type: \`${doc._type}\``];
|
|
221
|
+
if (slug !== `${doc._type}:${doc._id}`)
|
|
222
|
+
lines.push(`Slug: \`${slug}\``);
|
|
223
|
+
lines.push("");
|
|
224
|
+
const fieldLines = [];
|
|
225
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
226
|
+
if (key === "title")
|
|
227
|
+
continue;
|
|
228
|
+
if (key === "slug" && slug !== `${doc._type}:${doc._id}`)
|
|
229
|
+
continue;
|
|
230
|
+
const rendered = renderField(key, value);
|
|
231
|
+
if (rendered !== null)
|
|
232
|
+
fieldLines.push(rendered);
|
|
233
|
+
}
|
|
234
|
+
if (fieldLines.length > 0) {
|
|
235
|
+
lines.push(...fieldLines);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
lines.push("*[no renderable content — all fields were null, framework metadata, or unrecognized shapes]*");
|
|
239
|
+
}
|
|
240
|
+
return { content: lines.join("\n"), fidelity: "default", slug };
|
|
241
|
+
}
|
|
242
|
+
function extractDefaultSymbols(doc) {
|
|
243
|
+
// For unknown types we don't know the doc's intent — but if it has any
|
|
244
|
+
// Portable Text fields, those probably contain prose-with-inline-code
|
|
245
|
+
// that names symbols. Walk top-level fields, run the PT extractor on
|
|
246
|
+
// each PT array, and tag each extracted entry with the originating
|
|
247
|
+
// field name so reviewers can trace which field a symbol came from.
|
|
248
|
+
// Returns empty for shapes with no PT content (purely scalar docs like
|
|
249
|
+
// `marketingPage`); caller falls back to the rendered markdown.
|
|
250
|
+
const indexes = [];
|
|
251
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
252
|
+
if (SKIP_FIELDS.has(key))
|
|
253
|
+
continue;
|
|
254
|
+
if (isPortableTextArray(value)) {
|
|
255
|
+
indexes.push(tagWithFieldName(extractSymbolIndex(value), key));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return mergeSymbolIndexes(indexes);
|
|
259
|
+
}
|
|
260
|
+
function tagWithFieldName(index, fieldName) {
|
|
261
|
+
return {
|
|
262
|
+
symbols: index.symbols.map((entry) => ({
|
|
263
|
+
symbol: entry.symbol,
|
|
264
|
+
provenance: {
|
|
265
|
+
...entry.provenance,
|
|
266
|
+
snippet: `[${fieldName}] ${entry.provenance.snippet}`,
|
|
267
|
+
},
|
|
268
|
+
})),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const defaultRenderer = {
|
|
272
|
+
render: renderDefault,
|
|
273
|
+
extractSymbols: extractDefaultSymbols,
|
|
274
|
+
};
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Registry
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
const BUILT_IN_RENDERERS = {
|
|
279
|
+
article: articleRenderer,
|
|
280
|
+
typesReference: typesReferenceRenderer,
|
|
281
|
+
};
|
|
282
|
+
function rendererFor(type) {
|
|
283
|
+
return BUILT_IN_RENDERERS[type] ?? defaultRenderer;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Render a document using the registered renderer for its `_type`, falling
|
|
287
|
+
* back to the default walker. The returned `fidelity` flag tells callers
|
|
288
|
+
* whether to emit the "info: dedicated renderer would help" log.
|
|
289
|
+
*/
|
|
290
|
+
export async function renderDocument(doc, ctx = {}) {
|
|
291
|
+
return rendererFor(doc._type).render(doc, ctx);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Extract a symbol-reference index for a document using its registered
|
|
295
|
+
* renderer (or the default walker for unknown types). Used by the
|
|
296
|
+
* grader-context pathway (W0197) to feed the LLM judge a compact
|
|
297
|
+
* deterministic recognition reference instead of the full rendered doc.
|
|
298
|
+
*
|
|
299
|
+
* Returns an empty `SymbolIndex` (`{ symbols: [] }`) when extraction
|
|
300
|
+
* yields nothing — callers interpret this as the signal to fall back to
|
|
301
|
+
* the rendered markdown.
|
|
302
|
+
*/
|
|
303
|
+
export async function extractSymbolsForDoc(doc, ctx = {}) {
|
|
304
|
+
return rendererFor(doc._type).extractSymbols(doc, ctx);
|
|
305
|
+
}
|
|
306
|
+
/** Exported for tests and consumers that want the registered set. */
|
|
307
|
+
export const REGISTERED_RENDERER_TYPES = Object.keys(BUILT_IN_RENDERERS).sort();
|
package/dist/sanity/queries.d.ts
CHANGED
|
@@ -20,34 +20,34 @@ export declare const FEATURE_AREA_QUERIES: {
|
|
|
20
20
|
/**
|
|
21
21
|
* Other Frameworks — Nuxt, React Router/Remix, Astro
|
|
22
22
|
*/
|
|
23
|
-
readonly frameworks: "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref in [\n \"b2c937d0-34b3-435e-818b-d0c8520ea9b8\",\n \"e223241c-e207-41e0-8e12-f80e78203cf1\",\n \"73bebb61-e508-42b7-b44b-c4703ae42d01\"\n ]\n || (primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n && (title match \"Remix*\" || title match \"Nuxt*\"\n || title match \"Astro*\" || title match \"SvelteKit*\"))\n )\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
23
|
+
readonly frameworks: "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref in [\n \"b2c937d0-34b3-435e-818b-d0c8520ea9b8\",\n \"e223241c-e207-41e0-8e12-f80e78203cf1\",\n \"73bebb61-e508-42b7-b44b-c4703ae42d01\"\n ]\n || (primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n && (title match \"Remix*\" || title match \"Nuxt*\"\n || title match \"Astro*\" || title match \"SvelteKit*\"))\n )\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
24
24
|
/**
|
|
25
25
|
* Functions / Compute / AI — serverless functions, webhooks
|
|
26
26
|
* Section: "Compute and AI" (12 articles)
|
|
27
27
|
*/
|
|
28
|
-
readonly functions: "\n *[_type == \"article\"\n && primarySection._ref == \"3024ce79-c196-49ee-a237-a327d6a6348f\"\n && !(_id in path(\"drafts.**\"))\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
28
|
+
readonly functions: "\n *[_type == \"article\"\n && primarySection._ref == \"3024ce79-c196-49ee-a237-a327d6a6348f\"\n && !(_id in path(\"drafts.**\"))\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
29
29
|
/**
|
|
30
30
|
* GROQ Query Language — introduction, syntax, joins, filtering, projections
|
|
31
31
|
* Combines: Content Lake section GROQ articles + Developer Guides articles
|
|
32
32
|
* about querying + title-matched GROQ articles across all sections.
|
|
33
33
|
*/
|
|
34
|
-
readonly groq: "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref == \"60a6bcb5-9706-4cab-9153-57725e28f4d0\"\n || primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n )\n && (\n title match \"GROQ*\"\n || title match \"*GROQ*\"\n || title match \"Query*\"\n || title match \"How Queries*\"\n || slug.current match \"groq-*\"\n || slug.current match \"query-*\"\n || slug.current match \"how-queries-*\"\n || slug.current match \"paginating-with-groq\"\n || slug.current match \"high-performance-groq\"\n )\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
34
|
+
readonly groq: "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref == \"60a6bcb5-9706-4cab-9153-57725e28f4d0\"\n || primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n )\n && (\n title match \"GROQ*\"\n || title match \"*GROQ*\"\n || title match \"Query*\"\n || title match \"How Queries*\"\n || slug.current match \"groq-*\"\n || slug.current match \"query-*\"\n || slug.current match \"how-queries-*\"\n || slug.current match \"paginating-with-groq\"\n || slug.current match \"high-performance-groq\"\n )\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
35
35
|
/**
|
|
36
36
|
* Next.js integration + Live Content API
|
|
37
37
|
* Combines: Next.js quickstart section + developer-guide articles
|
|
38
38
|
* mentioning Next.js or Live Content.
|
|
39
39
|
*/
|
|
40
|
-
readonly "nextjs-live": "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref == \"6208dff1-bba7-487a-a6bb-a0ffccb5846c\"\n || (primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n && (title match \"Next*\" || title match \"*Live*\"))\n )\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
40
|
+
readonly "nextjs-live": "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n && (\n primarySection._ref == \"6208dff1-bba7-487a-a6bb-a0ffccb5846c\"\n || (primarySection._ref == \"ea6b80fa-d0d2-4daa-8220-eb8abdcc9deb\"\n && (title match \"Next*\" || title match \"*Live*\"))\n )\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
41
41
|
/**
|
|
42
42
|
* Studio Setup & Customization
|
|
43
43
|
* Section: "Studio" (116 articles)
|
|
44
44
|
*/
|
|
45
|
-
readonly "studio-setup": "\n *[_type == \"article\"\n && primarySection._ref == \"d67e3879-0342-4a80-8a2d-e35908df35cc\"\n && !(_id in path(\"drafts.**\"))\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
45
|
+
readonly "studio-setup": "\n *[_type == \"article\"\n && primarySection._ref == \"d67e3879-0342-4a80-8a2d-e35908df35cc\"\n && !(_id in path(\"drafts.**\"))\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
46
46
|
/**
|
|
47
47
|
* Visual Editing — Presentation tool, overlays, live preview
|
|
48
48
|
* Section: "Visual Editing" (24 articles)
|
|
49
49
|
*/
|
|
50
|
-
readonly "visual-editing": "\n *[_type == \"article\"\n && primarySection._ref == \"4e0ef463-01e2-48db-9a31-38b3912ececd\"\n && !(_id in path(\"drafts.**\"))\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
50
|
+
readonly "visual-editing": "\n *[_type == \"article\"\n && primarySection._ref == \"4e0ef463-01e2-48db-9a31-38b3912ececd\"\n && !(_id in path(\"drafts.**\"))\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n ";
|
|
51
51
|
};
|
|
52
52
|
export type FeatureArea = keyof typeof FEATURE_AREA_QUERIES;
|
|
53
53
|
export declare const ALL_FEATURE_AREAS: FeatureArea[];
|
|
@@ -55,11 +55,11 @@ export declare const ALL_FEATURE_AREAS: FeatureArea[];
|
|
|
55
55
|
* Fetch a single article by its slug.
|
|
56
56
|
* Returns the same projection shape as the feature-area queries.
|
|
57
57
|
*/
|
|
58
|
-
export declare const ARTICLE_BY_SLUG_QUERY = "\n *[_type == \"article\"\n && slug.current == $slug\n && !(_id in path(\"drafts.**\"))\n ][0] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
58
|
+
export declare const ARTICLE_BY_SLUG_QUERY = "\n *[_type == \"article\"\n && slug.current == $slug\n && !(_id in path(\"drafts.**\"))\n ][0] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
59
59
|
/**
|
|
60
60
|
* Fetch all published articles (for full-corpus generation).
|
|
61
61
|
*/
|
|
62
|
-
export declare const ALL_ARTICLES_QUERY = "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n ] | order(primarySection->slug.current, title) {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
62
|
+
export declare const ALL_ARTICLES_QUERY = "\n *[_type == \"article\"\n && !(_id in path(\"drafts.**\"))\n ] | order(primarySection->slug.current, title) {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
63
63
|
/**
|
|
64
64
|
* Fetch article metadata for a list of slugs.
|
|
65
65
|
*
|
|
@@ -76,7 +76,7 @@ export declare const ARTICLES_METADATA_BY_SLUGS_QUERY = "\n *[_type == \"articl
|
|
|
76
76
|
*
|
|
77
77
|
* Returns the full article projection for content comparison.
|
|
78
78
|
*/
|
|
79
|
-
export declare const ARTICLE_BY_SLUG_WITH_PERSPECTIVE_QUERY = "\n *[_type == \"article\"\n && slug.current == $slug\n ][0] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
79
|
+
export declare const ARTICLE_BY_SLUG_WITH_PERSPECTIVE_QUERY = "\n *[_type == \"article\"\n && slug.current == $slug\n ][0] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
80
80
|
/**
|
|
81
81
|
* Fetch articles by their document IDs.
|
|
82
82
|
*
|
|
@@ -86,7 +86,28 @@ export declare const ARTICLE_BY_SLUG_WITH_PERSPECTIVE_QUERY = "\n *[_type == \"
|
|
|
86
86
|
*
|
|
87
87
|
* @param $ids — array of document ID strings
|
|
88
88
|
*/
|
|
89
|
-
export declare const ARTICLES_BY_IDS_QUERY = "\n *[_type == \"article\"\n && _id in $ids\n ] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
89
|
+
export declare const ARTICLES_BY_IDS_QUERY = "\n *[_type == \"article\"\n && _id in $ids\n ] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
90
|
+
/**
|
|
91
|
+
* Fetch arbitrary documents by their `_id` — no `_type` filter.
|
|
92
|
+
*
|
|
93
|
+
* Used by id-ref resolution so authors can pin any document type
|
|
94
|
+
* (`article`, `typesReference`, `marketingPage`, `glossaryEntry`, …),
|
|
95
|
+
* not just articles. The projection branches on `_type`:
|
|
96
|
+
*
|
|
97
|
+
* - `article` → reuses ARTICLE_PROJECTION's flatten + card-deref shape.
|
|
98
|
+
* - `typesReference` → derefs `library` and `latestVersion`, including
|
|
99
|
+
* the resolved file asset URL so the renderer can fetch typedoc JSON
|
|
100
|
+
* without a follow-up round-trip.
|
|
101
|
+
* - default `{...}` → returns the raw document fields, which the
|
|
102
|
+
* default walker in `document-renderers.ts` flattens to Markdown.
|
|
103
|
+
*
|
|
104
|
+
* Does NOT include the `!(_id in path("drafts.**"))` filter — id-ref
|
|
105
|
+
* resolution is intentional, drafts are queryable when perspective is
|
|
106
|
+
* draft-enabled, and authors are expected to pin published `_id`s.
|
|
107
|
+
*
|
|
108
|
+
* @param $ids — array of document ID strings
|
|
109
|
+
*/
|
|
110
|
+
export declare const DOCS_BY_IDS_QUERY = "\n *[_id in $ids] {\n _id,\n _type,\n _system,\n _createdAt,\n _updatedAt,\n _rev,\n _type == \"article\" => {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n _type != \"docsCardCollection\" => { ... },\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n },\n _type == \"typesReference\" => {\n title,\n autoPublish,\n \"slug\": slug,\n \"library\": library->{ _id, _type, title, npmName },\n \"latestVersion\": latestVersion->{\n _id,\n _type,\n semver,\n date,\n \"attachment\": {\n \"asset\": attachment.asset->{\n _id,\n _type,\n url,\n originalFilename,\n mimeType,\n size,\n extension\n }\n }\n }\n },\n !(_type in [\"article\", \"typesReference\"]) => { ... }\n }\n";
|
|
90
111
|
/**
|
|
91
112
|
* Fetch a single article by its document ID.
|
|
92
113
|
*
|
|
@@ -96,7 +117,7 @@ export declare const ARTICLES_BY_IDS_QUERY = "\n *[_type == \"article\"\n &&
|
|
|
96
117
|
*
|
|
97
118
|
* @param $id — document ID string
|
|
98
119
|
*/
|
|
99
|
-
export declare const ARTICLE_BY_ID_QUERY = "\n *[_type == \"article\"\n && _id == $id\n ][0] {\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
120
|
+
export declare const ARTICLE_BY_ID_QUERY = "\n *[_type == \"article\"\n && _id == $id\n ][0] {\n _id,\n _type,\n _rev,\n title,\n description,\n \"slug\": slug.current,\n \"section\": primarySection->{ \"slug\": slug.current, title },\n \"content\": content[] {\n // Pass through all standard blocks unchanged\n _type != \"docsCardCollection\" => { ... },\n // Resolve references inside card collections so we get titles/slugs\n _type == \"docsCardCollection\" => {\n ...,\n cards[] {\n ...,\n \"resolvedTitle\": reference->title,\n \"resolvedSlug\": reference->slug.current\n }\n }\n }\n}\n";
|
|
100
121
|
/**
|
|
101
122
|
* Resolve an article slug from a URL path segment.
|
|
102
123
|
*
|
package/dist/sanity/queries.js
CHANGED
|
@@ -43,8 +43,15 @@ const SECTION_IDS = {
|
|
|
43
43
|
* Returns the raw Portable Text `content` array so that the Node-side
|
|
44
44
|
* converter (@portabletext/markdown) can produce well-structured Markdown
|
|
45
45
|
* with headings, code fences, tables, callouts, etc.
|
|
46
|
+
*
|
|
47
|
+
* `_id`, `_type`, and `_rev` are projected so a result satisfies the
|
|
48
|
+
* `DocumentForRender` shape and can dispatch through the renderer
|
|
49
|
+
* registry uniformly with id-ref-resolved docs (W0197).
|
|
46
50
|
*/
|
|
47
51
|
const ARTICLE_PROJECTION = `{
|
|
52
|
+
_id,
|
|
53
|
+
_type,
|
|
54
|
+
_rev,
|
|
48
55
|
title,
|
|
49
56
|
description,
|
|
50
57
|
"slug": slug.current,
|
|
@@ -229,6 +236,77 @@ export const ARTICLES_BY_IDS_QUERY = `
|
|
|
229
236
|
&& _id in $ids
|
|
230
237
|
] ${ARTICLE_PROJECTION}
|
|
231
238
|
`;
|
|
239
|
+
/**
|
|
240
|
+
* Fetch arbitrary documents by their `_id` — no `_type` filter.
|
|
241
|
+
*
|
|
242
|
+
* Used by id-ref resolution so authors can pin any document type
|
|
243
|
+
* (`article`, `typesReference`, `marketingPage`, `glossaryEntry`, …),
|
|
244
|
+
* not just articles. The projection branches on `_type`:
|
|
245
|
+
*
|
|
246
|
+
* - `article` → reuses ARTICLE_PROJECTION's flatten + card-deref shape.
|
|
247
|
+
* - `typesReference` → derefs `library` and `latestVersion`, including
|
|
248
|
+
* the resolved file asset URL so the renderer can fetch typedoc JSON
|
|
249
|
+
* without a follow-up round-trip.
|
|
250
|
+
* - default `{...}` → returns the raw document fields, which the
|
|
251
|
+
* default walker in `document-renderers.ts` flattens to Markdown.
|
|
252
|
+
*
|
|
253
|
+
* Does NOT include the `!(_id in path("drafts.**"))` filter — id-ref
|
|
254
|
+
* resolution is intentional, drafts are queryable when perspective is
|
|
255
|
+
* draft-enabled, and authors are expected to pin published `_id`s.
|
|
256
|
+
*
|
|
257
|
+
* @param $ids — array of document ID strings
|
|
258
|
+
*/
|
|
259
|
+
export const DOCS_BY_IDS_QUERY = `
|
|
260
|
+
*[_id in $ids] {
|
|
261
|
+
_id,
|
|
262
|
+
_type,
|
|
263
|
+
_system,
|
|
264
|
+
_createdAt,
|
|
265
|
+
_updatedAt,
|
|
266
|
+
_rev,
|
|
267
|
+
_type == "article" => {
|
|
268
|
+
title,
|
|
269
|
+
description,
|
|
270
|
+
"slug": slug.current,
|
|
271
|
+
"section": primarySection->{ "slug": slug.current, title },
|
|
272
|
+
"content": content[] {
|
|
273
|
+
_type != "docsCardCollection" => { ... },
|
|
274
|
+
_type == "docsCardCollection" => {
|
|
275
|
+
...,
|
|
276
|
+
cards[] {
|
|
277
|
+
...,
|
|
278
|
+
"resolvedTitle": reference->title,
|
|
279
|
+
"resolvedSlug": reference->slug.current
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
_type == "typesReference" => {
|
|
285
|
+
title,
|
|
286
|
+
autoPublish,
|
|
287
|
+
"slug": slug,
|
|
288
|
+
"library": library->{ _id, _type, title, npmName },
|
|
289
|
+
"latestVersion": latestVersion->{
|
|
290
|
+
_id,
|
|
291
|
+
_type,
|
|
292
|
+
semver,
|
|
293
|
+
date,
|
|
294
|
+
"attachment": {
|
|
295
|
+
"asset": attachment.asset->{
|
|
296
|
+
_id,
|
|
297
|
+
_type,
|
|
298
|
+
url,
|
|
299
|
+
originalFilename,
|
|
300
|
+
mimeType,
|
|
301
|
+
size,
|
|
302
|
+
extension
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
!(_type in ["article", "typesReference"]) => { ... }
|
|
308
|
+
}
|
|
309
|
+
`;
|
|
232
310
|
/**
|
|
233
311
|
* Fetch a single article by its document ID.
|
|
234
312
|
*
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* symbol-index — Programmatic extractor that produces a flat list of
|
|
3
|
+
* identifiers the canonical reference legitimizes, each with a one-line
|
|
4
|
+
* provenance snippet.
|
|
5
|
+
*
|
|
6
|
+
* Used by the grader-context pathway (W0196 / W0197) so the LLM judge sees
|
|
7
|
+
* a compact, deterministic recognition reference instead of the full
|
|
8
|
+
* narrative doc — addresses the DOC-2117 prior-collision failure mode
|
|
9
|
+
* where a grader claimed `useEditDocument` did not exist.
|
|
10
|
+
*
|
|
11
|
+
* Two upstream sources, both fully programmatic (no LLM in the extractor):
|
|
12
|
+
*
|
|
13
|
+
* - `extractSymbolIndex(blocks)` — walks Sanity Portable Text content
|
|
14
|
+
* (article docs).
|
|
15
|
+
* - `extractSymbolsFromTypedoc(json, packageName)` — parses typedoc
|
|
16
|
+
* JSON (typesReference docs).
|
|
17
|
+
*
|
|
18
|
+
* `mergeSymbolIndexes(indexes)` combines indexes from multiple references
|
|
19
|
+
* into a single deduped index for a task.
|
|
20
|
+
*
|
|
21
|
+
* Source precedence (higher wins on dedup):
|
|
22
|
+
* 1. type-def — typedoc declarations: literal authoritative type
|
|
23
|
+
* surface, no editorial layer between extracted
|
|
24
|
+
* symbol and the package's actual exports.
|
|
25
|
+
* 2. heading — Section headings (`block` style h1..h4), including
|
|
26
|
+
* inline `code` marks within heading spans.
|
|
27
|
+
* 3. inline-code — Inline `code` marks within non-heading `block` spans.
|
|
28
|
+
* 4. code-block — Identifiers from `import` statements in `codeBlock`
|
|
29
|
+
* bodies. Body identifiers (`const foo = ...`) are
|
|
30
|
+
* intentionally not extracted — those are usage demos.
|
|
31
|
+
*/
|
|
32
|
+
export type SymbolProvenanceKind = "type-def" | "heading" | "inline-code" | "code-block";
|
|
33
|
+
/** Declaration kinds we surface from typedoc JSON for `type-def` provenance. */
|
|
34
|
+
export type TypeDefDeclarationKind = "function" | "class" | "interface" | "type" | "enum" | "variable" | "namespace";
|
|
35
|
+
export interface SymbolProvenance {
|
|
36
|
+
kind: SymbolProvenanceKind;
|
|
37
|
+
/** Human-readable line that locates the symbol in the source doc. */
|
|
38
|
+
snippet: string;
|
|
39
|
+
/** Heading style when kind === "heading". */
|
|
40
|
+
style?: "h1" | "h2" | "h3" | "h4";
|
|
41
|
+
/** Source filename when kind === "code-block" and one is set on the block. */
|
|
42
|
+
filename?: string;
|
|
43
|
+
/** Code block language tag when kind === "code-block". */
|
|
44
|
+
language?: string;
|
|
45
|
+
/** Declaration kind when kind === "type-def". */
|
|
46
|
+
declarationKind?: TypeDefDeclarationKind;
|
|
47
|
+
/** Source package (e.g. `@sanity/sdk-react`) when kind === "type-def". */
|
|
48
|
+
package?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface SymbolEntry {
|
|
51
|
+
symbol: string;
|
|
52
|
+
provenance: SymbolProvenance;
|
|
53
|
+
}
|
|
54
|
+
export interface SymbolIndex {
|
|
55
|
+
symbols: SymbolEntry[];
|
|
56
|
+
}
|
|
57
|
+
export declare function extractSymbolIndex(blocks: unknown): SymbolIndex;
|
|
58
|
+
/**
|
|
59
|
+
* Extract a symbol index from a typedoc JSON document (schema 2.x — the
|
|
60
|
+
* shape produced by `typedoc --json`). Each top-level export becomes a
|
|
61
|
+
* `type-def` provenance entry with the symbol name, declaration kind
|
|
62
|
+
* (function / interface / type / etc.), and the JSDoc summary as snippet.
|
|
63
|
+
*
|
|
64
|
+
* Type-def is the highest-precedence source: typedoc declarations are
|
|
65
|
+
* literal authoritative type surface, no editorial layer between them
|
|
66
|
+
* and the package's actual exports. If a symbol also appears in narrative
|
|
67
|
+
* docs (heading, inline code, code-block import) the type-def entry wins
|
|
68
|
+
* on dedup.
|
|
69
|
+
*
|
|
70
|
+
* `body` is the raw JSON string fetched from the typesReference's
|
|
71
|
+
* attachment URL. Returns an empty index for any unparseable input — this
|
|
72
|
+
* is best-effort recognition material; callers fall back to full-doc
|
|
73
|
+
* injection when extraction yields nothing.
|
|
74
|
+
*/
|
|
75
|
+
export declare function extractSymbolsFromTypedoc(body: string, packageName?: string): SymbolIndex;
|
|
76
|
+
/**
|
|
77
|
+
* Compute per-tier counts for a SymbolIndex. Used by the fetcher to
|
|
78
|
+
* populate the per-task manifest entry's `tierBreakdown` field.
|
|
79
|
+
*/
|
|
80
|
+
export declare function symbolIndexTierBreakdown(index: SymbolIndex): {
|
|
81
|
+
typeDef: number;
|
|
82
|
+
heading: number;
|
|
83
|
+
inlineCode: number;
|
|
84
|
+
codeBlock: number;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Combine multiple `SymbolIndex` instances (typically from different
|
|
88
|
+
* canonical references for the same task) into a single deduped index
|
|
89
|
+
* preserving precedence.
|
|
90
|
+
*/
|
|
91
|
+
export declare function mergeSymbolIndexes(indexes: readonly SymbolIndex[]): SymbolIndex;
|
|
92
|
+
/**
|
|
93
|
+
* Render a SymbolIndex as a compact markdown reference suitable for
|
|
94
|
+
* injection into a grader's `rubricPrompt` as ground-truth recognition
|
|
95
|
+
* material. Layout intentionally puts headings first so the most
|
|
96
|
+
* authoritative symbols anchor the top of the list.
|
|
97
|
+
*/
|
|
98
|
+
export declare function renderSymbolIndex(index: SymbolIndex, title?: string): string;
|