create-ampless 0.2.0-alpha.8 → 1.0.0-alpha.100

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 (140) hide show
  1. package/README.ja.md +77 -0
  2. package/README.md +6 -3
  3. package/dist/index.js +1282 -93
  4. package/dist/templates/_shared/AGENTS.ja.md +94 -0
  5. package/dist/templates/_shared/AGENTS.md +94 -0
  6. package/dist/templates/_shared/README.ja.md +239 -0
  7. package/dist/templates/_shared/README.md +239 -0
  8. package/dist/templates/_shared/RUNBOOK.ja.md +120 -0
  9. package/dist/templates/_shared/RUNBOOK.md +32 -58
  10. package/dist/templates/_shared/THEMES.ja.md +495 -0
  11. package/dist/templates/_shared/THEMES.md +523 -0
  12. package/dist/templates/_shared/amplify/auth/post-confirmation/resource.ts +7 -0
  13. package/dist/templates/_shared/amplify/backend.custom.ts +41 -0
  14. package/dist/templates/_shared/amplify/backend.ts +20 -2
  15. package/dist/templates/_shared/amplify/data/get-media-by-src.js +45 -0
  16. package/dist/templates/_shared/amplify/data/get-published-post.js +9 -12
  17. package/dist/templates/_shared/amplify/data/list-posts-by-tag.js +6 -9
  18. package/dist/templates/_shared/amplify/data/list-published-posts.js +10 -11
  19. package/dist/templates/_shared/amplify/data/resource.custom.ts +32 -0
  20. package/dist/templates/_shared/amplify/data/resource.ts +32 -15
  21. package/dist/templates/_shared/amplify/events/dispatcher/resource.ts +1 -0
  22. package/dist/templates/_shared/amplify/events/processor-trusted/resource.ts +1 -0
  23. package/dist/templates/_shared/amplify/events/processor-untrusted/resource.ts +5 -0
  24. package/dist/templates/_shared/amplify/functions/api-key-renewer/resource.ts +1 -0
  25. package/dist/templates/_shared/amplify/functions/mcp-handler/handler.ts +1 -0
  26. package/dist/templates/_shared/amplify/functions/mcp-handler/resource.ts +12 -0
  27. package/dist/templates/_shared/amplify/functions/plugin-secret-handler/handler.ts +11 -0
  28. package/dist/templates/_shared/amplify/functions/plugin-secret-handler/resource.ts +12 -0
  29. package/dist/templates/_shared/amplify/functions/user-admin/handler.ts +4 -0
  30. package/dist/templates/_shared/amplify/functions/user-admin/resource.ts +13 -0
  31. package/dist/templates/_shared/amplify/secrets/.gitkeep +0 -0
  32. package/dist/templates/_shared/amplify/secrets/encryption-key.ts +9 -0
  33. package/dist/templates/_shared/app/(admin)/admin/mcp-tokens/page.tsx +5 -0
  34. package/dist/templates/_shared/app/(admin)/admin/plugins/page.tsx +5 -0
  35. package/dist/templates/_shared/app/(admin)/admin/users/page.tsx +5 -0
  36. package/dist/templates/_shared/app/globals.css +55 -39
  37. package/dist/templates/_shared/app/layout.tsx +51 -16
  38. package/dist/templates/_shared/app/providers.tsx +0 -2
  39. package/dist/templates/_shared/app/raw/[slug]/route.ts +10 -0
  40. package/dist/templates/_shared/app/static/[slug]/[[...path]]/route.ts +14 -0
  41. package/dist/templates/_shared/cms.config.ts +64 -23
  42. package/dist/templates/_shared/components/site-chrome/site-sidebar.tsx +3 -4
  43. package/dist/templates/_shared/components/tag-list.tsx +9 -2
  44. package/dist/templates/_shared/components.json +1 -1
  45. package/dist/templates/_shared/docs/plugin-author-guide.ja.md +934 -0
  46. package/dist/templates/_shared/docs/plugin-author-guide.md +1336 -0
  47. package/dist/templates/_shared/lib/admin.ts +12 -12
  48. package/dist/templates/_shared/lib/ampless.ts +2 -8
  49. package/dist/templates/_shared/lib/amplify.ts +0 -5
  50. package/dist/templates/_shared/package.json +51 -40
  51. package/dist/templates/_shared/plugins/README.ja.md +139 -0
  52. package/dist/templates/_shared/plugins/README.md +143 -0
  53. package/dist/templates/_shared/proxy.ts +23 -8
  54. package/dist/templates/blog/README.ja.md +22 -0
  55. package/dist/templates/blog/README.md +17 -47
  56. package/dist/templates/blog/manifest.ts +1 -1
  57. package/dist/templates/blog/pages/feed.ts +4 -5
  58. package/dist/templates/blog/pages/home.tsx +10 -15
  59. package/dist/templates/blog/pages/post.tsx +42 -14
  60. package/dist/templates/blog/pages/sitemap.ts +4 -5
  61. package/dist/templates/blog/pages/tag.tsx +8 -10
  62. package/dist/templates/blog/tokens.css +26 -40
  63. package/dist/templates/corporate/README.ja.md +18 -0
  64. package/dist/templates/corporate/README.md +12 -14
  65. package/dist/templates/corporate/pages/feed.ts +3 -4
  66. package/dist/templates/corporate/pages/home.tsx +7 -12
  67. package/dist/templates/corporate/pages/post.tsx +20 -14
  68. package/dist/templates/corporate/pages/sitemap.ts +3 -4
  69. package/dist/templates/corporate/pages/tag.tsx +8 -10
  70. package/dist/templates/corporate/tokens.css +17 -39
  71. package/dist/templates/dads/README.ja.md +31 -0
  72. package/dist/templates/dads/README.md +13 -17
  73. package/dist/templates/dads/pages/feed.ts +3 -4
  74. package/dist/templates/dads/pages/home.tsx +7 -12
  75. package/dist/templates/dads/pages/post.tsx +20 -14
  76. package/dist/templates/dads/pages/sitemap.ts +3 -4
  77. package/dist/templates/dads/pages/tag.tsx +8 -10
  78. package/dist/templates/dads/tokens.css +22 -42
  79. package/dist/templates/docs/README.ja.md +24 -0
  80. package/dist/templates/docs/README.md +10 -13
  81. package/dist/templates/docs/pages/feed.ts +3 -4
  82. package/dist/templates/docs/pages/home.tsx +6 -9
  83. package/dist/templates/docs/pages/post.tsx +19 -13
  84. package/dist/templates/docs/pages/sitemap.ts +3 -4
  85. package/dist/templates/docs/pages/tag.tsx +8 -10
  86. package/dist/templates/docs/tokens.css +17 -39
  87. package/dist/templates/landing/README.ja.md +20 -0
  88. package/dist/templates/landing/README.md +14 -19
  89. package/dist/templates/landing/pages/feed.ts +4 -5
  90. package/dist/templates/landing/pages/home.tsx +7 -12
  91. package/dist/templates/landing/pages/post.tsx +20 -14
  92. package/dist/templates/landing/pages/sitemap.ts +3 -4
  93. package/dist/templates/landing/pages/tag.tsx +8 -10
  94. package/dist/templates/landing/tokens.css +17 -39
  95. package/dist/templates/minimal/README.ja.md +14 -0
  96. package/dist/templates/minimal/README.md +9 -47
  97. package/dist/templates/minimal/pages/feed.ts +4 -5
  98. package/dist/templates/minimal/pages/home.tsx +6 -8
  99. package/dist/templates/minimal/pages/post.tsx +19 -12
  100. package/dist/templates/minimal/pages/sitemap.ts +4 -5
  101. package/dist/templates/minimal/pages/tag.tsx +7 -8
  102. package/dist/templates/minimal/tokens.css +17 -39
  103. package/dist/templates/plugin-local/README.md +34 -0
  104. package/dist/templates/plugin-local/index.ts +39 -0
  105. package/dist/templates/plugin-standalone/CHANGELOG.md +1 -0
  106. package/dist/templates/plugin-standalone/README.ja.md +43 -0
  107. package/dist/templates/plugin-standalone/README.md +43 -0
  108. package/dist/templates/plugin-standalone/package.json +47 -0
  109. package/dist/templates/plugin-standalone/src/index.test.ts +16 -0
  110. package/dist/templates/plugin-standalone/src/index.ts +29 -0
  111. package/dist/templates/plugin-standalone/tsconfig.json +16 -0
  112. package/dist/templates/plugin-standalone/tsup.config.ts +8 -0
  113. package/package.json +1 -1
  114. package/dist/templates/_shared/app/(admin)/admin/sites/page.tsx +0 -5
  115. package/dist/templates/_shared/app/site/[siteId]/raw/[slug]/route.ts +0 -5
  116. package/dist/templates/_shared/components/i18n-provider.tsx +0 -15
  117. package/dist/templates/_shared/lib/admin-site-client.ts +0 -10
  118. package/dist/templates/_shared/lib/admin-site.ts +0 -12
  119. package/dist/templates/_shared/lib/amplify-server.ts +0 -7
  120. package/dist/templates/_shared/lib/auth-server.ts +0 -15
  121. package/dist/templates/_shared/lib/cn.ts +0 -5
  122. package/dist/templates/_shared/lib/i18n.ts +0 -35
  123. package/dist/templates/_shared/lib/kv-provider.ts +0 -7
  124. package/dist/templates/_shared/lib/media.ts +0 -6
  125. package/dist/templates/_shared/lib/posts-provider.ts +0 -7
  126. package/dist/templates/_shared/lib/posts-public.ts +0 -27
  127. package/dist/templates/_shared/lib/posts.ts +0 -12
  128. package/dist/templates/_shared/lib/seo.ts +0 -11
  129. package/dist/templates/_shared/lib/site-settings.ts +0 -11
  130. package/dist/templates/_shared/lib/storage.ts +0 -10
  131. package/dist/templates/_shared/lib/theme-actions.ts +0 -5
  132. package/dist/templates/_shared/lib/theme-active.ts +0 -10
  133. package/dist/templates/_shared/lib/theme-config.ts +0 -12
  134. package/dist/templates/_shared/lib/upload.ts +0 -6
  135. /package/dist/templates/_shared/app/{site/[siteId]/[slug] → [slug]}/page.tsx +0 -0
  136. /package/dist/templates/_shared/app/{site/[siteId]/feed.xml → feed.xml}/route.ts +0 -0
  137. /package/dist/templates/_shared/app/{site/[siteId]/og → og}/[slug]/route.ts +0 -0
  138. /package/dist/templates/_shared/app/{site/[siteId]/page.tsx → page.tsx} +0 -0
  139. /package/dist/templates/_shared/app/{site/[siteId]/sitemap.xml → sitemap.xml}/route.ts +0 -0
  140. /package/dist/templates/_shared/app/{site/[siteId]/tag → tag}/[tag]/page.tsx +0 -0
