@ampless/runtime 1.0.0-alpha.18 → 1.0.0-alpha.20
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/README.ja.md +5 -3
- package/README.md +5 -3
- package/dist/dispatchers/index.d.ts +4 -4
- package/dist/index.d.ts +19 -0
- package/dist/index.js +17 -3
- package/dist/middleware.d.ts +5 -4
- package/dist/middleware.js +6 -5
- package/dist/routes/index.d.ts +56 -43
- package/dist/routes/index.js +44 -35
- package/package.json +3 -3
package/README.ja.md
CHANGED
|
@@ -88,8 +88,10 @@ middleware はリクエストごとに AppSync から `post.format` /
|
|
|
88
88
|
キャッシュした上で、適切な内部ハンドラーにリクエストを書き換えます。
|
|
89
89
|
|
|
90
90
|
- 通常の投稿 → 書き換えなし、`app/[slug]/page.tsx` で配信
|
|
91
|
-
- `metadata.no_layout: true` HTML
|
|
92
|
-
`app/
|
|
91
|
+
- `metadata.no_layout: true` HTML → `/raw/<slug>`、
|
|
92
|
+
`app/raw/[slug]/route.ts` で配信
|
|
93
|
+
- `format: 'static'` → `/static/<slug>(/<path>)`、
|
|
94
|
+
`app/static/[slug]/[[...path]]/route.ts` で配信
|
|
93
95
|
|
|
94
96
|
また、`post.metadata.cache`(auto / deep / hot)+ `post.updatedAt` +
|
|
95
97
|
`cms.config.cache.{cooldownMs, freshTtlSeconds, deepTtlSeconds}` から
|
|
@@ -100,7 +102,7 @@ middleware はリクエストごとに AppSync から `post.format` /
|
|
|
100
102
|
|
|
101
103
|
- `@ampless/runtime` — `createAmpless`、ランタイム型、`renderBody`・`renderThemeCss`・フォーマットコンバーターの再エクスポート
|
|
102
104
|
- `@ampless/runtime/middleware` — `createAmplessMiddleware`、`defaultMatcherConfig`
|
|
103
|
-
- `@ampless/runtime/routes` — `createOgRouteHandler`、`createSitemapRouteHandler`、`createFeedRouteHandler`、`
|
|
105
|
+
- `@ampless/runtime/routes` — `createOgRouteHandler`、`createSitemapRouteHandler`、`createFeedRouteHandler`、`createRawRouteHandler`、`createStaticRouteHandler`
|
|
104
106
|
- `@ampless/runtime/dispatchers` — `createThemeHomeDispatcher`、`createThemePostDispatcher`、`createThemeTagDispatcher`(それぞれ対応する `*Metadata` ファクトリーあり)
|
|
105
107
|
|
|
106
108
|
## テンプレートに残るもの
|
package/README.md
CHANGED
|
@@ -88,8 +88,10 @@ from AppSync (apiKey auth) on each request, caches the result in a
|
|
|
88
88
|
rewrites the request to the right internal handler:
|
|
89
89
|
|
|
90
90
|
- themed post → no rewrite, served by `app/[slug]/page.tsx`
|
|
91
|
-
- `metadata.no_layout: true` HTML
|
|
92
|
-
|
|
91
|
+
- `metadata.no_layout: true` HTML → `/raw/<slug>`, served by
|
|
92
|
+
`app/raw/[slug]/route.ts`
|
|
93
|
+
- `format: 'static'` → `/static/<slug>(/<path>)`, served by
|
|
94
|
+
`app/static/[slug]/[[...path]]/route.ts`
|
|
93
95
|
|
|
94
96
|
It also computes `Cache-Control` from `post.metadata.cache` (auto /
|
|
95
97
|
deep / hot) + `post.updatedAt` + `cms.config.cache.{cooldownMs,
|
|
@@ -100,7 +102,7 @@ See `docs/CONTENT.md` for the cache strategy contract.
|
|
|
100
102
|
|
|
101
103
|
- `@ampless/runtime` — `createAmpless`, runtime types, and re-exports of `renderBody`, `renderThemeCss`, format converters
|
|
102
104
|
- `@ampless/runtime/middleware` — `createAmplessMiddleware`, `defaultMatcherConfig`
|
|
103
|
-
- `@ampless/runtime/routes` — `createOgRouteHandler`, `createSitemapRouteHandler`, `createFeedRouteHandler`, `
|
|
105
|
+
- `@ampless/runtime/routes` — `createOgRouteHandler`, `createSitemapRouteHandler`, `createFeedRouteHandler`, `createRawRouteHandler`, `createStaticRouteHandler`
|
|
104
106
|
- `@ampless/runtime/dispatchers` — `createThemeHomeDispatcher`, `createThemePostDispatcher`, `createThemeTagDispatcher` (each with a matching `*Metadata` factory)
|
|
105
107
|
|
|
106
108
|
## What's still in the template
|
|
@@ -32,10 +32,10 @@ type ThemePostMetadata = (props: Props$1) => Promise<Metadata>;
|
|
|
32
32
|
* in middleware. Middleware fetches `post.format` / `post.metadata` /
|
|
33
33
|
* `post.updatedAt` from AppSync once per slug (Lambda-memory LRU, 60s
|
|
34
34
|
* TTL) and rewrites the request to either this dispatcher (themed
|
|
35
|
-
* render)
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* render), `/raw/<slug>` (no_layout HTML), or `/static/<slug>(/...)`
|
|
36
|
+
* (static bundle). That keeps the decision in one place and avoids the
|
|
37
|
+
* dispatcher's extra AppSync round-trip when the post is a themed
|
|
38
|
+
* render.
|
|
39
39
|
*
|
|
40
40
|
* If the theme doesn't declare a Post component at all, returns
|
|
41
41
|
* Next.js's notFound() (404).
|
package/dist/index.d.ts
CHANGED
|
@@ -108,6 +108,18 @@ interface ResolvedTheme {
|
|
|
108
108
|
}
|
|
109
109
|
interface ThemeActiveApi {
|
|
110
110
|
resolveActiveTheme(): Promise<ResolvedTheme>;
|
|
111
|
+
/**
|
|
112
|
+
* Read the stored `theme.active` value directly from the S3 cache
|
|
113
|
+
* with `cache: 'no-store'` — bypassing Next.js's fetch cache and
|
|
114
|
+
* any tag-based revalidation. Returns the raw stored name (which
|
|
115
|
+
* may be a theme that isn't in the registry) or `null` when the
|
|
116
|
+
* S3 file is missing / unreadable.
|
|
117
|
+
*
|
|
118
|
+
* Used by the admin theme-switch flow to poll until the trusted
|
|
119
|
+
* processor has propagated a KvStore write to S3, so the post-
|
|
120
|
+
* switch hard reload doesn't race the cache rebuild.
|
|
121
|
+
*/
|
|
122
|
+
readStoredActiveThemeFresh(): Promise<string | null>;
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
type ColorScheme = 'auto' | 'light' | 'dark';
|
|
@@ -213,6 +225,13 @@ interface Ampless {
|
|
|
213
225
|
listPostsByTag(tag: string, opts?: ListPostsByTagOptions): Promise<ListPostsResult>;
|
|
214
226
|
loadSiteSettings(): Promise<EffectiveSiteSettings>;
|
|
215
227
|
resolveActiveTheme(): Promise<ResolvedTheme>;
|
|
228
|
+
/**
|
|
229
|
+
* Fresh-read of the stored `theme.active` value from S3, bypassing
|
|
230
|
+
* the Next.js fetch cache. Returns the raw stored name (no fallback
|
|
231
|
+
* to defaultTheme) or `null` when the cache file is missing. Used
|
|
232
|
+
* by the admin to poll for processor propagation after a switch.
|
|
233
|
+
*/
|
|
234
|
+
readStoredActiveThemeFresh(): Promise<string | null>;
|
|
216
235
|
loadThemeConfig(): Promise<EffectiveThemeConfig>;
|
|
217
236
|
postMetadata(post: Post): Promise<Metadata>;
|
|
218
237
|
siteMetadata(): Promise<Metadata>;
|
package/dist/index.js
CHANGED
|
@@ -178,14 +178,17 @@ function createSeo(cmsConfig, settingsApi) {
|
|
|
178
178
|
// src/theme-active.ts
|
|
179
179
|
import { headers } from "next/headers";
|
|
180
180
|
function createThemeActive(registry, storage) {
|
|
181
|
-
|
|
181
|
+
function settingsUrl() {
|
|
182
182
|
if (!storage.isStorageConfigured()) return null;
|
|
183
|
-
let url;
|
|
184
183
|
try {
|
|
185
|
-
|
|
184
|
+
return storage.publicAssetUrl("public/site-settings.json");
|
|
186
185
|
} catch {
|
|
187
186
|
return null;
|
|
188
187
|
}
|
|
188
|
+
}
|
|
189
|
+
async function fetchActiveFromCache() {
|
|
190
|
+
const url = settingsUrl();
|
|
191
|
+
if (!url) return null;
|
|
189
192
|
const res = await fetch(url, {
|
|
190
193
|
next: { revalidate: 60, tags: ["site-settings"] }
|
|
191
194
|
});
|
|
@@ -194,7 +197,17 @@ function createThemeActive(registry, storage) {
|
|
|
194
197
|
const v = flat["theme.active"];
|
|
195
198
|
return typeof v === "string" ? v : null;
|
|
196
199
|
}
|
|
200
|
+
async function fetchActiveFresh() {
|
|
201
|
+
const url = settingsUrl();
|
|
202
|
+
if (!url) return null;
|
|
203
|
+
const res = await fetch(url, { cache: "no-store" });
|
|
204
|
+
if (!res.ok) return null;
|
|
205
|
+
const flat = await res.json();
|
|
206
|
+
const v = flat["theme.active"];
|
|
207
|
+
return typeof v === "string" ? v : null;
|
|
208
|
+
}
|
|
197
209
|
return {
|
|
210
|
+
readStoredActiveThemeFresh: () => fetchActiveFresh(),
|
|
198
211
|
async resolveActiveTheme() {
|
|
199
212
|
let previewOverride = null;
|
|
200
213
|
try {
|
|
@@ -601,6 +614,7 @@ function createAmpless(opts) {
|
|
|
601
614
|
listPostsByTag: (tag, o) => posts.listPostsByTag(tag, o),
|
|
602
615
|
loadSiteSettings: () => settings.loadSiteSettings(),
|
|
603
616
|
resolveActiveTheme: () => themeActive.resolveActiveTheme(),
|
|
617
|
+
readStoredActiveThemeFresh: () => themeActive.readStoredActiveThemeFresh(),
|
|
604
618
|
loadThemeConfig: () => themeConfig.loadThemeConfig(),
|
|
605
619
|
postMetadata: (post) => seo.postMetadata(post),
|
|
606
620
|
siteMetadata: () => seo.siteMetadata(),
|
package/dist/middleware.d.ts
CHANGED
|
@@ -27,10 +27,11 @@ declare function computeCacheControl(flags: {
|
|
|
27
27
|
* - AppSync flag fetch (`format` / `metadata` / `updatedAt`) per
|
|
28
28
|
* slug, with a 200-entry LRU keyed by slug, 60s TTL. Hot slugs
|
|
29
29
|
* cost zero queries for the cache lifetime.
|
|
30
|
-
* - Rewrite to `/
|
|
31
|
-
* (`format=html` + `metadata.no_layout=true`)
|
|
32
|
-
*
|
|
33
|
-
* `
|
|
30
|
+
* - Rewrite to `/raw/<slug>` when the post is no_layout HTML
|
|
31
|
+
* (`format=html` + `metadata.no_layout=true`).
|
|
32
|
+
* - Rewrite to `/static/<slug>(/<path>)` when the post is a static
|
|
33
|
+
* bundle (`format='static'`). Themed posts (default) get no
|
|
34
|
+
* rewrite — `app/[slug]/page.tsx` serves them directly.
|
|
34
35
|
* - `Cache-Control` header computed from `post.metadata.cache` +
|
|
35
36
|
* `post.updatedAt` + `cms.config.cache.*` and set on the response.
|
|
36
37
|
* - `?previewTheme` / `?previewColorScheme` → `x-preview-theme` /
|
package/dist/middleware.js
CHANGED
|
@@ -127,8 +127,9 @@ var RESERVED_PREFIXES = /* @__PURE__ */ new Set([
|
|
|
127
127
|
"sitemap.xml",
|
|
128
128
|
"og",
|
|
129
129
|
"tag",
|
|
130
|
-
// internal rewrite
|
|
131
|
-
"
|
|
130
|
+
// internal rewrite targets — direct hits should not be middleware-driven
|
|
131
|
+
"raw",
|
|
132
|
+
"static"
|
|
132
133
|
]);
|
|
133
134
|
function createAmplessMiddleware(opts) {
|
|
134
135
|
return async function middleware(request) {
|
|
@@ -161,12 +162,12 @@ function createAmplessMiddleware(opts) {
|
|
|
161
162
|
let response;
|
|
162
163
|
if (restPath.length === 0) {
|
|
163
164
|
if (flags.format === "html" && flags.metadata?.no_layout === true) {
|
|
164
|
-
url.pathname = `/
|
|
165
|
+
url.pathname = `/raw/${slug}`;
|
|
165
166
|
response = NextResponse.rewrite(url, {
|
|
166
167
|
request: { headers: requestHeaders }
|
|
167
168
|
});
|
|
168
169
|
} else if (flags.format === "static") {
|
|
169
|
-
url.pathname = `/
|
|
170
|
+
url.pathname = `/static/${slug}`;
|
|
170
171
|
response = NextResponse.rewrite(url, {
|
|
171
172
|
request: { headers: requestHeaders }
|
|
172
173
|
});
|
|
@@ -175,7 +176,7 @@ function createAmplessMiddleware(opts) {
|
|
|
175
176
|
}
|
|
176
177
|
} else {
|
|
177
178
|
if (flags.format === "static") {
|
|
178
|
-
url.pathname = `/
|
|
179
|
+
url.pathname = `/static/${slug}/${restPath.join("/")}`;
|
|
179
180
|
response = NextResponse.rewrite(url, {
|
|
180
181
|
request: { headers: requestHeaders }
|
|
181
182
|
});
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -2,18 +2,18 @@ import { Ampless } from '../index.js';
|
|
|
2
2
|
import 'ampless';
|
|
3
3
|
import 'next';
|
|
4
4
|
|
|
5
|
-
interface Ctx$
|
|
5
|
+
interface Ctx$4 {
|
|
6
6
|
params: Promise<{
|
|
7
7
|
slug: string;
|
|
8
8
|
}>;
|
|
9
9
|
}
|
|
10
|
-
type OgRouteHandler = (req: Request, ctx: Ctx$
|
|
10
|
+
type OgRouteHandler = (req: Request, ctx: Ctx$4) => Promise<Response>;
|
|
11
11
|
declare function createOgRouteHandler(ampless: Ampless): OgRouteHandler;
|
|
12
12
|
|
|
13
|
-
interface Ctx$
|
|
13
|
+
interface Ctx$3 {
|
|
14
14
|
params: Promise<Record<string, never>>;
|
|
15
15
|
}
|
|
16
|
-
type SitemapRouteHandler = (req: Request, ctx: Ctx$
|
|
16
|
+
type SitemapRouteHandler = (req: Request, ctx: Ctx$3) => Promise<Response>;
|
|
17
17
|
/**
|
|
18
18
|
* Sitemap route delegate. Looks up the active theme and forwards to
|
|
19
19
|
* whichever `routes.sitemap` handler the theme provides. Themes
|
|
@@ -21,10 +21,10 @@ type SitemapRouteHandler = (req: Request, ctx: Ctx$2) => Promise<Response>;
|
|
|
21
21
|
*/
|
|
22
22
|
declare function createSitemapRouteHandler(ampless: Ampless): SitemapRouteHandler;
|
|
23
23
|
|
|
24
|
-
interface Ctx$
|
|
24
|
+
interface Ctx$2 {
|
|
25
25
|
params: Promise<Record<string, never>>;
|
|
26
26
|
}
|
|
27
|
-
type FeedRouteHandler = (req: Request, ctx: Ctx$
|
|
27
|
+
type FeedRouteHandler = (req: Request, ctx: Ctx$2) => Promise<Response>;
|
|
28
28
|
/**
|
|
29
29
|
* Feed route delegate. Looks up the active theme and forwards to
|
|
30
30
|
* whichever `routes.feed` handler the theme provides. Themes without
|
|
@@ -32,55 +32,68 @@ type FeedRouteHandler = (req: Request, ctx: Ctx$1) => Promise<Response>;
|
|
|
32
32
|
*/
|
|
33
33
|
declare function createFeedRouteHandler(ampless: Ampless): FeedRouteHandler;
|
|
34
34
|
|
|
35
|
-
interface Ctx {
|
|
35
|
+
interface Ctx$1 {
|
|
36
36
|
params: Promise<{
|
|
37
37
|
slug: string;
|
|
38
|
-
path?: string[];
|
|
39
38
|
}>;
|
|
40
39
|
}
|
|
41
|
-
type
|
|
40
|
+
type RawRouteHandler = (req: Request, ctx: Ctx$1) => Promise<Response>;
|
|
42
41
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
42
|
+
* Internal route handler for `format: 'html'` posts whose
|
|
43
|
+
* `metadata.no_layout === true` — the body is its own complete HTML
|
|
44
|
+
* document and ships as the entire response (no Next.js root layout,
|
|
45
|
+
* no theme chrome).
|
|
46
|
+
*
|
|
47
|
+
* Mounted at `app/raw/[slug]/route.ts`; reached via middleware rewrite
|
|
48
|
+
* of `/<slug>` → `/raw/<slug>`. Never hit directly — middleware adds
|
|
49
|
+
* `raw` to its reserved-prefix list so a user post with slug `raw`
|
|
50
|
+
* passes through to a 404 rather than reaching this handler with the
|
|
51
|
+
* wrong content.
|
|
47
52
|
*
|
|
48
|
-
*
|
|
53
|
+
* Trust model: the body is emitted verbatim, same trust shape as
|
|
54
|
+
* regular `format: 'html'` posts on the themed route. See
|
|
55
|
+
* docs/architecture/04-access-layer-mcp.md §"editor の信頼モデル".
|
|
49
56
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
* Cache-Control: deliberately omitted from the response. Middleware
|
|
58
|
+
* computes the strategy from `post.metadata.cache` + `post.updatedAt`
|
|
59
|
+
* and sets the header on the rewritten response.
|
|
60
|
+
*/
|
|
61
|
+
declare function createRawRouteHandler(ampless: Ampless): RawRouteHandler;
|
|
62
|
+
|
|
63
|
+
interface Ctx {
|
|
64
|
+
params: Promise<{
|
|
65
|
+
slug: string;
|
|
66
|
+
path?: string[];
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
69
|
+
type StaticRouteHandler = (req: Request, ctx: Ctx) => Promise<Response>;
|
|
70
|
+
/**
|
|
71
|
+
* Internal route handler for `format: 'static'` posts — the body is a
|
|
72
|
+
* manifest describing a bundle of files in S3 at
|
|
73
|
+
* `public/static/<slug>/`. The bundle entrypoint is served at
|
|
74
|
+
* `/<slug>/`, every internal file at `/<slug>/<relative-path>`.
|
|
58
75
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* next/dist/build/route-discovery.js). The `r/` prefix is an
|
|
65
|
-
* internal filesystem detail — the public URL stays `/<slug>(/...)`.
|
|
66
|
-
* - `params.path` is `undefined` (or `[]`) for single-segment
|
|
67
|
-
* requests, an array of remaining segments otherwise.
|
|
76
|
+
* Mounted at `app/static/[slug]/[[...path]]/route.ts`; reached via
|
|
77
|
+
* middleware rewrite of `/<slug>(/<path>)` → `/static/<slug>(/<path>)`.
|
|
78
|
+
* Never hit directly — middleware adds `static` to its reserved-prefix
|
|
79
|
+
* list so a user post with slug `static` passes through to a 404
|
|
80
|
+
* rather than landing here with the wrong content.
|
|
68
81
|
*
|
|
69
|
-
* Trailing-slash responsibility lives here
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
82
|
+
* Trailing-slash responsibility lives here: `/<slug>` (no trailing
|
|
83
|
+
* slash) 308-redirects to `/<slug>/` so relative asset paths inside
|
|
84
|
+
* the bundle resolve correctly — `<img src="img.png">` must resolve
|
|
85
|
+
* to `/<slug>/img.png`, not the site root.
|
|
73
86
|
*
|
|
74
|
-
* Trust model:
|
|
75
|
-
*
|
|
76
|
-
* bundle assets are served via short-lived S3 presigned URLs; the
|
|
77
|
-
* bucket stays private. See docs/architecture/04-access-layer-mcp.md
|
|
78
|
-
* §"editor の信頼モデル".
|
|
87
|
+
* Trust model: assets are served via short-lived S3 presigned URLs;
|
|
88
|
+
* the bucket stays private.
|
|
79
89
|
*
|
|
80
90
|
* Cache-Control: deliberately omitted from the responses here.
|
|
81
91
|
* Middleware computes the strategy from `metadata.cache` +
|
|
82
|
-
* `post.updatedAt` and sets
|
|
92
|
+
* `post.updatedAt` and sets it on the rewritten response. The 302
|
|
93
|
+
* presigned redirects also rely on S3's own headers for the actual
|
|
94
|
+
* asset bytes — adding a stale-while-revalidate window on the 302
|
|
95
|
+
* would risk serving an expired presign to repeat visitors.
|
|
83
96
|
*/
|
|
84
|
-
declare function
|
|
97
|
+
declare function createStaticRouteHandler(ampless: Ampless): StaticRouteHandler;
|
|
85
98
|
|
|
86
|
-
export { type FeedRouteHandler, type OgRouteHandler, type SitemapRouteHandler, type
|
|
99
|
+
export { type FeedRouteHandler, type OgRouteHandler, type RawRouteHandler, type SitemapRouteHandler, type StaticRouteHandler, createFeedRouteHandler, createOgRouteHandler, createRawRouteHandler, createSitemapRouteHandler, createStaticRouteHandler };
|
package/dist/routes/index.js
CHANGED
|
@@ -65,11 +65,32 @@ function createFeedRouteHandler(ampless) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// src/routes/
|
|
68
|
+
// src/routes/raw.ts
|
|
69
|
+
function createRawRouteHandler(ampless) {
|
|
70
|
+
return async function GET(_request, { params }) {
|
|
71
|
+
const { slug } = await params;
|
|
72
|
+
const post = await ampless.getPublishedPost(slug);
|
|
73
|
+
if (!post) {
|
|
74
|
+
return new Response("Not Found", { status: 404 });
|
|
75
|
+
}
|
|
76
|
+
if (post.format !== "html" || post.metadata?.no_layout !== true) {
|
|
77
|
+
return new Response("Not Found", { status: 404 });
|
|
78
|
+
}
|
|
79
|
+
return new Response(ampless.renderBody(post), {
|
|
80
|
+
status: 200,
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
83
|
+
// Cache-Control set by middleware.
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/routes/static.ts
|
|
69
90
|
import { createServerRunner } from "@aws-amplify/adapter-nextjs";
|
|
70
91
|
import { cookies } from "next/headers";
|
|
71
92
|
import { getUrl } from "aws-amplify/storage/server";
|
|
72
|
-
function
|
|
93
|
+
function createStaticRouteHandler(ampless) {
|
|
73
94
|
const { runWithAmplifyServerContext } = createServerRunner({
|
|
74
95
|
config: ampless.outputs
|
|
75
96
|
});
|
|
@@ -80,40 +101,27 @@ function createUnderscoreRouteHandler(ampless) {
|
|
|
80
101
|
if (!post) {
|
|
81
102
|
return new Response("Not Found", { status: 404 });
|
|
82
103
|
}
|
|
104
|
+
if (post.format !== "static") {
|
|
105
|
+
return new Response("Not Found", { status: 404 });
|
|
106
|
+
}
|
|
83
107
|
if (restSegments.length === 0) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Cache-Control set by middleware (cache strategy depends on
|
|
90
|
-
// post.metadata.cache + post.updatedAt).
|
|
91
|
-
}
|
|
92
|
-
});
|
|
108
|
+
const url = new URL(request.url);
|
|
109
|
+
if (!url.pathname.endsWith("/")) {
|
|
110
|
+
const next = new URL(url);
|
|
111
|
+
next.pathname = `${url.pathname}/`;
|
|
112
|
+
return Response.redirect(next.toString(), 308);
|
|
93
113
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const presignedUrl2 = await signStaticAsset({
|
|
104
|
-
runWithAmplifyServerContext,
|
|
105
|
-
slug,
|
|
106
|
-
rest: entrypoint
|
|
107
|
-
});
|
|
108
|
-
if (!presignedUrl2) {
|
|
109
|
-
return new Response("Not Found", { status: 404 });
|
|
110
|
-
}
|
|
111
|
-
return Response.redirect(presignedUrl2, 302);
|
|
114
|
+
const body2 = post.body ?? null;
|
|
115
|
+
const entrypoint = typeof body2?.entrypoint === "string" && body2.entrypoint ? body2.entrypoint : "index.html";
|
|
116
|
+
const presignedUrl2 = await signStaticAsset({
|
|
117
|
+
runWithAmplifyServerContext,
|
|
118
|
+
slug,
|
|
119
|
+
rest: entrypoint
|
|
120
|
+
});
|
|
121
|
+
if (!presignedUrl2) {
|
|
122
|
+
return new Response("Not Found", { status: 404 });
|
|
112
123
|
}
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
if (post.format !== "static") {
|
|
116
|
-
return new Response("Not Found", { status: 404 });
|
|
124
|
+
return Response.redirect(presignedUrl2, 302);
|
|
117
125
|
}
|
|
118
126
|
if (restSegments.some(
|
|
119
127
|
(s) => !s || s === "." || s === ".." || s.includes("/") || s.includes("\\") || s.includes("\0")
|
|
@@ -156,7 +164,7 @@ async function signStaticAsset({
|
|
|
156
164
|
});
|
|
157
165
|
} catch (err) {
|
|
158
166
|
console.error(
|
|
159
|
-
`[
|
|
167
|
+
`[static-route] presign failed for ${objectPath}:`,
|
|
160
168
|
err
|
|
161
169
|
);
|
|
162
170
|
return null;
|
|
@@ -165,6 +173,7 @@ async function signStaticAsset({
|
|
|
165
173
|
export {
|
|
166
174
|
createFeedRouteHandler,
|
|
167
175
|
createOgRouteHandler,
|
|
176
|
+
createRawRouteHandler,
|
|
168
177
|
createSitemapRouteHandler,
|
|
169
|
-
|
|
178
|
+
createStaticRouteHandler
|
|
170
179
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/runtime",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.20",
|
|
4
4
|
"description": "Public-side runtime for ampless: post fetching, theme dispatch, middleware, public route handlers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"lucide-react": "^1.16.0",
|
|
50
50
|
"marked": "^18.0.4",
|
|
51
51
|
"tailwind-merge": "^3.6.0",
|
|
52
|
-
"ampless": "1.0.0-alpha.
|
|
53
|
-
"@ampless/plugin-og-image": "0.2.0-alpha.
|
|
52
|
+
"ampless": "1.0.0-alpha.14",
|
|
53
|
+
"@ampless/plugin-og-image": "0.2.0-alpha.14"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"next": "^15 || ^16",
|