@stackbox/cms 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +119 -7
  2. package/dist/AGENTS.md +215 -0
  3. package/dist/blocks.d.ts +6 -13
  4. package/dist/blocks.d.ts.map +1 -1
  5. package/dist/blocks.js +2 -0
  6. package/dist/blocks.js.map +1 -1
  7. package/dist/build.d.ts +14 -0
  8. package/dist/build.d.ts.map +1 -0
  9. package/dist/build.js +145 -0
  10. package/dist/build.js.map +1 -0
  11. package/dist/cache.d.ts +14 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +93 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/cli.d.ts +3 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +16 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/index.d.ts +12 -8
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/link.d.ts +3 -7
  24. package/dist/link.d.ts.map +1 -1
  25. package/dist/link.js +12 -19
  26. package/dist/link.js.map +1 -1
  27. package/dist/pages.d.ts +15 -46
  28. package/dist/pages.d.ts.map +1 -1
  29. package/dist/pages.js +2 -0
  30. package/dist/pages.js.map +1 -1
  31. package/dist/plugin.d.ts +22 -0
  32. package/dist/plugin.d.ts.map +1 -0
  33. package/dist/plugin.js +155 -0
  34. package/dist/plugin.js.map +1 -0
  35. package/dist/plugins/blog/index.d.ts +6 -5
  36. package/dist/plugins/blog/index.d.ts.map +1 -1
  37. package/dist/plugins/blog/index.js +16 -0
  38. package/dist/plugins/blog/index.js.map +1 -1
  39. package/dist/plugins/random-quote/assets/quotes.json +230 -0
  40. package/dist/plugins/random-quote/index.d.ts +3 -1
  41. package/dist/plugins/random-quote/index.d.ts.map +1 -1
  42. package/dist/plugins/random-quote/index.js +15 -1
  43. package/dist/plugins/random-quote/index.js.map +1 -1
  44. package/dist/plugins/sitemap/index.d.ts +14 -0
  45. package/dist/plugins/sitemap/index.d.ts.map +1 -0
  46. package/dist/plugins/sitemap/index.js +119 -0
  47. package/dist/plugins/sitemap/index.js.map +1 -0
  48. package/dist/render-head.d.ts +2 -2
  49. package/dist/render-head.d.ts.map +1 -1
  50. package/dist/render-page.d.ts +2 -4
  51. package/dist/render-page.d.ts.map +1 -1
  52. package/dist/render-page.js +7 -3
  53. package/dist/render-page.js.map +1 -1
  54. package/dist/site.d.ts +10 -17
  55. package/dist/site.d.ts.map +1 -1
  56. package/dist/site.js +138 -7
  57. package/dist/site.js.map +1 -1
  58. package/dist/slot-content.d.ts +3 -19
  59. package/dist/slot-content.d.ts.map +1 -1
  60. package/dist/slot-content.js.map +1 -1
  61. package/dist/slot-handle.d.ts +7 -25
  62. package/dist/slot-handle.d.ts.map +1 -1
  63. package/dist/slot-handle.js +29 -14
  64. package/dist/slot-handle.js.map +1 -1
  65. package/dist/stackbox/context.d.ts +2 -51
  66. package/dist/stackbox/context.d.ts.map +1 -1
  67. package/dist/stackbox/context.js.map +1 -1
  68. package/dist/templates.d.ts +7 -57
  69. package/dist/templates.d.ts.map +1 -1
  70. package/dist/templates.js.map +1 -1
  71. package/dist/types.d.ts +277 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +2 -0
  74. package/dist/types.js.map +1 -0
  75. package/package.json +13 -3
  76. package/src/plugins/blog/AGENTS.md +5 -1
  77. package/src/plugins/blog/index.ts +22 -5
  78. package/src/plugins/random-quote/AGENTS.md +30 -7
  79. package/src/plugins/random-quote/index.ts +17 -1
  80. package/src/plugins/random-quote/public_assets/widget.css +5 -0
  81. package/src/plugins/sitemap/AGENTS.md +82 -0
  82. package/src/plugins/sitemap/index.ts +156 -0
  83. package/AGENTS.md +0 -87
  84. package/dist/modules/blog/module.d.ts +0 -20
  85. package/dist/modules/blog/module.d.ts.map +0 -1
  86. package/dist/modules/blog/module.js +0 -52
  87. package/dist/modules/blog/module.js.map +0 -1
  88. package/dist/modules/blog/posts.d.ts +0 -15
  89. package/dist/modules/blog/posts.d.ts.map +0 -1
  90. package/dist/modules/blog/posts.js +0 -46
  91. package/dist/modules/blog/posts.js.map +0 -1
  92. package/dist/plugins/blog/module.d.ts +0 -20
  93. package/dist/plugins/blog/module.d.ts.map +0 -1
  94. package/dist/plugins/blog/module.js +0 -52
  95. package/dist/plugins/blog/module.js.map +0 -1
  96. package/dist/plugins/blog/plugin.d.ts +0 -20
  97. package/dist/plugins/blog/plugin.d.ts.map +0 -1
  98. package/dist/plugins/blog/plugin.js +0 -52
  99. package/dist/plugins/blog/plugin.js.map +0 -1
  100. package/dist/plugins/random-quote/module.d.ts +0 -9
  101. package/dist/plugins/random-quote/module.d.ts.map +0 -1
  102. package/dist/plugins/random-quote/module.js +0 -31
  103. package/dist/plugins/random-quote/module.js.map +0 -1
  104. package/dist/plugins/random-quote/plugin.d.ts +0 -8
  105. package/dist/plugins/random-quote/plugin.d.ts.map +0 -1
  106. package/dist/plugins/random-quote/plugin.js +0 -31
  107. package/dist/plugins/random-quote/plugin.js.map +0 -1
  108. package/dist/plugins/random-quote/quotes.json +0 -230
  109. package/dist/public/_sb/plugins/random-quote/quotes.json +0 -230
  110. package/src/plugins/random-quote/public_assets/quotes.json +0 -230
  111. /package/{dist/plugins/random-quote/public_assets → src/plugins/random-quote/assets}/quotes.json +0 -0