@@ -1,19 +1,18 @@
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
- // NOTE: We intentionally do NOT pass an `ampless` instance to
11
- // createAdmin. That would import `lib/ampless.ts`, which imports
12
- // `themes-registry`, which (via theme pages `lib/i18n.ts`) imports
13
- // back to this file a circular chain that crashes with a TDZ
14
- // ReferenceError on `ampless` at module init. `createAdmin` builds
15
- // its own internal Ampless when omitted, which is functionally
16
- // equivalent for admin's needs.
8
+ // NOTE: we pass `ampless` as a thunk (not the resolved instance) so we
9
+ // don't have a static `import './ampless'` at the top of this file.
10
+ // A static import would form the cycle
11
+ // `lib/admin.ts lib/ampless.ts themes-registry themes lib/admin.ts`
12
+ // and crash with a TDZ ReferenceError on `ampless` at module init.
13
+ // The thunk uses dynamic `import()` so `lib/ampless.ts` only loads on
14
+ // the first `loadSiteSettings` / `loadThemeConfig` call (request
15
+ // time), by which point every module has finished initialising.
17
16
 
18
17
  import outputs from '../amplify_outputs.json'
19
18
  import cmsConfig from '@/cms.config'
@@ -22,9 +21,10 @@ import { createAdmin } from '@ampless/admin'
22
21
  export const admin = createAdmin({
23
22
  outputs,
24
23
  cmsConfig,
24
+ ampless: async () => (await import('./ampless')).ampless,
25
25
  locale: (cmsConfig as { locale?: string }).locale ?? 'en',
26
26
  })
