@stackbox/cms 0.0.3 → 0.0.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.
Files changed (117) hide show
  1. package/AGENTS.md +87 -0
  2. package/README.md +57 -17
  3. package/dist/blocks.d.ts +18 -0
  4. package/dist/blocks.d.ts.map +1 -0
  5. package/dist/blocks.js +25 -0
  6. package/dist/blocks.js.map +1 -0
  7. package/dist/index.d.ts +12 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +12 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/link.d.ts +10 -0
  12. package/dist/link.d.ts.map +1 -0
  13. package/dist/link.js +128 -0
  14. package/dist/link.js.map +1 -0
  15. package/dist/modules/blog/module.d.ts +20 -0
  16. package/dist/modules/blog/module.d.ts.map +1 -0
  17. package/dist/modules/blog/module.js +52 -0
  18. package/dist/modules/blog/module.js.map +1 -0
  19. package/dist/modules/blog/posts.d.ts +15 -0
  20. package/dist/modules/blog/posts.d.ts.map +1 -0
  21. package/dist/modules/blog/posts.js +46 -0
  22. package/dist/modules/blog/posts.js.map +1 -0
  23. package/dist/modules.d.ts +18 -0
  24. package/dist/modules.d.ts.map +1 -0
  25. package/dist/modules.js +25 -0
  26. package/dist/modules.js.map +1 -0
  27. package/dist/pages.d.ts +55 -0
  28. package/dist/pages.d.ts.map +1 -0
  29. package/dist/pages.js +85 -0
  30. package/dist/pages.js.map +1 -0
  31. package/dist/plugins/blog/index.d.ts +20 -0
  32. package/dist/plugins/blog/index.d.ts.map +1 -0
  33. package/dist/plugins/blog/index.js +52 -0
  34. package/dist/plugins/blog/index.js.map +1 -0
  35. package/dist/plugins/blog/module.d.ts +20 -0
  36. package/dist/plugins/blog/module.d.ts.map +1 -0
  37. package/dist/plugins/blog/module.js +52 -0
  38. package/dist/plugins/blog/module.js.map +1 -0
  39. package/dist/plugins/blog/plugin.d.ts +20 -0
  40. package/dist/plugins/blog/plugin.d.ts.map +1 -0
  41. package/dist/plugins/blog/plugin.js +52 -0
  42. package/dist/plugins/blog/plugin.js.map +1 -0
  43. package/dist/plugins/blog/posts.d.ts +15 -0
  44. package/dist/plugins/blog/posts.d.ts.map +1 -0
  45. package/dist/plugins/blog/posts.js +46 -0
  46. package/dist/plugins/blog/posts.js.map +1 -0
  47. package/dist/plugins/random-quote/index.d.ts +12 -0
  48. package/dist/plugins/random-quote/index.d.ts.map +1 -0
  49. package/dist/plugins/random-quote/index.js +21 -0
  50. package/dist/plugins/random-quote/index.js.map +1 -0
  51. package/dist/plugins/random-quote/module.d.ts +9 -0
  52. package/dist/plugins/random-quote/module.d.ts.map +1 -0
  53. package/dist/plugins/random-quote/module.js +31 -0
  54. package/dist/plugins/random-quote/module.js.map +1 -0
  55. package/dist/plugins/random-quote/plugin.d.ts +8 -0
  56. package/dist/plugins/random-quote/plugin.d.ts.map +1 -0
  57. package/dist/plugins/random-quote/plugin.js +31 -0
  58. package/dist/plugins/random-quote/plugin.js.map +1 -0
  59. package/dist/plugins/random-quote/public_assets/quotes.json +230 -0
  60. package/dist/plugins/random-quote/quotes.json +230 -0
  61. package/dist/public/_sb/plugins/random-quote/quotes.json +230 -0
  62. package/dist/render-head.d.ts +7 -0
  63. package/dist/render-head.d.ts.map +1 -0
  64. package/dist/render-head.js +27 -0
  65. package/dist/render-head.js.map +1 -0
  66. package/dist/render-page.d.ts +6 -0
  67. package/dist/render-page.d.ts.map +1 -0
  68. package/dist/render-page.js +30 -0
  69. package/dist/render-page.js.map +1 -0
  70. package/dist/routing.d.ts +3 -0
  71. package/dist/routing.d.ts.map +1 -0
  72. package/dist/routing.js +11 -0
  73. package/dist/routing.js.map +1 -0
  74. package/dist/site.d.ts +21 -0
  75. package/dist/site.d.ts.map +1 -0
  76. package/dist/site.js +102 -0
  77. package/dist/site.js.map +1 -0
  78. package/dist/slot-content.d.ts +30 -0
  79. package/dist/slot-content.d.ts.map +1 -0
  80. package/dist/slot-content.js +34 -0
  81. package/dist/slot-content.js.map +1 -0
  82. package/dist/slot-handle.d.ts +32 -0
  83. package/dist/slot-handle.d.ts.map +1 -0
  84. package/dist/slot-handle.js +68 -0
  85. package/dist/slot-handle.js.map +1 -0
  86. package/dist/stackbox/context.d.ts +52 -0
  87. package/dist/stackbox/context.d.ts.map +1 -0
  88. package/dist/stackbox/context.js +165 -0
  89. package/dist/stackbox/context.js.map +1 -0
  90. package/dist/templates.d.ts +63 -0
  91. package/dist/templates.d.ts.map +1 -0
  92. package/dist/templates.js +106 -0
  93. package/dist/templates.js.map +1 -0
  94. package/package.json +19 -6
  95. package/src/plugins/blog/AGENTS.md +131 -0
  96. package/src/plugins/random-quote/AGENTS.md +79 -0
  97. package/src/plugins/random-quote/index.ts +33 -0
  98. package/src/plugins/random-quote/public_assets/quotes.json +230 -0
  99. package/.claude/settings.local.json +0 -8
  100. package/src/index.ts +0 -67
  101. package/src/modules.ts +0 -58
  102. package/src/pages.ts +0 -198
  103. package/src/render-head.ts +0 -32
  104. package/src/render-page.ts +0 -51
  105. package/src/routing.ts +0 -14
  106. package/src/site.ts +0 -153
  107. package/src/slot-content.ts +0 -77
  108. package/src/slot-handle.ts +0 -131
  109. package/src/stackbox/context.ts +0 -257
  110. package/src/templates.ts +0 -230
  111. package/test/async-render.test.ts +0 -103
  112. package/test/blog.test.ts +0 -93
  113. package/test/fetch-routing.test.ts +0 -76
  114. package/test/stackbox-context.test.ts +0 -44
  115. package/tsconfig.json +0 -19
  116. /package/src/{modules/blog/module.ts → plugins/blog/index.ts} +0 -0
  117. /package/src/{modules → plugins}/blog/posts.ts +0 -0
