create-ampless 1.0.0-alpha.58 → 1.0.0-alpha.59

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.
Files changed (61) hide show
  1. package/dist/index.js +1 -10
  2. package/dist/templates/_shared/AGENTS.ja.md +10 -8
  3. package/dist/templates/_shared/AGENTS.md +10 -8
  4. package/dist/templates/_shared/README.ja.md +22 -2
  5. package/dist/templates/_shared/README.md +22 -2
  6. package/dist/templates/_shared/app/layout.tsx +8 -8
  7. package/dist/templates/_shared/app/providers.tsx +0 -2
  8. package/dist/templates/_shared/components/site-chrome/site-sidebar.tsx +2 -2
  9. package/dist/templates/_shared/components.json +1 -1
  10. package/dist/templates/_shared/lib/admin.ts +4 -6
  11. package/dist/templates/_shared/lib/ampless.ts +2 -8
  12. package/dist/templates/_shared/lib/amplify.ts +0 -5
  13. package/dist/templates/_shared/package.json +2 -2
  14. package/dist/templates/blog/pages/feed.ts +2 -2
  15. package/dist/templates/blog/pages/home.tsx +8 -10
  16. package/dist/templates/blog/pages/post.tsx +9 -12
  17. package/dist/templates/blog/pages/sitemap.ts +2 -2
  18. package/dist/templates/blog/pages/tag.tsx +7 -9
  19. package/dist/templates/corporate/pages/feed.ts +2 -2
  20. package/dist/templates/corporate/pages/home.tsx +6 -8
  21. package/dist/templates/corporate/pages/post.tsx +9 -12
  22. package/dist/templates/corporate/pages/sitemap.ts +2 -2
  23. package/dist/templates/corporate/pages/tag.tsx +7 -9
  24. package/dist/templates/dads/pages/feed.ts +2 -2
  25. package/dist/templates/dads/pages/home.tsx +6 -8
  26. package/dist/templates/dads/pages/post.tsx +9 -12
  27. package/dist/templates/dads/pages/sitemap.ts +2 -2
  28. package/dist/templates/dads/pages/tag.tsx +7 -9
  29. package/dist/templates/docs/pages/feed.ts +2 -2
  30. package/dist/templates/docs/pages/home.tsx +4 -6
  31. package/dist/templates/docs/pages/post.tsx +7 -10
  32. package/dist/templates/docs/pages/sitemap.ts +2 -2
  33. package/dist/templates/docs/pages/tag.tsx +6 -8
  34. package/dist/templates/landing/pages/feed.ts +2 -2
  35. package/dist/templates/landing/pages/home.tsx +6 -8
  36. package/dist/templates/landing/pages/post.tsx +9 -12
  37. package/dist/templates/landing/pages/sitemap.ts +2 -2
  38. package/dist/templates/landing/pages/tag.tsx +7 -9
  39. package/dist/templates/minimal/pages/feed.ts +2 -2
  40. package/dist/templates/minimal/pages/home.tsx +5 -6
  41. package/dist/templates/minimal/pages/post.tsx +8 -10
  42. package/dist/templates/minimal/pages/sitemap.ts +2 -2
  43. package/dist/templates/minimal/pages/tag.tsx +6 -7
  44. package/package.json +1 -1
  45. package/dist/templates/_shared/components/i18n-provider.tsx +0 -15
  46. package/dist/templates/_shared/lib/amplify-server.ts +0 -7
  47. package/dist/templates/_shared/lib/auth-server.ts +0 -15
  48. package/dist/templates/_shared/lib/cn.ts +0 -5
  49. package/dist/templates/_shared/lib/i18n.ts +0 -35
  50. package/dist/templates/_shared/lib/kv-provider.ts +0 -8
  51. package/dist/templates/_shared/lib/media.ts +0 -6
  52. package/dist/templates/_shared/lib/posts-provider.ts +0 -8
  53. package/dist/templates/_shared/lib/posts-public.ts +0 -27
  54. package/dist/templates/_shared/lib/posts.ts +0 -12
  55. package/dist/templates/_shared/lib/seo.ts +0 -11
  56. package/dist/templates/_shared/lib/site-settings.ts +0 -11
  57. package/dist/templates/_shared/lib/storage.ts +0 -10
  58. package/dist/templates/_shared/lib/theme-actions.ts +0 -5
  59. package/dist/templates/_shared/lib/theme-active.ts +0 -10
  60. package/dist/templates/_shared/lib/theme-config.ts +0 -12
  61. package/dist/templates/_shared/lib/upload.ts +0 -6