@@ -0,0 +1,277 @@
1
+ import { html, type HSHtml } from "@hyperspan/html";
2
+ import type { z } from "zod";
3
+ type HtmlSafe = ReturnType<typeof html.raw>;
4
+ export declare namespace Stackbox {
5
+ type CookieOptions = {
6
+ maxAge?: number;
7
+ domain?: string;
8
+ path?: string;
9
+ expires?: Date;
10
+ httpOnly?: boolean;
11
+ secure?: boolean;
12
+ sameSite?: "lax" | "strict" | true;
13
+ };
14
+ type Cookies = {
15
+ _req: globalThis.Request;
16
+ _responseHeaders: Headers | undefined;
17
+ _parsedCookies: Record<string, string>;
18
+ _encrypt: ((str: string) => string) | undefined;
19
+ _decrypt: ((str: string) => string) | undefined;
20
+ get: (name: string) => string | undefined;
21
+ set: (name: string, value: string, options?: CookieOptions) => void;
22
+ delete: (name: string) => void;
23
+ };
24
+ type Request = {
25
+ url: URL;
26
+ raw: globalThis.Request;
27
+ method: string;
28
+ headers: Headers;
29
+ query: URLSearchParams;
30
+ cookies: Cookies;
31
+ text: () => Promise<string>;
32
+ json: <T = unknown>() => Promise<T>;
33
+ formData: () => Promise<FormData>;
34
+ urlencoded: () => Promise<URLSearchParams>;
35
+ };
36
+ type Response = {
37
+ cookies: Cookies;
38
+ headers: Headers;
39
+ status: number | undefined;
40
+ html: (html: string, options?: ResponseInit) => Promise<globalThis.Response>;
41
+ json: (json: unknown, options?: ResponseInit) => Promise<globalThis.Response>;
42
+ text: (text: string, options?: ResponseInit) => Promise<globalThis.Response>;
43
+ redirect: (url: string, options?: ResponseInit) => Promise<globalThis.Response>;
44
+ error: (error: Error, options?: ResponseInit) => Promise<globalThis.Response>;
45
+ notFound: (options?: ResponseInit) => Promise<globalThis.Response>;
46
+ merge: (response: globalThis.Response) => Promise<globalThis.Response>;
47
+ };
48
+ interface Context<TEnv extends Record<string, unknown> = Record<string, unknown>> {
49
+ env: TEnv;
50
+ req: Request;
51
+ res: Response;
52
+ }
53
+ type CacheBounds = {
54
+ min?: number;
55
+ max?: number;
56
+ };
57
+ type CacheConfig = CacheBounds | false;
58
+ type CacheEntry = {
59
+ html: string;
60
+ expiresAt: number;
61
+ };
62
+ type CacheAdapter = {
63
+ get(key: string): CacheEntry | undefined | Promise<CacheEntry | undefined>;
64
+ set(key: string, entry: CacheEntry): void | Promise<void>;
65
+ };
66
+ type SiteConfig<T extends Record<string, unknown> = Record<string, unknown>> = {
67
+ readonly __kind: "siteConfig";
68
+ readonly config: T;
69
+ };
70
+ type BlockRenderResult = HSHtml | Promise<HSHtml>;
71
+ type RenderContext = {
72
+ siteConfig: SiteConfig;
73
+ ctx?: Context;
74
+ page?: SitePage;
75
+ hooks?: SiteHooks;
76
+ };
77
+ type Block = {
78
+ readonly __kind: "block";
79
+ readonly name: string;
80
+ readonly cache?: CacheConfig;
81
+ readonly source?: string;
82
+ render(ctx: RenderContext): BlockRenderResult;
83
+ };
84
+ type BlockOptionsOf<F> = F extends (options?: infer O) => unknown ? [O] extends [undefined] ? undefined : O : never;
85
+ type BlockFactory<TOptions = undefined> = (options?: TOptions) => Block;
86
+ type DefaultSlotContent = Block | string;
87
+ type SlotOptions = {
88
+ required?: boolean;
89
+ primary?: true;
90
+ schema?: z.ZodTypeAny;
91
+ };
92
+ type SlotDefinition = {
93
+ name: string;
94
+ options?: SlotOptions;
95
+ };
96
+ type SlotMeta = {
97
+ isDefault: boolean;
98
+ required: boolean;
99
+ schema?: z.ZodTypeAny;
100
+ };
101
+ type SlotNamesFrom<S extends readonly SlotDefinition[]> = S[number]["name"];
102
+ type RequiredSlotNamesFrom<S extends readonly SlotDefinition[]> = Extract<S[number], {
103
+ options: {
104
+ required: true;
105
+ };
106
+ } | {
107
+ options: {
108
+ primary: true;
109
+ };
110
+ }>["name"];
111
+ type SlotContentFromDefinition<D extends SlotDefinition> = D extends {
112
+ options: {
113
+ schema: infer Schema extends z.ZodTypeAny;
114
+ };
115
+ } ? z.input<Schema> : DefaultSlotContent;
116
+ type PageSlotsInput<S extends readonly SlotDefinition[], Required extends string> = {
117
+ [K in Required]: SlotContentFromDefinition<Extract<S[number], {
118
+ name: K;
119
+ }>>[];
120
+ } & Partial<{
121
+ [K in Exclude<SlotNamesFrom<S>, Required>]: SlotContentFromDefinition<Extract<S[number], {
122
+ name: K;
123
+ }>>[];
124
+ }>;
125
+ type SlotRenderValue = HSHtml | HtmlSafe | Promise<HSHtml>;
126
+ type Slot<TContent = DefaultSlotContent, D extends SlotDefinition = SlotDefinition> = {
127
+ readonly name: D["name"];
128
+ readonly definition: D;
129
+ readonly content: readonly TContent[];
130
+ render(): SlotRenderValue;
131
+ };
132
+ type TemplateSlotsFrom<S extends readonly SlotDefinition[]> = {
133
+ [K in SlotNamesFrom<S>]: Slot<SlotContentFromDefinition<Extract<S[number], {
134
+ name: K;
135
+ }>>, Extract<S[number], {
136
+ name: K;
137
+ }>>;
138
+ };
139
+ type PageMeta = {
140
+ description?: string;
141
+ robots?: string;
142
+ canonical?: string;
143
+ ogTitle?: string;
144
+ ogDescription?: string;
145
+ ogImage?: string;
146
+ twitterCard?: string;
147
+ };
148
+ type Page<Slots extends string = string, RequiredSlots extends Slots = never, Definitions extends readonly SlotDefinition[] = readonly SlotDefinition[]> = {
149
+ readonly __kind: "page";
150
+ path: string;
151
+ template: TemplateDescriptor<Slots, RequiredSlots, Definitions>;
152
+ title: string;
153
+ meta?: PageMeta;
154
+ cache?: CacheConfig;
155
+ source?: string;
156
+ slots: PageSlotsInput<Definitions, RequiredSlots>;
157
+ };
158
+ type SitePage = {
159
+ readonly __kind: "page";
160
+ path: string;
161
+ template: TemplateDescriptor<string, string>;
162
+ title: string;
163
+ meta?: PageMeta;
164
+ cache?: CacheConfig;
165
+ source?: string;
166
+ slots: Partial<Record<string, DefaultSlotContent[]>>;
167
+ };
168
+ type PageRenderView = Omit<Page, "template" | "slots">;
169
+ type TemplateRenderContext<S extends readonly SlotDefinition[] = readonly SlotDefinition[]> = {
170
+ head?: HSHtml;
171
+ siteConfig: SiteConfig;
172
+ page: PageRenderView;
173
+ slots: TemplateSlotsFrom<S>;
174
+ };
175
+ type TemplateRenderFn = (ctx: {
176
+ head?: HSHtml;
177
+ siteConfig: SiteConfig;
178
+ page: PageRenderView;
179
+ slots: Record<string, Slot>;
180
+ }) => HSHtml;
181
+ type TemplateDescriptor<Slots extends string = string, RequiredSlots extends Slots = never, Definitions extends readonly SlotDefinition[] = readonly SlotDefinition[]> = {
182
+ readonly __kind: "template";
183
+ siteConfig: SiteConfig;
184
+ formatPageTitle: (title: string) => string;
185
+ render: TemplateRenderFn;
186
+ slots: Record<Slots, SlotMeta>;
187
+ requiredSlots: readonly RequiredSlots[];
188
+ readonly __definitions?: Definitions;
189
+ };
190
+ type PluginRoute = {
191
+ path: string;
192
+ fetch: (request: globalThis.Request, ctx: Context) => globalThis.Response | Promise<globalThis.Response>;
193
+ };
194
+ type PluginRouteContext = {
195
+ site: Site;
196
+ };
197
+ type PluginBuildContext = {
198
+ site: Site;
199
+ outDir: string;
200
+ publicDir: string;
201
+ };
202
+ type Plugin = {
203
+ readonly __kind: "plugin";
204
+ readonly name: string;
205
+ readonly description: string;
206
+ readonly version: string;
207
+ readonly keywords: readonly string[];
208
+ readonly root: string;
209
+ readonly assetsDir: string;
210
+ readonly publicAssetsDir: string;
211
+ routes?: (ctx: PluginRouteContext) => readonly PluginRoute[] | Promise<readonly PluginRoute[]>;
212
+ build?: (ctx: PluginBuildContext) => void | Promise<void>;
213
+ };
214
+ type CreatePluginOptions = {
215
+ name: string;
216
+ description: string;
217
+ version: string;
218
+ keywords: readonly string[];
219
+ root: string;
220
+ routes?: (ctx: PluginRouteContext) => readonly PluginRoute[] | Promise<readonly PluginRoute[]>;
221
+ build?: (ctx: PluginBuildContext) => void | Promise<void>;
222
+ };
223
+ type Site<T extends Record<string, unknown> = Record<string, unknown>> = {
224
+ readonly __kind: "site";
225
+ readonly siteConfig: SiteConfig<T>;
226
+ readonly pages: readonly SitePage[];
227
+ readonly plugins: readonly Plugin[];
228
+ fetch(request: globalThis.Request, env?: Record<string, unknown>): Promise<globalThis.Response>;
229
+ };
230
+ type RenderSlotItemInfo = {
231
+ item: Block | string;
232
+ slot: string;
233
+ index: number;
234
+ page: SitePage;
235
+ ctx?: Context;
236
+ };
237
+ type AfterRenderInfo = {
238
+ page: SitePage;
239
+ ctx?: Context;
240
+ };
241
+ type BeforeResponseInfo = {
242
+ request: globalThis.Request;
243
+ page?: SitePage;
244
+ ctx: Context;
245
+ };
246
+ type SiteHooks = {
247
+ shouldCache?: (request: globalThis.Request) => boolean;
248
+ renderSlotItem?: (html: string, info: RenderSlotItemInfo) => string | Promise<string>;
249
+ afterRender?: (html: string, info: AfterRenderInfo) => string | Promise<string>;
250
+ beforeResponse?: (response: globalThis.Response, info: BeforeResponseInfo) => globalThis.Response | Promise<globalThis.Response>;
251
+ };
252
+ type BuildSettings = {
253
+ readonly outDir: string;
254
+ readonly publicDir: string;
255
+ };
256
+ type BuildOptions = {
257
+ site: Site;
258
+ outDir?: string;
259
+ publicDir?: string;
260
+ };
261
+ type BuildResult = {
262
+ readonly settings: BuildSettings;
263
+ readonly plugins: readonly Plugin[];
264
+ };
265
+ type BuildCliArgs = {
266
+ readonly command: "build";
267
+ readonly site?: string;
268
+ readonly outDir?: string;
269
+ readonly publicDir?: string;
270
+ };
271
+ type PluginAssetRequest = {
272
+ plugin: string;
273
+ relativePath: string;
274
+ };
275
+ }
276
+ export {};
277
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbox/cms",
3
- "version": "0.0.5",
3
+ "version": "0.1.0",
4
4
  "description": "Stackbox CMS engine",