27
27
 
28
28
  // Convenience: the server-side translation helper. Client components
29
- // should use `useT()` from `@/components/i18n-provider` instead.
29
+ // should use `useT()` from `@ampless/admin/components` instead.
30
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
- }
@@ -3,61 +3,72 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
6
9
  "scripts": {
7
10
  "dev": "next dev",
8
11
  "build": "next build",
9
12
  "start": "next start",
10
13
  "lint": "next lint",
11
- "sandbox": "ampx sandbox"
14
+ "sandbox": "ampx sandbox --once && next dev",
15
+ "update-ampless": "npx create-ampless@latest upgrade",
16
+ "copy-theme": "npx create-ampless@latest copy-theme"
12
17
  },
13
18
  "dependencies": {
14
- "@aws-amplify/adapter-nextjs": "^1.6.0",
15
- "@radix-ui/react-dialog": "^1.1.4",
16
- "@radix-ui/react-label": "^2.1.1",
17
- "@radix-ui/react-slot": "^1.1.1",
18
- "@tiptap/extension-image": "^3.23.4",
19
- "@tiptap/extension-link": "^3.23.4",
20
- "@tiptap/pm": "^3.23.4",
21
- "@tiptap/react": "^3.23.4",
22
- "@tiptap/starter-kit": "^3.23.4",
23
- "@ampless/plugin-og-image": "^0.2.0-alpha.1",
24
- "@ampless/plugin-rss": "^0.2.0-alpha.1",
25
- "@ampless/plugin-seo": "^0.2.0-alpha.1",
26
- "@ampless/plugin-webhook": "^0.2.0-alpha.1",
27
- "@ampless/admin": "^0.2.0-alpha.6",
28
- "@ampless/backend": "^0.2.0-alpha.2",
29
- "@ampless/runtime": "^0.2.0-alpha.3",
19
+ "@aws-amplify/adapter-nextjs": "^1.7.3",
20
+ "@radix-ui/react-dialog": "^1.1.15",
21
+ "@radix-ui/react-label": "^2.1.8",
22
+ "@radix-ui/react-slot": "^1.2.4",
23
+ "@tiptap/extension-image": "^3.23.6",
24
+ "@tiptap/extension-link": "^3.23.6",
25
+ "@tiptap/pm": "^3.23.6",
26
+ "@tiptap/react": "^3.23.6",
27
+ "@tiptap/starter-kit": "^3.23.6",
28
+ "@ampless/plugin-analytics-ga4": "^0.2.0-alpha.14",
29
+ "@ampless/plugin-cookie-consent": "^0.1.0-alpha.5",
30
+ "@ampless/plugin-gtm": "^0.2.0-alpha.13",
31
+ "@ampless/plugin-og-image": "^0.2.0-alpha.30",
32
+ "@ampless/plugin-plausible": "^0.2.0-alpha.13",
33
+ "@ampless/plugin-reading-time": "^0.1.0-alpha.3",
34
+ "@ampless/plugin-rss": "^0.2.0-alpha.30",
35
+ "@ampless/plugin-schema-jsonld": "^0.1.1-alpha.9",
36
+ "@ampless/plugin-seo": "^0.2.0-alpha.30",
37
+ "@ampless/plugin-webhook": "^0.2.0-alpha.31",
38
+ "@ampless/admin": "^1.0.0-alpha.59",
39
+ "@ampless/backend": "^1.0.0-alpha.49",
40
+ "@ampless/runtime": "^1.0.0-alpha.38",
30
41
  "@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
31
- "ampless": "^0.2.0-alpha.1",
32
- "aws-amplify": "^6.10.0",
42
+ "ampless": "^1.0.0-alpha.30",
43
+ "aws-amplify": "^6.17.0",
33
44
  "class-variance-authority": "^0.7.1",
34
45
  "clsx": "^2.1.1",
35
46
  "lucide-react": "^1.16.0",
36
47
  "next": "^16.2.6",
37
- "react": "^19.0.0",
38
- "react-dom": "^19.0.0",
39
- "react-image-crop": "^11.0.7",
48
+ "react": "^19.2.6",
49
+ "react-dom": "^19.2.6",
50
+ "react-image-crop": "^11.0.10",
40
51
  "tailwind-merge": "^3.6.0"
41
52
  },
