create-ampless 1.0.0-alpha.113 → 1.0.0-alpha.115
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.
|
@@ -973,6 +973,67 @@ admin は再デプロイなしにいつでも secret をローテーションで
|
|
|
973
973
|
|
|
974
974
|
---
|
|
975
975
|
|
|
976
|
+
## 9b. プラグインのデータの保存場所
|
|
977
|
+
|
|
978
|
+
プラグインが所有するデータは以下の 5 つのストレージ領域に置かれる可能性があり、**現状の書き込み経路は領域ごとに異なります**。3 つのファミリに分かれます:
|
|
979
|
+
|
|
980
|
+
- **KvStore** — admin/editor が AppSync 経由で書き込みます。プラグインの hook には KvStore write helper は提供されていません。
|
|
981
|
+
- **PluginSecret + PluginSecretIndicator** — `plugin-secret-handler` Lambda が書き込みます。admin/editor が `setPluginSecret` / `clearPluginSecret` AppSync mutation を呼ぶと、handler Lambda が DDB に書く流れです。trusted processor は `ctx.secret<T>()` で `PluginSecret` を読み取りますが、どちらの secret テーブルにも書き込みません。
|
|
982
|
+
- **S3 `public/plugins/{instanceId ?? name}/*`** — trusted Lambda の hook context (`ctx.writePublicAsset(...)`) から書き込みます。プラグインの hook が直接書き込めるのはこの領域だけです。
|
|
983
|
+
|
|
984
|
+
それ以外 — `Post`、`Page`、`Media`、`PostTag` DynamoDB テーブル、`public/site-settings.json` S3 ミラー、他プラグインの namespace — への書き込みは禁止です。現状 runtime が強制しているわけではなく、信頼(および将来の IAM 強化)によって担保されます。
|
|
985
|
+
|
|
986
|
+
| 領域 | パス / 識別子 | アクセスレベル | Phase |
|
|
987
|
+
|---|---|---|---|
|
|
988
|
+
| KvStore(admin 設定) | DynamoDB `pk='siteconfig'`、`sk='plugins.<instanceId>.<fieldKey>'` | admin/editor が AppSync 経由で書く(プラグインの hook context には KvStore write helper は現状提供されていない) | Phase 2 |
|
|
989
|
+
| KvStore(runtime 状態/キャッシュ) | DynamoDB `pk='pluginstate:<plugin>:...'`(TTL 任意) | admin/editor が AppSync 経由で書く(プラグインの hook context には KvStore write helper は現状提供されていない) | 現行 |
|
|
990
|
+
| PluginSecret | DynamoDB `PluginSecret` テーブル、`sk='plugins.<instanceId>.<fieldKey>'` | `trusted` 限定(IAM 専用 AppSync 認証) | Phase 6a |
|
|
991
|
+
| PluginSecretIndicator | DynamoDB `PluginSecretIndicator` テーブル、`sk='plugins.<instanceId>.<fieldKey>'` | `trusted` + admin/editor(indicator 読み取り) | Phase 6a |
|
|
992
|
+
| S3 プラグイン成果物 | `public/plugins/{instanceId ?? name}/*` | `trusted` 限定(`writePublicAsset`) | Phase 3 |
|
|
993
|
+
|
|
994
|
+
**cleanup は自動ではありません。** `cms.config.ts` からプラグインを外しても、5 領域のデータは自動削除されません。将来の lifecycle-dispatch PR が `uninstall` フックの起動メカニズムを追加するまで(§9c 参照)、オペレータによる手動削除が必要です。
|
|
995
|
+
|
|
996
|
+
**独自 DynamoDB テーブル。** プラグインが ampless スキーマ外に独自の DynamoDB テーブルを持つ場合、lifecycle 管理(アンインストール時の cleanup を含む)はプラグイン著者の責任です。ampless は外部テーブルを把握しておらず、将来の `uninstall` cleanup grant は上記 5 領域のみをカバーします。
|
|
997
|
+
|
|
998
|
+
詳細な設計根拠と IAM grant 設計については [`docs/architecture/08-plugin-architecture.ja.md`](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.ja.md#プラグインが所有するデータ領域) を参照してください。
|
|
999
|
+
|
|
1000
|
+
---
|
|
1001
|
+
|
|
1002
|
+
## 9c. `uninstall` フック(Phase 1 予約)
|
|
1003
|
+
|
|
1004
|
+
`AmplessPlugin.uninstall` は **Phase 1 型予約** です — 現在 runtime はこれを呼び出しません。hook 名とシグネチャをプラグインコードが出回る前に確定し、将来の lifecycle-dispatch PR が名前・形を変更せずに起動を配線できるようにするのが目的です。
|
|
1005
|
+
|
|
1006
|
+
**Phase 1 スコープ**: hook 名とシグネチャのみ予約。`ctx` には cleanup helper (`deletePublicAsset` / `deletePluginSetting` / `deletePluginSecret`) がまだありません — 今日 `await ctx.deletePublicAsset(...)` と書くと TypeScript エラーです。これらの helper が lifecycle-dispatch PR で追加される際は `PluginUninstallContext` への追加(additive)であり、空のボディを宣言済みのプラグインへの破壊変更はありません。
|
|
1007
|
+
|
|
1008
|
+
**今日の推奨宣言** — 空のボディ:
|
|
1009
|
+
|
|
1010
|
+
```ts
|
|
1011
|
+
// 例: S3 成果物と secret を書く trusted plugin
|
|
1012
|
+
definePlugin({
|
|
1013
|
+
name: 'my-trusted-plugin',
|
|
1014
|
+
apiVersion: 1,
|
|
1015
|
+
trust_level: 'trusted',
|
|
1016
|
+
capabilities: ['eventHooks', 'writePublicAsset', 'secretSettings'],
|
|
1017
|
+
hooks: { 'content.published': async (_evt, ctx) => { /* ... */ } },
|
|
1018
|
+
uninstall: async (_ctx) => {
|
|
1019
|
+
// Phase 1 予約: runtime は今日このフックを呼び出しません。
|
|
1020
|
+
// また `ctx` には cleanup helper
|
|
1021
|
+
// (`deletePublicAsset` / `deletePluginSetting` /
|
|
1022
|
+
// `deletePluginSecret`) がまだありません。
|
|
1023
|
+
// 空ボディを宣言しておくのが forward-compat の推奨形です —
|
|
1024
|
+
// 将来 lifecycle-dispatch PR がリリースされたとき、helper が
|
|
1025
|
+
// `PluginUninstallContext` に追加され、そこで cleanup ボディを
|
|
1026
|
+
// 実装します。今日の空宣言を再パブリッシュしなくても呼び出し
|
|
1027
|
+
// イベントは受け取れますが、実際の cleanup ボディを追加するには
|
|
1028
|
+
// 再パブリッシュが必要です。
|
|
1029
|
+
},
|
|
1030
|
+
})
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
**冪等性。** lifecycle-dispatch PR がリリースされると、`uninstall` フックは trusted Lambda の IAM コンテキストで実行されます。SQS 配送は at-least-once なので cleanup ボディが複数回実行される可能性があります。安全にリトライできるよう設計してください(S3 の `deleteObject` は冪等、DDB の conditional delete も key が消えていれば safe)。
|
|
1034
|
+
|
|
1035
|
+
---
|
|
1036
|
+
|
|
976
1037
|
## 10. ウォークスルー: GA4 を Phase 1 から Phase 2 に移行する
|
|
977
1038
|
|
|
978
1039
|
Phase 1 の GA4 プラグインは measurement ID を constructor 引数で受けていました。Phase 2 では後方互換のためにその引数を残しつつ、値は `ctx.setting()` 経由で読みます。
|
|
@@ -1323,6 +1323,99 @@ describe('webhookPlugin signing', () => {
|
|
|
1323
1323
|
|
|
1324
1324
|
---
|
|
1325
1325
|
|
|
1326
|
+
## 9b. Where your plugin's data lives
|
|
1327
|
+
|
|
1328
|
+
Plugin-owned data may live in the five storage areas listed below; the
|
|
1329
|
+
**current write paths differ by area** and fall into three families:
|
|
1330
|
+
|
|
1331
|
+
- **KvStore** — admin/editor write through AppSync. Plugin hooks have no
|
|
1332
|
+
KvStore write helper today.
|
|
1333
|
+
- **PluginSecret + PluginSecretIndicator** — written by the
|
|
1334
|
+
`plugin-secret-handler` Lambda, which is invoked by admin/editor through
|
|
1335
|
+
the `setPluginSecret` / `clearPluginSecret` AppSync mutations. The
|
|
1336
|
+
trusted processor reads `PluginSecret` via `ctx.secret<T>()` but does
|
|
1337
|
+
NOT write to either secret table.
|
|
1338
|
+
- **S3 `public/plugins/{instanceId ?? name}/*`** — written by the trusted
|
|
1339
|
+
Lambda's hook context (`ctx.writePublicAsset(...)`). This is the only
|
|
1340
|
+
area a plugin hook writes to directly today.
|
|
1341
|
+
|
|
1342
|
+
All other areas — the `Post`, `Page`, `Media`, and `PostTag` DynamoDB
|
|
1343
|
+
tables, the `public/site-settings.json` S3 mirror, and any other plugin's
|
|
1344
|
+
namespace — are off-limits. The runtime does not enforce this today; it is
|
|
1345
|
+
a contract enforced by trust (and future IAM hardening).
|
|
1346
|
+
|
|
1347
|
+
| Area | Path / identifier | Access level | Phase |
|
|
1348
|
+
|---|---|---|---|
|
|
1349
|
+
| KvStore (admin settings) | DynamoDB `pk='siteconfig'`, `sk='plugins.<instanceId>.<fieldKey>'` | admin/editor via AppSync; plugin hook write helper is not provided today | Phase 2 |
|
|
1350
|
+
| KvStore (runtime state/cache) | DynamoDB `pk='pluginstate:<plugin>:...'` with optional TTL | admin/editor via AppSync; plugin hook write helper is not provided today | Current |
|
|
1351
|
+
| PluginSecret | DynamoDB `PluginSecret` table, `sk='plugins.<instanceId>.<fieldKey>'` | `trusted` only (IAM-only AppSync auth) | Phase 6a |
|
|
1352
|
+
| PluginSecretIndicator | DynamoDB `PluginSecretIndicator` table, `sk='plugins.<instanceId>.<fieldKey>'` | `trusted` + admin/editor (read indicator) | Phase 6a |
|
|
1353
|
+
| S3 plugin assets | `public/plugins/{instanceId ?? name}/*` | `trusted` only (`writePublicAsset`) | Phase 3 |
|
|
1354
|
+
|
|
1355
|
+
**Cleanup is not automatic.** Removing a plugin from `cms.config.ts` leaves
|
|
1356
|
+
orphan data in all five areas. Manual operator cleanup is required until the
|
|
1357
|
+
future lifecycle-dispatch PR ships the invocation mechanism for the `uninstall`
|
|
1358
|
+
hook (see §9c below).
|
|
1359
|
+
|
|
1360
|
+
**Custom DynamoDB tables.** If your plugin provisions its own DynamoDB table
|
|
1361
|
+
outside the ampless schema, lifecycle management (including cleanup on uninstall)
|
|
1362
|
+
is your responsibility. ampless has no visibility into external tables and the
|
|
1363
|
+
future `uninstall` cleanup grants cover only the five areas above.
|
|
1364
|
+
|
|
1365
|
+
See [`docs/architecture/08-plugin-architecture.md`](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.md#plugin-owned-data-areas)
|
|
1366
|
+
for the full rationale and the IAM grant design.
|
|
1367
|
+
|
|
1368
|
+
---
|
|
1369
|
+
|
|
1370
|
+
## 9c. `uninstall` hook (Phase 1 reservation)
|
|
1371
|
+
|
|
1372
|
+
`AmplessPlugin.uninstall` is a **Phase 1 type reservation** — the runtime does
|
|
1373
|
+
not call it today. Its purpose is to lock in the hook name and signature before
|
|
1374
|
+
any plugin code ships, so the future lifecycle-dispatch PR can wire the
|
|
1375
|
+
invocation without renaming or reshaping anything.
|
|
1376
|
+
|
|
1377
|
+
**Phase 1 scope**: only the hook name and signature are reserved. The `ctx`
|
|
1378
|
+
does **not** yet carry cleanup helpers (`deletePublicAsset` /
|
|
1379
|
+
`deletePluginSetting` / `deletePluginSecret`) — writing
|
|
1380
|
+
`await ctx.deletePublicAsset(...)` today is a TypeScript error. When those
|
|
1381
|
+
helpers land (in the lifecycle-dispatch PR), they are added to
|
|
1382
|
+
`PluginUninstallContext` additively, with no breaking change to plugins that
|
|
1383
|
+
declared an empty body in advance.
|
|
1384
|
+
|
|
1385
|
+
**Recommended declaration today** — an empty body:
|
|
1386
|
+
|
|
1387
|
+
```ts
|
|
1388
|
+
// Example: a trusted plugin that writes assets and stores secrets
|
|
1389
|
+
definePlugin({
|
|
1390
|
+
name: 'my-trusted-plugin',
|
|
1391
|
+
apiVersion: 1,
|
|
1392
|
+
trust_level: 'trusted',
|
|
1393
|
+
capabilities: ['eventHooks', 'writePublicAsset', 'secretSettings'],
|
|
1394
|
+
hooks: { 'content.published': async (_evt, ctx) => { /* ... */ } },
|
|
1395
|
+
uninstall: async (_ctx) => {
|
|
1396
|
+
// Phase 1 reservation: the runtime does not invoke this hook
|
|
1397
|
+
// today, AND `ctx` does not yet carry cleanup helpers
|
|
1398
|
+
// (`deletePublicAsset` / `deletePluginSetting` /
|
|
1399
|
+
// `deletePluginSecret`). Declaring an empty body is the
|
|
1400
|
+
// recommended forward-compat shape — when the future
|
|
1401
|
+
// lifecycle-dispatch PR ships, the helpers land on
|
|
1402
|
+
// `PluginUninstallContext` additively, and you fill in the
|
|
1403
|
+
// body THEN. Plugins that shipped the empty declaration
|
|
1404
|
+
// today do not need to re-publish for the signature change,
|
|
1405
|
+
// but a re-publish is required to add the actual cleanup
|
|
1406
|
+
// body.
|
|
1407
|
+
},
|
|
1408
|
+
})
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
**Idempotency.** When the lifecycle-dispatch PR ships, the `uninstall` hook
|
|
1412
|
+
runs in a trusted-Lambda IAM context. The SQS delivery is at-least-once, so
|
|
1413
|
+
your cleanup body may run more than once — design it to be safe to retry
|
|
1414
|
+
(e.g. `deleteObject` is idempotent on S3; a conditional `delete` on DDB is
|
|
1415
|
+
safe if the key is already gone).
|
|
1416
|
+
|
|
1417
|
+
---
|
|
1418
|
+
|
|
1326
1419
|
## 10. Walk-through: migrating GA4 from Phase 1 to Phase 2
|
|
1327
1420
|
|
|
1328
1421
|
The Phase 1 GA4 plugin took the measurement ID through a
|
|
@@ -25,21 +25,21 @@
|
|
|
25
25
|
"@tiptap/pm": "^3.23.6",
|
|
26
26
|
"@tiptap/react": "^3.23.6",
|
|
27
27
|
"@tiptap/starter-kit": "^3.23.6",
|
|
28
|
-
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.
|
|
29
|
-
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.
|
|
30
|
-
"@ampless/plugin-gtm": "^0.2.0-alpha.
|
|
31
|
-
"@ampless/plugin-og-image": "^0.2.0-alpha.
|
|
32
|
-
"@ampless/plugin-plausible": "^0.2.0-alpha.
|
|
33
|
-
"@ampless/plugin-reading-time": "^0.1.0-alpha.
|
|
34
|
-
"@ampless/plugin-rss": "^0.2.0-alpha.
|
|
35
|
-
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.
|
|
36
|
-
"@ampless/plugin-seo": "^0.2.0-alpha.
|
|
37
|
-
"@ampless/plugin-webhook": "^0.2.0-alpha.
|
|
38
|
-
"@ampless/admin": "^1.0.0-alpha.
|
|
39
|
-
"@ampless/backend": "^1.0.0-alpha.
|
|
40
|
-
"@ampless/runtime": "^1.0.0-alpha.
|
|
28
|
+
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.20",
|
|
29
|
+
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.11",
|
|
30
|
+
"@ampless/plugin-gtm": "^0.2.0-alpha.19",
|
|
31
|
+
"@ampless/plugin-og-image": "^0.2.0-alpha.36",
|
|
32
|
+
"@ampless/plugin-plausible": "^0.2.0-alpha.19",
|
|
33
|
+
"@ampless/plugin-reading-time": "^0.1.0-alpha.9",
|
|
34
|
+
"@ampless/plugin-rss": "^0.2.0-alpha.36",
|
|
35
|
+
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.15",
|
|
36
|
+
"@ampless/plugin-seo": "^0.2.0-alpha.36",
|
|
37
|
+
"@ampless/plugin-webhook": "^0.2.0-alpha.37",
|
|
38
|
+
"@ampless/admin": "^1.0.0-alpha.69",
|
|
39
|
+
"@ampless/backend": "^1.0.0-alpha.58",
|
|
40
|
+
"@ampless/runtime": "^1.0.0-alpha.45",
|
|
41
41
|
"@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
|
|
42
|
-
"ampless": "^1.0.0-alpha.
|
|
42
|
+
"ampless": "^1.0.0-alpha.36",
|
|
43
43
|
"aws-amplify": "^6.17.0",
|
|
44
44
|
"class-variance-authority": "^0.7.1",
|
|
45
45
|
"clsx": "^2.1.1",
|