@riebeckite/plugin-webmention 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,7 +108,7 @@ manifest time, mentions whose target is not a published entry are reported as
108
108
 
109
109
  ## Exports
110
110
 
111
- - `webmention()` / `webmentionPlugin()`
111
+ - `webmention()` / `webmention()`
112
112
  - `MemoryWebmentionProvider`, provider/capability types and errors
113
113
  - `parseWebmentionSource`, `findTargetLink`, `verifyWebmention`,
114
114
  `createWebmentionSourceFetcher`
package/README_ja.md CHANGED
@@ -2,29 +2,16 @@
2
2
 
3
3
  <!-- Generated from docs/docs/plugins/webmention.ja.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
4
4
 
5
- Webmention を受信し、送信元ドキュメントが本当にターゲットへリンクしているかを検証し、
6
- プラガブルな provider 経由で保存して、検証済みのメンションを記事の近くに表示します。
7
- コアプラグインには **Cloudflare・Worker・データベース・ベンダー固有のコードは一切なく**、
8
- 依存は `@riebeckite/core` だけです。
5
+ Webmention を受信し、送信元ドキュメントが本当にターゲットへリンクしているかを検証し、差し替え可能なプロバイダー経由で保存して、検証済みのメンションを記事の近くに表示します。コアプラグインには **Cloudflare・Worker・データベース・ベンダー固有のコードは一切なく**、依存は `@riebeckite/core` だけです。
9
6
 
10
7
  [English](./README.md)
11
8
 
12
9
  ## 設計
13
10
 
14
- - `WebmentionProvider` が storage / runtime の境界です。`store` / `query` の
15
- capability を公開し、`store(mention)` と `query(query)` を持ちます。認証情報・
16
- データベースハンドル・実行時バインディングは adapter 内に留まり、プラグインの
17
- options や生成物へ漏れません。
18
- - `MemoryWebmentionProvider` はテスト・ローカルプレビューで使う provider の例として
19
- 同梱されています。プロセス/isolate をまたいで永続化はしません。
20
- - プラグインは `endpoints` を宣言します。`POST {endpoint}` が Webmention を受信し、
21
- `GET {endpoint}` が検証済みメンションのフィードを返します。ルートフレームワークの
22
- 詳細はこのパッケージへ入りません。HonoX 連携(`mountRiebeckiteEndpoints`)が
23
- エンドポイントをホストルーターへマウントします。
24
- - 検証は注入可能な `WebmentionSourceFetcher` を通じて送信元を取得し、ターゲットへの
25
- 発リンクを確認したうえで、軽量な引用メタデータ(title・excerpt・author・公開日・
26
- `rel` 由来の種別)を記録します。既定の fetcher は loopback とプライベートネット
27
- ワーク宛てを拒否し、本文サイズを制限し、タイムアウトします。
11
+ - `WebmentionProvider` がストレージとランタイムの境界です。`store` / `query` のケイパビリティを公開し、`store(mention)` と `query(query)` を持ちます。認証情報・データベースハンドル・実行時バインディングはアダプター内に留まり、プラグインの options や生成物へ漏れません。
12
+ - `MemoryWebmentionProvider` はテスト・ローカルプレビュー・プロバイダーの実装例として同梱されています。プロセスや isolate をまたいで永続化はしません。
13
+ - プラグインは `endpoints` を宣言します。`POST {endpoint}` が Webmention を受信し、`GET {endpoint}` が検証済みメンションのフィードを返します。ルートフレームワークの詳細はこのパッケージへ入りません。HonoX 連携(`mountRiebeckiteEndpoints`)がエンドポイントをホストルーターへマウントします。
14
+ - 検証は注入可能な `WebmentionSourceFetcher` を通じて送信元を取得し、ターゲットへの発リンクを確認したうえで、軽量な引用メタデータ(title・excerpt・author・公開日・`rel` 由来の種別)を記録します。既定のフェッチャーはループバックとプライベートネットワーク宛てを拒否し、本文サイズを制限し、タイムアウトします。
28
15
 
29
16
  ## 使い方
30
17
 
@@ -39,27 +26,21 @@ export default {
39
26
  };
