@soloworks/smking-next 0.16.1 → 0.17.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,24 @@
1
1
  # @soloworks/smking-next
2
2
 
3
+ ## 0.17.0 — 2026-05-26
4
+
5
+ **SDK self-report (heartbeat) — dashboard SDK Health chip stops getting WAF false-negatives.**
6
+
7
+ ### Added
8
+
9
+ - Every `getAeoContent()` POST now piggybacks an `sdk_meta` field on the request body so saas can upsert `sdk_health_snapshots` with `source='sdk_heartbeat'`. The dashboard "SDK Health" chip reads this signal as primary, falling back to active probe only when fresh (<24h) heartbeat is absent.
10
+ - Payload shape: `{ sdk: "next", sdk_version, app_env, host }`. All fields optional saas-side; host is parsed from the request URL via `new URL(url).hostname`.
11
+
12
+ ### Why
13
+
14
+ Customer Cloudflare WAFs commonly block saas's Vercel ASN egress (Vercel + Googlebot UA = reverse-DNS mismatch → 403 reject). Active probe from saas → customer site reliably fails on protected sites like sleepytofu.com even when SDK is fully operational. SDK-initiated heartbeat bypasses the WAF because the customer site is the originator — saas never needs to fetch back.
15
+
16
+ ### Migration
17
+
18
+ No customer action required. Patch your SDK and the dashboard chip updates automatically on the next page render that triggers `getAeoContent`. Saas accepts the field since 2026-05; older saas versions ignore unknown body fields silently.
19
+
20
+ Minor bump — backward-compatible feature. `^0.16` consumers must update their constraint to `^0.17`.
21
+
3
22
  ## 0.16.1 — 2026-05-25
4
23
 
5
24
  **New `<SmkingRuntime />` component — one-time mount in customer root layout to load saas-served CSS + Web Component runtime JS.**
package/README.md CHANGED
@@ -84,6 +84,30 @@ Content-Type: application/json
84
84
 
85
85
  Response: `{ revalidated: number, errors: number }`. `errors > 0` means some tags couldn't be revalidated (others still succeeded — partial-success delivery).
86
86
 
87
+ ## Mount the runtime once (v0.16.1+)
88
+
89
+ Add `<SmkingRuntime />` once in your root layout — it emits a `<link>` to the saas-served CSS and a `<script async>` to the bundled Web Component runtime IIFE. Browser caches both per saas-controlled stale-while-revalidate headers, so the cost amortises across every `<SmkingCms>` instance on the page.
90
+
91
+ ```tsx
92
+ // app/layout.tsx
93
+ import { SmkingRuntime } from "@soloworks/smking-next";
94
+
95
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
96
+ return (
97
+ <html>
98
+ <body>
99
+ <SmkingRuntime />
100
+ {children}
101
+ </body>
102
+ </html>
103
+ );
104
+ }
105
+ ```
106
+
107
+ Optional `baseUrl` prop overrides `process.env.SMKING_BASE_URL`. Default: `https://smking.app`.
108
+
109
+ Without `<SmkingRuntime />`, `<SmkingCms>` content still renders but the Tailwind utility classes from the dashboard's cva variants resolve to dead strings — the page reaches the browser unstyled. The wizard installer auto-adds this mount; if you're upgrading manually from < v0.16.1, add the one line above.
110
+
87
111
  ## CMS rendering (optional, v0.11.0+)
88
112
 
89
113
  The base install only wires AEO. If you author content in the smking dashboard's CMS and want to render it on your Next.js site, use the `<SmkingCms slug="…" />` Server Component.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soloworks/smking-next",
3
- "version": "0.16.1",
3
+ "version": "0.17.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",
package/src/lib/client.ts CHANGED
@@ -8,6 +8,27 @@ import { normalizePath, resolveRequestPath } from "./path";
8
8
  const DEFAULT_REVALIDATE_SECONDS = 3600;
9
9
  const FETCH_TIMEOUT_MS = 2000;
10
10
 
11
+ /**
12
+ * SDK version self-reported in heartbeat `sdk_meta.sdk_version`. Must stay
13
+ * in sync with package.json `version` — `publish-smking-package` skill
14
+ * bumps both together.
15
+ */
16
+ const SDK_VERSION = "0.17.0";
17
+
18
+ /**
19
+ * Extract hostname from a full URL string. Returns null on parse failure
20
+ * (malformed URL would otherwise throw). Used to populate heartbeat
21
+ * `sdk_meta.host` without pulling next/headers into this client.
22
+ */
23
+ function safeHostname(input: string | undefined): string | null {
24
+ if (!input) return null;
25
+ try {
26
+ return new URL(input).hostname;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
11
32
  const _warnedKeys = new Set<string>();
12
33
  function warnOnce(key: string, message: string): void {
13
34
  if (process.env.NODE_ENV === "production") return;
@@ -81,7 +102,22 @@ export async function getAeoContent(
81
102
  const res = await fetch(`${baseUrl}/api/v1/public/aeo`, {
82
103
  method: "POST",
83
104
  headers: { "content-type": "application/json" },
84
- body: JSON.stringify({ key: params.apiKey, path, url }),
105
+ body: JSON.stringify({
106
+ key: params.apiKey,
107
+ path,
108
+ url,
109
+ // Heartbeat piggyback — saas upserts into sdk_health_snapshots so
110
+ // the dashboard can show 'SDK active (last seen Nm ago)' without
111
+ // needing an outbound probe (which Cloudflare WAFs commonly block).
112
+ // Saas accepts this optional field via /api/v1/public/aeo POST since
113
+ // 2026-05; older saas versions silently ignore it.
114
+ sdk_meta: {
115
+ sdk: "next",
116
+ sdk_version: SDK_VERSION,
117
+ app_env: process.env.NODE_ENV ?? null,
118
+ host: safeHostname(url),
119
+ },
120
+ }),
85
121
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
86
122
  next: {
87
123
  revalidate: params.revalidate ?? DEFAULT_REVALIDATE_SECONDS,