42
53
  "devDependencies": {
43
- "@aws-amplify/backend": "^1.13.0",
44
- "@aws-amplify/backend-cli": "^1.4.0",
45
- "@aws-sdk/client-appsync": "^3.717.0",
46
- "@aws-sdk/client-cognito-identity-provider": "^3.717.0",
47
- "@aws-sdk/client-dynamodb": "^3.717.0",
48
- "@aws-sdk/client-s3": "^3.1048.0",
49
- "@aws-sdk/client-sqs": "^3.717.0",
50
- "@aws-sdk/lib-dynamodb": "^3.717.0",
51
- "@aws-sdk/util-dynamodb": "^3.717.0",
52
- "@tailwindcss/postcss": "^4.0.0",
53
- "@tailwindcss/typography": "^0.5.16",
54
- "@types/aws-lambda": "^8.10.147",
55
- "@types/node": "^22.10.0",
56
- "@types/react": "^19.0.0",
57
- "@types/react-dom": "^19.0.0",
58
- "aws-cdk-lib": "^2.174.0",
59
- "postcss": "^8.4.0",
60
- "tailwindcss": "^4.0.0",
54
+ "@aws-amplify/backend": "^1.22.0",
55
+ "@aws-amplify/backend-cli": "^1.8.2",
56
+ "@aws-sdk/client-appsync": "^3.1053.0",
57
+ "@aws-sdk/client-cognito-identity-provider": "^3.1053.0",
58
+ "@aws-sdk/client-dynamodb": "^3.1053.0",
59
+ "@aws-sdk/client-s3": "^3.1053.0",
60
+ "@aws-sdk/client-sqs": "^3.1053.0",
61
+ "@aws-sdk/lib-dynamodb": "^3.1053.0",
62
+ "@aws-sdk/util-dynamodb": "^3.996.2",
63
+ "@tailwindcss/postcss": "^4.3.0",
64
+ "@tailwindcss/typography": "^0.5.19",
65
+ "@types/aws-lambda": "^8.10.161",
66
+ "@types/node": "^22.19.19",
67
+ "@types/react": "^19.2.15",
68
+ "@types/react-dom": "^19.2.3",
69
+ "aws-cdk-lib": "^2.257.0",
70
+ "postcss": "^8.5.15",
71
+ "tailwindcss": "^4.3.0",
61
72
  "typescript": "^6.0.3"
62
73
  }
63
74
  }