40
27
  ```
41
28
 
42
- provider を省略するとインメモリ provider が使われます。ローカルプレビューには
43
- 十分ですが、プロセスをまたぐとメンションは失われます。本産では永続 adapter
44
- ([`@riebeckite/webmention-cloudflare`](https://github.com/Rerurate514/riebeckite/blob/main/packages/integrations/webmention-cloudflare/README_ja.md) を参照)
45
- を指定してください。
29
+ プロバイダーを省略するとインメモリのプロバイダーが使われます。ローカルプレビューには十分ですが、プロセスをまたぐとメンションは失われます。本番では永続アダプター([`@riebeckite/webmention-cloudflare`](https://github.com/Rerurate514/riebeckite/blob/main/packages/integrations/webmention-cloudflare/README_ja.md) を参照)を指定してください。
46
30
 
47
31
  ## エンドポイント
48
32
 
49
- | Method | Path | 動作 |
33
+ | メソッド | パス | 動作 |
50
34
  | ------ | -------------------------- | ---- |
51
35
  | `POST` | `/webmentions`(既定) | `source` と `target` を含む `application/x-www-form-urlencoded` または `application/json` を受け付けます。受理時は `202`、拒否時は `error` コード付きの `400`、ストレージ不能時は `503` を返します。 |
52
- | `GET` | `/webmentions`(既定) | 検証済みメンションのフィードを JSON(`{ version, generatedAt, count, mentions }`)で返します。`?target=`・`?limit=`・`?since=` に対応。query 不能な provider では `501` を返します。 |
36
+ | `GET` | `/webmentions`(既定) | 検証済みメンションのフィードを JSON(`{ version, generatedAt, count, mentions }`)で返します。`?target=`・`?limit=`・`?since=` に対応。query に対応しないプロバイダーでは `501` を返します。 |
53
37
 
54
38
  拒否コード: `invalid_request`、`missing_source_or_target`、`target_not_found`、
55
39
  `invalid_source`、`invalid_target`、`source_unreachable`、`no_link_found`。
56
40
 
57
- ## 表示
41
+ ## 記事近くのメンション表示
58
42
 
59
- Site が JSX の配置を所有します。Provider のインスタンスは Site の composition で保持し、
60
- 同じインスタンスを `webmention()` へ渡してから、`getWebmentionSectionData` で
61
- コンポーネントの型付き props を取得します。helper は `PublicContentView` を受け取るため、
62
- draft と scheduled の entry は描画できません。
43
+ サイトが JSX の配置を所有します。プロバイダーのインスタンスはサイトの構成で保持し、同じインスタンスを `webmention()` へ渡してから、`getWebmentionSectionData` でコンポーネントの型付き props を取得します。ヘルパーは `PublicContentView` を受け取るため、draft と scheduled のエントリは描画できません。
63
44
 
64
45
  ```tsx
