@usezanin/blog 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ npm install @usezanin/blog
8
8
 
9
9
  ## Setup
10
10
 
11
- Env vars: `ZANIN_BASE_URL`, `ZANIN_SITE_ID`, `ZANIN_SITE_KEY` (from the Zanin dashboard), optionally `ZANIN_SITE_URL` (your site's public origin, for canonical URLs/RSS) and `ZANIN_REVALIDATE_SECRET`.
11
+ Env vars: `ZANIN_BASE_URL`, `ZANIN_SITE_ID`, `ZANIN_SITE_KEY` (from the Zanin dashboard), optionally `ZANIN_SITE_URL` (your site's public origin, for canonical URLs/RSS) and `ZANIN_REVALIDATE_SECRET`. Rendering only reads, so make `ZANIN_SITE_KEY` a **read-only key scoped to this site** (Settings → API keys) rather than a full-write workspace credential.
12
12
 
13
13
  ```tsx
14
14
  // app/layout.tsx — import the themable styles once
@@ -57,6 +57,20 @@ import { createRevalidateHandler } from "@usezanin/blog";
57
57
  export const POST = createRevalidateHandler();
58
58
  ```
59
59
 
60
+ ### Static export (`output: "export"`)
61
+
62
+ Statically exported sites have no server at runtime, so:
63
+
64
+ ```ts
65
+ // app/blog/rss.xml/route.ts — add force-static so the file is emitted at build time
66
+ import { createRssHandler } from "@usezanin/blog";
67
+ export const dynamic = "force-static";
68
+ export const GET = createRssHandler();
69
+ // same for app/blog/sitemap.xml/route.ts
70
+ ```
71
+
72
+ Skip `createRevalidateHandler` entirely — there is no ISR cache to bust. Instead point the site's revalidate webhook URL (Zanin dashboard → Settings → Sites) at a [Vercel Deploy Hook](https://vercel.com/docs/deploy-hooks) (or your CI's equivalent): publishing a post then triggers a rebuild that pulls fresh content through `generateStaticParams`. The build environment needs `ZANIN_SITE_KEY` — use a read-only site-scoped key.
73
+
60
74
  ## Theming — fully customizable
61
75
 
62
76
  Blogs are themable end to end: colors, fonts, sizes, spacing, radius, content width, and index layout.
@@ -85,7 +99,8 @@ Custom fonts: load them however you like (`next/font`, `<link>` tags) and refere
85
99
 
86
100
  ## What you get
87
101
 
88
- - Server-rendered MDX with built-in components: `Callout`, `Figure`, `Chart` (bar/line/area/pie from inline data), `CTA`, `YouTube`, `Tweet`, `ComparisonTable`, `FAQ` (with FAQPage JSON-LD), styled code blocks
102
+ - Server-rendered MDX with built-in components: `Callout`, `Figure`, `Chart` (bar/line/area/pie from inline data), `CTA`, `YouTube`, `Tweet`, `ComparisonTable`, `FAQ` (takes `items={[{ q, a }]}` and emits FAQPage JSON-LD), styled code blocks — exact prop tables live in `builtinComponentDocs` (re-exported here), the `get_builtin_components` MCP tool, and the deployed `/docs` page
103
+ - `validateMdx(source, { knownComponents })` — the same compiler `BlogPost` uses, as a linter: MDX errors, unknown component tags, unknown/missing props on built-ins
89
104
  - Per-site custom components: agents upload TSX to the CMS; the SDK loads the compiled result at render time — no redeploy
90
105
  - SEO: canonical/OG/Twitter metadata, JSON-LD Article schema, static params for SSG, RSS + sitemap
91
106
  - ISR with instant, webhook-driven revalidation on publish
package/dist/config.d.ts CHANGED
@@ -9,7 +9,10 @@ export interface ZaninBlogConfig {
9
9
  baseUrl?: string;
10
10
  /** Site id from the Zanin dashboard. Falls back to ZANIN_SITE_ID. */
11
11
  siteId?: string;
12
- /** Workspace API key ("zn_..."). Falls back to ZANIN_SITE_KEY / ZANIN_API_KEY. */
12
+ /**
13
+ * API key ("zn_..."). Falls back to ZANIN_SITE_KEY / ZANIN_API_KEY.
14
+ * Rendering only reads, so use a read-only key scoped to this site.
15
+ */
13
16
  apiKey?: string;
14
17
  /** Path the blog is mounted at in the host site. Default "/blog". */
15
18
  basePath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wIAAwI;IACxI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,kHAAkH;IAClH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,MAAM,GAAE,eAAoB,GAAG,uBAAuB,CAuBnF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wIAAwI;IACxI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,kHAAkH;IAClH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,MAAM,GAAE,eAAoB,GAAG,uBAAuB,CAuBnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsCA,MAAM,UAAU,aAAa,CAAC,SAA0B,EAAE;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC1D,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAE3E,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACzF,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM;QACN,MAAM;QACN,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC;QACvD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5E,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,GAAG;QACpC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAChF,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7D,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAyCA,MAAM,UAAU,aAAa,CAAC,SAA0B,EAAE;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC1D,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAE3E,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACzF,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM;QACN,MAAM;QACN,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC;QACvD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5E,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,GAAG;QACpC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAChF,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7D,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAChD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export { indexLayoutClass, mergeThemes, themeToStyle, type ZaninTheme } from "./
7
7
  export { createRevalidateHandler, createRssHandler, createSitemapHandler, } from "./handlers.js";
8
8
  export { fetchCustomComponents } from "./custom-components.js";
9
9
  export { builtinComponents, renderMdx } from "./mdx.js";
10
+ export { validateMdx, type MdxIssue, type MdxValidation, type ValidateMdxOptions, } from "./validate.js";
11
+ export { builtinComponentDocs, builtinComponentNames, type BuiltinComponentDoc, type BuiltinComponentProp, } from "@usezanin/client";
10
12
  export { generateBlogMetadata, generateBlogStaticParams } from "./seo.js";
11
13
  export { Callout } from "./components/Callout.js";
12
14
  export { Chart, type ChartProps } from "./components/Chart.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EACL,WAAW,EACX,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ export { indexLayoutClass, mergeThemes, themeToStyle } from "./theme.js";
7
7
  export { createRevalidateHandler, createRssHandler, createSitemapHandler, } from "./handlers.js";
8
8
  export { fetchCustomComponents } from "./custom-components.js";
9
9
  export { builtinComponents, renderMdx } from "./mdx.js";
10
+ export { validateMdx, } from "./validate.js";
11
+ export { builtinComponentDocs, builtinComponentNames, } from "@usezanin/client";
10
12
  export { generateBlogMetadata, generateBlogStaticParams } from "./seo.js";
11
13
  export { Callout } from "./components/Callout.js";
12
14
  export { Chart } from "./components/Chart.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAuB,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAmB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAuB,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EACL,WAAW,GAIZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GAGtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAmB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type MdxIssue, type MdxValidation } from "@usezanin/client";
2
+ export type { MdxIssue, MdxValidation };
3
+ export interface ValidateMdxOptions {
4
+ /**
5
+ * Component tags available besides the built-ins (per-site custom
6
+ * components and/or host-provided components).
7
+ */
8
+ knownComponents?: string[];
9
+ }
10
+ /**
11
+ * Validates an MDX post body with the exact pipeline the SDK uses to render
12
+ * it (@mdx-js/mdx + remark-gfm), then lints component usage:
13
+ *
14
+ * - errors: MDX syntax/compile failures, missing required props on built-ins
15
+ * - warnings: unknown component tags, unknown props on built-in components
16
+ */
17
+ export declare function validateMdx(source: string, options?: ValidateMdxOptions): Promise<MdxValidation>;
18
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;AAwBxC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,aAAa,CAAC,CAwFxB"}
@@ -0,0 +1,101 @@
1
+ import { compile } from "@mdx-js/mdx";
2
+ import { builtinComponentDocs, builtinComponentNames, } from "@usezanin/client";
3
+ import remarkGfm from "remark-gfm";
4
+ function collectJsxElements(tree, out) {
5
+ if (tree.type === "mdxJsxFlowElement" || tree.type === "mdxJsxTextElement") {
6
+ out.push(tree);
7
+ }
8
+ for (const child of tree.children ?? []) {
9
+ collectJsxElements(child, out);
10
+ }
11
+ }
12
+ /**
13
+ * Validates an MDX post body with the exact pipeline the SDK uses to render
14
+ * it (@mdx-js/mdx + remark-gfm), then lints component usage:
15
+ *
16
+ * - errors: MDX syntax/compile failures, missing required props on built-ins
17
+ * - warnings: unknown component tags, unknown props on built-in components
18
+ */
19
+ export async function validateMdx(source, options = {}) {
20
+ const errors = [];
21
+ const warnings = [];
22
+ const known = new Set([...builtinComponentNames, ...(options.knownComponents ?? [])]);
23
+ const elements = [];
24
+ const collector = () => (tree) => {
25
+ collectJsxElements(tree, elements);
26
+ };
27
+ try {
28
+ await compile(source, { remarkPlugins: [remarkGfm, collector] });
29
+ }
30
+ catch (err) {
31
+ const message = err;
32
+ errors.push({
33
+ severity: "error",
34
+ rule: "mdx-syntax",
35
+ message: message.reason ?? message.message ?? String(err),
36
+ ...(typeof message.line === "number" ? { line: message.line } : {}),
37
+ ...(typeof message.column === "number" ? { column: message.column } : {}),
38
+ });
39
+ }
40
+ for (const el of elements) {
41
+ const name = el.name;
42
+ // Skip fragments, lowercase HTML tags and member expressions (Foo.Bar).
43
+ if (!name || !/^[A-Z]/.test(name) || name.includes("."))
44
+ continue;
45
+ const position = {
46
+ ...(el.position?.start?.line !== undefined ? { line: el.position.start.line } : {}),
47
+ ...(el.position?.start?.column !== undefined ? { column: el.position.start.column } : {}),
48
+ };
49
+ if (!known.has(name)) {
50
+ warnings.push({
51
+ severity: "warning",
52
+ rule: "unknown-component",
53
+ component: name,
54
+ message: `<${name}> is not a built-in component and no custom component with that name exists on this site. The post will fail to render unless the host site supplies it via the components prop. Built-ins: ${builtinComponentNames.join(", ")}.`,
55
+ ...position,
56
+ });
57
+ continue;
58
+ }
59
+ const doc = builtinComponentDocs.find((d) => d.name === name);
60
+ if (!doc)
61
+ continue; // custom component — no prop metadata to lint against
62
+ const attrs = el.attributes ?? [];
63
+ // A spread ({...props}) can supply anything; skip prop-level checks.
64
+ const hasSpread = attrs.some((a) => a.type === "mdxJsxExpressionAttribute");
65
+ const attrNames = attrs
66
+ .filter((a) => a.type === "mdxJsxAttribute" && typeof a.name === "string")
67
+ .map((a) => a.name);
68
+ const validProps = new Set(doc.props.map((p) => p.name));
69
+ for (const attr of attrNames) {
70
+ if (!validProps.has(attr)) {
71
+ warnings.push({
72
+ severity: "warning",
73
+ rule: "unknown-prop",
74
+ component: name,
75
+ message: `<${name}> has no "${attr}" prop. Valid props: ${doc.props.map((p) => p.name).join(", ") || "(none)"}.`,
76
+ ...position,
77
+ });
78
+ }
79
+ }
80
+ if (!hasSpread) {
81
+ for (const prop of doc.props) {
82
+ if (prop.required && !attrNames.includes(prop.name)) {
83
+ errors.push({
84
+ severity: "error",
85
+ rule: "missing-required-prop",
86
+ component: name,
87
+ message: `<${name}> is missing its required "${prop.name}" prop (${prop.type}) and will fail to render.`,
88
+ ...position,
89
+ });
90
+ }
91
+ }
92
+ }
93
+ }
94
+ return {
95
+ valid: errors.length === 0,
96
+ errors,
97
+ warnings,
98
+ knownComponents: [...known].sort(),
99
+ };
100
+ }
101
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usezanin/blog",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "React SDK for rendering Zanin blogs in Next.js sites \u2014 index/post components, built-in MDX components, SEO, RSS and sitemap",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mdx-js/mdx": "^3.1.0",
44
- "@usezanin/client": "^0.2.0",
44
+ "@usezanin/client": "^0.3.0",
45
45
  "recharts": "^2.15.0",
46
46
  "remark-gfm": "^4.0.0"
47
47
  },