@@ -0,0 +1,139 @@
1
+ > English: [README.md](./README.md)
2
+
3
+ # サイトローカルプラグイン
4
+
5
+ このディレクトリは **このサイト専用のプラグイン置き場**。別 npm
6
+ パッケージとして publish したくない小さなカスタマイズを書く場所。
7
+
8
+ `update-ampless` はこのディレクトリのファイルに一切触らない。ここに置いた
9
+ ものは消えない、上書きされない。
10
+
11
+ ## いつ使うか
12
+
13
+ 詳しい判断軸は [プラグイン作者ガイド](../docs/plugin-author-guide.ja.md)
14
+ §0「テーマとプラグインの境界線」に書いてある。プラグインが初めて、または
15
+ 「これはテーマ側に書くべきか、プラグインか」で迷ったら先にそちらを読む。
16
+ ざっくり言うと:
17
+
18
+ - **テーマ** = ページの見た目 (レイアウト / コンポーネント / CSS)
19
+ - **プラグイン** = admin で編集する設定、バックグラウンド処理、
20
+ テーマを横断する注入、機械可読メタデータ、複数サイトで共有したい機能
21
+
22
+ その中でも *ローカル* プラグイン (このディレクトリ) を使うのは、サイト
23
+ 固有の機能で:
24
+
25
+ - プラグイン surface (`publicHead` / `publicBodyEnd` / `metadata` /
26
+ `eventHooks` 等) が必要
27
+ - このサイト限定の用途 (一度きりのフッタークレジット、サイト特有の
28
+ JSON-LD 拡張、まだ汎用化したくない analytics スニペット等)
29
+ - 別パッケージとして version 上げて publish するのは大袈裟
30
+
31
+ 複数サイトで使いたくなったら、独立 npm パッケージに切り出す:
32
+ `npx create-ampless@latest plugin <name> --standalone` で publish 用の
33
+ ディレクトリ一式が scaffold される。
34
+
35
+ ## クライアントサイドスクリプトに関する注意
36
+
37
+ `publicHead` / `publicBodyEnd` で `inlineScript` を返す場合、その body の
38
+ 中で **可視 DOM 要素を挿入してはいけない**。React の hydration は DOM が
39
+ virtual DOM と一致していることを前提にしていて、外部からの DOM 操作は
40
+ reconciliation で消されてコンソールに hydration error が出る。
41
+ 安全なパターンは `window.dataLayer` 等のグローバル状態操作、自分で隔離
42
+ コンテナを持つ外部ウィジェットローダ、そして SSR 専用 descriptor (`meta` /
43
+ `link` / `noscript` / `iframe`)。詳細は [作者ガイド §6](../docs/plugin-author-guide.ja.md)
44
+ 「クライアント側 DOM 変更は禁止」セクションを参照。
45
+
46
+ ## 最小例
47
+
48
+ `plugins/footer-credit/index.ts`:
49
+
50
+ ```typescript
51
+ import { definePlugin, type AmplessPlugin } from 'ampless'
52
+
53
+ export interface FooterCreditOptions {
54
+ instanceId?: string
55
+ }
56
+
57
+ export default function footerCreditPlugin(
58
+ options: FooterCreditOptions = {}
59
+ ): AmplessPlugin {
60
+ const instanceId = options.instanceId ?? 'footer-credit'
61
+ return definePlugin({
62
+ name: 'footer-credit',
63
+ instanceId,
64
+ apiVersion: 1,
65
+ trust_level: 'untrusted',
66
+ displayName: { en: 'Footer credit', ja: 'フッタークレジット' },
67
+ capabilities: ['publicBody', 'adminSettings'],
68
+ settings: {
69
+ public: [
70
+ {
71
+ type: 'text',
72
+ key: 'html',
73
+ label: { en: 'Snippet', ja: 'スニペット' },
74
+ default: '',
75
+ },
76
+ ],
77
+ },
78
+ publicBodyEnd(ctx) {
79
+ const html = (ctx.setting<string>('html') ?? '').trim()
80
+ if (!html) return []
81
+ return [{ type: 'noscript', id: `footer-credit-${instanceId}`, html }]
82
+ },
83
+ })
84
+ }
85
+ ```
86
+
87
+ `cms.config.ts` で register:
88
+
89
+ ```typescript
90
+ import footerCreditPlugin from './plugins/footer-credit'
91
+
92
+ export default defineConfig({
93
+ // ...
94
+ plugins: [
95
+ // ...既存 plugin...
96
+ footerCreditPlugin(),
97
+ ],
98
+ })
99
+ ```
100
+
101
+ これだけ。`next dev` 再起動 → `/admin/plugins` でスニペットを設定すれば、
102
+ すべての公開ページの `</body>` 直前にレンダされる。
103
+
104
+ ## 宣言できる surface
105
+
106
+ プラグインは `definePlugin({...})` で組み立てるただのオブジェクト。型は
107
+ `node_modules/ampless/dist/plugin.d.ts` を見るか、[プラグイン作者ガイド][guide]
108
+ の散文で。
109
+
110
+ 現状 active な capability:
111
+
112
+ | capability | 用途 |
113
+ |---|---|
114
+ | `publicHead` | `<head>` に site-wide で挿入される descriptor |
115
+ | `publicBody` | `</body>` 直前に site-wide で挿入される descriptor |
116
+ | `metadata` | post ごとの Next.js Metadata 拡張 |
117
+ | `eventHooks` | trusted/untrusted の背景ハンドラ (content lifecycle / media event 等) |
118
+ | `adminSettings` | `settings.public[]` で宣言する admin 編集可能な設定 |
119
+ | `writePublicAsset` | trusted plugin が `public/plugins/<instanceId>/...` に書き込み |
120
+ | `schema` | post ごとの JSON-LD を `publicBodyForPost` 経由で (テーマ側で `ampless.publicBodyForPost(post)` を呼ぶ前提) |
121
+ | `publicHtmlForPost` | post ごとの可視 HTML(読了時間バッジ等)を `publicHtmlForPost` 経由で(テーマ側で `ampless.publicHtmlForPost(post)` を呼ぶ前提) |
122
+
123
+ **`publicHtmlForPost` を使うファーストパーティプラグイン:**
124
+ - `@ampless/plugin-reading-time` — 投稿本文から読了時間を推定し、本文の前後に設定可能なバッジを注入します。
125
+
126
+ [guide]: https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md
127
+
128
+ ## TypeScript 設定
129
+
130
+ `tsconfig.json` の `**/*.ts` include glob でこのディレクトリも自動的に
131
+ カバーされている。`import x from './plugins/foo'` も
132
+ `import x from '@/plugins/foo'` も追加設定なしで動く。
133
+
134
+ ## `update-ampless` はどうなるか
135
+
136
+ 何もしない。`plugins/` は upgrade ツールの protected list に入っているので、
137
+ ここに置いたファイルは上書きも削除もされない。代償として **この README
138
+ も更新されない** — プラグイン API に大きな変更があるときは、本家リポジトリの
139
+ [プラグイン作者ガイド][guide] で最新版を確認すること。
@@ -0,0 +1,143 @@
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+
3
+ # Site-local plugins
4
+
5
+ This directory is for **plugins that belong only to this site** — small
6
+ customizations you don't want to publish as a separate npm package.
7
+
8
+ `update-ampless` never touches files in this directory, so anything you
9
+ add here is yours to keep.
10
+
11
+ ## When to use a local plugin
12
+
13
+ The longer answer lives in §0 ("Theme vs Plugin Boundary") of the
14
+ [plugin author guide](../docs/plugin-author-guide.md) — read that
15
+ first if you're new to plugins, or if you're unsure whether a feature
16
+ belongs in a plugin or a theme. Short version:
17
+
18
+ - **Theme** = what the page looks like (layout, components, CSS).
19
+ - **Plugin** = admin-editable config, background processing,
20
+ theme-agnostic injection, machine-readable metadata, or anything
21
+ you want to share between sites.
22
+
23
+ Reach for a *local* plugin (this directory) when you want a feature
24
+ that:
25
+
26
+ - needs a plugin surface (`publicHead` / `publicBodyEnd` /
27
+ `metadata` / `eventHooks` / etc.)
28
+ - is specific to this site (a one-off footer credit, a custom JSON-LD
29
+ enrichment, an analytics snippet you're not yet ready to ship as a
30
+ reusable package)
31
+ - you'd rather not version-bump or republish to iterate on
32
+
33
+ When the plugin grows useful for more than one site, lift it into its
34
+ own npm package — `npx create-ampless@latest plugin <name> --standalone`
35
+ scaffolds the directory ready for `npm publish`.
36
+
37
+ ## A note on client-side scripts
38
+
39
+ If your `publicHead` / `publicBodyEnd` returns an `inlineScript`, the
40
+ script body **must not insert visible DOM elements** into the page.
41
+ React's hydration assumes the DOM matches its virtual DOM, and any
42
+ foreign nodes get wiped during reconciliation (plus a console error).
43
+ Safe patterns are `window.dataLayer`-style global state, external
44
+ widget loaders that manage their own isolated container, and
45
+ SSR-only descriptors (`meta` / `link` / `noscript` / `iframe`).
46
+ See the [author guide §6](../docs/plugin-author-guide.md) "Client-side
47
+ DOM mutation: don't" section for the full story.
48
+
49
+ ## Minimal example
50
+
51
+ `plugins/footer-credit/index.ts`:
52
+
53
+ ```typescript
54
+ import { definePlugin, type AmplessPlugin } from 'ampless'
55
+
56
+ export interface FooterCreditOptions {
57
+ instanceId?: string
58
+ }
59
+
60
+ export default function footerCreditPlugin(
61
+ options: FooterCreditOptions = {}
62
+ ): AmplessPlugin {
63
+ const instanceId = options.instanceId ?? 'footer-credit'
64
+ return definePlugin({
65
+ name: 'footer-credit',
66
+ instanceId,
67
+ apiVersion: 1,
68
+ trust_level: 'untrusted',
69
+ displayName: { en: 'Footer credit', ja: 'フッタークレジット' },
70
+ capabilities: ['publicBody', 'adminSettings'],
71
+ settings: {
72
+ public: [
73
+ {
74
+ type: 'text',
75
+ key: 'html',
76
+ label: { en: 'Snippet', ja: 'スニペット' },
77
+ default: '',
78
+ },
79
+ ],
80
+ },
81
+ publicBodyEnd(ctx) {
82
+ const html = (ctx.setting<string>('html') ?? '').trim()
83
+ if (!html) return []
84
+ return [{ type: 'noscript', id: `footer-credit-${instanceId}`, html }]
85
+ },
86
+ })
87
+ }
88
+ ```
89
+
90
+ Register it in `cms.config.ts`:
91
+
92
+ ```typescript
93
+ import footerCreditPlugin from './plugins/footer-credit'
94
+
95
+ export default defineConfig({
96
+ // ...
97
+ plugins: [
98
+ // ...existing plugins...
99
+ footerCreditPlugin(),
100
+ ],
101
+ })
102
+ ```
103
+
104
+ That's it. Restart `next dev` and visit `/admin/plugins` to configure
105
+ the snippet, then any public page renders it before `</body>`.
106
+
107
+ ## What you can declare
108
+
109
+ A plugin is just an object built with `definePlugin({...})`. The full
110
+ shape lives in `node_modules/ampless/dist/plugin.d.ts` (or [the plugin
111
+ author guide][guide] if you prefer prose).
112
+
113
+ Capabilities currently active:
114
+
115
+ | capability | purpose |
116
+ |---|---|
117
+ | `publicHead` | descriptors rendered in `<head>` site-wide |
118
+ | `publicBody` | descriptors rendered before `</body>` site-wide |
119
+ | `metadata` | per-post Next.js Metadata contributions |
120
+ | `eventHooks` | trusted/untrusted background handlers (content lifecycle, media events, ...) |
121
+ | `adminSettings` | admin-editable settings declared via `settings.public[]` |
122
+ | `writePublicAsset` | trusted plugins can write namespaced files under `public/plugins/<instanceId>/...` |
123
+ | `schema` | per-post JSON-LD via `publicBodyForPost` (theme template must call `ampless.publicBodyForPost(post)`) |
124
+ | `publicHtmlForPost` | per-post visible HTML (e.g. reading-time badge, breadcrumbs) via `publicHtmlForPost` (theme template must call `ampless.publicHtmlForPost(post)`) |
125
+
126
+ **First-party plugins that use `publicHtmlForPost`:**
127
+ - `@ampless/plugin-reading-time` — estimates read time from the post body and injects a configurable badge before or after the content.
128
+
129
+ [guide]: https://github.com/heavymoons/ampless/blob/main/packages/ampless/docs/plugin-author-guide.md
130
+
131
+ ## What about TypeScript?
132
+
133
+ `tsconfig.json` already covers this directory through its `**/*.ts`
134
+ include glob. Both `import x from './plugins/foo'` and
135
+ `import x from '@/plugins/foo'` work; no extra setup needed.
136
+
137
+ ## What `update-ampless` does
138
+
139
+ Nothing. `plugins/` is in the upgrade tool's protected list, so files
140
+ here are never overwritten or deleted on upgrade. The flip side is that
141
+ this README does not get refreshed either — when ampless ships
142
+ significant changes to the plugin API, check the [plugin author guide][guide]
143
+ in the canonical repo for the up-to-date version.
@@ -1,18 +1,33 @@
1
1
  // Public-site proxy (Next.js 16 rename of "middleware"). Implementation
