@ampless/runtime 1.0.0-beta.60 → 1.0.0-beta.62
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/dist/chunk-BS7KLDDE.js +10 -0
- package/dist/index.d.ts +21 -12
- package/dist/index.js +36 -3
- package/dist/middleware.d.ts +6 -0
- package/dist/middleware.js +11 -2
- package/package.json +3 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/request-headers.ts
|
|
2
|
+
var AMPLESS_PATHNAME_HEADER = "x-ampless-pathname";
|
|
3
|
+
var PREVIEW_THEME_HEADER = "x-preview-theme";
|
|
4
|
+
var PREVIEW_COLOR_SCHEME_HEADER = "x-preview-color-scheme";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
AMPLESS_PATHNAME_HEADER,
|
|
8
|
+
PREVIEW_THEME_HEADER,
|
|
9
|
+
PREVIEW_COLOR_SCHEME_HEADER
|
|
10
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -336,6 +336,20 @@ interface PluginHeadApi {
|
|
|
336
336
|
* `pluginSettings.loadAll()`.
|
|
337
337
|
*/
|
|
338
338
|
contextForPlugins(): Promise<(plugin: AmplessPlugin) => PluginPublicRenderContext>;
|
|
339
|
+
/**
|
|
340
|
+
* Non-gated variant of `renderHead()` for use by the admin post preview.
|
|
341
|
+
*
|
|
342
|
+
* WHY this bypasses `isPublicRequest()`: the preview iframe is editor-only
|
|
343
|
+
* and must faithfully show what the published article looks like — including
|
|
344
|
+
* content-decoration plugins like mermaid/highlight that only register their
|
|
345
|
+
* scripts via `publicHead`. The `isPublicRequest()` gate correctly blocks
|
|
346
|
+
* analytics on public pages served to the admin or login routes, but for the
|
|
347
|
+
* preview we intentionally collect ALL publicHead descriptors so diagrams and
|
|
348
|
+
* syntax highlighting render. Analytics scripts (GA4/GTM/Plausible) also fire
|
|
349
|
+
* on preview as a side-effect; this is accepted (preview is not end-user
|
|
350
|
+
* traffic). Do NOT use this from public layouts — use `renderHead()` there.
|
|
351
|
+
*/
|
|
352
|
+
renderHeadForPreview(): Promise<ReactNode>;
|
|
339
353
|
}
|
|
340
354
|
/**
|
|
341
355
|
* Position-bucketed result of `renderHtmlForPost`. Themes embed these
|
|
@@ -367,18 +381,6 @@ interface PublicHtmlForPostResult {
|
|
|
367
381
|
* U+2029 → '\u2029'
|
|
368
382
|
*/
|
|
369
383
|
declare function escapeJsonLdInlineBody(value: string): string;
|
|
370
|
-
/**
|
|
371
|
-
* Create the head/body renderer for a `Config`. The constructor-time
|
|
372
|
-
* pass logs a single dev warning when two plugins share an
|
|
373
|
-
* `instanceId ?? name`; everything else happens at render time so
|
|
374
|
-
* descriptors reflect per-request site config.
|
|
375
|
-
*
|
|
376
|
-
* `pluginSettings` (Phase 2) is the runtime accessor that pulls
|
|
377
|
-
* admin-managed `settings.public` values from the S3 site-settings
|
|
378
|
-
* cache. Within a single request we fetch once via `loadAll()` and
|
|
379
|
-
* bind a per-plugin `ctx.setting(key)` accessor before invoking
|
|
380
|
-
* either `publicHead` or `publicBodyEnd`.
|
|
381
|
-
*/
|
|
382
384
|
declare function createPluginHead(cmsConfig: Config, pluginSettings: PluginSettingsApi): PluginHeadApi;
|
|
383
385
|
|
|
384
386
|
interface ThemesRegistry {
|
|
@@ -588,6 +590,13 @@ interface Ampless {
|
|
|
588
590
|
postMetadata(post: Post): Promise<Metadata>;
|
|
589
591
|
siteMetadata(): Promise<Metadata>;
|
|
590
592
|
publicHead(): Promise<ReactNode>;
|
|
593
|
+
/**
|
|
594
|
+
* Non-gated head collector for the admin post preview. Collects ALL
|
|
595
|
+
* `publicHead` descriptors without calling `isPublicRequest()` so
|
|
596
|
+
* content-decoration plugins (mermaid, highlight) render in preview.
|
|
597
|
+
* Do NOT call from public layouts — use `publicHead()` there.
|
|
598
|
+
*/
|
|
599
|
+
publicHeadForPreview(): Promise<ReactNode>;
|
|
591
600
|
publicBodyEnd(): Promise<ReactNode>;
|
|
592
601
|
/**
|
|
593
602
|
* Per-post body descriptors (Phase 4 `schema` capability). Theme
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AMPLESS_PATHNAME_HEADER,
|
|
3
|
+
PREVIEW_COLOR_SCHEME_HEADER,
|
|
4
|
+
PREVIEW_THEME_HEADER
|
|
5
|
+
} from "./chunk-BS7KLDDE.js";
|
|
1
6
|
import {
|
|
2
7
|
_resetStreamS3Cache,
|
|
3
8
|
streamS3Object,
|
|
@@ -238,6 +243,7 @@ function createSeo(cmsConfig, settingsApi) {
|
|
|
238
243
|
}
|
|
239
244
|
|
|
240
245
|
// src/plugin-head.ts
|
|
246
|
+
import { headers } from "next/headers";
|
|
241
247
|
import {
|
|
242
248
|
Fragment as Fragment2,
|
|
243
249
|
createElement as createElement2
|
|
@@ -1428,6 +1434,20 @@ function collectHtmlForPost(plugins, site, snapshot, post) {
|
|
|
1428
1434
|
afterContent: toReactNode(after, "afterContent")
|
|
1429
1435
|
};
|
|
1430
1436
|
}
|
|
1437
|
+
async function isPublicRequest() {
|
|
1438
|
+
let h;
|
|
1439
|
+
try {
|
|
1440
|
+
h = await headers();
|
|
1441
|
+
} catch {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
const p = h.get(AMPLESS_PATHNAME_HEADER);
|
|
1445
|
+
if (!p) return false;
|
|
1446
|
+
if (p === "/admin" || p.startsWith("/admin/")) return false;
|
|
1447
|
+
if (p === "/login" || p.startsWith("/login/")) return false;
|
|
1448
|
+
if (h.get(PREVIEW_THEME_HEADER) || h.get(PREVIEW_COLOR_SCHEME_HEADER)) return false;
|
|
1449
|
+
return true;
|
|
1450
|
+
}
|
|
1431
1451
|
function createPluginHead(cmsConfig, pluginSettings) {
|
|
1432
1452
|
const plugins = (cmsConfig.plugins ?? []).filter(isPlugin2);
|
|
1433
1453
|
const validPlugins = [];
|
|
@@ -1532,6 +1552,7 @@ function createPluginHead(cmsConfig, pluginSettings) {
|
|
|
1532
1552
|
}
|
|
1533
1553
|
return {
|
|
1534
1554
|
async renderHead() {
|
|
1555
|
+
if (!await isPublicRequest()) return null;
|
|
1535
1556
|
const snapshot = await pluginSettings.loadAll();
|
|
1536
1557
|
return collectFor(
|
|
1537
1558
|
validPlugins,
|
|
@@ -1542,6 +1563,7 @@ function createPluginHead(cmsConfig, pluginSettings) {
|
|
|
1542
1563
|
);
|
|
1543
1564
|
},
|
|
1544
1565
|
async renderBodyEnd() {
|
|
1566
|
+
if (!await isPublicRequest()) return null;
|
|
1545
1567
|
const snapshot = await pluginSettings.loadAll();
|
|
1546
1568
|
return collectFor(
|
|
1547
1569
|
validPlugins,
|
|
@@ -1567,6 +1589,16 @@ function createPluginHead(cmsConfig, pluginSettings) {
|
|
|
1567
1589
|
async contextForPlugins() {
|
|
1568
1590
|
const snapshot = await pluginSettings.loadAll();
|
|
1569
1591
|
return (plugin) => makeCtx(plugin, cmsConfig.site, snapshot);
|
|
1592
|
+
},
|
|
1593
|
+
async renderHeadForPreview() {
|
|
1594
|
+
const snapshot = await pluginSettings.loadAll();
|
|
1595
|
+
return collectFor(
|
|
1596
|
+
validPlugins,
|
|
1597
|
+
cmsConfig.site,
|
|
1598
|
+
snapshot,
|
|
1599
|
+
(p) => p.publicHead,
|
|
1600
|
+
renderHeadDescriptor
|
|
1601
|
+
);
|
|
1570
1602
|
}
|
|
1571
1603
|
};
|
|
1572
1604
|
}
|
|
@@ -1664,7 +1696,7 @@ function createPluginSettings(storage) {
|
|
|
1664
1696
|
}
|
|
1665
1697
|
|
|
1666
1698
|
// src/theme-active.ts
|
|
1667
|
-
import { headers } from "next/headers";
|
|
1699
|
+
import { headers as headers2 } from "next/headers";
|
|
1668
1700
|
function createThemeActive(registry, storage) {
|
|
1669
1701
|
function settingsUrl() {
|
|
1670
1702
|
if (!storage.isStorageConfigured()) return null;
|
|
@@ -1699,8 +1731,8 @@ function createThemeActive(registry, storage) {
|
|
|
1699
1731
|
async resolveActiveTheme() {
|
|
1700
1732
|
let previewOverride = null;
|
|
1701
1733
|
try {
|
|
1702
|
-
const h = await
|
|
1703
|
-
previewOverride = h.get(
|
|
1734
|
+
const h = await headers2();
|
|
1735
|
+
previewOverride = h.get(PREVIEW_THEME_HEADER);
|
|
1704
1736
|
} catch {
|
|
1705
1737
|
}
|
|
1706
1738
|
if (previewOverride && previewOverride in registry.themes) {
|
|
@@ -1807,6 +1839,7 @@ function createAmpless(opts) {
|
|
|
1807
1839
|
postMetadata: (post) => seo.postMetadata(post),
|
|
1808
1840
|
siteMetadata: () => seo.siteMetadata(),
|
|
1809
1841
|
publicHead: () => pluginHead.renderHead(),
|
|
1842
|
+
publicHeadForPreview: () => pluginHead.renderHeadForPreview(),
|
|
1810
1843
|
publicBodyEnd: () => pluginHead.renderBodyEnd(),
|
|
1811
1844
|
publicBodyForPost: (post) => pluginHead.renderBodyForPost(post),
|
|
1812
1845
|
publicHtmlForPost: (post) => pluginHead.renderHtmlForPost(post),
|
package/dist/middleware.d.ts
CHANGED
|
@@ -37,6 +37,12 @@ declare function computeCacheControl(flags: {
|
|
|
37
37
|
* - `?previewTheme` / `?previewColorScheme` → `x-preview-theme` /
|
|
38
38
|
* `x-preview-color-scheme` header forwarding for the admin's
|
|
39
39
|
* iframe-based theme preview.
|
|
40
|
+
* - `x-ampless-pathname` marker header set unconditionally on every
|
|
41
|
+
* request this middleware handles (public routes only). Server
|
|
42
|
+
* components such as `renderHead` / `renderBodyEnd` read this header
|
|
43
|
+
* to confirm a public, non-preview request before emitting analytics
|
|
44
|
+
* or consent scripts. Routes excluded from the matcher (admin / api /
|
|
45
|
+
* login) never receive a runtime-set marker, so those layouts stay clean.
|
|
40
46
|
*/
|
|
41
47
|
declare function createAmplessMiddleware(opts: CreateMiddlewareOpts): MiddlewareFn;
|
|
42
48
|
/**
|
package/dist/middleware.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AMPLESS_PATHNAME_HEADER,
|
|
3
|
+
PREVIEW_COLOR_SCHEME_HEADER,
|
|
4
|
+
PREVIEW_THEME_HEADER
|
|
5
|
+
} from "./chunk-BS7KLDDE.js";
|
|
6
|
+
|
|
1
7
|
// src/middleware.ts
|
|
2
8
|
import { NextResponse } from "next/server";
|
|
3
9
|
var runtime = "nodejs";
|
|
@@ -137,10 +143,13 @@ function createAmplessMiddleware(opts) {
|
|
|
137
143
|
const previewTheme = url.searchParams.get("previewTheme");
|
|
138
144
|
const previewColorScheme = url.searchParams.get("previewColorScheme");
|
|
139
145
|
const requestHeaders = new Headers(request.headers);
|
|
140
|
-
|
|
146
|
+
requestHeaders.delete(PREVIEW_THEME_HEADER);
|
|
147
|
+
requestHeaders.delete(PREVIEW_COLOR_SCHEME_HEADER);
|
|
148
|
+
if (previewTheme) requestHeaders.set(PREVIEW_THEME_HEADER, previewTheme);
|
|
141
149
|
if (previewColorScheme) {
|
|
142
|
-
requestHeaders.set(
|
|
150
|
+
requestHeaders.set(PREVIEW_COLOR_SCHEME_HEADER, previewColorScheme);
|
|
143
151
|
}
|
|
152
|
+
requestHeaders.set(AMPLESS_PATHNAME_HEADER, url.pathname);
|
|
144
153
|
const passthrough = () => NextResponse.next({ request: { headers: requestHeaders } });
|
|
145
154
|
const segments = url.pathname.split("/").filter(Boolean);
|
|
146
155
|
if (segments.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/runtime",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.62",
|
|
4
4
|
"description": "Public-side runtime for ampless: post fetching, theme dispatch, middleware, public route handlers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"marked": "^18.0.4",
|
|
52
52
|
"sanitize-html": "^2.17.4",
|
|
53
53
|
"tailwind-merge": "^3.6.0",
|
|
54
|
-
"@ampless/plugin-og-image": "0.2.0-beta.
|
|
55
|
-
"ampless": "1.0.0-beta.
|
|
54
|
+
"@ampless/plugin-og-image": "0.2.0-beta.52",
|
|
55
|
+
"ampless": "1.0.0-beta.52"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@aws-amplify/adapter-nextjs": "^1",
|