@soloworks/smking-next 0.18.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 +28 -0
- package/package.json +1 -1
- package/src/components/smking-aeo.tsx +1 -14
- package/src/components/smking-cms.tsx +12 -0
- package/src/lib/client.ts +4 -0
- package/src/lib/safe-json.ts +21 -0
- package/src/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
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
|
+
|
|
19
|
+
## 0.19.0 — 2026-05-29
|
|
20
|
+
|
|
21
|
+
**CMS base path reported via heartbeat — dashboard "View page" resolves real URLs without per-page visits.**
|
|
22
|
+
|
|
23
|
+
`getAeoContent()`'s heartbeat (`sdk_meta`) now sends `cms_base_path` from
|
|
24
|
+
`SMKING_CMS_PATH` (default `/blog`). smking stores it site-level and builds
|
|
25
|
+
"View page" links as `domain + base path + slug` — a page resolves the
|
|
26
|
+
moment any page on the site is crawled, instead of waiting for that specific
|
|
27
|
+
page to be visited (supersedes 0.18.0's per-slug URL reporting). Set
|
|
28
|
+
`SMKING_CMS_PATH` if your CMS catch-all is mounted somewhere other than
|
|
29
|
+
`/blog`.
|
|
30
|
+
|
|
3
31
|
## 0.18.0 — 2026-05-28
|
|
4
32
|
|
|
5
33
|
**CMS live-URL reporting — `<SmkingCms>` now tells the dashboard each page's real public URL.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soloworks/smking-next",
|
|
3
|
-
"version": "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
|
package/src/lib/client.ts
CHANGED
|
@@ -110,7 +110,11 @@ export async function getAeoContent(
|
|
|
110
110
|
sdk_version: SDK_VERSION,
|
|
111
111
|
app_env: process.env.NODE_ENV ?? null,
|
|
112
112
|
host: safeHostname(url),
|
|
113
|
+
// Site-level CMS base path → smking dashboard "View page" URLs.
|
|
114
|
+
// Default "/blog"; override with SMKING_CMS_PATH (wizard sets it).
|
|
115
|
+
cms_base_path: process.env.SMKING_CMS_PATH ?? "/blog",
|
|
113
116
|
},
|
|
117
|
+
|
|
114
118
|
}),
|
|
115
119
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
116
120
|
next: {
|
|
@@ -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
|
/**
|