65
46
  const mentions = await getWebmentionSectionData({
@@ -72,42 +53,35 @@ const mentions = await getWebmentionSectionData({
72
53
  <WebmentionSection {...mentions} />
73
54
  ```
74
55
 
75
- 空の Provider は空の model を返し、`WebmentionSection` は何も描画しません。Plugin は
76
- ビルド時に manifest HTML を注入しません。より低レベルな query が必要な場合は、
77
- `getWebmentionsForEntry({ manifest, config, provider, slug })` を直接呼ぶか、JSON フィードを
78
- 利用できます。
56
+ 空のプロバイダーは空のモデルを返し、`WebmentionSection` は何も描画しません。プラグインはビルド時にマニフェストの HTML を注入しません。より低レベルな query が必要な場合は、`getWebmentionsForEntry({ manifest, config, provider, slug })` を直接呼ぶか、JSON フィードを利用できます。
79
57
 
80
58
  ## オプション
81
59
 
82
60
  | オプション | 既定値 | 説明 |
83
61
  | ---------- | ------ | ---- |
84
- | `provider` | インメモリ | `WebmentionProvider` を実装するストレージ adapter。 |
62
+ | `provider` | インメモリ | `WebmentionProvider` を実装するストレージアダプター。 |
85
63
  | `endpoint` | `"/webmentions"` | 受信(POST)とフィード(GET)のパス。 |
86
64
  | `headingText` | `"Mentions"` | 表示セクションの見出し。 |
87
65
  | `limit` | `20` | 記事ごとに表示する最大メンション数。 |
88
66
  | `className` | `"rr-webmention"` | 表示セクションのルート CSS クラス。 |
89
- | `allowedTargets` | `[]` | 公開 entry 以外で受理する絶対ターゲット URL。 |
90
- | `fetchSource` | グローバル `fetch` | 送信元 fetcher の差し替え(テスト・独自 runtime)。 |
67
+ | `allowedTargets` | `[]` | 公開エントリ以外で受理する絶対ターゲット URL。 |
68
+ | `fetchSource` | グローバル `fetch` | 送信元フェッチャーの差し替え(テスト・独自ランタイム)。 |
91
69
  | `timeoutMs` | `10000` | 送信元取得のタイムアウト。 |
92
70
  | `maxBytes` | `1000000` | 受理する送信元ドキュメントの最大サイズ。 |
93
71
  | `userAgent` | プラグイン既定 | 検証時の `User-Agent`。 |
94
72
  | `allowPrivateHosts` | `false` | プライベートネットワーク宛ての取得を許可します。 |
95
73
  | `nofollow` | `true` | 表示する送信元リンクへ `rel="nofollow ugc"` を付与します。 |
96
74
 
97
- ブラウザへ渡るのは JSON 安全な値だけです。このプラグインは client entry も
98
- `publicConfig` も登録せず、provider の認証情報は options に入りません。
75
+ ブラウザへ渡るのは JSON 安全な値だけです。このプラグインはクライアントエントリも `publicConfig` も登録せず、プロバイダーの認証情報は options に入りません。
99
76
 
100
77
  ## 診断
101
78
 
102
- `addDiagnostics` は provider の capability 不足
103
- (`webmention-render-requires-query`、`webmention-receive-requires-store`)を報告します。
104
- manifest 生成時、公開 entry に一致しないターゲットのメンションは
105
- `webmention-unmatched-target` として報告されます。
79
+ `addDiagnostics` はプロバイダーのケイパビリティ不足(`webmention-render-requires-query`、`webmention-receive-requires-store`)を報告します。マニフェスト生成時、公開エントリに一致しないターゲットのメンションは `webmention-unmatched-target` として報告されます。
106
80
 
107
81
  ## エクスポート
108
82
 
109
- - `webmention()` / `webmentionPlugin()`
110
- - `MemoryWebmentionProvider`、provider / capability の型とエラー
83
+ - `webmention()` / `webmention()`
84
+ - `MemoryWebmentionProvider`、プロバイダーとケイパビリティの型およびエラー
111
85
  - `parseWebmentionSource`、`findTargetLink`、`verifyWebmention`、
112
86
  `createWebmentionSourceFetcher`
113
87
  - `WebmentionSection`、`getWebmentionSectionData`、`getWebmentionsForEntry`、
@@ -6,3 +6,5 @@ export type WebmentionSectionProps = {
6
6
  };
7
7
  declare const WebmentionSection: ({ mentions, heading, className, }: Readonly<WebmentionSectionProps>) => import("hono/jsx/jsx-dev-runtime").JSX.Element;
8
8
  export default WebmentionSection;
9
+ export type ArticleWebmentionSectionProps = Omit<WebmentionSectionProps, "mentions">;
10
+ export declare function ArticleWebmentionSection(props: Readonly<ArticleWebmentionSectionProps>): Promise<any[] | import("hono/utils/html").HtmlEscapedString>;
@@ -1,3 +1,75 @@
1
+ // components/webmention-section.tsx
2
+ import { resolvePlugins } from "@riebeckite/core";
3
+ import { useArticleContext, useSiteContext } from "@riebeckite/ui";
4
+
5
+ // src/provider.ts
6
+ function supportsWebmentionCapability(provider, capability) {
7
+ return provider.capabilities.has(capability);
8
+ }
9
+
10
+ // src/url.ts
11
+ function normalizeWebmentionUrl(value) {
12
+ let url;
13
+ try {
14
+ url = new URL(value);
15
+ } catch {
16
+ return null;
17
+ }
18
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
19
+ if (url.username !== "" || url.password !== "") return null;
20
+ url.hash = "";
21
+ url.hostname = url.hostname.toLowerCase();
22
+ return url.toString();
23
+ }
24
+ function resolvePublicUrl(permalink, baseUrl) {
25
+ try {
26
+ return new URL(permalink, baseUrl ?? "https://example.invalid").toString();
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ // src/targets.ts
33
+ function entryPublicUrl(entry, config) {
34
+ if (config?.site.baseUrl) {
35
+ return resolvePublicUrl(entry.permalink, config.site.baseUrl);
36
+ }
37
+ return normalizeWebmentionUrl(entry.permalink);
38
+ }
39
+
40
+ // src/server.ts
41
+ var getWebmentionSectionData = async ({
42
+ view,
43
+ slug,
44
+ config,
45
+ provider,
46
+ heading = "Mentions",
47
+ className = "rr-webmention",
48
+ limit
49
+ }) => {
50
+ const entry = view.findRoutableBySlug(slug);
51
+ if (!entry || !supportsWebmentionCapability(provider, "query")) {
52
+ return { mentions: [], heading, className };
53
+ }
54
+ const target = entryPublicUrl(entry, config);
55
+ if (target === null) return { mentions: [], heading, className };
56
+ const result = await provider.query({
57
+ type: "mentions_for_target",
58
+ target,
59
+ limit
60
+ });
61
+ return {
62
+ mentions: [...result.mentions].sort(compareMentions),
63
+ heading,
64
+ className
65
+ };
66
+ };
67
+ function compareMentions(left, right) {
68
+ return (right.publishedAt ?? right.verifiedAt).localeCompare(
69
+ left.publishedAt ?? left.verifiedAt
70
+ ) || left.source.localeCompare(right.source);
71
+ }
72
+
1
73
  // components/webmention-section.tsx
2
74
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
3
75
  var WebmentionSection = (({
@@ -40,6 +112,29 @@ var WebmentionSection = (({
40
112
  );
41
113
  });
42
114
  var webmention_section_default = WebmentionSection;
115
+ async function ArticleWebmentionSection(props) {
116
+ const site = useSiteContext();
117
+ const article = useArticleContext();
118
+ if (!site || !article?.entry) return [];
119
+ const provider = findProvider(site.config.plugins);
120
+ if (!provider) return [];
121
+ const data = await getWebmentionSectionData({
122
+ view: site.view,
123
+ slug: article.entry.slug,
124
+ config: site.config,
125
+ provider,
126
+ heading: props.heading,
127
+ className: props.className
128
+ });
129
+ return /* @__PURE__ */ jsx(WebmentionSection, { ...data });
130
+ }
131
+ function findProvider(plugins) {
132
+ const options = resolvePlugins([...plugins]).find(
133
+ (plugin) => plugin.name === "webmention"
134
+ )?.options;
135
+ return options?.provider;
136
+ }
43
137
  export {
138
+ ArticleWebmentionSection,
44
139
  webmention_section_default as default
45
140
  };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { buildWebmentionDiagnostics } from "./src/diagnostics.js";
2
2
  import type { WebmentionOptions } from "./src/types.js";
3
- export type { WebmentionSectionProps } from "./components/webmention-section.js";
4
- export { default as WebmentionSection } from "./components/webmention-section.js";
3
+ export type { ArticleWebmentionSectionProps, WebmentionSectionProps, } from "./components/webmention-section.js";
4
+ export { ArticleWebmentionSection, default as WebmentionSection, } from "./components/webmention-section.js";
5
5
  export { createWebmentionEndpoints } from "./src/endpoints.js";
6
6
  export type { WebmentionFeed, WebmentionFeedEntry, WebmentionFeedMention, } from "./src/feed.js";
7
7
  export { buildFeed, createWebmentionFeedHandler, summarizeEntry, } from "./src/feed.js";
@@ -24,8 +24,6 @@ export { normalizeWebmentionUrl, resolvePublicUrl, urlComparisonKey, } from "./s
24
24
  export type { WebmentionRejectionReason, WebmentionSourceDocument, WebmentionSourceFetcher, WebmentionSourceFetcherOptions, WebmentionVerification, } from "./src/verify.js";
25
25
  export { createWebmentionSourceFetcher, DEFAULT_WEBMENTION_MAX_BYTES, DEFAULT_WEBMENTION_TIMEOUT_MS, DEFAULT_WEBMENTION_USER_AGENT, verifyWebmention, } from "./src/verify.js";
26
26
  export type { WebmentionOptions as WebmentionPluginOptions };
27
- /** Alias matching the `*Plugin` suffix used by other plugin factories. */
28
- export declare const webmentionPlugin: typeof webmention;
29
27
  /**
30
28
  * Receives Webmentions and displays verified ones as "mentions" near articles.
31
29
  *
package/dist/index.js CHANGED
@@ -1055,6 +1055,8 @@ function compareMentions2(left, right) {
1055
1055
  }
1056
1056
 
1057
1057
  // components/webmention-section.tsx
1058
+ import { resolvePlugins } from "@riebeckite/core";
1059
+ import { useArticleContext, useSiteContext } from "@riebeckite/ui";
1058
1060
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
1059
1061
  var WebmentionSection = (({
1060
1062
  mentions,
@@ -1096,6 +1098,28 @@ var WebmentionSection = (({
1096
1098
  );
1097
1099
  });
1098
1100
  var webmention_section_default = WebmentionSection;
1101
+ async function ArticleWebmentionSection(props) {
1102
+ const site = useSiteContext();
1103
+ const article = useArticleContext();
1104
+ if (!site || !article?.entry) return [];
1105
+ const provider = findProvider(site.config.plugins);
1106
+ if (!provider) return [];
1107
+ const data = await getWebmentionSectionData({
1108
+ view: site.view,
1109
+ slug: article.entry.slug,
1110
+ config: site.config,
1111
+ provider,
1112
+ heading: props.heading,
1113
+ className: props.className
1114
+ });
1115
+ return /* @__PURE__ */ jsx(WebmentionSection, { ...data });
1116
+ }
1117
+ function findProvider(plugins) {
1118
+ const options = resolvePlugins([...plugins]).find(
1119
+ (plugin) => plugin.name === "webmention"
1120
+ )?.options;
1121
+ return options?.provider;
1122
+ }
1099
1123
 
1100
1124
  // src/render.ts
1101
1125
  import { escapeHtml, escapeHtmlAttribute } from "@riebeckite/core";
@@ -1132,7 +1156,6 @@ function formatDate(value) {
1132
1156
  }
1133
1157
 
1134
1158
  // index.ts
1135
- var webmentionPlugin = webmention;
1136
1159
  function webmention(options = {}) {
1137
1160
  const resolved = resolveWebmentionOptions(options);
1138
1161
  const provider = resolved.provider;
@@ -1221,6 +1244,7 @@ function webmention(options = {}) {
1221
1244
  });
1222
1245
  }
1223
1246
  export {
1247
+ ArticleWebmentionSection,
1224
1248
  DEFAULT_WEBMENTION_CLASS_NAME,
1225
1249
  DEFAULT_WEBMENTION_ENDPOINT,
1226
1250
  DEFAULT_WEBMENTION_HEADING_TEXT,
@@ -1266,6 +1290,5 @@ export {
1266
1290
  verifyWebmention,
1267
1291
  webmention,
1268
1292
  webmentionKey,
1269
- webmentionPlugin,
1270
1293
  webmentionTypeForRels
1271
1294
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-webmention",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,8 +21,9 @@
21
21
  "./style.css": "./style.css"
22
22
  },
23
23
  "dependencies": {
24
- "hono": "^4.12.25",
25
- "@riebeckite/core": "0.0.31"
24
+ "@riebeckite/core": "0.0.33",
25
+ "@riebeckite/ui": "0.0.33",
26
+ "hono": "^4.12.25"
26
27
  },
27
28
  "devDependencies": {
28
29
  "tsx": "^4.22.4"