@soloworks/smking-next 0.21.2 → 0.21.3
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 +10 -0
- package/bin/install.mjs +0 -0
- package/package.json +7 -7
- package/src/cms-blocks.ts +56 -12
- package/src/components/smking-aeo.tsx +16 -0
- package/src/index.ts +0 -1
- package/src/lib/version.ts +1 -1
- package/src/types.ts +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @soloworks/smking-next
|
|
2
2
|
|
|
3
|
+
## 0.21.3 — 2026-07-03
|
|
4
|
+
|
|
5
|
+
**AEO original-vs-enhanced audits can now fetch the true host HTML.**
|
|
6
|
+
|
|
7
|
+
`<SmkingAEO>` now honors `x-smking-origin-mode: raw` by returning `null`
|
|
8
|
+
before calling the SaaS. This lets Page Zero fetch a customer page's real
|
|
9
|
+
pre-injection HTML for source-change detection, then compare it against the
|
|
10
|
+
served enhanced version. No customer code change; customers already on `^0.21`
|
|
11
|
+
receive it on the next install/update.
|
|
12
|
+
|
|
3
13
|
## 0.21.2 — 2026-07-02
|
|
4
14
|
|
|
5
15
|
**CMS taxonomy page types caught up with the SaaS catalog (types only — no runtime change).**
|
package/bin/install.mjs
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soloworks/smking-next",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.3",
|
|
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",
|
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
"perplexity",
|
|
66
66
|
"smking"
|
|
67
67
|
],
|
|
68
|
+
"scripts": {
|
|
69
|
+
"typecheck": "tsc --noEmit",
|
|
70
|
+
"test": "vitest run",
|
|
71
|
+
"test:watch": "vitest"
|
|
72
|
+
},
|
|
68
73
|
"peerDependencies": {
|
|
69
74
|
"next": "^15.0.0 || ^16.0.0",
|
|
70
75
|
"react": "^18.0.0 || ^19.0.0"
|
|
@@ -86,10 +91,5 @@
|
|
|
86
91
|
"react-dom": "19.2.4",
|
|
87
92
|
"typescript": "^5",
|
|
88
93
|
"vitest": "^4.1.5"
|
|
89
|
-
},
|
|
90
|
-
"scripts": {
|
|
91
|
-
"typecheck": "tsc --noEmit",
|
|
92
|
-
"test": "vitest run",
|
|
93
|
-
"test:watch": "vitest"
|
|
94
94
|
}
|
|
95
|
-
}
|
|
95
|
+
}
|
package/src/cms-blocks.ts
CHANGED
|
@@ -27,6 +27,35 @@
|
|
|
27
27
|
* slug-prefix derived, tag mode targets `taxonomies.slug`.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
export interface YouTubeEmbedOptions {
|
|
31
|
+
autoplay?: boolean;
|
|
32
|
+
ccLangPref?: string;
|
|
33
|
+
ccLoadPolicy?: boolean;
|
|
34
|
+
color?: "red" | "white";
|
|
35
|
+
controls?: boolean;
|
|
36
|
+
disablekb?: boolean;
|
|
37
|
+
enablejsapi?: boolean;
|
|
38
|
+
end?: number;
|
|
39
|
+
fs?: boolean;
|
|
40
|
+
hl?: string;
|
|
41
|
+
ivLoadPolicy?: "show" | "hide";
|
|
42
|
+
list?: string;
|
|
43
|
+
listType?: "playlist" | "user_uploads";
|
|
44
|
+
loop?: boolean;
|
|
45
|
+
origin?: string;
|
|
46
|
+
playlist?: string;
|
|
47
|
+
playsinline?: boolean;
|
|
48
|
+
relatedMode?: "all" | "same-channel";
|
|
49
|
+
start?: number;
|
|
50
|
+
widgetReferrer?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface HeroYouTubeProps extends YouTubeEmbedOptions {
|
|
54
|
+
url: string;
|
|
55
|
+
videoId: string;
|
|
56
|
+
thumbnailUrl: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
30
59
|
export interface HeroProps {
|
|
31
60
|
title: string;
|
|
32
61
|
/** Generic visible meta lines. Legacy drafts may still also carry subtitle. */
|
|
@@ -39,6 +68,9 @@ export interface HeroProps {
|
|
|
39
68
|
/** Legacy single-line fallback for SDK Mode B consumers. */
|
|
40
69
|
subtitle?: string;
|
|
41
70
|
image?: { url: string; alt: string };
|
|
71
|
+
/** YouTube hero media. Article body renders the embed; cards / metadata use
|
|
72
|
+
* the thumbnail so list-like pages never mount a player. */
|
|
73
|
+
youtube?: HeroYouTubeProps;
|
|
42
74
|
cta?: { label: string; href: string };
|
|
43
75
|
/** Header layout — omitted = the original centred hero; "start" = the
|
|
44
76
|
* Apple-newsroom-style left-aligned header; "cover" = title-only banner over
|
|
@@ -122,22 +154,16 @@ export interface SearchProps {
|
|
|
122
154
|
/** Content column vs wide breakout (default wide). Mirrors the media
|
|
123
155
|
* blocks (image/embed). */
|
|
124
156
|
widthMode?: "content" | "wide";
|
|
157
|
+
/** Quick-link cluster alignment beside the search trigger. Defaults left. */
|
|
158
|
+
linkAlign?: "left" | "right";
|
|
159
|
+
/** Quick-link visual treatment. Defaults badge. */
|
|
160
|
+
linkStyle?: "badge" | "plain";
|
|
125
161
|
/** Optional quick links adjacent to search. */
|
|
126
162
|
links?: SearchQuickLink[];
|
|
127
163
|
/** Publish-time materialized snapshot — every published page on the site. */
|
|
128
164
|
snapshot?: NavSnapshotEntry[];
|
|
129
165
|
}
|
|
130
166
|
|
|
131
|
-
export interface RecentPostsProps extends ModuleHeader {
|
|
132
|
-
/** Author-set number of latest posts to show (1..50). */
|
|
133
|
-
limit: number;
|
|
134
|
-
/** Content column vs wide breakout (default content). Mirrors the media
|
|
135
|
-
* blocks. */
|
|
136
|
-
widthMode?: "content" | "wide";
|
|
137
|
-
/** Publish-time materialized snapshot — whole-site newest-first. */
|
|
138
|
-
snapshot?: NavSnapshotEntry[];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
167
|
/** One author-picked source for nav-related-posts. Same shape as
|
|
142
168
|
* `CategoryIndexItem` (a category path OR a tag), reused so the source picker
|
|
143
169
|
* UI stays identical across blocks. */
|
|
@@ -369,7 +395,7 @@ export interface ButtonGroupProps {
|
|
|
369
395
|
* url = renders nothing on the customer side. `widthMode` matches the other
|
|
370
396
|
* media blocks (content column vs wide breakout).
|
|
371
397
|
*/
|
|
372
|
-
export interface EmbedProps {
|
|
398
|
+
export interface EmbedProps extends YouTubeEmbedOptions {
|
|
373
399
|
url?: string;
|
|
374
400
|
caption?: string;
|
|
375
401
|
widthMode?: "content" | "wide";
|
|
@@ -416,6 +442,15 @@ export type LatestNewsSlot =
|
|
|
416
442
|
source?: "category-by-path" | "tag";
|
|
417
443
|
};
|
|
418
444
|
|
|
445
|
+
/** Whole-block source mode for `latest-news`. Omitted = `manual` so old
|
|
446
|
+
* blocks keep their curated slots exactly. */
|
|
447
|
+
export type LatestNewsMode =
|
|
448
|
+
| "manual"
|
|
449
|
+
| "latest"
|
|
450
|
+
| "auto-category"
|
|
451
|
+
| "category"
|
|
452
|
+
| "tag";
|
|
453
|
+
|
|
419
454
|
/**
|
|
420
455
|
* One materialized `latest-news` card: the resolved article entry (the
|
|
421
456
|
* article itself for an `article` slot, or the category's latest post for a
|
|
@@ -436,8 +471,18 @@ export interface LatestNewsCardSnapshot {
|
|
|
436
471
|
* feeds; reuses `NavSnapshotEntry` for each resolved card.
|
|
437
472
|
*/
|
|
438
473
|
export interface LatestNewsProps extends ModuleHeader {
|
|
474
|
+
/** Whole-block source mode. Omitted/manual uses `slots`; other modes resolve
|
|
475
|
+
* cards dynamically at publish time. */
|
|
476
|
+
mode?: LatestNewsMode;
|
|
439
477
|
/** Author-curated slots, 3–6, in display order (first = feature card). */
|
|
440
478
|
slots: LatestNewsSlot[];
|
|
479
|
+
/** Card count for automatic modes. The magazine grid supports 3–6 cards. */
|
|
480
|
+
limit?: number;
|
|
481
|
+
/** Selected category/tag path for `category` / `tag` modes. */
|
|
482
|
+
sourcePath?: string;
|
|
483
|
+
/** Editor-facing label for the selected category/tag; used as the card
|
|
484
|
+
* eyebrow in automatic source modes. */
|
|
485
|
+
sourceLabel?: string;
|
|
441
486
|
/** Content column vs wide breakout (default wide). Mirrors the media
|
|
442
487
|
* blocks. */
|
|
443
488
|
widthMode?: "content" | "wide";
|
|
@@ -454,7 +499,6 @@ export type Block =
|
|
|
454
499
|
props: NavTaxonomyListProps;
|
|
455
500
|
}
|
|
456
501
|
| { component: "search"; id: string; props: SearchProps }
|
|
457
|
-
| { component: "nav-recent-posts"; id: string; props: RecentPostsProps }
|
|
458
502
|
| { component: "nav-related-posts"; id: string; props: RelatedPostsProps }
|
|
459
503
|
| { component: "article-tags"; id: string; props: ArticleTagsProps }
|
|
460
504
|
| { component: "nav-category-index"; id: string; props: CategoryIndexProps }
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { headers } from "next/headers";
|
|
2
|
+
|
|
1
3
|
import type { DiscoverParams } from "../types";
|
|
2
4
|
import { getAeoContent } from "../lib/client";
|
|
3
5
|
import { safeJson } from "../lib/safe-json";
|
|
@@ -16,6 +18,18 @@ const SR_ONLY_STYLE: React.CSSProperties = {
|
|
|
16
18
|
|
|
17
19
|
export interface SmkingAEOProps extends DiscoverParams {}
|
|
18
20
|
|
|
21
|
+
async function wantsOriginBypass(): Promise<boolean> {
|
|
22
|
+
try {
|
|
23
|
+
const requestHeaders = await headers();
|
|
24
|
+
return (
|
|
25
|
+
requestHeaders.get("x-smking-origin-mode")?.trim().toLowerCase() ===
|
|
26
|
+
"raw"
|
|
27
|
+
);
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
/**
|
|
20
34
|
* Server Component that injects AEO + SEO content for the current
|
|
21
35
|
* request. Place once in the root layout, inside `<body>`:
|
|
@@ -57,6 +71,8 @@ export interface SmkingAEOProps extends DiscoverParams {}
|
|
|
57
71
|
* arbitrary content fetched anywhere.
|
|
58
72
|
*/
|
|
59
73
|
export async function SmkingAEO(props: SmkingAEOProps) {
|
|
74
|
+
if (await wantsOriginBypass()) return null;
|
|
75
|
+
|
|
60
76
|
const aeo = await getAeoContent(props);
|
|
61
77
|
if (!aeo || aeo.status !== "ready") return null;
|
|
62
78
|
|
package/src/index.ts
CHANGED
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.
|
|
1
|
+
export const SDK_VERSION = "0.21.3";
|