5
5
  "type": "module",
6
6
  "license": "BSD-3-Clause",
@@ -14,7 +14,6 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- "AGENTS.md",
18
17
  "src/plugins"
19
18
  ],
20
19
  "exports": {
@@ -30,13 +29,24 @@
30
29
  "types": "./dist/plugins/random-quote/index.d.ts",
31
30
  "import": "./dist/plugins/random-quote/index.js"
32
31
  },
32
+ "./plugins/sitemap": {
33
+ "types": "./dist/plugins/sitemap/index.d.ts",
34
+ "import": "./dist/plugins/sitemap/index.js"
35
+ },
33
36
  "./link": {
34
37
  "types": "./dist/link.d.ts",
35
38
  "import": "./dist/link.js"
39
+ },
40
+ "./build": {
41
+ "types": "./dist/build.d.ts",
42
+ "import": "./dist/build.js"
36
43
  }
37
44
  },
45
+ "bin": {
46
+ "stackbox-cms": "./dist/cli.js"
47
+ },
38
48
  "scripts": {
39
- "build": "tsc -p tsconfig.json && node scripts/copy-plugin-assets.mjs",
49
+ "build": "node scripts/emit-dist.mjs && node scripts/copy-plugin-assets.mjs && node scripts/generate-agents-md.mjs",
40
50
  "test": "npm run build && node --import tsx --test test/*.test.ts",
41
51
  "typecheck": "tsc --noEmit -p tsconfig.json",
42
52
  "prepack": "npm run build"
@@ -18,6 +18,7 @@ The user asks for any of:
18
18
 
19
19
  ## Exports
20
20
 
21
+ - **default export** — `createPlugin()` registration; pass to `createSite({ plugins })`
21
22
  - `createBlog()` — factory; returns `{ posts, listings }`
22
23
  - `BlogPostContent`, `BlogListingContent`, `Blog`, `BlogPost`, `BlogOptions` — types
23
24
  - `content[]` on post/listing objects accepts **blocks or HTML strings** — optional plugin blocks can be added later (e.g. share buttons)
@@ -60,7 +61,8 @@ Post paths: `{pathPrefix}/{slug}` where slug comes from frontmatter or filename.
60
61
  | --- | --- |
61
62
  | `content/blog/*.md` | Create markdown posts with frontmatter |
62
63
  | `pages/blog.ts` | Call `createBlog()`, map `listings` and `posts` to `createPage()` |
63
- | `server.ts` | Register `...blogListingPages` and `...blogPostPages` in `createSite({ pages })` |
64
+ | `server.ts` | Register the default export plus `...blogListingPages` and `...blogPostPages` in `createSite({ pages, plugins })` |
65
+ | `package.json` | Add `"build": "stackbox-cms build"` so registered plugin assets are copied |
64
66
  | `templates/site-template.ts` | Reuse existing site template (or create one) — blog does not ship its own |
65
67
 
66
68
  ## Markdown contract
@@ -121,10 +123,12 @@ export const blogPostPages = blog.posts.map((post) =>
121
123
 
122
124
  ```ts
123
125
  // server.ts
126
+ import blogPlugin from "@stackbox/cms/plugins/blog";
124
127
  import { blogListingPages, blogPostPages } from "./pages/blog";
125
128
 
126
129
  export default createSite(siteConfig, {
127
130
  pages: [homePage, ...blogListingPages, ...blogPostPages],
131
+ plugins: [blogPlugin],
128
132
  });
129
133
  ```
130
134
 
@@ -1,19 +1,36 @@
1
- import type { PageMeta } from "../../pages.js";
2
- import type { DefaultSlotContent } from "../../slot-content.js";
1
+ import { createPlugin } from "../../plugin.js";
2
+ import type { Stackbox as SB } from "../../types.js";
3
3
  import { loadPosts, type BlogOptions, type BlogPost } from "./posts.js";
4
4
 
5
+ export default createPlugin({
6
+ name: "sb-blog",
7
+ description:
8
+ "Load markdown posts at bundle time and return listing and post content objects to wire into createPage().",
9
+ version: "1.0.0",
10
+ keywords: [
11
+ "blog",
12
+ "posts",
13
+ "articles",
14
+ "journal",
15
+ "markdown posts",
16
+ "blog listing",
17
+ "blog page",
18
+ ],
19
+ root: import.meta.dirname,
20
+ });
21
+
5
22
  export type { BlogPost, BlogOptions } from "./posts.js";
6
23
 
7
24
  export type BlogPostContent = {
8
25
  path: string;
9
26
  title: string;
10
- meta?: PageMeta;
11
- content: DefaultSlotContent[];
27
+ meta?: SB.PageMeta;
28
+ content: SB.DefaultSlotContent[];
12
29
  };
13
30
 
14
31
  export type BlogListingContent = {
15
32
  path: string;
16
- content: DefaultSlotContent[];
33
+ content: SB.DefaultSlotContent[];
17
34
  };
18
35
 
19
36
  export type Blog = {
@@ -17,8 +17,9 @@ This plugin is a **block-only** example — it exports no page factory or conten
17
17
 
18
18
  ## Exports
19
19
 
20
+ - **default export** — `createPlugin()` registration; pass to `createSite({ plugins })` to serve `public_assets/`
20
21
  - `randomQuoteBlock(options?)` — block factory; `{ quotes }` overrides bundled defaults
21
- - `defaultQuotes` — bundled quote array (imported from `public_assets/quotes.json`)
22
+ - `defaultQuotes` — bundled quote array (imported from `assets/quotes.json`)
22
23
  - `pickRandomQuote(quotes)` — picks one quote at random
23
24
  - `Quote` — type `{ text, author }`
24
25
  - `RandomQuoteBlockOptions` — type `{ quotes?: readonly Quote[] }`
@@ -37,25 +38,37 @@ import myQuotes from "../content/quotes.json" with { type: "json" };
37
38
  slots: { sidebar: [randomQuoteBlock({ quotes: myQuotes })] }
38
39
  ```
39
40
 
40
- Custom JSON must be an array of `{ text, author }` objects — same shape as bundled `public_assets/quotes.json`.
41
-
42
- Bundled quotes are also served as a public asset at `/_sb/plugins/random-quote/quotes.json` (via `pluginAssetPath("random-quote", "quotes.json")` from `@stackbox/cms/link`).
41
+ Custom JSON must be an array of `{ text, author }` objects — same shape as bundled `assets/quotes.json`.
43
42
 
44
43
  Each request renders a randomly selected quote from the configured list.
45
44
 
46
- ## Public assets
45
+ ## Assets
46
+
47
+ - `assets/quotes.json` — private; imported into JS as `defaultQuotes`
48
+ - `public_assets/widget.css` — public; served at `/_sb/plugins/sb-random-quote/widget.css` **only after** this plugin is registered
49
+
50
+ ```ts
51
+ import randomQuotePlugin from "@stackbox/cms/plugins/random-quote";
52
+
53
+ export default createSite(siteConfig, {
54
+ pages: [homePage],
55
+ plugins: [randomQuotePlugin],
56
+ });
57
+ ```
47
58
 
48
- Bundled quotes live in `public_assets/quotes.json`. The build copies this folder to `dist/public/_sb/plugins/random-quote/` automatically — do not edit the copy script.
59
+ Use `pluginAssetPath("sb-random-quote", "widget.css")` from `@stackbox/cms/link` for the stylesheet href. Do not copy plugin assets by scanning a `plugins/` folder — run `npx stackbox-cms build` so the package build script copies only registered plugins.
49
60
 
50
61
  ## Files to create or update
51
62
 
52
63
  | File | Action |
53
64
  | --- | --- |
65
+ | `server.ts` | Register the default export in `createSite({ plugins })` so public assets are served |
66
+ | `package.json` | Add `"build": "stackbox-cms build"` so registered plugin assets are copied |
54
67
  | Any page using the block | Import `randomQuoteBlock` and add to a slot array |
55
68
  | `content/quotes.json` | Optional — user's own quotes when not using bundled defaults |
56
69
  | `templates/site-template.ts` | Ensure the target slot exists (e.g. `sidebar`, `content`) |
57
70
 
58
- No `pages/*.ts` or `server.ts` changes required unless adding the block to a new page.
71
+ Register the plugin in `server.ts` whenever the block is used so `public_assets/` are served. No `pages/*.ts` changes unless adding the block to a new page.
59
72
 
60
73
  ## Wiring example
61
74
 
@@ -76,4 +89,14 @@ export const homePage = createPage(siteTemplate, {
76
89
  });
77
90
  ```
78
91
 
92
+ ```ts
93
+ // server.ts
94
+ import randomQuotePlugin from "@stackbox/cms/plugins/random-quote";
95
+
96
+ export default createSite(siteConfig, {
97
+ pages: [homePage],
98
+ plugins: [randomQuotePlugin],
99
+ });
100
+ ```
101
+
79
102
  Use `randomQuoteBlock()` without options to keep bundled defaults. Combine with other blocks or HTML strings in the same slot.
@@ -1,6 +1,22 @@
1
1
  import { html } from "@hyperspan/html";
2
2
  import { createBlock } from "../../blocks.js";
3
- import bundledQuotes from "./public_assets/quotes.json" with { type: "json" };
3
+ import { createPlugin } from "../../plugin.js";
4
+ import bundledQuotes from "./assets/quotes.json" with { type: "json" };
5
+
6
+ export default createPlugin({
7
+ name: "sb-random-quote",
8
+ description:
9
+ "A block that renders a random inspirational quote, using bundled defaults or a site-supplied list.",
10
+ version: "1.0.0",
11
+ keywords: [
12
+ "random quote",
13
+ "quote of the day",
14
+ "inspirational quote",
15
+ "sidebar quote",
16
+ "quotation",
17
+ ],
18
+ root: import.meta.dirname,
19
+ });
4
20
 
5
21
  export type Quote = {
6
22
  text: string;
@@ -0,0 +1,5 @@
1
+ [data-block="random-quote"] {
2
+ margin: 0;
3
+ padding: 0.75rem 1rem;
4
+ border-left: 3px solid currentColor;
5
+ }
@@ -0,0 +1,82 @@
1
+ # Sitemap plugin — agent playbook
2
+
3
+ ## Use when
4
+
5
+ The user asks for any of:
6
+
7
+ - sitemap, sitemap.xml, XML sitemap
8
+ - SEO crawl list, search engine indexing URLs
9
+ - static sitemap file in the public folder
10
+
11
+ ## Do not
12
+
13
+ - Hand-roll sitemap XML — use `createSitemap()` or the default export
14
+ - Put `/sitemap.xml` in `createPage()` — the plugin registers that route
15
+ - Forget to register the plugin on `createSite({ plugins })`
16
+
17
+ ## Exports
18
+
19
+ - **default export** — `createSitemap()` registration; pass to `createSite({ plugins })`
20
+ - `createSitemap(options?)` — factory when you need `baseUrl`, `extraUrls`, or `exclude`
21
+ - `renderSitemapXml(site, options?)` — shared XML renderer (fetch + build use this)
22
+ - `collectSitemapUrls(site, options?)` — absolute `<loc>` URLs
23
+ - `SitemapOptions` — type
24
+
25
+ ## API
26
+
27
+ ```ts
28
+ import sitemapPlugin, { createSitemap } from "@stackbox/cms/plugins/sitemap";
29
+
30
+ createSitemap({
31
+ baseUrl?: string, // defaults to siteConfig.config.url
32
+ extraUrls?: string[], // site-relative or absolute
33
+ exclude?: string[] | ((path) => boolean),
34
+ });
35
+ ```
36
+
37
+ **URLs included**
38
+
39
+ - Every `createPage()` path (origin + path)
40
+ - `extraUrls`
41
+ - Pages with `meta.robots` containing `noindex` are excluded
42
+ - Paths in `exclude` are dropped
43
+
44
+ **Serving and static file**
45
+
46
+ - `fetch("/sitemap.xml")` when the plugin is registered
47
+ - `stackbox-cms build` writes `{publicDir}/sitemap.xml` via the plugin `build` hook
48
+
49
+ ## Files to create or update
50
+
51
+ | File | Action |
52
+ | --- | --- |
53
+ | `server.ts` | Register the default export or `createSitemap(...)` in `createSite({ plugins })` |
54
+ | `site.config.ts` | Ensure `url` is set (used as sitemap origin unless `baseUrl` is passed) |
55
+ | `package.json` | Add `"build": "stackbox-cms build"` |
56
+
57
+ ## Wiring example
58
+
59
+ ```ts
60
+ // server.ts
61
+ import sitemapPlugin from "@stackbox/cms/plugins/sitemap";
62
+
63
+ export default createSite(siteConfig, {
64
+ pages: [homePage, aboutPage],
65
+ plugins: [sitemapPlugin],
66
+ });
67
+ ```
68
+
69
+ ```ts
70
+ // With extra URLs and exclusions
71
+ import { createSitemap } from "@stackbox/cms/plugins/sitemap";
72
+
73
+ export default createSite(siteConfig, {
74
+ pages: [homePage, aboutPage],
75
+ plugins: [
76
+ createSitemap({
77
+ extraUrls: ["/llms.txt"],
78
+ exclude: ["/thanks"],
79
+ }),
80
+ ],
81
+ });
82
+ ```