@@ -0,0 +1,131 @@
1
+ # Blog plugin — agent playbook
2
+
3
+ ## Use when
4
+
5
+ The user asks for any of:
6
+
7
+ - blog, blog page, blog listing, blog posts
8
+ - articles, journal, news posts
9
+ - markdown posts, writing posts from markdown files
10
+ - paginated blog index
11
+
12
+ ## Do not
13
+
14
+ - Hand-roll a markdown loader or frontmatter parser — use `createBlog()`
15
+ - Invent page factories like `createPostPages()` or `createListingPage()` — the API returns **content objects**; you wire them with `createPage()`
16
+ - Skip `createPage()` composition — the blog plugin has no template dependency
17
+ - Put blog routes only in `server.ts` without a dedicated `pages/blog.ts`
18
+
19
+ ## Exports
20
+
21
+ - `createBlog()` — factory; returns `{ posts, listings }`
22
+ - `BlogPostContent`, `BlogListingContent`, `Blog`, `BlogPost`, `BlogOptions` — types
23
+ - `content[]` on post/listing objects accepts **blocks or HTML strings** — optional plugin blocks can be added later (e.g. share buttons)
24
+
25
+ ## API
26
+
27
+ ```ts
28
+ import { createBlog } from "@stackbox/cms/plugins/blog";
29
+
30
+ const blog = createBlog({
31
+ contentPath: string, // directory of .md files (bundle time)
32
+ pathPrefix: string, // e.g. "/blog"
33
+ postsPerPage?: number, // optional — omit to put all posts on one listing page
34
+ });
35
+ ```
36
+
37
+ Returns:
38
+
39
+ ```ts
40
+ {
41
+ posts: readonly BlogPostContent[]; // one per markdown file
42
+ listings: readonly BlogListingContent[]; // one or more listing pages
43
+ }
44
+ ```
45
+
46
+ Each `BlogPostContent`: `{ path, title, meta?, content }` — `content` is slot-ready HTML (or blocks) for the primary slot.
47
+
48
+ Each `BlogListingContent`: `{ path, content }` — post list HTML for the primary slot.
49
+
50
+ Listing paths:
51
+
52
+ - Page 1: `pathPrefix` (normalized, no trailing slash)
53
+ - Page 2+: `{pathPrefix}/page/2`, `{pathPrefix}/page/3`, …
54
+
55
+ Post paths: `{pathPrefix}/{slug}` where slug comes from frontmatter or filename.
56
+
57
+ ## Files to create or update
58
+
59
+ | File | Action |
60
+ | --- | --- |
61
+ | `content/blog/*.md` | Create markdown posts with frontmatter |
62
+ | `pages/blog.ts` | Call `createBlog()`, map `listings` and `posts` to `createPage()` |
63
+ | `server.ts` | Register `...blogListingPages` and `...blogPostPages` in `createSite({ pages })` |
64
+ | `templates/site-template.ts` | Reuse existing site template (or create one) — blog does not ship its own |
65
+
66
+ ## Markdown contract
67
+
68
+ Frontmatter fields (YAML between `---` delimiters):
69
+
70
+ - `title` — post title (defaults to slug)
71
+ - `description` — optional, becomes page meta description
72
+ - `slug` — URL segment (defaults to filename without `.md`)
73
+
74
+ Body is markdown; rendered to HTML at bundle time.
75
+
76
+ Example:
77
+
78
+ ```md
79
+ ---
80
+ title: Hello World
81
+ description: A greeting post
82
+ slug: hello
83
+ ---
84
+ Hello **world**.
85
+ ```
86
+
87
+ ## Wiring example
88
+
89
+ ```ts
90
+ // pages/blog.ts
91
+ import { join } from "node:path";
92
+ import { createPage } from "@stackbox/cms";
93
+ import { createBlog } from "@stackbox/cms/plugins/blog";
94
+ import { siteTemplate } from "../templates/site-template";
95
+
96
+ const blog = createBlog({
97
+ contentPath: join(import.meta.dirname, "../content/blog"),
98
+ pathPrefix: "/blog",
99
+ postsPerPage: 10,
100
+ });
101
+
102
+ export const blogListingPages = blog.listings.map((listing, index) =>
103
+ createPage(siteTemplate, {
104
+ path: listing.path,
105
+ title: index === 0 ? "Blog" : `Blog — page ${index + 1}`,
106
+ slots: {
107
+ content: [...listing.content, "<p>Subscribe for updates</p>"],
108
+ },
109
+ }),
110
+ );
111
+
112
+ export const blogPostPages = blog.posts.map((post) =>
113
+ createPage(siteTemplate, {
114
+ path: post.path,
115
+ title: post.title,
116
+ meta: post.meta,
117
+ slots: { content: [...post.content] },
118
+ }),
119
+ );
120
+ ```
121
+
122
+ ```ts
123
+ // server.ts
124
+ import { blogListingPages, blogPostPages } from "./pages/blog";
125
+
126
+ export default createSite(siteConfig, {
127
+ pages: [homePage, ...blogListingPages, ...blogPostPages],
128
+ });
129
+ ```
130
+
131
+ You may spread extra slot content alongside `listing.content` or `post.content` — blocks or HTML strings (newsletter signup, share buttons, etc.).
@@ -0,0 +1,79 @@
1
+ # Random quote plugin — agent playbook
2
+
3
+ ## Use when
4
+
5
+ The user asks for any of:
6
+
7
+ - random quote, quote of the day, inspirational quote
8
+ - sidebar quote, footer quote, block that shows a quote
9
+ - embed a rotating or random quotation on a page
10
+
11
+ This plugin is a **block-only** example — it exports no page factory or content objects. Drop the block into any slot.
12
+
13
+ ## Do not
14
+
15
+ - Hand-roll quote rotation logic — use `randomQuoteBlock()` from this plugin
16
+ - Build a full quote page unless the user explicitly asks — this plugin ships a **block**, not routes
17
+
18
+ ## Exports
19
+
20
+ - `randomQuoteBlock(options?)` — block factory; `{ quotes }` overrides bundled defaults
21
+ - `defaultQuotes` — bundled quote array (imported from `public_assets/quotes.json`)
22
+ - `pickRandomQuote(quotes)` — picks one quote at random
23
+ - `Quote` — type `{ text, author }`
24
+ - `RandomQuoteBlockOptions` — type `{ quotes?: readonly Quote[] }`
25
+
26
+ ## API
27
+
28
+ ```ts
29
+ import { randomQuoteBlock } from "@stackbox/cms/plugins/random-quote";
30
+
31
+ // Bundled quotes (default):
32
+ slots: { sidebar: [randomQuoteBlock()] }
33
+
34
+ // Custom quotes from the user's site:
35
+ import myQuotes from "../content/quotes.json" with { type: "json" };
36
+
37
+ slots: { sidebar: [randomQuoteBlock({ quotes: myQuotes })] }
38
+ ```
39
+
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`).
43
+
44
+ Each request renders a randomly selected quote from the configured list.
45
+
46
+ ## Public assets
47
+
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.
49
+
50
+ ## Files to create or update
51
+
52
+ | File | Action |
53
+ | --- | --- |
54
+ | Any page using the block | Import `randomQuoteBlock` and add to a slot array |
55
+ | `content/quotes.json` | Optional — user's own quotes when not using bundled defaults |
56
+ | `templates/site-template.ts` | Ensure the target slot exists (e.g. `sidebar`, `content`) |
57
+
58
+ No `pages/*.ts` or `server.ts` changes required unless adding the block to a new page.
59
+
60
+ ## Wiring example
61
+
62
+ ```ts
63
+ // pages/home.ts
64
+ import { createPage } from "@stackbox/cms";
65
+ import { randomQuoteBlock } from "@stackbox/cms/plugins/random-quote";
66
+ import myQuotes from "../content/quotes.json" with { type: "json" };
67
+ import { siteTemplate } from "../templates/site-template";
68
+
69
+ export const homePage = createPage(siteTemplate, {
70
+ path: "/",
71
+ title: "Home",
72
+ slots: {
73
+ content: ["<p>Welcome.</p>"],
74
+ sidebar: [randomQuoteBlock({ quotes: myQuotes })],
75
+ },
76
+ });
77
+ ```
78
+
79
+ Use `randomQuoteBlock()` without options to keep bundled defaults. Combine with other blocks or HTML strings in the same slot.
@@ -0,0 +1,33 @@
1
+ import { html } from "@hyperspan/html";
2
+ import { createBlock } from "../../blocks.js";
3
+ import bundledQuotes from "./public_assets/quotes.json" with { type: "json" };
4
+
5
+ export type Quote = {
6
+ text: string;
7
+ author: string;
8
+ };
9
+
10
+ export type RandomQuoteBlockOptions = {
11
+ /** Custom quotes. Omit to use bundled defaults. */
12
+ quotes?: readonly Quote[];
13
+ };
14
+
15
+ export const defaultQuotes: readonly Quote[] = bundledQuotes;
16
+
17
+ export function pickRandomQuote(quotes: readonly Quote[]): Quote {
18
+ if (quotes.length === 0) {
19
+ throw new Error("random-quote: quotes must contain at least one quote");
20
+ }
21
+ return quotes[Math.floor(Math.random() * quotes.length)]!;
22
+ }
23
+
24
+ export const randomQuoteBlock = createBlock({
25
+ name: "random-quote",
26
+ render(options?: RandomQuoteBlockOptions) {
27
+ const quote = pickRandomQuote(options?.quotes ?? defaultQuotes);
28
+ return html`<blockquote data-block="random-quote" cite="${quote.author}">
29
+ <p>${quote.text}</p>
30
+ <footer>— ${quote.author}</footer>
31
+ </blockquote>`;
32
+ },
33
+ });
@@ -0,0 +1,230 @@
1
+ [
2
+ {
3
+ "text": "You can beat 40 scholars with one fact, but you can't beat one idiot with 40 facts.",
4
+ "author": "Rumi"
5
+ },
6
+ {
7
+ "text": "You will observe with concern how long a useful truth may be known and exist before it is generally recieved and practices on",
8
+ "author": "Benjamin Franklin"
9
+ },
10
+ {
11
+ "text": "Life can only be understood backwards; but it must be lived forwards.",
12
+ "author": "Søren Kierkegaard"
13
+ },
14
+ {
15
+ "text": "We are what we repeatedly do. Excellence, then, is not an act, but a habit.",
16
+ "author": "Will Durant"
17
+ },
18
+ {
19
+ "text": "Time has a wonderful way of showing us what really matters.",
20
+ "author": "Margaret Peters"
21
+ },
22
+ {
23
+ "text": "When a person can't find a deep sense of meaning, they distract themselves with pleasure.",
24
+ "author": "Viktor Frankl"
25
+ },
26
+ {
27
+ "text": "To attain knowledge, add things everyday. To attain wisdom, remove things every day.",
28
+ "author": "Lao Tzu"
29
+ },
30
+ {
31
+ "text": "At the end of the day, we can endure much more than we think we can.",
32
+ "author": "Frida Kahlo"
33
+ },
34
+ {
35
+ "text": "People rarely succeed unless they have fun in what they are doing.",
36
+ "author": "Dale Carnegie"
37
+ },
38
+ {
39
+ "text": "If you don't design your own life plan, chances are you'll fall into someone else's plan.",
40
+ "author": "Jim Rohn"
41
+ },
42
+ {
43
+ "text": "Not everything that is faced can be changed, but nothing can be changed until it is faced.",
44
+ "author": "James Baldwin"
45
+ },
46
+ {
47
+ "text": "The surface of the Earth is the shore of the cosmic ocean.",
48
+ "author": "Carl Sagan"
49
+ },
50
+ {
51
+ "text": "I do not feel obliged to believe that that same God who has endowed us with senses, reason, and intellect has intended to forgo their use",
52
+ "author": "Galileo Galilei"
53
+ },
54
+ {
55
+ "text": "In science the authority embodied in the opinion of thousands is not worth a spark of reason in one man.",
56
+ "author": "Galileo Galilei"
57
+ },
58
+ {
59
+ "text": "Mathematics is the language in which God wrote the universe.",
60
+ "author": "Galileo Galilei"
61
+ },
62
+ {
63
+ "text": "Ignorance is preferable to error; and he is less remote from the truth who believes nothing, than he who believes what is wrong.",
64
+ "author": "Thomas Jefferson"
65
+ },
66
+ {
67
+ "text": "The essence of Government is power; and power, lodged as it must be in human hands, will ever be liable to abuse.",
68
+ "author": "James Madison"
69
+ },
70
+ {
71
+ "text": "A workman who wants to do his work well must first prepare his tools.",
72
+ "author": "Confucius"
73
+ },
74
+ {
75
+ "text": "It is only the very wisest and the very stupidest who cannot change.",
76
+ "author": "Confucius"
77
+ },
78
+ {
79
+ "text": "Our wretched species is so made that those who walk on the well-trodden path always throw stones at those who are showing a new road.",
80
+ "author": "Voltaire"
81
+ },
82
+ {
83
+ "text": "I have always thought the actions of men the best interpreters of their thoughts.",
84
+ "author": "John Locke"
85
+ },
86
+ {
87
+ "text": "There are two tragedies in life. One is not to get your heart's desire. The other is to get it.",
88
+ "author": "George Bernard Shaw"
89
+ },
90
+ {
91
+ "text": "Marriage is the only adventure open to the cowardly.",
92
+ "author": "Voltaire"
93
+ },
94
+ {
95
+ "text": "Even the rich are hungry for love, for being cared for, for being wanted, for having someone to call their own.",
96
+ "author": "Mother Teresa"
97
+ },
98
+ {
99
+ "text": "Loneliness and the feeling of being unwanted is the most terrible poverty.",
100
+ "author": "Mother Teresa"
101
+ },
102
+ {
103
+ "text": "To know what life is worth you have to risk it once in a while.",
104
+ "author": "Jean-Paul Sartre"
105
+ },
106
+ {
107
+ "text": "Conformity is the jailer of freedom and the enemy of growth.",
108
+ "author": "John F. Kennedy"
109
+ },
110
+ {
111
+ "text": "Man is not born to solve the problem of the universe, but to find out what he has to do; and to restrain himself within the limits of his comprehension.",
112
+ "author": "Goethe"
113
+ },
114
+ {
115
+ "text": "Love does not dominate, it cultivates. And that is more.",
116
+ "author": "Goethe"
117
+ },
118
+ {
119
+ "text": "He alone is great and happy who fills his own station of independence, and has neither to command nor to obey.",
120
+ "author": "Goethe"
121
+ },
122
+ {
123
+ "text": "If you wish to advance into the infinite, explore the finite in all directions.",
124
+ "author": "Goethe"
125
+ },
126
+ {
127
+ "text": "One ought to hold on to one's heart; for if one lets it go, one soon loses control of the head too.",
128
+ "author": "Friedrich Nietzsche"
129
+ },
130
+ {
131
+ "text": "A faith is a necessity to man. Woe to him who believes nothing.",
132
+ "author": "Victor Hugo"
133
+ },
134
+ {
135
+ "text": "To live a life which is a perpetual falsehood is to suffer unknown tortures.",
136
+ "author": "Victor Hugo"
137
+ },
138
+ {
139
+ "text": "A man may be a fine genius, and yet understand nothing of an art which he has not studied.",
140
+ "author": "Victor Hugo"
141
+ },
142
+ {
143
+ "text": "How much more grievous are the consequences of anger than the causes of it.",
144
+ "author": "Marcus Aurelius"
145
+ },
146
+ {
147
+ "text": "You always own the option of having no opinion. There is never any need to get worked up or to trouble your soul about things you can't control. These things are not asking to be judged by you. Leave them alone.",
148
+ "author": "Marcus Aurelius"
149
+ },
150
+ {
151
+ "text": "It never ceases to amaze me: we all love ourselves more than other people, but care more about their opinions than our own.",
152
+ "author": "Marcus Aurelius"
153
+ },
154
+ {
155
+ "text": "External things are not the problem. It's your assessment of them. Which you can erase right now.",
156
+ "author": "Marcus Aurelius"
157
+ },
158
+ {
159
+ "text": "The man who reads nothing at all is more educated than the man who reads nothing but newspapers.",
160
+ "author": "Thomas Jefferson"
161
+ },
162
+ {
163
+ "text": "Nothing gives one person so much advantage over anohter as to remain always cool and unruffled under all circumstances.",
164
+ "author": "Thomas Jefferson"
165
+ },
166
+ {
167
+ "text": "Doubt is an uncomfortable condition, but certainty is a ridiculous one.",
168
+ "author": "Voltaire"
169
+ },
170
+ {
171
+ "text": "The longer we dwell on our misfortunes, the greater is their power to harm us.",
172
+ "author": "Voltaire"
173
+ },
174
+ {
175
+ "text": "Those who can make you believe absurdities, can make you commit atrocities.",
176
+ "author": "Voltaire"
177
+ },
178
+ {
179
+ "text": "It is dangerous to be right in matters on which the established authorities are wrong.",
180
+ "author": "Voltaire"
181
+ },
182
+ {
183
+ "text": "One can stand anything except a succession of ordinary days.",
184
+ "author": "Goethe"
185
+ },
186
+ {
187
+ "text": "Good times become good memories and bad times become good lessons in life.",
188
+ "author": "Unknown"
189
+ },
190
+ {
191
+ "text": "It's better to have a short life that is full of what you like doing, than a long life spent in a miserable way.",
192
+ "author": "Alan Watts"
193
+ },
194
+ {
195
+ "text": "To have faith is to trust yourself to the water. When you swim you don't grab hold of the water, because if you do you will sink and drown. Instead you relax, and float.",
196
+ "author": "Alan Watts"
197
+ },
198
+ {
199
+ "text": "I've been searching for years for the ideal place. And I've come to the realization that the only way to find it is to be it.",
200
+ "author": "Alan Watts"
201
+ },
202
+ {
203
+ "text": "I would rather be governed by the first 2,000 people in the Boston telephone directory than by the 2,000 people on the faculty of Harvard University.",
204
+ "author": "William F. Buckley Jr."
205
+ },
206
+ {
207
+ "text": "Never confuse education with intelligence, you can have a PhD and still be an idiot.",
208
+ "author": "Richard Feynman"
209
+ },
210
+ {
211
+ "text": "The enemy is anybody who's going to get you killed, no matter which side he's on.",
212
+ "author": "Joseph Heller"
213
+ },
214
+ {
215
+ "text": "People often say motivation doesn't last. Neither does bathing—that's why we recommend it daily.",
216
+ "author": "Zig Ziglar"
217
+ },
218
+ {
219
+ "text": "Live free ot die; death is not the worst of evils.",
220
+ "author": "General John Stark"
221
+ },
222
+ {
223
+ "text": "Equality only exists in theory. Social law can give all men equal rights. Nature will never give them equal faculties.",
224
+ "author": "Napoleon"
225
+ },
226
+ {
227
+ "text": "One who does not want to reason exists as a fanatic; one who ignores how to reason exists as a fool; and one who will not dare to reason exists as a slave.",
228
+ "author": "Carlos Drummond de Andrade"
229
+ }
230
+ ]
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(node -e \"import\\('@hyperspan/html'\\).then\\(m => console.log\\('html export:', typeof m.html\\)\\)\")",
5
- "Bash(npm run *)"
6
- ]
7
- }
8
- }
package/src/index.ts DELETED
@@ -1,67 +0,0 @@
1
- export { html } from "@hyperspan/html";
2
- export {
3
- createModule,
4
- isModule,
5
- type Module,
6
- type ModuleFactory,
7
- type ModuleOptionsOf,
8
- type ModuleRenderResult,
9
- } from "./modules.js";
10
- export {
11
- createPage,
12
- isPage,
13
- PageValidationError,
14
- toPageRenderView,
15
- validateSlotContentItem,
16
- type Page,
17
- type PageMeta,
18
- type PageRenderView,
19
- type RenderContext,
20
- type SitePage,
21
- } from "./pages.js";
22
- export { renderStandardHead } from "./render-head.js";
23
- export { renderPage, RenderError } from "./render-page.js";
24
- export { matchPagePath, normalizePathname } from "./routing.js";
25
- export {
26
- createSite,
27
- createSiteConfig,
28
- isSite,
29
- isSiteConfig,
30
- SiteError,
31
- type Site,
32
- type SiteConfig,
33
- } from "./site.js";
34
- export { createContext, Stackbox } from "./stackbox/context.js";
35
- export {
36
- anySlotContentSchema,
37
- moduleSlotContentSchema,
38
- slotHasContent,
39
- SlotContentValidationError,
40
- stringSlotContentSchema,
41
- type DefaultSlotContent,
42
- type PageSlotsInput,
43
- type SlotContentFromDefinition,
44
- } from "./slot-content.js";
45
- export {
46
- buildPageSlots,
47
- buildStubSlots,
48
- renderSlotContent,
49
- slotSentinel,
50
- SLOT_SENTINEL_PREFIX,
51
- type Slot,
52
- type SlotRenderValue,
53
- type TemplateSlotsFrom,
54
- } from "./slot-handle.js";
55
- export {
56
- createTemplate,
57
- isTemplate,
58
- TemplateBuildError,
59
- type RequiredSlotNamesFrom,
60
- type SlotDefinition,
61
- type SlotMeta,
62
- type SlotNamesFrom,
63
- type SlotOptions,
64
- type TemplateDescriptor,
65
- type TemplateRenderContext,
66
- type TemplateRenderFn,
67
- } from "./templates.js";
package/src/modules.ts DELETED
@@ -1,58 +0,0 @@
1
- import type { RenderContext } from "./pages.js";
2
- import type { HSHtml } from "@hyperspan/html";
3
-
4
- export type ModuleRenderResult = HSHtml | Promise<HSHtml>;
5
-
6
- export type Module = {
7
- readonly __kind: "module";
8
- readonly name: string;
9
- render(ctx: RenderContext): ModuleRenderResult;
10
- };
11
-
12
- type ModuleDef<TOptions = undefined> = {
13
- name: string;
14
- render(options: TOptions, ctx?: RenderContext): ModuleRenderResult;
15
- };
16
-
17
- export type ModuleOptionsOf<F> = F extends (options?: infer O) => unknown
18
- ? [O] extends [undefined]
19
- ? undefined
20
- : O
21
- : never;
22
-
23
- export type ModuleFactory<TOptions = undefined> = (
24
- options?: TOptions,
25
- ) => Module;
26
-
27
- function validateModuleName(name: string): void {
28
- if (!name || name.length === 0) {
29
- throw new Error("createModule(def): name is required");
30
- }
31
- }
32
-
33
- export function createModule<TOptions = undefined>(
34
- def: ModuleDef<TOptions>,
35
- ): ModuleFactory<TOptions> {
36
- validateModuleName(def.name);
37
-
38
- if (typeof def.render !== "function") {
39
- throw new Error("createModule(def): render is required");
40
- }
41
-
42
- return ((options?: TOptions) => ({
43
- __kind: "module" as const,
44
- name: def.name,
45
- render: (ctx: RenderContext) => def.render(options as TOptions, ctx),
46
- })) as ModuleFactory<TOptions>;
47
- }
48
-
49
- export function isModule(value: unknown): value is Module {
50
- return (
51
- typeof value === "object" &&
52
- value !== null &&
53
- (value as Module).__kind === "module" &&
54
- typeof (value as Module).name === "string" &&
55
- (value as Module).name.length > 0 &&
56
- typeof (value as Module).render === "function"
57
- );
58
- }