@soloworks/smking-next 0.19.0 → 0.20.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @soloworks/smking-next
2
2
 
3
+ ## 0.20.0 — 2026-06-02
4
+
5
+ **`<SmkingCms>` now emits the structured-data engine's JSON-LD `@graph` inline.**
6
+
7
+ CMS pages get a full multi-entity `@graph` (BlogPosting / Organization /
8
+ WebSite / BreadcrumbList / FAQPage, `@id`-linked) generated by the smking
9
+ structured-data engine and served via `/api/v1/public/page`. `<SmkingCms>`
10
+ inlines it as `<script type="application/ld+json">` — JSON-LD is valid in
11
+ `<body>`, so no head hoisting needed (unlike `<title>`/`<meta>`, which
12
+ React 19 still hoists for you). The `safeJson` escaper (`</` + U+2028/U+2029)
13
+ is extracted into a shared `lib/safe-json.ts` that `<SmkingAEO>` imports too.
14
+
15
+ No customer code change — `<SmkingCms>` picks up the new `jsonLd` field
16
+ automatically once a page is republished under the engine. Bump your
17
+ constraint to `^0.20`.
18
+
3
19
  ## 0.19.0 — 2026-05-29
4
20
 
5
21
  **CMS base path reported via heartbeat — dashboard "View page" resolves real URLs without per-page visits.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soloworks/smking-next",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "AI-native SEO (AEO) for Next.js — auto-inject JSON-LD, FAQ, AI summary, and SEO metadata so AI crawlers (ChatGPT, Perplexity, Google AI) can cite your pages.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/sillyleo/smking/tree/main/packages/smking-next",
@@ -1,5 +1,6 @@
1
1
  import type { DiscoverParams } from "../types";
2
2
  import { getAeoContent } from "../lib/client";
3
+ import { safeJson } from "../lib/safe-json";
3
4
 
4
5
  const SR_ONLY_STYLE: React.CSSProperties = {
5
6
  position: "absolute",
@@ -118,17 +119,3 @@ export async function SmkingAEO(props: SmkingAEOProps) {
118
119
  </>
119
120
  );
120
121
  }
121
-
122
- /**
123
- * Escape JSON for safe `<script>` embedding. Covers the three values
124
- * that can break out:
125
- * - `</` (any `</script>`-like sequence)
126
- * - U+2028 / U+2029 (treated as line terminators inside a JS string
127
- * literal, which would corrupt the embedded JSON)
128
- */
129
- function safeJson(value: Record<string, unknown>): string {
130
- return JSON.stringify(value)
131
- .replace(/<\//g, "<\\/")
132
- .replace(/\u2028/g, "\\u2028")
133
- .replace(/\u2029/g, "\\u2029");
134
- }
@@ -1,4 +1,5 @@
1
1
  import { getCmsPage } from "../lib/cms-client";
2
+ import { safeJson } from "../lib/safe-json";
2
3
  import type { CmsParams } from "../types";
3
4
 
4
5
  /**
@@ -72,6 +73,17 @@ export async function SmkingCms(props: CmsParams) {
72
73
  <link rel="canonical" href={seo.canonicalUrl} data-smking="cms" />
73
74
  )}
74
75
 
76
+ {/* Engine-precomputed @graph. JSON-LD is valid in <body>, so no
77
+ head hoisting needed (unlike title/meta above). */}
78
+ {data.page.jsonLd && (
79
+ <script
80
+ type="application/ld+json"
81
+ data-smking="cms"
82
+ // eslint-disable-next-line react/no-danger -- escaped via safeJson; engine output is schema-bounded
83
+ dangerouslySetInnerHTML={{ __html: safeJson(data.page.jsonLd) }}
84
+ />
85
+ )}
86
+
75
87
  {/* Raw passthrough — bodyHtml is SaaS-emitted Plate output (Tailwind
76
88
  utility + slate-* hooks + data-slate-* attrs all intact). No
77
89
  article shell, no canonical CSS injection: what the dashboard
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Escape an object for safe `<script type="application/ld+json">`
3
+ * embedding. Covers the sequences that can break out of / corrupt the
4
+ * script:
5
+ * - `</` (any `</script>`-like sequence)
6
+ * - U+2028 / U+2029 (JS string-literal line terminators)
7
+ *
8
+ * The U+2028 / U+2029 patterns are built via `new RegExp("\\u2028", ...)`
9
+ * (escaped-backslash strings) so this source file stays pure ASCII — no
10
+ * invisible line-separator characters to corrupt on edit.
11
+ *
12
+ * Shared by `<SmkingAEO>` and `<SmkingCms>`. Engine output is
13
+ * schema-bounded, but this guard is mandatory defence-in-depth — never
14
+ * inline JSON-LD without it.
15
+ */
16
+ export function safeJson(value: Record<string, unknown>): string {
17
+ return JSON.stringify(value)
18
+ .replace(/<\//g, "<\\/")
19
+ .replace(new RegExp("\\u2028", "g"), "\\u2028")
20
+ .replace(new RegExp("\\u2029", "g"), "\\u2029");
21
+ }
package/src/types.ts CHANGED
@@ -191,6 +191,12 @@ export interface CmsPage {
191
191
  excerpt?: string | null;
192
192
  featuredImageUrl?: string | null;
193
193
  publishedAt: string | null;
194
+ /**
195
+ * v0.M1+ engine-precomputed schema.org @graph. `<SmkingCms>` inlines it
196
+ * as `<script type="application/ld+json">`. Null until the page is
197
+ * republished under the structured-data engine.
198
+ */
199
+ jsonLd?: Record<string, unknown> | null;
194
200
  }
195
201
 
196
202
  /**