2
2
  // moved to `@ampless/runtime` (L1 extraction); this file wires the
3
- // project's `cms.config` into the factory and re-exports the default
4
- // matcher. The runtime export name is still `createAmplessMiddleware`
5
- // for API stability; only the user-side file convention (proxy.ts +
6
- // `export const proxy`) follows Next 16's rename.
3
+ // project's `cms.config` + AppSync endpoint into the factory and
4
+ // re-exports the default matcher. The runtime export name is still
5
+ // `createAmplessMiddleware` for API stability; only the user-side
6
+ // file convention (proxy.ts + `export const proxy`) follows Next 16's
7
+ // rename.
7
8
  //
8
- // See `@ampless/runtime/middleware` for behaviour details: multi-site
9
- // host rewrite, `<slug>.html` raw route, `?previewTheme=` header
10
- // forwarding, multi-site Cache-Control override.
9
+ // See `@ampless/runtime/middleware` for behaviour details: AppSync
10
+ // flag fetch + Lambda-memory LRU + `/raw/<slug>` rewrite for no_layout
11
+ // HTML, `/static/<slug>(/<path>)` rewrite for static bundles, plus
12
+ // per-post Cache-Control.
11
13
 
12
14
  import cmsConfig from './cms.config'
15
+ import outputs from './amplify_outputs.json'
13
16
  import { createAmplessMiddleware } from '@ampless/runtime/middleware'
