nimbus-docs 0.1.3 → 0.1.5
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/dist/cli/index.js +611 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/content.d.ts +5 -74
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +2 -2
- package/dist/content.js.map +1 -1
- package/dist/diagnostic-DZf0z79l.d.ts +123 -0
- package/dist/diagnostic-DZf0z79l.d.ts.map +1 -0
- package/dist/index.d.ts +1006 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +779 -174
- package/dist/index.js.map +1 -1
- package/dist/rules-DnAP-j89.js +5836 -0
- package/dist/rules-DnAP-j89.js.map +1 -0
- package/dist/schemas.d.ts +84 -105
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +114 -31
- package/dist/schemas.js.map +1 -1
- package/dist/strict-keys-BiXiT3pq.js +35 -0
- package/dist/strict-keys-BiXiT3pq.js.map +1 -0
- package/dist/types.d.ts +62 -21
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -4
- package/src/components/NimbusHead.astro +0 -4
package/dist/schemas.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "astro/zod";
|
|
2
|
+
import * as astro_content0 from "astro:content";
|
|
2
3
|
|
|
3
4
|
//#region src/schemas.d.ts
|
|
4
5
|
interface DocSchemaConfig {
|
|
@@ -9,89 +10,72 @@ interface DocSchemaConfig {
|
|
|
9
10
|
* Build a customizable docs schema. Use this when composing schemas outside
|
|
10
11
|
* the `docsCollection()` factory (e.g. multiple docs collections with
|
|
11
12
|
* different shapes).
|
|
13
|
+
*
|
|
14
|
+
* For typed Astro `image()` fields or per-collection field narrowing, use
|
|
15
|
+
* the richer `defineSchema(ctx => ...)` factory instead.
|
|
12
16
|
*/
|
|
13
|
-
declare function defineDocSchema(config?: DocSchemaConfig): z.ZodObject<{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
style: "style";
|
|
59
|
-
}>;
|
|
60
|
-
attrs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
61
|
-
content: z.ZodOptional<z.ZodString>;
|
|
62
|
-
}, z.core.$strip>>>;
|
|
63
|
-
draft: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
-
noindex: z.ZodDefault<z.ZodBoolean>;
|
|
65
|
-
llms: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
-
aiDeprioritize: z.ZodDefault<z.ZodBoolean>;
|
|
67
|
-
pagefind: z.ZodDefault<z.ZodBoolean>;
|
|
68
|
-
tableOfContents: z.ZodOptional<z.ZodObject<{
|
|
69
|
-
minHeadingLevel: z.ZodDefault<z.ZodNumber>;
|
|
70
|
-
maxHeadingLevel: z.ZodDefault<z.ZodNumber>;
|
|
71
|
-
}, z.core.$strip>>;
|
|
72
|
-
lastUpdated: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
73
|
-
socialImage: z.ZodOptional<z.ZodString>;
|
|
74
|
-
prev: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
75
|
-
link: z.ZodOptional<z.ZodString>;
|
|
76
|
-
label: z.ZodOptional<z.ZodString>;
|
|
77
|
-
}, z.core.$strip>, z.ZodLiteral<false>]>>;
|
|
78
|
-
next: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
79
|
-
link: z.ZodOptional<z.ZodString>;
|
|
80
|
-
label: z.ZodOptional<z.ZodString>;
|
|
81
|
-
}, z.core.$strip>, z.ZodLiteral<false>]>>;
|
|
82
|
-
previousSlug: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
83
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
84
|
-
[x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
85
|
-
}, z.core.$strip>;
|
|
17
|
+
declare function defineDocSchema(config?: DocSchemaConfig): z.ZodObject<Readonly<{
|
|
18
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
19
|
+
}>, z.core.$loose>;
|
|
20
|
+
/**
|
|
21
|
+
* Factory options for `defineSchema`.
|
|
22
|
+
*
|
|
23
|
+
* - `extend`: additional fields merged into the framework schema. Use
|
|
24
|
+
* this for user-defined frontmatter (`author`, `tags`, `cover`).
|
|
25
|
+
* - `narrow`: replaces framework fields with tighter types within this
|
|
26
|
+
* collection. Use when a collection has stricter rules — e.g.
|
|
27
|
+
* `{ mode: z.literal("doc") }` says no landing pages in this collection.
|
|
28
|
+
*/
|
|
29
|
+
interface DefineSchemaOptions {
|
|
30
|
+
extend?: z.ZodTypeAny;
|
|
31
|
+
narrow?: Record<string, z.ZodTypeAny>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a typed docs schema with access to the Astro `SchemaContext`.
|
|
35
|
+
* Use this when you want typed image fields (`ctx.image()`), per-
|
|
36
|
+
* collection narrowing of framework fields, or both. The simpler
|
|
37
|
+
* `defineDocSchema({ fields })` factory is still available for the
|
|
38
|
+
* common case of just adding fields.
|
|
39
|
+
*
|
|
40
|
+
* import { defineCollection } from "astro:content";
|
|
41
|
+
* import { z } from "astro/zod";
|
|
42
|
+
* import { defineSchema } from "nimbus-docs/schemas";
|
|
43
|
+
*
|
|
44
|
+
* export const collections = {
|
|
45
|
+
* docs: defineCollection({
|
|
46
|
+
* loader: ...,
|
|
47
|
+
* schema: defineSchema((ctx) => ({
|
|
48
|
+
* extend: z.object({
|
|
49
|
+
* cover: ctx.image().optional(),
|
|
50
|
+
* author: z.string().optional(),
|
|
51
|
+
* }),
|
|
52
|
+
* narrow: {
|
|
53
|
+
* mode: z.literal("doc"), // no landing pages here
|
|
54
|
+
* },
|
|
55
|
+
* })),
|
|
56
|
+
* }),
|
|
57
|
+
* };
|
|
58
|
+
*/
|
|
59
|
+
declare function defineSchema(factory: (ctx: astro_content0.SchemaContext) => DefineSchemaOptions): (ctx: astro_content0.SchemaContext) => z.ZodObject<Readonly<{
|
|
60
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
61
|
+
}>, z.core.$loose> | z.ZodIntersection<z.ZodObject<any, z.core.$strip>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
86
62
|
/** Default docs schema. Equivalent to `defineDocSchema()`. */
|
|
87
|
-
declare const docsSchema: z.ZodObject<{
|
|
63
|
+
declare const docsSchema: z.ZodObject<Readonly<{
|
|
64
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
65
|
+
}>, z.core.$loose>;
|
|
66
|
+
/** Schema for partials (`<Render file="..." />` snippets). */
|
|
67
|
+
declare const partialsSchema: z.ZodDefault<z.ZodObject<{
|
|
68
|
+
params: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
}, z.core.$strip>>;
|
|
70
|
+
/** Docs frontmatter, framework fields type-checked, extra keys allowed. */
|
|
71
|
+
declare const lenientDocsSchema: z.ZodObject<{
|
|
88
72
|
title: z.ZodString;
|
|
89
73
|
description: z.ZodOptional<z.ZodString>;
|
|
90
|
-
|
|
74
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
91
75
|
doc: "doc";
|
|
92
|
-
|
|
76
|
+
custom: "custom";
|
|
93
77
|
}>>;
|
|
94
|
-
sidebar: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
sidebar: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
95
79
|
order: z.ZodOptional<z.ZodNumber>;
|
|
96
80
|
label: z.ZodOptional<z.ZodString>;
|
|
97
81
|
badge: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
@@ -109,40 +93,37 @@ declare const docsSchema: z.ZodObject<{
|
|
|
109
93
|
}, z.core.$strip>]>>;
|
|
110
94
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
111
95
|
hideChildren: z.ZodOptional<z.ZodBoolean>;
|
|
112
|
-
}, z.core.$strip>>;
|
|
113
|
-
hero: z.ZodOptional<z.ZodObject<{
|
|
114
|
-
title: z.ZodOptional<z.ZodString>;
|
|
115
|
-
tagline: z.ZodOptional<z.ZodString>;
|
|
116
|
-
actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
117
|
-
text: z.ZodString;
|
|
118
|
-
link: z.ZodString;
|
|
119
|
-
variant: z.ZodDefault<z.ZodEnum<{
|
|
120
|
-
primary: "primary";
|
|
121
|
-
secondary: "secondary";
|
|
122
|
-
outline: "outline";
|
|
123
|
-
}>>;
|
|
124
|
-
icon: z.ZodOptional<z.ZodString>;
|
|
125
|
-
}, z.core.$strip>>>;
|
|
126
|
-
}, z.core.$strip>>;
|
|
96
|
+
}, z.core.$strip>]>>;
|
|
127
97
|
head: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
128
98
|
tag: z.ZodEnum<{
|
|
129
|
-
link: "link";
|
|
130
99
|
meta: "meta";
|
|
100
|
+
link: "link";
|
|
131
101
|
script: "script";
|
|
132
102
|
style: "style";
|
|
133
103
|
}>;
|
|
134
104
|
attrs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
135
105
|
content: z.ZodOptional<z.ZodString>;
|
|
136
106
|
}, z.core.$strip>>>;
|
|
107
|
+
banner: z.ZodOptional<z.ZodObject<{
|
|
108
|
+
content: z.ZodString;
|
|
109
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
110
|
+
note: "note";
|
|
111
|
+
tip: "tip";
|
|
112
|
+
caution: "caution";
|
|
113
|
+
danger: "danger";
|
|
114
|
+
}>>;
|
|
115
|
+
dismissible: z.ZodOptional<z.ZodObject<{
|
|
116
|
+
id: z.ZodString;
|
|
117
|
+
days: z.ZodOptional<z.ZodNumber>;
|
|
118
|
+
}, z.core.$strip>>;
|
|
119
|
+
}, z.core.$strip>>;
|
|
137
120
|
draft: z.ZodDefault<z.ZodBoolean>;
|
|
138
121
|
noindex: z.ZodDefault<z.ZodBoolean>;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
pagefind: z.ZodDefault<z.ZodBoolean>;
|
|
142
|
-
tableOfContents: z.ZodOptional<z.ZodObject<{
|
|
122
|
+
searchable: z.ZodOptional<z.ZodBoolean>;
|
|
123
|
+
tableOfContents: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
143
124
|
minHeadingLevel: z.ZodDefault<z.ZodNumber>;
|
|
144
125
|
maxHeadingLevel: z.ZodDefault<z.ZodNumber>;
|
|
145
|
-
}, z.core.$strip>>;
|
|
126
|
+
}, z.core.$strip>]>>;
|
|
146
127
|
lastUpdated: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
147
128
|
socialImage: z.ZodOptional<z.ZodString>;
|
|
148
129
|
prev: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
@@ -154,13 +135,11 @@ declare const docsSchema: z.ZodObject<{
|
|
|
154
135
|
label: z.ZodOptional<z.ZodString>;
|
|
155
136
|
}, z.core.$strip>, z.ZodLiteral<false>]>>;
|
|
156
137
|
previousSlug: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
157
|
-
}, z.core.$
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
/** Schema for partials (`<Render file="..." />` snippets). */
|
|
161
|
-
declare const partialsSchema: z.ZodDefault<z.ZodObject<{
|
|
138
|
+
}, z.core.$loose>;
|
|
139
|
+
/** Partials frontmatter, framework fields type-checked, extra keys allowed. */
|
|
140
|
+
declare const lenientPartialsSchema: z.ZodObject<{
|
|
162
141
|
params: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
163
|
-
}, z.core.$
|
|
142
|
+
}, z.core.$loose>;
|
|
164
143
|
/** One row in a component's `props` frontmatter array. */
|
|
165
144
|
declare const componentPropSchema: z.ZodObject<{
|
|
166
145
|
name: z.ZodString;
|
|
@@ -183,5 +162,5 @@ declare const componentsSchema: z.ZodObject<{
|
|
|
183
162
|
}, z.core.$strip>>>;
|
|
184
163
|
}, z.core.$strip>;
|
|
185
164
|
//#endregion
|
|
186
|
-
export { ComponentProp, DocSchemaConfig, componentsSchema, defineDocSchema, docsSchema, partialsSchema };
|
|
165
|
+
export { ComponentProp, DefineSchemaOptions, DocSchemaConfig, componentsSchema, defineDocSchema, defineSchema, docsSchema, lenientDocsSchema, lenientPartialsSchema, partialsSchema };
|
|
187
166
|
//# sourceMappingURL=schemas.d.ts.map
|
package/dist/schemas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","names":[],"sources":["../src/schemas.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","names":[],"sources":["../src/schemas.ts"],"mappings":";;;;UAiBiB,eAAA;EA6Oc;EA3O7B,MAAA,GAAS,MAAA,SAAe,CAAA,CAAE,UAAA;AAAA;;;;;;;;;iBA2OZ,eAAA,CAAgB,MAAA,GAAQ,eAAA,GAAoB,CAAA,CAAA,SAAA,CAAA,QAAA;EAAA;;;;;;;;;;;UAkB3C,mBAAA;EACf,MAAA,GAAS,CAAA,CAAE,UAAA;EACX,MAAA,GAAS,MAAA,SAAe,CAAA,CAAE,UAAA;AAAA;AAF5B;;;;;;;;;;;;;;;;;AA+BA;;;;;;;;;AA/BA,iBA+BgB,YAAA,CACd,OAAA,GAAU,GAAA,EADgB,cAAA,CACa,aAAA,KAAkB,mBAAA,IAEjD,GAAA,EAFoE,cAAA,CAEvC,aAAA,KAAa,CAAA,CAAA,SAAA,CAAA,QAAA;EAAA;;;cA8BvC,UAAA,EAAU,CAAA,CAAA,SAAA,CAAA,QAAA;EAAA;;;cAWV,cAAA,EAAc,CAAA,CAAA,UAAA,CAAA,CAAA,CAAA,SAAA;;;;cAad,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAGjB,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;cAU5B,mBAAA,EAAmB,CAAA,CAAA,SAAA;;;;;;;KAQb,aAAA,GAAgB,CAAA,CAAE,KAAA,QAAa,mBAAA;;cAG9B,gBAAA,EAAgB,CAAA,CAAA,SAAA"}
|
package/dist/schemas.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as withStrictKeys } from "./strict-keys-BiXiT3pq.js";
|
|
1
2
|
import { z } from "astro/zod";
|
|
2
3
|
|
|
3
4
|
//#region src/schemas.ts
|
|
@@ -27,29 +28,14 @@ const sidebarBadgeSchema = z.union([z.string(), z.object({
|
|
|
27
28
|
"danger"
|
|
28
29
|
], { error: "\"variant\" must be one of: default, info, note, success, tip, warning, caution, danger" }).default("default")
|
|
29
30
|
})]);
|
|
30
|
-
const
|
|
31
|
+
const sidebarSchema = z.object({
|
|
31
32
|
order: z.number({ error: "\"sidebar.order\" must be a number" }).optional(),
|
|
32
33
|
label: z.string({ error: "\"sidebar.label\" must be a string" }).optional(),
|
|
33
34
|
badge: sidebarBadgeSchema.optional(),
|
|
34
35
|
hidden: z.boolean({ error: "\"sidebar.hidden\" must be true or false" }).optional(),
|
|
35
36
|
hideChildren: z.boolean({ error: "\"sidebar.hideChildren\" must be true or false" }).optional()
|
|
36
|
-
}).optional();
|
|
37
|
-
const heroActionSchema = z.object({
|
|
38
|
-
text: z.string({ error: "hero action needs a \"text\" field" }),
|
|
39
|
-
link: z.string({ error: "hero action needs a \"link\" field" }),
|
|
40
|
-
variant: z.enum([
|
|
41
|
-
"primary",
|
|
42
|
-
"secondary",
|
|
43
|
-
"outline"
|
|
44
|
-
], { error: "hero action \"variant\" must be \"primary\", \"secondary\", or \"outline\"" }).default("primary"),
|
|
45
|
-
icon: z.string().optional()
|
|
46
37
|
});
|
|
47
|
-
const
|
|
48
|
-
title: z.string().optional(),
|
|
49
|
-
tagline: z.string().optional(),
|
|
50
|
-
actions: z.array(heroActionSchema).optional()
|
|
51
|
-
}).optional();
|
|
52
|
-
const prevNextFrontmatterSchema = z.union([
|
|
38
|
+
const prevNextSchema = z.union([
|
|
53
39
|
z.string(),
|
|
54
40
|
z.object({
|
|
55
41
|
link: z.string().optional(),
|
|
@@ -67,27 +53,81 @@ const headElementSchema = z.object({
|
|
|
67
53
|
attrs: z.record(z.string(), z.string()).default({}),
|
|
68
54
|
content: z.string().optional()
|
|
69
55
|
});
|
|
56
|
+
const bannerSchema = z.object({
|
|
57
|
+
content: z.string({ error: "banner \"content\" must be a string" }),
|
|
58
|
+
type: z.enum([
|
|
59
|
+
"note",
|
|
60
|
+
"tip",
|
|
61
|
+
"caution",
|
|
62
|
+
"danger"
|
|
63
|
+
], { error: "banner \"type\" must be one of: note, tip, caution, danger" }).optional(),
|
|
64
|
+
dismissible: z.object({
|
|
65
|
+
id: z.string({ error: "banner \"dismissible.id\" must be a string — a stable identifier you bump when banner content meaningfully changes" }),
|
|
66
|
+
days: z.number({ error: "banner \"dismissible.days\" must be a number" }).optional()
|
|
67
|
+
}).optional()
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Frontmatter keys that used to exist but no longer do. When user content
|
|
71
|
+
* still carries one of these, Zod's default `.strip()` behavior would
|
|
72
|
+
* silently drop it — the page would build, but with subtly different
|
|
73
|
+
* behavior (the old toggle just disappears). That's a confusing failure
|
|
74
|
+
* mode for an authoring contract.
|
|
75
|
+
*
|
|
76
|
+
* `withUnknownKeyCheck` (below) consults this map. Hits get the friendly
|
|
77
|
+
* migration message verbatim; everything else falls through to a generic
|
|
78
|
+
* "Unknown frontmatter key" error so typos don't sneak past either.
|
|
79
|
+
*
|
|
80
|
+
* Entries removed in the v0 → v1 cleanup:
|
|
81
|
+
*
|
|
82
|
+
* - `template` → `mode`: same shape, but `"splash"` is now `"custom"`.
|
|
83
|
+
* - `pagefind` → `searchable`: same boolean; default now derives from
|
|
84
|
+
* `noindex` (a non-crawlable page is non-searchable by default).
|
|
85
|
+
* - `llms`: removed. All published pages are listed in `/llms.txt`; the
|
|
86
|
+
* escape hatch is `noindex: true`.
|
|
87
|
+
* - `aiDeprioritize`: removed. The framework no longer emits an
|
|
88
|
+
* agent-downrank signal.
|
|
89
|
+
* - `hero`: removed. Compose the hero in MDX body with user-owned
|
|
90
|
+
* components instead of a frontmatter contract.
|
|
91
|
+
*/
|
|
92
|
+
const REMOVED_FRONTMATTER_KEYS = {
|
|
93
|
+
template: "was renamed to \"mode\". Replace `template: \"doc\"` with `mode: \"doc\"`, and `template: \"splash\"` with `mode: \"custom\"`.",
|
|
94
|
+
pagefind: "was renamed to \"searchable\". Same boolean shape; the default now derives from `noindex` (a non-crawlable page is non-searchable unless you set `searchable: true` explicitly).",
|
|
95
|
+
llms: "was removed. Every published page is now listed in /llms.txt; use `noindex: true` to keep a page out of both search engines and the LLM index.",
|
|
96
|
+
aiDeprioritize: "was removed. The framework no longer emits an agent-downrank signal. If you want a page hidden from agents, use `noindex: true`.",
|
|
97
|
+
hero: "was removed. Compose your hero in the MDX body using user-owned components; there is no longer a `hero` frontmatter contract."
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Apply this AFTER any `.extend()` so user-added fields are recognized
|
|
101
|
+
* as valid. Wraps the schema in `.passthrough().superRefine()` so removed
|
|
102
|
+
* keys raise a guided migration error; other unknown keys raise a
|
|
103
|
+
* generic error pointing at `defineSchema({ extend: ... })`.
|
|
104
|
+
*/
|
|
105
|
+
function withFrontmatterKeyCheck(schema) {
|
|
106
|
+
return withStrictKeys(schema, {
|
|
107
|
+
removedKeys: REMOVED_FRONTMATTER_KEYS,
|
|
108
|
+
contextLabel: "Frontmatter key",
|
|
109
|
+
unknownHint: (key) => `If you meant to add a custom field, declare it in your collection's schema via \`defineSchema({ extend: z.object({ ${key}: ... }) })\`.`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
70
112
|
function baseDocSchema() {
|
|
71
113
|
return z.object({
|
|
72
114
|
title: z.string({ error: (iss) => iss.input === void 0 ? "Missing required \"title\" in frontmatter. Every doc needs:\n\n ---\n title: \"Your Page Title\"\n ---" : `"title" must be a string, received ${typeof iss.input}` }),
|
|
73
115
|
description: z.string({ error: "\"description\" must be a string" }).optional(),
|
|
74
|
-
|
|
75
|
-
sidebar:
|
|
76
|
-
hero: heroSchema,
|
|
116
|
+
mode: z.enum(["doc", "custom"], { error: "\"mode\" must be \"doc\" or \"custom\"" }).default("doc"),
|
|
117
|
+
sidebar: z.union([z.literal(false), sidebarSchema]).optional(),
|
|
77
118
|
head: z.array(headElementSchema).default([]),
|
|
119
|
+
banner: bannerSchema.optional(),
|
|
78
120
|
draft: z.boolean({ error: "\"draft\" must be true or false" }).default(false),
|
|
79
121
|
noindex: z.boolean({ error: "\"noindex\" must be true or false" }).default(false),
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
pagefind: z.boolean({ error: "\"pagefind\" must be true or false" }).default(true),
|
|
83
|
-
tableOfContents: z.object({
|
|
122
|
+
searchable: z.boolean({ error: "\"searchable\" must be true or false" }).optional(),
|
|
123
|
+
tableOfContents: z.union([z.literal(false), z.object({
|
|
84
124
|
minHeadingLevel: z.number({ error: "\"minHeadingLevel\" must be a number (1-6)" }).int().min(1).max(6).default(2),
|
|
85
125
|
maxHeadingLevel: z.number({ error: "\"maxHeadingLevel\" must be a number (1-6)" }).int().min(1).max(6).default(3)
|
|
86
|
-
}).refine((v) => v.minHeadingLevel <= v.maxHeadingLevel, { message: "minHeadingLevel must be <= maxHeadingLevel" }).optional(),
|
|
126
|
+
}).refine((v) => v.minHeadingLevel <= v.maxHeadingLevel, { message: "minHeadingLevel must be <= maxHeadingLevel" })]).optional(),
|
|
87
127
|
lastUpdated: z.coerce.date({ error: "\"lastUpdated\" must be a valid date (e.g. 2024-01-15)" }).optional(),
|
|
88
128
|
socialImage: z.string({ error: "\"socialImage\" must be a string (path or URL)" }).optional(),
|
|
89
|
-
prev:
|
|
90
|
-
next:
|
|
129
|
+
prev: prevNextSchema,
|
|
130
|
+
next: prevNextSchema,
|
|
91
131
|
previousSlug: z.union([z.string(), z.array(z.string())], { error: "\"previousSlug\" must be a string or array of strings" }).optional()
|
|
92
132
|
});
|
|
93
133
|
}
|
|
@@ -95,16 +135,59 @@ function baseDocSchema() {
|
|
|
95
135
|
* Build a customizable docs schema. Use this when composing schemas outside
|
|
96
136
|
* the `docsCollection()` factory (e.g. multiple docs collections with
|
|
97
137
|
* different shapes).
|
|
138
|
+
*
|
|
139
|
+
* For typed Astro `image()` fields or per-collection field narrowing, use
|
|
140
|
+
* the richer `defineSchema(ctx => ...)` factory instead.
|
|
98
141
|
*/
|
|
99
142
|
function defineDocSchema(config = {}) {
|
|
100
143
|
const base = baseDocSchema();
|
|
101
|
-
|
|
102
|
-
|
|
144
|
+
return withFrontmatterKeyCheck(config.fields ? base.extend(config.fields) : base);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Build a typed docs schema with access to the Astro `SchemaContext`.
|
|
148
|
+
* Use this when you want typed image fields (`ctx.image()`), per-
|
|
149
|
+
* collection narrowing of framework fields, or both. The simpler
|
|
150
|
+
* `defineDocSchema({ fields })` factory is still available for the
|
|
151
|
+
* common case of just adding fields.
|
|
152
|
+
*
|
|
153
|
+
* import { defineCollection } from "astro:content";
|
|
154
|
+
* import { z } from "astro/zod";
|
|
155
|
+
* import { defineSchema } from "nimbus-docs/schemas";
|
|
156
|
+
*
|
|
157
|
+
* export const collections = {
|
|
158
|
+
* docs: defineCollection({
|
|
159
|
+
* loader: ...,
|
|
160
|
+
* schema: defineSchema((ctx) => ({
|
|
161
|
+
* extend: z.object({
|
|
162
|
+
* cover: ctx.image().optional(),
|
|
163
|
+
* author: z.string().optional(),
|
|
164
|
+
* }),
|
|
165
|
+
* narrow: {
|
|
166
|
+
* mode: z.literal("doc"), // no landing pages here
|
|
167
|
+
* },
|
|
168
|
+
* })),
|
|
169
|
+
* }),
|
|
170
|
+
* };
|
|
171
|
+
*/
|
|
172
|
+
function defineSchema(factory) {
|
|
173
|
+
return (ctx) => {
|
|
174
|
+
const { extend, narrow } = factory(ctx);
|
|
175
|
+
let schema = baseDocSchema();
|
|
176
|
+
if (narrow) schema = schema.extend(narrow);
|
|
177
|
+
if (extend) if (extend instanceof z.ZodObject) schema = schema.merge(extend);
|
|
178
|
+
else return schema.and(extend);
|
|
179
|
+
return withFrontmatterKeyCheck(schema);
|
|
180
|
+
};
|
|
103
181
|
}
|
|
104
182
|
/** Default docs schema. Equivalent to `defineDocSchema()`. */
|
|
105
183
|
const docsSchema = defineDocSchema();
|
|
184
|
+
const partialsObjectSchema = z.object({ params: z.array(z.string()).optional() });
|
|
106
185
|
/** Schema for partials (`<Render file="..." />` snippets). */
|
|
107
|
-
const partialsSchema =
|
|
186
|
+
const partialsSchema = partialsObjectSchema.default({});
|
|
187
|
+
/** Docs frontmatter, framework fields type-checked, extra keys allowed. */
|
|
188
|
+
const lenientDocsSchema = baseDocSchema().passthrough();
|
|
189
|
+
/** Partials frontmatter, framework fields type-checked, extra keys allowed. */
|
|
190
|
+
const lenientPartialsSchema = partialsObjectSchema.passthrough();
|
|
108
191
|
/** One row in a component's `props` frontmatter array. */
|
|
109
192
|
const componentPropSchema = z.object({
|
|
110
193
|
name: z.string({ error: "prop needs a \"name\"" }),
|
|
@@ -121,5 +204,5 @@ const componentsSchema = z.object({
|
|
|
121
204
|
});
|
|
122
205
|
|
|
123
206
|
//#endregion
|
|
124
|
-
export { componentsSchema, defineDocSchema, docsSchema, partialsSchema };
|
|
207
|
+
export { componentsSchema, defineDocSchema, defineSchema, docsSchema, lenientDocsSchema, lenientPartialsSchema, partialsSchema };
|
|
125
208
|
//# sourceMappingURL=schemas.js.map
|
package/dist/schemas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["/**\n * Content schemas for Nimbus.\n *\n * `docsSchema` is the default frontmatter contract for the `docs` collection.\n * `partialsSchema` is the contract for `<Render file=\"...\" />` partials.\n * `defineDocSchema(config)` returns a customizable schema for advanced users\n * composing schemas outside the `docsCollection()` factory.\n *\n * Error messages target content authors, not framework developers.\n * Astro 6 ships Zod v4 via `astro/zod`. The v4 API uses a single `error`\n * field on every schema constructor — NOT v3's `required_error` /\n * `invalid_type_error` / `errorMap`.\n */\n\nimport { z } from \"astro/zod\";\n\nexport interface DocSchemaConfig {\n /** Additional frontmatter fields merged into the default schema. */\n fields?: Record<string, z.ZodTypeAny>;\n}\n\n// ---------------------------------------------------------------------------\n// Building blocks\n// ---------------------------------------------------------------------------\n\nconst sidebarBadgeSchema = z.union([\n z.string(),\n z.object({\n text: z.string({ error: 'sidebar badge needs a \"text\" field' }),\n variant: z\n .enum(\n [\"default\", \"info\", \"note\", \"success\", \"tip\", \"warning\", \"caution\", \"danger\"],\n {\n error:\n '\"variant\" must be one of: default, info, note, success, tip, warning, caution, danger',\n },\n )\n .default(\"default\"),\n }),\n]);\n\nconst sidebarFrontmatterSchema = z\n .object({\n order: z.number({ error: '\"sidebar.order\" must be a number' }).optional(),\n label: z.string({ error: '\"sidebar.label\" must be a string' }).optional(),\n badge: sidebarBadgeSchema.optional(),\n hidden: z.boolean({ error: '\"sidebar.hidden\" must be true or false' }).optional(),\n hideChildren: z\n .boolean({ error: '\"sidebar.hideChildren\" must be true or false' })\n .optional(),\n })\n .optional();\n\nconst heroActionSchema = z.object({\n text: z.string({ error: 'hero action needs a \"text\" field' }),\n link: z.string({ error: 'hero action needs a \"link\" field' }),\n variant: z\n .enum([\"primary\", \"secondary\", \"outline\"], {\n error: 'hero action \"variant\" must be \"primary\", \"secondary\", or \"outline\"',\n })\n .default(\"primary\"),\n icon: z.string().optional(),\n});\n\nconst heroSchema = z\n .object({\n title: z.string().optional(),\n tagline: z.string().optional(),\n actions: z.array(heroActionSchema).optional(),\n })\n .optional();\n\nconst prevNextFrontmatterSchema = z\n .union([\n z.string(),\n z.object({ link: z.string().optional(), label: z.string().optional() }),\n z.literal(false),\n ])\n .optional();\n\nconst headElementSchema = z.object({\n tag: z.enum([\"meta\", \"link\", \"script\", \"style\"], {\n error: 'head element \"tag\" must be \"meta\", \"link\", \"script\", or \"style\"',\n }),\n attrs: z.record(z.string(), z.string()).default({}),\n content: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Base docs schema\n// ---------------------------------------------------------------------------\n\nfunction baseDocSchema() {\n return z.object({\n title: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing required \"title\" in frontmatter. Every doc needs:\\n\\n ---\\n title: \"Your Page Title\"\\n ---'\n : `\"title\" must be a string, received ${typeof iss.input}`,\n }),\n description: z.string({ error: '\"description\" must be a string' }).optional(),\n template: z\n .enum([\"doc\", \"splash\"], {\n error: '\"template\" must be \"doc\" or \"splash\"',\n })\n .default(\"doc\"),\n sidebar: sidebarFrontmatterSchema,\n hero: heroSchema,\n head: z.array(headElementSchema).default([]),\n draft: z.boolean({ error: '\"draft\" must be true or false' }).default(false),\n noindex: z.boolean({ error: '\"noindex\" must be true or false' }).default(false),\n /** Include this page in llms.txt and AI consumption indexes */\n llms: z.boolean({ error: '\"llms\" must be true or false' }).default(true),\n /** Signal AI crawlers to deprioritize this page */\n aiDeprioritize: z\n .boolean({ error: '\"aiDeprioritize\" must be true or false' })\n .default(false),\n pagefind: z.boolean({ error: '\"pagefind\" must be true or false' }).default(true),\n tableOfContents: z\n .object({\n minHeadingLevel: z\n .number({ error: '\"minHeadingLevel\" must be a number (1-6)' })\n .int()\n .min(1)\n .max(6)\n .default(2),\n maxHeadingLevel: z\n .number({ error: '\"maxHeadingLevel\" must be a number (1-6)' })\n .int()\n .min(1)\n .max(6)\n .default(3),\n })\n .refine((v) => v.minHeadingLevel <= v.maxHeadingLevel, {\n message: \"minHeadingLevel must be <= maxHeadingLevel\",\n })\n .optional(),\n lastUpdated: z.coerce\n .date({ error: '\"lastUpdated\" must be a valid date (e.g. 2024-01-15)' })\n .optional(),\n /**\n * Explicit per-page social/OG image override (path or absolute URL).\n * When omitted, the page route is expected to fall back to a\n * programmatically-generated card or the site-wide `config.socialImage`.\n */\n socialImage: z\n .string({ error: '\"socialImage\" must be a string (path or URL)' })\n .optional(),\n prev: prevNextFrontmatterSchema,\n next: prevNextFrontmatterSchema,\n /**\n * Versioning rename escape hatch.\n *\n * When a page is renamed between versions (the URL slug changes), the\n * newer version's frontmatter declares the slug it had in an older\n * version. The framework uses this to link the pages as cross-version\n * alternates and to emit a `<link rel=\"canonical\">` to the current\n * version's URL.\n *\n * Example: `docs-v1/old-name.mdx` was renamed in v2 to `new-name.mdx`.\n * On the new page (`docs/new-name.mdx`, current version), set:\n *\n * previousSlug: old-name\n *\n * Now `/new-name` and `/v1/old-name` are linked as the same logical\n * page in `<head>` alternates, and the v1 page's canonical points to\n * `/new-name`.\n *\n * Accepts a single slug string (the page's id in the older version)\n * or an array of strings when the page has been renamed across more\n * than one version.\n */\n previousSlug: z\n .union([z.string(), z.array(z.string())], {\n error: '\"previousSlug\" must be a string or array of strings',\n })\n .optional(),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a customizable docs schema. Use this when composing schemas outside\n * the `docsCollection()` factory (e.g. multiple docs collections with\n * different shapes).\n */\nexport function defineDocSchema(config: DocSchemaConfig = {}) {\n const base = baseDocSchema();\n if (config.fields) {\n return base.extend(config.fields);\n }\n return base;\n}\n\n/** Default docs schema. Equivalent to `defineDocSchema()`. */\nexport const docsSchema = defineDocSchema();\n\n/** Schema for partials (`<Render file=\"...\" />` snippets). */\nexport const partialsSchema = z\n .object({\n /**\n * Declared parameters this partial accepts.\n * Suffix with `?` for optional params: `[\"name\", \"deprecated?\"]`\n */\n params: z.array(z.string()).optional(),\n })\n .default({});\n\n// ---------------------------------------------------------------------------\n// Components collection — used by sites documenting their own UI components.\n// Pair with `componentsCollection()` from `nimbus-docs/content`. Authoring\n// pattern: hero `<Showcase>` block + `<Example>` blocks in the MDX body, with\n// `props` declared in frontmatter for a generated prop table.\n// ---------------------------------------------------------------------------\n\n/** One row in a component's `props` frontmatter array. */\nconst componentPropSchema = z.object({\n name: z.string({ error: 'prop needs a \"name\"' }),\n type: z.string({ error: 'prop needs a \"type\"' }),\n defaultValue: z.string().optional(),\n required: z.boolean().default(false),\n description: z.string({ error: 'prop needs a \"description\"' }),\n});\n\nexport type ComponentProp = z.infer<typeof componentPropSchema>;\n\n/** Default schema for the components collection. */\nexport const componentsSchema = z.object({\n title: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing \"title\" in frontmatter — display name used in the sidebar and page header.'\n : `\"title\" must be a string, received ${typeof iss.input}`,\n }),\n tagline: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing \"tagline\" in frontmatter — one-sentence summary shown under the title.'\n : `\"tagline\" must be a string, received ${typeof iss.input}`,\n }),\n props: z.array(componentPropSchema).default([]),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["/**\n * Content schemas for Nimbus.\n *\n * `docsSchema` is the default frontmatter contract for the `docs` collection.\n * `partialsSchema` is the contract for `<Render file=\"...\" />` partials.\n * `defineDocSchema(config)` returns a customizable schema for advanced users\n * composing schemas outside the `docsCollection()` factory.\n *\n * Error messages target content authors, not framework developers.\n * Astro 6 ships Zod v4 via `astro/zod`. The v4 API uses a single `error`\n * field on every schema constructor — NOT v3's `required_error` /\n * `invalid_type_error` / `errorMap`.\n */\n\nimport { z } from \"astro/zod\";\nimport { withStrictKeys } from \"./_internal/strict-keys.js\";\n\nexport interface DocSchemaConfig {\n /** Additional frontmatter fields merged into the default schema. */\n fields?: Record<string, z.ZodTypeAny>;\n}\n\n// ---------------------------------------------------------------------------\n// Building blocks\n// ---------------------------------------------------------------------------\n\nconst sidebarBadgeSchema = z.union([\n z.string(),\n z.object({\n text: z.string({ error: 'sidebar badge needs a \"text\" field' }),\n variant: z\n .enum(\n [\"default\", \"info\", \"note\", \"success\", \"tip\", \"warning\", \"caution\", \"danger\"],\n {\n error:\n '\"variant\" must be one of: default, info, note, success, tip, warning, caution, danger',\n },\n )\n .default(\"default\"),\n }),\n]);\n\nconst sidebarSchema = z.object({\n order: z.number({ error: '\"sidebar.order\" must be a number' }).optional(),\n label: z.string({ error: '\"sidebar.label\" must be a string' }).optional(),\n badge: sidebarBadgeSchema.optional(),\n hidden: z.boolean({ error: '\"sidebar.hidden\" must be true or false' }).optional(),\n hideChildren: z\n .boolean({ error: '\"sidebar.hideChildren\" must be true or false' })\n .optional(),\n});\n\nconst prevNextSchema = z\n .union([\n z.string(),\n z.object({ link: z.string().optional(), label: z.string().optional() }),\n z.literal(false),\n ])\n .optional();\n\nconst headElementSchema = z.object({\n tag: z.enum([\"meta\", \"link\", \"script\", \"style\"], {\n error: 'head element \"tag\" must be \"meta\", \"link\", \"script\", or \"style\"',\n }),\n attrs: z.record(z.string(), z.string()).default({}),\n content: z.string().optional(),\n});\n\n// Mirrors `BannerProps` in types.ts. Layouts consume this directly off\n// `entry.data.banner` and render the `<Banner>` component with it, so the\n// schema is framework-owned (not user-extensible territory).\nconst bannerSchema = z.object({\n content: z.string({ error: 'banner \"content\" must be a string' }),\n type: z\n .enum([\"note\", \"tip\", \"caution\", \"danger\"], {\n error: 'banner \"type\" must be one of: note, tip, caution, danger',\n })\n .optional(),\n dismissible: z\n .object({\n id: z.string({\n error: 'banner \"dismissible.id\" must be a string — a stable identifier you bump when banner content meaningfully changes',\n }),\n days: z.number({ error: 'banner \"dismissible.days\" must be a number' }).optional(),\n })\n .optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Removed/renamed frontmatter keys — surface migration errors loudly\n// ---------------------------------------------------------------------------\n\n/**\n * Frontmatter keys that used to exist but no longer do. When user content\n * still carries one of these, Zod's default `.strip()` behavior would\n * silently drop it — the page would build, but with subtly different\n * behavior (the old toggle just disappears). That's a confusing failure\n * mode for an authoring contract.\n *\n * `withUnknownKeyCheck` (below) consults this map. Hits get the friendly\n * migration message verbatim; everything else falls through to a generic\n * \"Unknown frontmatter key\" error so typos don't sneak past either.\n *\n * Entries removed in the v0 → v1 cleanup:\n *\n * - `template` → `mode`: same shape, but `\"splash\"` is now `\"custom\"`.\n * - `pagefind` → `searchable`: same boolean; default now derives from\n * `noindex` (a non-crawlable page is non-searchable by default).\n * - `llms`: removed. All published pages are listed in `/llms.txt`; the\n * escape hatch is `noindex: true`.\n * - `aiDeprioritize`: removed. The framework no longer emits an\n * agent-downrank signal.\n * - `hero`: removed. Compose the hero in MDX body with user-owned\n * components instead of a frontmatter contract.\n */\nconst REMOVED_FRONTMATTER_KEYS: Record<string, string> = {\n template:\n 'was renamed to \"mode\". Replace `template: \"doc\"` with `mode: \"doc\"`, and `template: \"splash\"` with `mode: \"custom\"`.',\n pagefind:\n 'was renamed to \"searchable\". Same boolean shape; the default now derives from `noindex` (a non-crawlable page is non-searchable unless you set `searchable: true` explicitly).',\n llms:\n \"was removed. Every published page is now listed in /llms.txt; use `noindex: true` to keep a page out of both search engines and the LLM index.\",\n aiDeprioritize:\n \"was removed. The framework no longer emits an agent-downrank signal. If you want a page hidden from agents, use `noindex: true`.\",\n hero:\n \"was removed. Compose your hero in the MDX body using user-owned components; there is no longer a `hero` frontmatter contract.\",\n};\n\n/**\n * Apply this AFTER any `.extend()` so user-added fields are recognized\n * as valid. Wraps the schema in `.passthrough().superRefine()` so removed\n * keys raise a guided migration error; other unknown keys raise a\n * generic error pointing at `defineSchema({ extend: ... })`.\n */\nfunction withFrontmatterKeyCheck<T extends z.ZodObject<z.ZodRawShape>>(schema: T) {\n return withStrictKeys(schema, {\n removedKeys: REMOVED_FRONTMATTER_KEYS,\n contextLabel: \"Frontmatter key\",\n unknownHint: (key) =>\n `If you meant to add a custom field, declare it in your collection's schema via \\`defineSchema({ extend: z.object({ ${key}: ... }) })\\`.`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Base docs schema\n// ---------------------------------------------------------------------------\n\nfunction baseDocSchema() {\n return z.object({\n title: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing required \"title\" in frontmatter. Every doc needs:\\n\\n ---\\n title: \"Your Page Title\"\\n ---'\n : `\"title\" must be a string, received ${typeof iss.input}`,\n }),\n description: z.string({ error: '\"description\" must be a string' }).optional(),\n mode: z\n .enum([\"doc\", \"custom\"], {\n error: '\"mode\" must be \"doc\" or \"custom\"',\n })\n .default(\"doc\"),\n sidebar: z.union([z.literal(false), sidebarSchema]).optional(),\n head: z.array(headElementSchema).default([]),\n banner: bannerSchema.optional(),\n draft: z.boolean({ error: '\"draft\" must be true or false' }).default(false),\n noindex: z.boolean({ error: '\"noindex\" must be true or false' }).default(false),\n /**\n * Whether this page is included in the site search index. When omitted,\n * derives from `noindex` (a page that's not crawlable is by default not\n * searchable). Set explicitly to override — e.g. `{ noindex: true,\n * searchable: true }` keeps the page out of search engines but findable\n * in the site's own search.\n */\n searchable: z\n .boolean({ error: '\"searchable\" must be true or false' })\n .optional(),\n tableOfContents: z\n .union([\n z.literal(false),\n z\n .object({\n minHeadingLevel: z\n .number({ error: '\"minHeadingLevel\" must be a number (1-6)' })\n .int()\n .min(1)\n .max(6)\n .default(2),\n maxHeadingLevel: z\n .number({ error: '\"maxHeadingLevel\" must be a number (1-6)' })\n .int()\n .min(1)\n .max(6)\n .default(3),\n })\n .refine((v) => v.minHeadingLevel <= v.maxHeadingLevel, {\n message: \"minHeadingLevel must be <= maxHeadingLevel\",\n }),\n ])\n .optional(),\n lastUpdated: z.coerce\n .date({ error: '\"lastUpdated\" must be a valid date (e.g. 2024-01-15)' })\n .optional(),\n /**\n * Explicit per-page social/OG image override (path or absolute URL).\n * When omitted, the page route is expected to fall back to a\n * programmatically-generated card or the site-wide `config.socialImage`.\n */\n socialImage: z\n .string({ error: '\"socialImage\" must be a string (path or URL)' })\n .optional(),\n prev: prevNextSchema,\n next: prevNextSchema,\n /**\n * Versioning rename escape hatch.\n *\n * When a page is renamed between versions (the URL slug changes), the\n * newer version's frontmatter declares the slug it had in an older\n * version. The framework uses this to link the pages as cross-version\n * alternates and to emit a `<link rel=\"canonical\">` to the current\n * version's URL.\n *\n * Example: `docs-v1/old-name.mdx` was renamed in v2 to `new-name.mdx`.\n * On the new page (`docs/new-name.mdx`, current version), set:\n *\n * previousSlug: old-name\n *\n * Now `/new-name` and `/v1/old-name` are linked as the same logical\n * page in `<head>` alternates, and the v1 page's canonical points to\n * `/new-name`.\n *\n * Accepts a single slug string (the page's id in the older version)\n * or an array of strings when the page has been renamed across more\n * than one version.\n */\n previousSlug: z\n .union([z.string(), z.array(z.string())], {\n error: '\"previousSlug\" must be a string or array of strings',\n })\n .optional(),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a customizable docs schema. Use this when composing schemas outside\n * the `docsCollection()` factory (e.g. multiple docs collections with\n * different shapes).\n *\n * For typed Astro `image()` fields or per-collection field narrowing, use\n * the richer `defineSchema(ctx => ...)` factory instead.\n */\nexport function defineDocSchema(config: DocSchemaConfig = {}) {\n const base = baseDocSchema();\n // Apply the unknown-key check AFTER extends so user-added fields are\n // recognized as valid. Order matters — `.passthrough().superRefine()`\n // returns a ZodEffects, which can't be further `.extend()`-ed.\n const merged = config.fields ? base.extend(config.fields) : base;\n return withFrontmatterKeyCheck(merged);\n}\n\n/**\n * Factory options for `defineSchema`.\n *\n * - `extend`: additional fields merged into the framework schema. Use\n * this for user-defined frontmatter (`author`, `tags`, `cover`).\n * - `narrow`: replaces framework fields with tighter types within this\n * collection. Use when a collection has stricter rules — e.g.\n * `{ mode: z.literal(\"doc\") }` says no landing pages in this collection.\n */\nexport interface DefineSchemaOptions {\n extend?: z.ZodTypeAny;\n narrow?: Record<string, z.ZodTypeAny>;\n}\n\n/**\n * Build a typed docs schema with access to the Astro `SchemaContext`.\n * Use this when you want typed image fields (`ctx.image()`), per-\n * collection narrowing of framework fields, or both. The simpler\n * `defineDocSchema({ fields })` factory is still available for the\n * common case of just adding fields.\n *\n * import { defineCollection } from \"astro:content\";\n * import { z } from \"astro/zod\";\n * import { defineSchema } from \"nimbus-docs/schemas\";\n *\n * export const collections = {\n * docs: defineCollection({\n * loader: ...,\n * schema: defineSchema((ctx) => ({\n * extend: z.object({\n * cover: ctx.image().optional(),\n * author: z.string().optional(),\n * }),\n * narrow: {\n * mode: z.literal(\"doc\"), // no landing pages here\n * },\n * })),\n * }),\n * };\n */\nexport function defineSchema(\n factory: (ctx: import(\"astro:content\").SchemaContext) => DefineSchemaOptions,\n) {\n return (ctx: import(\"astro:content\").SchemaContext) => {\n const { extend, narrow } = factory(ctx);\n let schema = baseDocSchema() as z.ZodObject<any>;\n\n // narrowing first (overrides framework fields)\n if (narrow) {\n schema = schema.extend(narrow);\n }\n\n // additive extension (new user fields). Prefer .merge for ZodObject\n // (preserves object-ness for downstream .extend); fall back to .and\n // for any other Zod type (intersection, union, etc.).\n if (extend) {\n if (extend instanceof z.ZodObject) {\n schema = schema.merge(extend);\n } else {\n // Intersection path — `.and()` returns ZodIntersection, on which\n // we can't run `withUnknownKeyCheck` (it operates on ZodObject's\n // `.shape`). Users on this path lose the removed-key migration\n // diagnostic; they own that trade-off by reaching for the\n // non-object extend.\n return schema.and(extend);\n }\n }\n\n return withFrontmatterKeyCheck(schema);\n };\n}\n\n/** Default docs schema. Equivalent to `defineDocSchema()`. */\nexport const docsSchema = defineDocSchema();\n\nconst partialsObjectSchema = z.object({\n /**\n * Declared parameters this partial accepts.\n * Suffix with `?` for optional params: `[\"name\", \"deprecated?\"]`\n */\n params: z.array(z.string()).optional(),\n});\n\n/** Schema for partials (`<Render file=\"...\" />` snippets). */\nexport const partialsSchema = partialsObjectSchema.default({});\n\n// ---------------------------------------------------------------------------\n// Lenient variants — used by `nimbus-docs lint` (`nimbus/frontmatter-shape`).\n//\n// The standalone lint CLI can't yet see a site's extended\n// `content.config.ts` schema, so it validates the *types* of the fields\n// the framework owns while tolerating user-added fields (passthrough).\n// Unknown-key detection is deferred to when the engine can load the real\n// per-collection schema.\n// ---------------------------------------------------------------------------\n\n/** Docs frontmatter, framework fields type-checked, extra keys allowed. */\nexport const lenientDocsSchema = baseDocSchema().passthrough();\n\n/** Partials frontmatter, framework fields type-checked, extra keys allowed. */\nexport const lenientPartialsSchema = partialsObjectSchema.passthrough();\n\n// ---------------------------------------------------------------------------\n// Components collection — used by sites documenting their own UI components.\n// Pair with `componentsCollection()` from `nimbus-docs/content`. Authoring\n// pattern: hero `<Showcase>` block + `<Example>` blocks in the MDX body, with\n// `props` declared in frontmatter for a generated prop table.\n// ---------------------------------------------------------------------------\n\n/** One row in a component's `props` frontmatter array. */\nconst componentPropSchema = z.object({\n name: z.string({ error: 'prop needs a \"name\"' }),\n type: z.string({ error: 'prop needs a \"type\"' }),\n defaultValue: z.string().optional(),\n required: z.boolean().default(false),\n description: z.string({ error: 'prop needs a \"description\"' }),\n});\n\nexport type ComponentProp = z.infer<typeof componentPropSchema>;\n\n/** Default schema for the components collection. */\nexport const componentsSchema = z.object({\n title: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing \"title\" in frontmatter — display name used in the sidebar and page header.'\n : `\"title\" must be a string, received ${typeof iss.input}`,\n }),\n tagline: z.string({\n error: (iss) =>\n iss.input === undefined\n ? 'Missing \"tagline\" in frontmatter — one-sentence summary shown under the title.'\n : `\"tagline\" must be a string, received ${typeof iss.input}`,\n }),\n props: z.array(componentPropSchema).default([]),\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/_internal/strict-keys.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wrap `schema` so unknown keys raise issues at parse time. Captures
|
|
4
|
+
* `Object.keys(schema.shape)` eagerly (before turning the schema into a
|
|
5
|
+
* `ZodEffects` via `superRefine`) so the known-key set reflects the
|
|
6
|
+
* schema as passed in. Call this AFTER any `.extend()` / `.merge()` so
|
|
7
|
+
* user-added fields are recognized.
|
|
8
|
+
*/
|
|
9
|
+
function withStrictKeys(schema, options) {
|
|
10
|
+
const knownKeys = new Set(Object.keys(schema.shape));
|
|
11
|
+
return schema.passthrough().superRefine((data, ctx) => {
|
|
12
|
+
if (!data || typeof data !== "object") return;
|
|
13
|
+
for (const key of Object.keys(data)) {
|
|
14
|
+
if (knownKeys.has(key)) continue;
|
|
15
|
+
const removal = options.removedKeys[key];
|
|
16
|
+
if (removal) ctx.addIssue({
|
|
17
|
+
code: "custom",
|
|
18
|
+
path: [key],
|
|
19
|
+
message: `${options.contextLabel} "${key}" ${removal}`
|
|
20
|
+
});
|
|
21
|
+
else {
|
|
22
|
+
const hint = options.unknownHint?.(key);
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: "custom",
|
|
25
|
+
path: [key],
|
|
26
|
+
message: hint ? `Unknown ${options.contextLabel.toLowerCase()} "${key}". ${hint}` : `Unknown ${options.contextLabel.toLowerCase()} "${key}".`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { withStrictKeys as t };
|
|
35
|
+
//# sourceMappingURL=strict-keys-BiXiT3pq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strict-keys-BiXiT3pq.js","names":[],"sources":["../src/_internal/strict-keys.ts"],"sourcesContent":["/**\n * Strict-key validation helper for Zod object schemas.\n *\n * Zod's default `.strip()` behavior silently drops unknown keys, which is\n * the wrong default for an authoring contract: a stale frontmatter or\n * config field that's been renamed or removed should fail loudly, not\n * vanish at parse time and produce subtly different behavior.\n *\n * This module wraps a `ZodObject` with `.passthrough().superRefine()` so\n * unknown keys survive the parse and get rejected with one issue per\n * key. Known-removed keys (passed via `removedKeys`) emit a friendly\n * migration message; everything else emits a generic \"unknown key\"\n * error with an actionable hint.\n *\n * Used by both the frontmatter schema (`src/schemas.ts`) and the config\n * schema (`src/_internal/validate.ts`). Each call site provides its own\n * `removedKeys` map and a `contextLabel` (\"Frontmatter key\" vs \"Config\n * field\" vs \"features.<key>\") so error messages read naturally.\n */\n\nimport type { z } from \"astro/zod\";\n\nexport interface StrictKeyOptions {\n /**\n * Map of removed-or-renamed keys → migration message. The message\n * follows the contextLabel + key prefix; phrase it as the back-half of\n * the sentence. Example: `'was renamed to \"mode\". Replace ...'`.\n */\n removedKeys: Record<string, string>;\n /**\n * Sentence-start label for the issue message. Examples: `Frontmatter\n * key`, `Config field`, `features sub-key`. The key name (quoted) is\n * appended automatically.\n */\n contextLabel: string;\n /**\n * Optional hint appended to the generic \"unknown key\" message for\n * keys NOT in `removedKeys`. Receives the offending key. Use this to\n * point users at the right escape hatch (e.g. how to add a custom\n * field in their schema).\n */\n unknownHint?: (key: string) => string;\n}\n\n/**\n * Wrap `schema` so unknown keys raise issues at parse time. Captures\n * `Object.keys(schema.shape)` eagerly (before turning the schema into a\n * `ZodEffects` via `superRefine`) so the known-key set reflects the\n * schema as passed in. Call this AFTER any `.extend()` / `.merge()` so\n * user-added fields are recognized.\n */\nexport function withStrictKeys<T extends z.ZodObject<z.ZodRawShape>>(\n schema: T,\n options: StrictKeyOptions,\n) {\n const knownKeys = new Set(Object.keys(schema.shape));\n return schema.passthrough().superRefine((data, ctx) => {\n if (!data || typeof data !== \"object\") return;\n for (const key of Object.keys(data as Record<string, unknown>)) {\n if (knownKeys.has(key)) continue;\n const removal = options.removedKeys[key];\n if (removal) {\n ctx.addIssue({\n code: \"custom\",\n path: [key],\n message: `${options.contextLabel} \"${key}\" ${removal}`,\n });\n } else {\n const hint = options.unknownHint?.(key);\n ctx.addIssue({\n code: \"custom\",\n path: [key],\n message: hint\n ? `Unknown ${options.contextLabel.toLowerCase()} \"${key}\". ${hint}`\n : `Unknown ${options.contextLabel.toLowerCase()} \"${key}\".`,\n });\n }\n }\n });\n}\n"],"mappings":";;;;;;;;AAmDA,SAAgB,eACd,QACA,SACA;CACA,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,OAAO,MAAM,CAAC;AACpD,QAAO,OAAO,aAAa,CAAC,aAAa,MAAM,QAAQ;AACrD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,OAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,EAAE;AAC9D,OAAI,UAAU,IAAI,IAAI,CAAE;GACxB,MAAM,UAAU,QAAQ,YAAY;AACpC,OAAI,QACF,KAAI,SAAS;IACX,MAAM;IACN,MAAM,CAAC,IAAI;IACX,SAAS,GAAG,QAAQ,aAAa,IAAI,IAAI,IAAI;IAC9C,CAAC;QACG;IACL,MAAM,OAAO,QAAQ,cAAc,IAAI;AACvC,QAAI,SAAS;KACX,MAAM;KACN,MAAM,CAAC,IAAI;KACX,SAAS,OACL,WAAW,QAAQ,aAAa,aAAa,CAAC,IAAI,IAAI,KAAK,SAC3D,WAAW,QAAQ,aAAa,aAAa,CAAC,IAAI,IAAI;KAC3D,CAAC;;;GAGN"}
|