@invisibleloop/pulse 0.5.1 → 0.6.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.
@@ -324,7 +324,9 @@ export function validateSpec(spec) {
324
324
 
325
325
  // meta
326
326
  if (spec.meta) {
327
- if (typeof spec.meta !== 'object') {
327
+ if (typeof spec.meta === 'function') {
328
+ errors.push('spec.meta must be a plain object, not a function. Individual fields (title, description, styles) can be async (ctx) => value functions, but meta itself must be {}')
329
+ } else if (typeof spec.meta !== 'object') {
328
330
  errors.push('spec.meta must be a plain object')
329
331
  } else if (spec.meta.schema !== undefined && typeof spec.meta.schema !== 'object') {
330
332
  errors.push('spec.meta.schema must be a plain object (JSON-LD)')
package/types/md.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Pulse — Markdown parser types
3
+ * @invisibleloop/pulse/md
4
+ */
5
+
6
+ /** Parsed frontmatter key/value pairs from the --- block */
7
+ export type Frontmatter = Record<string, string>
8
+
9
+ /** Result of parsing a markdown string */
10
+ export interface MdResult {
11
+ /** HTML string ready to pass to prose({ content }) */
12
+ html: string
13
+ /** Key/value pairs parsed from the --- frontmatter block */
14
+ frontmatter: Frontmatter
15
+ }
16
+
17
+ /** Request context passed to server fetchers */
18
+ export interface MdContext {
19
+ params?: Record<string, string>
20
+ [key: string]: unknown
21
+ }
22
+
23
+ /** Async server fetcher returned by md() */
24
+ export type MdFetcher = (ctx?: MdContext) => Promise<MdResult>
25
+
26
+ /**
27
+ * Create a server fetcher that reads and parses a markdown file.
28
+ *
29
+ * Path patterns support :param placeholders resolved from ctx.params:
30
+ * md('content/blog/:slug.md')
31
+ *
32
+ * The result is cached per request on ctx._mdCache so meta and server
33
+ * can both call the same fetcher without reading the file twice.
34
+ *
35
+ * Missing files throw an error with status 404 — add onViewError on
36
+ * dynamic routes to handle this gracefully.
37
+ *
38
+ * @example
39
+ * import { md } from '@invisibleloop/pulse/md'
40
+ * import { prose } from '@invisibleloop/pulse/ui'
41
+ *
42
+ * const post = md('content/blog/:slug.md')
43
+ *
44
+ * export default {
45
+ * route: '/blog/:slug',
46
+ * meta: {
47
+ * title: async (ctx) => (await post(ctx)).frontmatter.title,
48
+ * description: async (ctx) => (await post(ctx)).frontmatter.description,
49
+ * },
50
+ * server: { post },
51
+ * view: (state, { post }) => prose({ content: post.html }),
52
+ * onViewError: () => '<p>Post not found.</p>',
53
+ * }
54
+ */
55
+ export function md(pathPattern: string | URL): MdFetcher
56
+
57
+ /**
58
+ * Parse a markdown string into HTML and frontmatter.
59
+ * Use this when markdown comes from a database or API rather than a file.
60
+ *
61
+ * @example
62
+ * import { parseMd } from '@invisibleloop/pulse/md'
63
+ *
64
+ * const { html, frontmatter } = parseMd(record.body)
65
+ */
66
+ export function parseMd(source: string): MdResult