14
17
 
15
- export const proxy = createAmplessMiddleware({ cmsConfig })
18
+ // `amplify_outputs.json` is generated by `npx ampx sandbox` /
19
+ // `npx ampx pipeline-deploy` — the public API key has a 365-day TTL
20
+ // and is rotated monthly by the api-key-renewer Lambda. See
21
+ // RUNBOOK.md §"API key rotation".
22
+ export const proxy = createAmplessMiddleware({
23
+ cmsConfig,
24
+ appsyncUrl: outputs.data.url,
25
+ // The public API key is always present in production deploys (the
26
+ // backend declares `apiKeyAuthorizationMode`). The non-null assertion
27
+ // is honest about the assumption; `ampx sandbox` will surface a clear
28
+ // error long before middleware ever runs if it ever isn't.
29
+ apiKey: outputs.data.api_key!,
30
+ })
16
31
 
17
32
  // Next.js 16's Turbopack requires `config` to be a statically
18
33
  // analysable object literal — referencing an imported variable
@@ -0,0 +1,22 @@
1
+ > English: [README.md](./README.md)
2
+ >
3
+ # Blog テーマ
4
+
5
+ shadcn/ui のデフォルトに準じたニュートラル系モノクロ。時系列の投稿フィード、個別投稿ページ、タグ別一覧を提供します。
6
+
7
+ ## カスタマイズ
8
+
9
+ `/admin/sites/<siteId>/theme` で設定:
10
+
11
+ - **プライマリカラー** — ボタン、リンク、強調表示の背景色
12
+ - **アクセントカラー**
13
+ - **フォーカスリング**
14
+ - **破壊的操作** — 削除等の操作色
15
+ - **角丸**
16
+ - **本文フォント** — システムサンセリフ / セリフ (Georgia) / セリフ (Iowan) / 等幅
17
+ - **トップに固定する記事のスラッグ** — 公開済み投稿を 1 件ホームの先頭に固定
18
+ - **ロゴ画像 URL**
19
+ - **ヘッダーナビ** — ラベル + URL のペア
20
+ - **フッターリンク** — ラベル + URL のペア
21
+
22
+ サイトごとのライト/ダークカラースキームは ampless の標準テーマトークン経由で反映されます。
@@ -1,52 +1,22 @@
1
- # {{siteName}}
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+ >
3
+ # Blog theme
2
4
 
