ampless 1.0.0-alpha.32 → 1.0.0-alpha.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -575,7 +575,48 @@ interface PluginMetadata {
575
575
  types?: Record<string, string>;
576
576
  };
577
577
  }
578
- type PluginEventHandler<T extends EventType = EventType> = (event: AmplessEvent<T>, ctx: PluginRuntimeContext) => Promise<void>;
578
+ /**
579
+ * Hook return value reservation (Phase 1: type only, runtime no-op).
580
+ *
581
+ * Today's runtime (after-event SQS Lambdas) ignores the return value
582
+ * entirely. The type is widened so a future PR can land additive
583
+ * directives without breaking plugin authors who publish in the
584
+ * meantime. Likely first use case:
585
+ *
586
+ * - `metrics?: Record<string, number>` — emit observability data
587
+ * without touching CloudWatch SDK directly from the plugin.
588
+ *
589
+ * `cancel` / `post` rewrite-style directives are NOT enabled by this
590
+ * type alone — they would also require `before:*` events to be wired
591
+ * to plugins (currently reserved, see events.ts) and payload shapes
592
+ * to expose mutable bodies. This PR widens the return surface only;
593
+ * richer semantics need follow-on PRs.
594
+ *
595
+ * The `readonly __amplessPluginHookResult?: never` marker is a
596
+ * type-level nominal tag: it prevents `Promise<void | PluginHookResult>`
597
+ * from silently accepting unrelated promise types like
598
+ * `Promise<string>` or `Promise<number>`. Plugin authors do not need
599
+ * to set this field — it is optional and exists only to constrain
600
+ * what the union accepts.
601
+ */
602
+ interface PluginHookResult {
603
+ readonly __amplessPluginHookResult?: never;
604
+ }
605
+ /**
606
+ * Async event hook handler.
607
+ *
608
+ * The return value is reserved (see `PluginHookResult`). Today's
609
+ * runtime ignores it — returning `undefined` (`Promise<void>`) is
610
+ * the canonical "no-op" and matches existing plugin code without
611
+ * migration. Plugins that explicitly return a `PluginHookResult`
612
+ * object type-check today but do not change runtime behaviour
613
+ * until the matching capability PR lands.
614
+ *
615
+ * Non-PluginHookResult promise types (`Promise<string>`,
616
+ * `Promise<number>`, etc.) are rejected at compile time by the
617
+ * `__amplessPluginHookResult` private marker on `PluginHookResult`.
618
+ */
619
+ type PluginEventHandler<T extends EventType = EventType> = (event: AmplessEvent<T>, ctx: PluginRuntimeContext) => Promise<void | PluginHookResult>;
579
620
  /**
580
621
  * Runtime services injected into hook handlers by the Lambda processor.
581
622
  * Decoupling plugins from concrete AWS clients lets us swap implementations
@@ -1510,4 +1551,4 @@ declare function pickDefaultEntrypoint(files: readonly {
1510
1551
 
1511
1552
  declare const VERSION = "0.0.1";
1512
1553
 
1513
- export { type AmplessEvent, type AmplessPlugin, type AuthContext, type BundleExtractResult, type CacheConfig, type CacheStrategy, type Config, type ContentEventPayload, type ContentEventType, type ContentFormat, type ContentTypeDefinition, type CreatePostInput, DEFAULT_ENTRYPOINT, type DateFormat, type EventPayloadOf, type EventType, type ExtractedFile, type FieldDefinition, type FieldType, type ImageDisplay, type KvItem, type KvStore, type LinkListItem, type ListOptions, type LocalizedString, MAX_BUNDLE_BYTES, type Media, type MediaEventPayload, type MediaEventType, type MediaMetadata, type MediaProcessingDefaults, type OgImageConfig, type OgImageFont, type OgImageRenderContext, PLUGIN_KEY_PATTERN, type Page, type PluginBooleanField, type PluginCapability, type PluginCodeField, type PluginEventHandler, type PluginJsonField, type PluginMetadata, type PluginNumberField, type PluginPackageManifest, type PluginPublicRenderContext, type PluginRepeatableField, type PluginRepeatableSubField, type PluginRuntimeContext, type PluginSecretField, type PluginSelectField, type PluginSettingField, type PluginSettingsManifest, type PluginTextField, type PluginTextareaField, type PluginUrlField, type Post, type PostIndexEventPayload, type PostIndexEventType, type PostMetadata, type PostStatus, type PostsProvider, type PublicBodyDescriptor, type PublicHeadDescriptor, type PublicPostBodyDescriptor, type PublicPostHtmlDescriptor, type PublicPostHtmlPosition, type Role, SITE_CONFIG_PK, type ScriptStrategy, type SiteSettingsEventPayload, type SiteSettingsEventType, type StaticPostBody, type StaticPostFileMeta, type StreamEventName, TEXT_EXTENSIONS, type ThemeColorField, type ThemeField, type ThemeFieldType, type ThemeFontFamilyField, type ThemeImageField, type ThemeLengthField, type ThemeLinkListField, type ThemeManifest, type ThemeModule, type ThemeRouteContext, type ThemeSelectField, type ThemeTextField, type TrustLevel, type TrustedPluginRuntimeContext, VERSION, type ValidationIssue, bundlePrefix, createPost, decodeAwsJson, defineConfig, definePlugin, defineSchema, defineTheme, defineThemeModule, deletePost, deleteSiteSetting, detectContentEvents, encodeAwsJson, escapeXml, extractFirstImageUrl, findAbsolutePathRefs, flattenSettings, formatColorPair, formatDate, formatPublicAssetUrl, getKvStore, getPost, getPostById, getSiteSetting, hasKvStore, hasPostsProvider, isTagListUrl, isValidPluginKey, listPosts, listSiteSettings, mimeTypeFor, parseColorPair, parseLinkList, pickDefaultEntrypoint, resolveLocalized, resolvePluginSettings, resolveThemeValues, setKvStore, setPostsProvider, setSiteSetting, stringifyLinkList, stripCommonPrefix, themeSettingKey, unflattenSettings, updatePost, validateBundle, validateBundlePath, validatePluginSettingValue, validatePublicAssetKey, validateThemeValue };
1554
+ export { type AmplessEvent, type AmplessPlugin, type AuthContext, type BundleExtractResult, type CacheConfig, type CacheStrategy, type Config, type ContentEventPayload, type ContentEventType, type ContentFormat, type ContentTypeDefinition, type CreatePostInput, DEFAULT_ENTRYPOINT, type DateFormat, type EventPayloadOf, type EventType, type ExtractedFile, type FieldDefinition, type FieldType, type ImageDisplay, type KvItem, type KvStore, type LinkListItem, type ListOptions, type LocalizedString, MAX_BUNDLE_BYTES, type Media, type MediaEventPayload, type MediaEventType, type MediaMetadata, type MediaProcessingDefaults, type OgImageConfig, type OgImageFont, type OgImageRenderContext, PLUGIN_KEY_PATTERN, type Page, type PluginBooleanField, type PluginCapability, type PluginCodeField, type PluginEventHandler, type PluginHookResult, type PluginJsonField, type PluginMetadata, type PluginNumberField, type PluginPackageManifest, type PluginPublicRenderContext, type PluginRepeatableField, type PluginRepeatableSubField, type PluginRuntimeContext, type PluginSecretField, type PluginSelectField, type PluginSettingField, type PluginSettingsManifest, type PluginTextField, type PluginTextareaField, type PluginUrlField, type Post, type PostIndexEventPayload, type PostIndexEventType, type PostMetadata, type PostStatus, type PostsProvider, type PublicBodyDescriptor, type PublicHeadDescriptor, type PublicPostBodyDescriptor, type PublicPostHtmlDescriptor, type PublicPostHtmlPosition, type Role, SITE_CONFIG_PK, type ScriptStrategy, type SiteSettingsEventPayload, type SiteSettingsEventType, type StaticPostBody, type StaticPostFileMeta, type StreamEventName, TEXT_EXTENSIONS, type ThemeColorField, type ThemeField, type ThemeFieldType, type ThemeFontFamilyField, type ThemeImageField, type ThemeLengthField, type ThemeLinkListField, type ThemeManifest, type ThemeModule, type ThemeRouteContext, type ThemeSelectField, type ThemeTextField, type TrustLevel, type TrustedPluginRuntimeContext, VERSION, type ValidationIssue, bundlePrefix, createPost, decodeAwsJson, defineConfig, definePlugin, defineSchema, defineTheme, defineThemeModule, deletePost, deleteSiteSetting, detectContentEvents, encodeAwsJson, escapeXml, extractFirstImageUrl, findAbsolutePathRefs, flattenSettings, formatColorPair, formatDate, formatPublicAssetUrl, getKvStore, getPost, getPostById, getSiteSetting, hasKvStore, hasPostsProvider, isTagListUrl, isValidPluginKey, listPosts, listSiteSettings, mimeTypeFor, parseColorPair, parseLinkList, pickDefaultEntrypoint, resolveLocalized, resolvePluginSettings, resolveThemeValues, setKvStore, setPostsProvider, setSiteSetting, stringifyLinkList, stripCommonPrefix, themeSettingKey, unflattenSettings, updatePost, validateBundle, validateBundlePath, validatePluginSettingValue, validatePublicAssetKey, validateThemeValue };
@@ -563,6 +563,50 @@ React 19 はさらに、クライアントコンポーネントのレンダー
563
563
 
564
564
  投稿単位の見える出力には `publicHtmlForPost` を使ってください(Phase 6d — 上の例と §6 の `PublicPostHtmlDescriptor` 参照)。runtime が post 本文の周囲の固定スロットにサーバーサイド HTML を出すので、hydration と競合しません。
565
565
 
566
+ ---
567
+
568
+ ## 6a. 予約投稿とコンテンツイベント
569
+
570
+ ampless は**予約投稿**をサポートしています。`status: 'published'` かつ `publishedAt` が未来の投稿は、その時刻まで公開読み出しから隠されます。`publishedAt` を過ぎると、サイトの自然なキャッシュ有効期限(デフォルト ≤ ~5 分)の範囲内で公開されます — 秒単位の正確なトリガーはありません。
571
+
572
+ ### イベントは保存時に発火する(`publishedAt` のタイミングではない)
573
+
574
+ `content.published`(および `content.updated`)は DynamoDB Streams 経由で**投稿が保存されたとき**に emit されます。`publishedAt` が到来したときではありません。つまり、`content.published` に反応するプラグインは、投稿が未来日時の場合、**その投稿が公開される前**に実行されます。
575
+
576
+ 現在の投稿一覧から公開アセットを再構築する trusted プラグイン(RSS、sitemap、JSON インデックスなど)にとってはこれは問題ありません — `listPublishedPosts()` が未来日時の投稿をすでに除外しているため、再生成されたアセットはその投稿が公開されるまでリストに含まれません。
577
+
578
+ 一方、**外部通知プラグイン**(webhook、プッシュ通知、SNS 投稿など)には影響があります。投稿の URL が 404 を返しているまたはホームページにリダイレクトされている間に通知が配信されてしまいます。
579
+
580
+ ### 推奨パターン: `publishedAt` でゲートする
581
+
582
+ ディスパッチの前に `event.payload.publishedAt` を確認します。投稿が未来日時の場合はスキップまたは保留します:
583
+
584
+ ```ts
585
+ hooks: {
586
+ async 'content.published'(event, ctx) {
587
+ const { publishedAt } = event.payload
588
+
589
+ // 予約投稿には通知を送らない。イベントは保存時に発火するが、
590
+ // 投稿が公開されるのは publishedAt になってから。
591
+ if (publishedAt && new Date(publishedAt) > new Date()) {
592
+ return
593
+ }
594
+
595
+ // 投稿は今すぐ公開状態 — 通知しても安全。
596
+ await sendWebhook(event.payload, ctx)
597
+ },
598
+ }
599
+ ```
600
+
601
+ イベントペイロード内の `publishedAt` は UTC ISO 8601 文字列(`...Z`)です。`Date.now()` と比較する前に `new Date()` またはお好みの日付ライブラリでパースしてください。
602
+
603
+ ### 今後の対応
604
+
605
+ `content.published` の発火を保存時ではなく `publishedAt` のタイミングに合わせる機能 — EventBridge Scheduler または DynamoDB TTL トリガー Lambda によるスケジューラーコンポーネントが必要 — は計画中の機能強化です。現リリースのスコープには含まれていません。それまでの間は上記のパターンが通知プラグインにおける推奨ガードです。
606
+
607
+ オペレーター視点からの `publishedAt` セマンティクスの全体像は [`docs/scheduled-publishing.md`](https://github.com/heavymoons/ampless/blob/main/docs/scheduled-publishing.ja.md) を参照してください。
608
+
609
+ ---
566
610
 
567
611
  ### CSP nonce(Phase 1 予約)
568
612
 
@@ -617,7 +661,28 @@ npm 公開のスタンドアロンプラグインの場合は、`package.json#am
617
661
 
618
662
  ## 7. 非同期イベントフック
619
663
 
620
- `hooks` は SQS から到着したイベントを trust_level に対応する processor Lambda が受けて実行します。runtime context (`ctx`) の中身:
664
+ `hooks` は SQS から到着したイベントを trust_level に対応する processor Lambda が受けて実行します。
665
+
666
+ ### 戻り値の予約
667
+
668
+ `PluginEventHandler` の戻り値型は `Promise<void | PluginHookResult>`。
669
+ runtime は現状この戻り値を完全に無視する — 既存 plugin が
670
+ `Promise<void>` を返す形は migration 不要で動き続ける。
671
+ `PluginHookResult` は将来の directive (最初の用例として有力:
672
+ `metrics?: Record<string, number>` による observability emission) のた
673
+ めの予約で、今宣言するのは forward-compatibility のヒントとして
674
+ の扱い。注意: rewrite / cancel 系 directive は本 widening だけで
675
+ は有効化されない — `before:*` event の plugin 配線と payload 拡張
676
+ が別 PR で必要。
677
+
678
+ `PluginHookResult` には private な `__amplessPluginHookResult`
679
+ marker が付いており、union が `Promise<string>` / `Promise<number>`
680
+ 等の無関係な promise を silently 受け入れないようになっている —
681
+ plugin 作者がこの marker を明示的に設定する必要は無い。
682
+
683
+ ### Runtime context
684
+
685
+ runtime context (`ctx`) の中身:
621
686
 
622
687
  ```ts
623
688
  interface PluginRuntimeContext {
@@ -719,6 +719,72 @@ the example above and §6's `PublicPostHtmlDescriptor`). The runtime
719
719
  emits server-side HTML at fixed slots around the post body, so
720
720
  nothing races against hydration.
721
721
 
722
+ ---
723
+
724
+ ## 6a. Scheduled posts and content events
725
+
726
+ ampless supports **scheduled publishing**: a post with `status:
727
+ 'published'` and a future `publishedAt` is hidden from all public
728
+ reads until that time. Once `publishedAt` arrives, the post becomes
729
+ visible within the site's natural cache window (≤ ~5 minutes by
730
+ default) — there is no exact-time trigger.
731
+
732
+ ### Events fire at save time, not at `publishedAt`
733
+
734
+ `content.published` (and `content.updated`) are emitted via DynamoDB
735
+ Streams **when the post is saved**, not when `publishedAt` arrives.
736
+ This means a plugin that reacts to `content.published` will run
737
+ **before the post is publicly visible** when the post is future-dated.
738
+
739
+ For trusted plugins that rebuild public assets from the current post
740
+ list (RSS, sitemap, JSON indexes), this is harmless — `listPublishedPosts()`
741
+ already filters out future-dated posts, so the regenerated asset
742
+ simply omits the scheduled post until it goes live.
743
+
744
+ For **outbound-notification plugins** (webhook, push notification,
745
+ social post) the early fire matters: the notification will be
746
+ delivered to subscribers while the post URL still returns 404 or
747
+ redirects to the home page.
748
+
749
+ ### Recommended pattern: gate on `publishedAt`
750
+
751
+ Check `event.payload.publishedAt` before dispatching. Skip or defer
752
+ when the post is future-dated:
753
+
754
+ ```ts
755
+ hooks: {
756
+ async 'content.published'(event, ctx) {
757
+ const { publishedAt } = event.payload
758
+
759
+ // Skip notification for future-scheduled posts. The event fires
760
+ // at save time, but the post won't be public until publishedAt.
761
+ if (publishedAt && new Date(publishedAt) > new Date()) {
762
+ return
763
+ }
764
+
765
+ // Post is live now — safe to notify.
766
+ await sendWebhook(event.payload, ctx)
767
+ },
768
+ }
769
+ ```
770
+
771
+ The `publishedAt` value in the event payload is a UTC ISO 8601 string
772
+ (`...Z`). Parse it with `new Date()` or your preferred date library
773
+ before comparing against `Date.now()`.
774
+
775
+ ### Future work
776
+
777
+ Aligning event emission with the scheduled time — so `content.published`
778
+ fires at `publishedAt` rather than at save time — is a planned
779
+ enhancement. It requires a scheduler component (EventBridge Scheduler
780
+ or a DynamoDB TTL-triggered Lambda) and is not yet in scope for the
781
+ current release. Until then, the pattern above is the recommended
782
+ guard for notification plugins.
783
+
784
+ For a full description of `publishedAt` semantics from the operator's
785
+ perspective, see [`docs/scheduled-publishing.md`](https://github.com/heavymoons/ampless/blob/main/docs/scheduled-publishing.md).
786
+
787
+ ---
722
788
 
723
789
  ### CSP nonce (Phase 1 reservation)
724
790
 
@@ -794,7 +860,28 @@ manifest and the factory return value:
794
860
  ## 7. Async event hooks
795
861
 
796
862
  `hooks` runs inside the trust_level-matched processor Lambda when an
797
- event arrives via SQS. The runtime context (`ctx`) carries:
863
+ event arrives via SQS.
864
+
865
+ ### Return value reservation
866
+
867
+ `PluginEventHandler` returns `Promise<void | PluginHookResult>`. The
868
+ runtime currently ignores the return value entirely — existing
869
+ plugins returning `Promise<void>` keep working without migration.
870
+ `PluginHookResult` is reserved for a future directive (likely first:
871
+ `metrics?: Record<string, number>` for observability emission);
872
+ declaring it today is a forward-compatibility hint. Note: rewrite
873
+ or cancel directives are NOT enabled by this widening alone — they
874
+ require additional `before:*` event support and payload extensions
875
+ in separate PRs.
876
+
877
+ `PluginHookResult` carries a private `__amplessPluginHookResult`
878
+ marker so that the union does not silently accept unrelated promise
879
+ types (`Promise<string>` / `Promise<number>` etc.) — plugin authors
880
+ do not need to set this field.
881
+
882
+ ### Runtime context
883
+
884
+ The runtime context (`ctx`) carries:
798
885
 
799
886
  ```ts
800
887
  interface PluginRuntimeContext {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ampless",
3
- "version": "1.0.0-alpha.32",
3
+ "version": "1.0.0-alpha.34",
4
4
  "description": "Serverless CMS for AWS Amplify",
5
5
  "license": "MIT",
6
6
  "type": "module",