package/dist/index.js CHANGED
@@ -1747,16 +1747,7 @@ var AMPLESS_MANAGED_APP_PATHS = [
1747
1747
  "app/login",
1748
1748
  "app/site"
1749
1749
  ];
1750
- var AMPLESS_RETIRED_PATHS = [
1751
- // Retired alongside the in-deploy multi-site drop.
1752
- "lib/admin-site.ts",
1753
- "lib/admin-site-client.ts",
1754
- // Split into app/raw/[slug]/route.ts + app/static/[slug]/[[...path]]/route.ts.
1755
- // The old unified internal handler at app/r/[slug]/[[...path]]/route.ts
1756
- // is no longer wired to anything (middleware rewrites to /raw or
1757
- // /static now).
1758
- "app/r/[slug]/[[...path]]/route.ts"
1759
- ];
1750
+ var AMPLESS_RETIRED_PATHS = [];
1760
1751
  var AMPLESS_PACKAGES = /* @__PURE__ */ new Set([
1761
1752
  "ampless",
1762
1753
  "@ampless/admin",
@@ -43,22 +43,24 @@
43
43
  レスポンシブの目視確認、Markdown 要素のデザイン方針、よくある失敗
44
44
  — は [THEMES.ja.md](./THEMES.ja.md) を参照する。
45
45
 
46
- ## MCP サーバー(`@ampless/mcp-server`)
46
+ ## MCP サーバー(HTTP トランスポート)
47
47
 
48
- エージェントが投稿コンテンツを直接クエリ・編集できる。公開ツール: `list_posts`、`get_post`、`create_post`、`update_post`、`delete_post`、`upload_media`、`get_schema`。
48
+ エージェントが `mcp-handler` Lambda 経由で投稿コンテンツを直接クエリ・編集できる。公開ツール: `list_posts`、`get_post`、`create_post`、`update_post`、`delete_post`、`upload_media`、`get_schema`、`upload_static_bundle`、`list_static_files`、`delete_static_file`、`get_site_context`。
49
49
 
50
50
  登録方法:
51
51
 
52
- プロジェクトルートの `.mcp.json` に追加する:
52
+ 1. 管理画面の `/admin/mcp-tokens` で Bearer トークンを発行する。
53
+ 2. Amplify コンソールまたは `amplify_outputs.json` で `mcp-handler` の Function URL を確認する。
54
+ 3. プロジェクトルートの `.mcp.json` に追加する:
55
+
53
56
  ```json
54
57
  {
55
58
  "mcpServers": {
56
59
  "ampless": {
57
- "command": "npx",
58
- "args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
59
- "env": {
60
- "AMPLESS_MCP_EMAIL": "<your-admin-email>",
61
- "AMPLESS_MCP_PASSWORD": "<your-admin-password>"
60
+ "url": "https://<function-url-id>.lambda-url.<region>.on.aws/",
61
+ "transport": "http",
62
+ "headers": {
63
+ "Authorization": "Bearer amk_..."
62
64
  }
63
65
  }
64
66
  }
@@ -43,22 +43,24 @@ standard copy-and-edit flow, Claude Design handoff, AI-assisted
43
43
  implementation, responsive visual QA, Markdown styling expectations,
44
44
  and the common failure modes — see [THEMES.md](./THEMES.md).
45
45
 
46
- ## MCP server (`@ampless/mcp-server`)
46
+ ## MCP server (HTTP transport)
47
47
 
48
- Lets agents query and modify post content directly. Tools exposed: `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `upload_media`, `get_schema`.
48
+ Lets agents query and modify post content directly via the `mcp-handler` Lambda. Tools exposed: `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `upload_media`, `get_schema`, `upload_static_bundle`, `list_static_files`, `delete_static_file`, `get_site_context`.
49
49
 
50
50
  Registration:
51
51
 
52
- Add to `.mcp.json` at the project root:
52
+ 1. Issue a Bearer token from `/admin/mcp-tokens` in the admin UI.
53
+ 2. Find the `mcp-handler` Function URL in the Amplify console or `amplify_outputs.json`.
54
+ 3. Add to `.mcp.json` at the project root:
55
+
53
56
  ```json
54
57
  {
55
58
  "mcpServers": {
56
59
  "ampless": {
57
- "command": "npx",
58
- "args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
59
- "env": {
60
- "AMPLESS_MCP_EMAIL": "<your-admin-email>",
61
- "AMPLESS_MCP_PASSWORD": "<your-admin-password>"
60
+ "url": "https://<function-url-id>.lambda-url.<region>.on.aws/",
61
+ "transport": "http",
62
+ "headers": {
63
+ "Authorization": "Bearer amk_..."
62
64
  }
63
65
  }
64
66
  }
@@ -190,9 +190,29 @@ Amplify Hosting アプリの **Domain management** からドメインをバイ
190
190
 
191
191
  ## AI 連携(MCP)
192
192
 
193
- ampless は MCP(Model Context Protocol)サーバーを同梱しているので、Claude Desktop / Cursor / Claude Code など MCP に対応した AI クライアントから投稿の読み書きができます。
193
+ ampless は HTTP トランスポート + Bearer トークン認証による MCP(Model Context Protocol)サーバーを同梱しているので、Claude Desktop / Cursor / Claude Code など MCP に対応した AI クライアントから投稿の読み書きができます。
194
+
195
+ **セットアップ手順:**
196
+
197
+ 1. 管理画面の `/admin/mcp-tokens` にアクセスし、Bearer トークン(`amk_...`)を発行する。
198
+ 2. Amplify コンソールまたは `amplify_outputs.json` で `mcp-handler` Lambda の Function URL を確認する。
199
+ 3. MCP クライアントの設定ファイル(プロジェクトルートの `.mcp.json`、`claude_desktop_config.json` など)にエントリを追加する:
200
+
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "ampless": {
205
+ "url": "https://<function-url-id>.lambda-url.<region>.on.aws/",
206
+ "transport": "http",
207
+ "headers": {
208
+ "Authorization": "Bearer amk_..."
209
+ }
210
+ }
211
+ }
212
+ }
213
+ ```
194
214
 
195
- - **ローカル / sandbox** — グローバルに 1 度入れる: `npx -y @ampless/mcp-server@alpha` に `amplify_outputs.json` のパスを渡す
215
+ MCP のセットアップとトークン管理の詳細については [docs/architecture/04-access-layer-mcp.md](./docs/architecture/04-access-layer-mcp.md) を参照してください。
196
216
 
197
217
  ## ampless の更新
198
218
 
@@ -190,9 +190,29 @@ Bind a domain to your Amplify Hosting app in **Domain management** — Amplify p
190
190
 
191
191
  ## AI integration (MCP)
192
192
 
193
- ampless ships an MCP (Model Context Protocol) server so Claude Desktop / Cursor / Claude Code / anything that speaks MCP can read and write your posts.
193
+ ampless ships an MCP (Model Context Protocol) server so Claude Desktop / Cursor / Claude Code / anything that speaks MCP can read and write your posts via HTTP transport with Bearer token authentication.
194
+
195
+ **Setup:**
196
+
197
+ 1. Go to `/admin/mcp-tokens` in your admin UI and issue a Bearer token (`amk_...`).
198
+ 2. Find the `mcp-handler` Lambda Function URL in your Amplify console or `amplify_outputs.json`.
199
+ 3. Add an entry to your MCP client's config (`.mcp.json` at the project root, `claude_desktop_config.json`, etc.):
200
+
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "ampless": {
205
+ "url": "https://<function-url-id>.lambda-url.<region>.on.aws/",
206
+ "transport": "http",
207
+ "headers": {
208
+ "Authorization": "Bearer amk_..."
209
+ }
210
+ }
211
+ }
212
+ }
213
+ ```
194
214
 
195
- - **Local / sandbox** — install once globally: `npx -y @ampless/mcp-server@alpha` with the path to your `amplify_outputs.json`.
215
+ See [docs/architecture/04-access-layer-mcp.md](./docs/architecture/04-access-layer-mcp.md) for the full MCP setup and token management guide.
196
216
 
197
217
  ## Updating ampless
198
218
 
@@ -1,22 +1,22 @@
1
1
  import type { Metadata } from 'next'
2
2
  import { headers } from 'next/headers'
3
- import { validateColorScheme } from '@ampless/runtime'
3
+ import { renderThemeCss, validateColorScheme } from '@ampless/runtime'
4
+ import { getDictionary } from '@ampless/admin'
5
+ import { I18nProvider } from '@ampless/admin/components'
6
+ import { ampless } from '@/lib/ampless'
7
+ import { admin } from '@/lib/admin'
4
8
  import { Providers } from './providers'
5
- import { siteMetadata } from '@/lib/seo'
6
- import { loadThemeConfig, renderThemeCss } from '@/lib/theme-config'
7
- import { getLocale, getDictionary } from '@/lib/i18n'
8
- import { I18nProvider } from '@/components/i18n-provider'
9
9
  import './globals.css'
10
10
 
11
11
  export async function generateMetadata(): Promise<Metadata> {
12
- return siteMetadata()
12
+ return ampless.siteMetadata()
13
13
  }
14
14
 
15
15
  export default async function RootLayout({ children }: { children: React.ReactNode }) {
16
16
  const h = await headers()
17
- const theme = await loadThemeConfig()
17
+ const theme = await ampless.loadThemeConfig()
18
18
  const themeCss = renderThemeCss(theme.cssVars)
19
- const locale = getLocale()
19
+ const locale = admin.locale
20
20
  const dict = getDictionary(locale)
21
21
  // `data-color-scheme` pins the visitor to light or dark regardless
22
22
  // of their system `prefers-color-scheme`. `'auto'` (the default)
@@ -2,8 +2,6 @@
2
2
 
3
3
  import { useEffect } from 'react'
4
4
  import '@/lib/amplify'
5
- import '@/lib/posts-provider'
6
- import '@/lib/kv-provider'
7
5
 
8
6
  export function Providers({ children }: { children: React.ReactNode }) {
9
7
  useEffect(() => {
@@ -1,6 +1,6 @@
1
1
  import Link from 'next/link'
2
2
  import { parseLinkList, isTagListUrl } from 'ampless'
3
- import { listPostsByTag } from '@/lib/posts-public'
3
+ import { ampless } from '@/lib/ampless'
4
4
 
5
5
  interface Props {
6
6
  links: string | undefined
@@ -27,7 +27,7 @@ export async function SiteSidebar({ links, className }: Props) {
27
27
  items.map(async (item) => {
28
28
  const tagRef = isTagListUrl(item.url)
29
29
  if (!tagRef) return { type: 'link' as const, label: item.label, url: item.url }
30
- const { items: posts } = await listPostsByTag(tagRef.tag, { limit: 50 })
30
+ const { items: posts } = await ampless.listPostsByTag(tagRef.tag, { limit: 50 })
31
31
  return {
32
32
  type: 'tagSection' as const,
33
33
  label: item.label,
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "aliases": {
14
14
  "components": "@/components",
15
- "utils": "@/lib/cn",
15
+ "utils": "@ampless/runtime/ui",
16
16
  "ui": "@/components/ui",
17
17
  "lib": "@/lib",
18
18
  "hooks": "@/hooks"
@@ -1,16 +1,14 @@
1
1
  // Wired-up admin UI factory. Single source of truth for the admin
2
2
  // library — every admin route shell, API route shell, and form
3
- // re-export shim imports the `admin` value from here.
4
- //
5
- // L2 architectural change (admin extraction): admin UI now lives in
6
- // `@ampless/admin`. This module wires the project's
3
+ // imports the `admin` value from here. Admin UI lives in
4
+ // `@ampless/admin`; this module wires the project's
7
5
  // `amplify_outputs.json` and `cms.config` into a single `Admin`
8
6
  // instance.
9
7
  //
10
8
  // NOTE: we pass `ampless` as a thunk (not the resolved instance) so we
11
9
  // don't have a static `import './ampless'` at the top of this file.
12
10
  // A static import would form the cycle
13
- // `lib/admin.ts → lib/ampless.ts → themes-registry → themes → lib/i18n.ts → lib/admin.ts`
11
+ // `lib/admin.ts → lib/ampless.ts → themes-registry → themes → lib/admin.ts`
14
12
  // and crash with a TDZ ReferenceError on `ampless` at module init.
15
13
  // The thunk uses dynamic `import()` so `lib/ampless.ts` only loads on
16
14
  // the first `loadSiteSettings` / `loadThemeConfig` call (request
@@ -28,5 +26,5 @@ export const admin = createAdmin({
28
26
  })
29
27
 
30
28
  // Convenience: the server-side translation helper. Client components
31
- // should use `useT()` from `@/components/i18n-provider` instead.
29
+ // should use `useT()` from `@ampless/admin/components` instead.
32
30
  export const t = admin.t
@@ -1,15 +1,9 @@
1
1
  // Wired-up ampless runtime instance. Single source of truth for the
2
2
  // public-side library — every route handler, dispatcher, and theme
3
- // component imports the `ampless` value from here.
4
- //
5
- // L1 architectural change (runtime extraction): public-side
6
- // behaviour now lives in `@ampless/runtime`. This module wires the
3
+ // component imports the `ampless` value from here. Public-side
4
+ // behaviour lives in `@ampless/runtime`; this module wires the
7
5
  // project's `amplify_outputs.json`, `cms.config`, and themes registry
8
6
  // into a single `Ampless` instance.
9
- //
10
- // Admin-side modules (post providers, kv-provider, auth, etc.) stay
11
- // in `templates/_shared/lib/` for now — they move into `@ampless/admin`
12
- // in L2.
13
7
 
14
8
  import outputs from '../amplify_outputs.json'
15
9
  import cmsConfig from '@/cms.config'
@@ -13,8 +13,3 @@ import { Amplify } from 'aws-amplify'
13
13
  import outputs from '../amplify_outputs.json'
14
14
 
15
15
  Amplify.configure(outputs, { ssr: true })
16
-
17
- export function configureAmplify() {
18
- // module-level side effect above already ran; keep this as a no-op
19
- // for callers that still import it as a function for back-compat.
20
- }
@@ -29,8 +29,8 @@
29
29
  "@ampless/plugin-rss": "^0.2.0-alpha.15",
30
30
  "@ampless/plugin-seo": "^0.2.0-alpha.15",
31
31
  "@ampless/plugin-webhook": "^0.2.0-alpha.15",
32
- "@ampless/admin": "^1.0.0-alpha.37",
33
- "@ampless/backend": "^1.0.0-alpha.28",
32
+ "@ampless/admin": "^1.0.0-alpha.38",
33
+ "@ampless/backend": "^1.0.0-alpha.29",
34
34
  "@ampless/runtime": "^1.0.0-alpha.21",
35
35
  "@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
36
36
  "ampless": "^1.0.0-alpha.15",
@@ -1,4 +1,4 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
4
  request: Request
@@ -7,7 +7,7 @@ interface Ctx {
7
7
  // /feed.xml proxy — plugin-rss regenerates the feed on content events
8
8
  // and writes it to `public/plugins/rss/feed.xml`.
9
9
  export async function blogFeedHandler(_ctx: Ctx): Promise<Response> {
10
- const url = publicAssetUrl('public/plugins/rss/feed.xml')
10
+ const url = ampless.publicAssetUrl('public/plugins/rss/feed.xml')
11
11
  const upstream = await fetch(url, { cache: 'no-store' })
12
12
  if (!upstream.ok) {
13
13
  return new Response(
@@ -1,26 +1,24 @@
1
1
  import Link from 'next/link'
2
2
  import { formatDate, parseLinkList, type ThemeRouteContext } from 'ampless'
3
- import { listPublishedPosts, getPublishedPost } from '@/lib/posts-public'
4
- import { loadSiteSettings } from '@/lib/site-settings'
5
- import { loadThemeConfig } from '@/lib/theme-config'
6
- import { renderBody } from '@/lib/posts'
3
+ import { renderBody } from '@ampless/runtime'
4
+ import { ampless } from '@/lib/ampless'
5
+ import { admin } from '@/lib/admin'
7
6
  import { TagList } from '@/components/tag-list'
8
7
  import { SiteHeader } from '@/components/site-chrome/site-header'
9
8
  import { SiteFooter } from '@/components/site-chrome/site-footer'
10
- import { t } from '@/lib/i18n'
11
9
 
12
10
  export default async function BlogHome(_: ThemeRouteContext) {
13
11
  const [settings, theme, postsResult] = await Promise.all([
14
- loadSiteSettings(),
15
- loadThemeConfig(),
16
- listPublishedPosts(),
12
+ ampless.loadSiteSettings(),
13
+ ampless.loadThemeConfig(),
14
+ ampless.listPublishedPosts(),
17
15
  ])
18
16
 
19
17
  // Featured (pinned) post: render the body inline above the list,
20
18
  // then drop the same slug from the feed so it doesn't show twice.
21
19
  // Missing / unpublished slugs return null and the section is skipped.
22
20
  const featuredSlug = theme.values.featuredSlug?.trim()
23
- const featured = featuredSlug ? await getPublishedPost(featuredSlug) : null
21
+ const featured = featuredSlug ? await ampless.getPublishedPost(featuredSlug) : null
24
22
  const posts = featured
25
23
  ? postsResult.items.filter((p) => p.slug !== featured.slug)
26
24
  : postsResult.items
@@ -69,7 +67,7 @@ export default async function BlogHome(_: ThemeRouteContext) {
69
67
  )}
70
68
 
71
69
  {posts.length === 0 ? (
72
- !featured && <p className="text-gray-500">{t('public.noPosts')}</p>
70
+ !featured && <p className="text-gray-500">{admin.t('public.noPosts')}</p>
73
71
  ) : (
74
72
  <ul className="space-y-8">
75
73
  {posts.map((post) => (
@@ -2,32 +2,29 @@ import type { Metadata } from 'next'
2
2
  import Link from 'next/link'
3
3
  import { notFound } from 'next/navigation'
4
4
  import { formatDate, parseLinkList, type ThemeRouteContext } from 'ampless'
5
- import { renderBody } from '@/lib/posts'
5
+ import { renderBody } from '@ampless/runtime'
6
+ import { ampless } from '@/lib/ampless'
7
+ import { admin } from '@/lib/admin'
6
8
  import { LightboxBinder } from '@/components/lightbox-content'
7
9
  import { TagList } from '@/components/tag-list'
8
- import { postMetadata } from '@/lib/seo'
9
- import { loadSiteSettings } from '@/lib/site-settings'
10
- import { loadThemeConfig } from '@/lib/theme-config'
11
- import { getPublishedPost } from '@/lib/posts-public'
12
10
  import { SiteHeader } from '@/components/site-chrome/site-header'
13
11
  import { SiteFooter } from '@/components/site-chrome/site-footer'
14
- import { t } from '@/lib/i18n'
15
12
 
16
13
  type PostCtx = ThemeRouteContext<{ slug: string }>
17
14
 
18
15
  export async function generatePostMetadata({ params }: PostCtx): Promise<Metadata> {
19
16
  const { slug } = await params
20
- const post = await getPublishedPost(slug)
17
+ const post = await ampless.getPublishedPost(slug)
21
18
  if (!post) return {}
22
- return postMetadata(post)
19
+ return ampless.postMetadata(post)
23
20
  }
24
21
 
25
22
  export default async function BlogPost({ params }: PostCtx) {
26
23
  const { slug } = await params
27
24
  const [post, settings, theme] = await Promise.all([
28
- getPublishedPost(slug),
29
- loadSiteSettings(),
30
- loadThemeConfig(),
25
+ ampless.getPublishedPost(slug),
26
+ ampless.loadSiteSettings(),
27
+ ampless.loadThemeConfig(),
31
28
  ])
32
29
  if (!post) notFound()
33
30
 
@@ -53,7 +50,7 @@ export default async function BlogPost({ params }: PostCtx) {
53
50
 
54
51
  <main className="mx-auto max-w-2xl px-6 py-12">
55
52
  <nav className="mb-8">
56
- <Link href="/" className="text-sm text-gray-500 hover:underline">{t('public.back')}</Link>
53
+ <Link href="/" className="text-sm text-gray-500 hover:underline">{admin.t('public.back')}</Link>
57
54
  </nav>
58
55
 
59
56
  <article>
@@ -1,4 +1,4 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
4
  request: Request
@@ -7,7 +7,7 @@ interface Ctx {
7
7
  // /sitemap.xml proxy — plugin-seo regenerates the sitemap on every
8
8
  // content event and writes it to `public/plugins/seo/sitemap.xml`.
9
9
  export async function blogSitemapHandler(_ctx: Ctx): Promise<Response> {
10
- const url = publicAssetUrl('public/plugins/seo/sitemap.xml')
10
+ const url = ampless.publicAssetUrl('public/plugins/seo/sitemap.xml')
11
11
  const upstream = await fetch(url, { cache: 'no-store' })
12
12
  if (!upstream.ok) {
13
13
  return new Response(
@@ -1,20 +1,18 @@
1
1
  import Link from 'next/link'
2
2
  import { notFound } from 'next/navigation'
3
3
  import { formatDate, parseLinkList, type ThemeRouteContext } from 'ampless'
4
- import { listPostsByTag } from '@/lib/posts-public'
5
- import { loadSiteSettings } from '@/lib/site-settings'
6
- import { loadThemeConfig } from '@/lib/theme-config'
4
+ import { ampless } from '@/lib/ampless'
5
+ import { admin } from '@/lib/admin'
7
6
  import { SiteHeader } from '@/components/site-chrome/site-header'
8
7
  import { SiteFooter } from '@/components/site-chrome/site-footer'
9
- import { t } from '@/lib/i18n'
10
8
 
11
9
  export default async function BlogTag({ params }: ThemeRouteContext<{ tag: string }>) {
12
10
  const { tag } = await params
13
11
  const decodedTag = decodeURIComponent(tag)
14
12
  const [{ items: posts }, settings, theme] = await Promise.all([
15
- listPostsByTag(decodedTag, { limit: 50 }),
16
- loadSiteSettings(),
17
- loadThemeConfig(),
13
+ ampless.listPostsByTag(decodedTag, { limit: 50 }),
14
+ ampless.loadSiteSettings(),
15
+ ampless.loadThemeConfig(),
18
16
  ])
19
17
 
20
18
  if (posts.length === 0) notFound()
@@ -36,11 +34,11 @@ export default async function BlogTag({ params }: ThemeRouteContext<{ tag: strin
36
34
 
37
35
  <main className="mx-auto max-w-2xl px-6 py-12">
38
36
  <nav className="mb-8">
39
- <Link href="/" className="text-sm text-gray-500 hover:underline">{t('public.home')}</Link>
37
+ <Link href="/" className="text-sm text-gray-500 hover:underline">{admin.t('public.home')}</Link>
40
38
  </nav>
41
39
 
42
40
  <header className="mb-12 border-b pb-6">
43
- <p className="text-sm text-gray-500">{t('public.tagLabel')}</p>
41
+ <p className="text-sm text-gray-500">{admin.t('public.tagLabel')}</p>
44
42
  <h1 className="text-4xl font-bold tracking-tight">#{decodedTag}</h1>
45
43
  </header>
46
44
 
@@ -1,11 +1,11 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
4
  request: Request
5
5
  }
6
6
 
7
7
  export async function corporateFeedHandler(_ctx: Ctx): Promise<Response> {
8
- const url = publicAssetUrl('public/plugins/rss/feed.xml')
8
+ const url = ampless.publicAssetUrl('public/plugins/rss/feed.xml')
9
9
  const upstream = await fetch(url, { cache: 'no-store' })
10
10
  if (!upstream.ok) {
11
11
  return new Response(
@@ -1,23 +1,21 @@
1
1
  import Link from 'next/link'
2
2
  import { formatDate, type ThemeRouteContext } from 'ampless'
3
- import { listPublishedPosts, getPublishedPost } from '@/lib/posts-public'
4
- import { loadSiteSettings } from '@/lib/site-settings'
5
- import { loadThemeConfig } from '@/lib/theme-config'
6
- import { renderBody } from '@/lib/posts'
3
+ import { renderBody } from '@ampless/runtime'
4
+ import { ampless } from '@/lib/ampless'
7
5
  import { SiteHeader } from '@/components/site-chrome/site-header'
8
6
  import { SiteFooter } from '@/components/site-chrome/site-footer'
9
7
 
10
8
  export default async function CorporateHome(_: ThemeRouteContext) {
11
9
  const [settings, theme, postsResult] = await Promise.all([
12
- loadSiteSettings(),
13
- loadThemeConfig(),
14
- listPublishedPosts({ limit: 8 }),
10
+ ampless.loadSiteSettings(),
11
+ ampless.loadThemeConfig(),
12
+ ampless.listPublishedPosts({ limit: 8 }),
15
13
  ])
16
14
 
17
15
  // Top-story embed between hero and news. Filtered out of news to
18
16
  // avoid duplication. Skipped silently if missing / unpublished.
19
17
  const featuredSlug = theme.values.featuredSlug?.trim()
20
- const featured = featuredSlug ? await getPublishedPost(featuredSlug) : null
18
+ const featured = featuredSlug ? await ampless.getPublishedPost(featuredSlug) : null
21
19
  const posts = featured
22
20
  ? postsResult.items.filter((p) => p.slug !== featured.slug)
23
21
  : postsResult.items
@@ -2,32 +2,29 @@ import type { Metadata } from 'next'
2
2
  import Link from 'next/link'
3
3
  import { notFound } from 'next/navigation'
4
4
  import { formatDate, type ThemeRouteContext } from 'ampless'
5
- import { renderBody } from '@/lib/posts'
5
+ import { renderBody } from '@ampless/runtime'
6
+ import { ampless } from '@/lib/ampless'
7
+ import { admin } from '@/lib/admin'
6
8
  import { LightboxBinder } from '@/components/lightbox-content'
7
9
  import { TagList } from '@/components/tag-list'
8
- import { postMetadata } from '@/lib/seo'
9
- import { loadSiteSettings } from '@/lib/site-settings'
10
- import { loadThemeConfig } from '@/lib/theme-config'
11
- import { getPublishedPost } from '@/lib/posts-public'
12
10
  import { SiteHeader } from '@/components/site-chrome/site-header'
13
11
  import { SiteFooter } from '@/components/site-chrome/site-footer'
14
- import { t } from '@/lib/i18n'
15
12
 
16
13
  type PostCtx = ThemeRouteContext<{ slug: string }>
17
14
 
18
15
  export async function generatePostMetadata({ params }: PostCtx): Promise<Metadata> {
19
16
  const { slug } = await params
20
- const post = await getPublishedPost(slug)
17
+ const post = await ampless.getPublishedPost(slug)
21
18
  if (!post) return {}
22
- return postMetadata(post)
19
+ return ampless.postMetadata(post)
23
20
  }
24
21
 
25
22
  export default async function CorporatePost({ params }: PostCtx) {
26
23
  const { slug } = await params
27
24
  const [post, settings, theme] = await Promise.all([
28
- getPublishedPost(slug),
29
- loadSiteSettings(),
30
- loadThemeConfig(),
25
+ ampless.getPublishedPost(slug),
26
+ ampless.loadSiteSettings(),
27
+ ampless.loadThemeConfig(),
31
28
  ])
32
29
  if (!post) notFound()
33
30
 
@@ -50,7 +47,7 @@ export default async function CorporatePost({ params }: PostCtx) {
50
47
  <main className="mx-auto max-w-3xl px-6 py-12">
51
48
  <nav className="mb-6">
52
49
  <Link href="/" className="text-sm text-[var(--muted-foreground)] hover:text-[var(--primary)]">
53
- {t('public.back')}
50
+ {admin.t('public.back')}
54
51
  </Link>
55
52
  </nav>
56
53
 
@@ -1,11 +1,11 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
4
  request: Request
5
5
  }
6
6
 
7
7
  export async function corporateSitemapHandler(_ctx: Ctx): Promise<Response> {
8
- const url = publicAssetUrl('public/plugins/seo/sitemap.xml')
8
+ const url = ampless.publicAssetUrl('public/plugins/seo/sitemap.xml')
9
9
  const upstream = await fetch(url, { cache: 'no-store' })
10
10
  if (!upstream.ok) {
11
11
  return new Response(
@@ -1,20 +1,18 @@
1
1
  import Link from 'next/link'
2
2
  import { notFound } from 'next/navigation'
3
3
  import { formatDate, type ThemeRouteContext } from 'ampless'
4
- import { listPostsByTag } from '@/lib/posts-public'
5
- import { loadSiteSettings } from '@/lib/site-settings'
6
- import { loadThemeConfig } from '@/lib/theme-config'
4
+ import { ampless } from '@/lib/ampless'
5
+ import { admin } from '@/lib/admin'
7
6
  import { SiteHeader } from '@/components/site-chrome/site-header'
8
7
  import { SiteFooter } from '@/components/site-chrome/site-footer'
9
- import { t } from '@/lib/i18n'
10
8
 
11
9
  export default async function CorporateTag({ params }: ThemeRouteContext<{ tag: string }>) {
12
10
  const { tag } = await params
13
11
  const decodedTag = decodeURIComponent(tag)
14
12
  const [{ items: posts }, settings, theme] = await Promise.all([
15
- listPostsByTag(decodedTag, { limit: 50 }),
16
- loadSiteSettings(),
17
- loadThemeConfig(),
13
+ ampless.listPostsByTag(decodedTag, { limit: 50 }),
14
+ ampless.loadSiteSettings(),
15
+ ampless.loadThemeConfig(),
18
16
  ])
19
17
  if (posts.length === 0) notFound()
20
18
 
@@ -32,12 +30,12 @@ export default async function CorporateTag({ params }: ThemeRouteContext<{ tag:
32
30
  <main className="mx-auto max-w-4xl px-6 py-12">
33
31
  <nav className="mb-6">
34
32
  <Link href="/" className="text-sm text-[var(--muted-foreground)] hover:text-[var(--primary)]">
35
- {t('public.home')}
33
+ {admin.t('public.home')}
36
34
  </Link>
37
35
  </nav>
38
36
 
39
37
  <header className="mb-10">
40
- <p className="text-sm text-[var(--muted-foreground)]">{t('public.tagLabel')}</p>
38
+ <p className="text-sm text-[var(--muted-foreground)]">{admin.t('public.tagLabel')}</p>
41
39
  <h1 className="text-3xl font-bold tracking-tight">#{decodedTag}</h1>
42
40
  </header>
43
41
 
@@ -1,11 +1,11 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
4
  request: Request
5
5
  }
6
6
 
7
7
  export async function dadsFeedHandler(_ctx: Ctx): Promise<Response> {
8
- const url = publicAssetUrl('public/plugins/rss/feed.xml')
8
+ const url = ampless.publicAssetUrl('public/plugins/rss/feed.xml')
9
9
  const upstream = await fetch(url, { cache: 'no-store' })
10
10
  if (!upstream.ok) {
11
11
  return new Response(