3
- A blog site powered by [ampless](https://github.com/heavymoons/ampless).
5
+ Neutral monochrome with shadcn/ui defaults. Chronological post feed, single-post page, and tag listing.
4
6
 
5
- ## Getting Started
7
+ ## Customizing
6
8
 
7
- This project uses Amplify Gen 2 for the backend (Cognito, DynamoDB, S3) and Next.js for the frontend.
9
+ In `/admin/sites/<siteId>/theme`:
8
10
 
9
- ```bash
10
- # 1. Install dependencies
11
- npm install
11
+ - **Primary color** — buttons, links, accent fills
12
+ - **Accent color**
13
+ - **Focus ring**
14
+ - **Destructive** — color used for destructive actions
15
+ - **Corner radius**
16
+ - **Body font** — System sans / Serif (Georgia) / Serif (Iowan) / Monospace
17
+ - **Featured post slug** — pin one published post to the top of the home page
18
+ - **Logo image URL**
19
+ - **Header navigation** — label + URL pairs
20
+ - **Footer links** — label + URL pairs
12
21
 
13
- # 2. Start a personal AWS sandbox (terminal 1)
14
- # Requires AWS credentials configured (`aws configure`).
15
- # First run takes ~5–10 min to provision resources.
16
- # Generates amplify_outputs.json when ready.
17
- npx ampx sandbox
18
-
19
- # 3. Start the Next.js dev server (terminal 2)
20
- npm run dev
21
- ```
22
-
23
- Then open [http://localhost:3000](http://localhost:3000).
24
-
25
- ## First admin user
26
-
27
- Open [http://localhost:3000/login](http://localhost:3000/login) and click **Create admin account**. The first user to register is automatically added to the `ampless-admin` Cognito group.
28
-
29
- After that, manage content from `/admin`:
30
-
31
- - `/admin` — dashboard
32
- - `/admin/posts` — list / create / edit posts (tiptap editor)
33
- - `/admin/media` — upload images to S3
34
-
35
- ## Production deploy
36
-
37
- ```bash
38
- git init && git add . && git commit -m "init"
39
- git remote add origin <your-repo>
40
- git push
41
- # Then connect the repo to AWS Amplify Hosting in the AWS console.
42
- ```
43
-
44
- ## Customize
45
-
46
- - `cms.config.ts` — site name, media delivery mode, plugins
47
- - `app/` — Next.js App Router pages (`(public)/` for the blog, `(admin)/` for the CMS)
48
- - `amplify/` — Amplify Gen 2 backend definitions (auth / data / storage)
49
-
50
- ## Plugins
51
-
52
- Enabled: {{plugins}}
22
+ Per-site light/dark color scheme is honored through the standard ampless theme tokens.
@@ -1,7 +1,7 @@
1
1
  import { defineTheme } from 'ampless'
2
2
 
3
3
  // Customizable fields for the Blog theme. Edit values in
4
- // `/admin/sites/<siteId>/theme` — they're stored in KvStore and applied
4
+ // `/admin/sites/default/theme` — they're stored in KvStore and applied
5
5
  // at render time as CSS variables on every public page.
6
6
  //
7
7
  // Labels / descriptions / groups accept either a plain string (works in
@@ -1,14 +1,13 @@
1
- import { publicAssetUrl } from '@/lib/storage'
1
+ import { ampless } from '@/lib/ampless'
2
2
 
3
3
  interface Ctx {
4
- siteId: string
5
4
  request: Request
6
5
  }
7
6
 
8
7
  // /feed.xml proxy — plugin-rss regenerates the feed on content events
9
- // and writes it to `public/plugins/rss/{siteId}/feed.xml`.
10
- export async function blogFeedHandler({ siteId }: Ctx): Promise<Response> {
11
- const url = publicAssetUrl(`public/plugins/rss/${siteId}/feed.xml`)
8
+ // and writes it to `public/plugins/rss/feed.xml`.
9
+ export async function blogFeedHandler(_ctx: Ctx): Promise<Response> {
10
+ const url = ampless.publicAssetUrl('public/plugins/rss/feed.xml')
12
11
  const upstream = await fetch(url, { cache: 'no-store' })
13
12
  if (!upstream.ok) {
14
13
  return new Response(