@kubohiroya/turbowarp-extension-security-dialog 0.3.0 → 0.3.1

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.ja.md CHANGED
@@ -7,7 +7,7 @@ TurboWarpの複数の機能拡張を、1つの一覧で確認するための共
7
7
 
8
8
  ## Pagesのカタログから機能拡張を選択する
9
9
 
10
- `ExtensionSelectionList` は既存のバナー・メタデータ・詳細表示を共有し、各行の右端へチェックボックスを1つ表示します。
10
+ `ExtensionSelectionList` は既存のバナー・メタデータを共有し、各行の右端へチェックボックスを1つ表示します。
11
11
  TurboWarp Plus一覧の対応範囲は [#28](https://github.com/kubohiroya/turbowarp-extension-security-dialog/issues/28) に記録しています。
12
12
  ホスト側でGitHub PagesのJSONをHTTPS取得し、このコンポーネントへ渡します。
13
13
 
@@ -44,19 +44,31 @@ const list = <ExtensionSelectionList entries={entries} locale="ja"
44
44
 
45
45
  任意のカテゴリ定義がある場合、ダッシュボードと同じ順序でカテゴリ見出し(`h2`)を表示します。
46
46
  日本語は `titleJa` があれば使用し、なければ `title` にフォールバックします。
47
- 検索で空になったカテゴリは非表示になりますが、選択状態と「詳細」の対象は維持します。
47
+ 検索で空になったカテゴリは非表示になりますが、選択状態は維持します。
48
48
  カテゴリのない旧JSONは従来の平坦な一覧です。カテゴリ付きと未指定の項目が混在する場合、未指定分は
49
49
  末尾の「その他の機能拡張」へ表示します。配色はホストのモーダル背景色・文字色に従います。
50
50
  強調色は `--twextsec-manual-accent` / `--twextsec-dependency-accent` /
51
51
  `--twextsec-unselected-accent` / `--twextsec-loaded-accent` で調整できます。
52
52
 
53
53
  一覧の表示・選択だけでは機能拡張のコードを読み込み・実行しません。
54
- JSONのメタデータを直ちに表示し、「詳細」を開いたときだけソースを文字列として取得します。
55
- 取得に失敗してもカタログのメタデータと選択は利用できます。
54
+ JSONのメタデータを直ちに表示し、選択一覧には「詳細」やJS配布URLを表示しません。
55
+ 選択状態と依存元の説明はアイコン画像の下へ表示します。名前の行は「Bubble v0.15.1 (ID: kubohiroyabubble) GitHub/npmアイコン」の順で、名前に続く項目は通常の太さ・名前より2pt小さい書体です。狭い幅では必要に応じて折り返します。
56
+ ドキュメントの `docsURL`(確認済みGitHub Pages)を表示し、GitHubのOctocat・npmのアイコンから
57
+ `repository` と `package` のページを開きます。URLがない項目にリンクを推測して付けることはしません。
58
+ 製作者は `author` を表示し、公開版package.jsonの `author.url` を収録した `authorURL` があれば名前をリンクにします。
59
+ 各行には「サンドボックスなし:必要/任意/不要/未確認」を表示し、必要な場合は警告アイコンを付けます。
56
60
  実行許可確認には `ExtensionSecurityDialog context="catalog"` を使用でき、キャンセル文言が選択一覧へ戻る案内になります。
57
61
  既定の `context="project"` はプロジェクト読み込み用の文言を維持します。
58
62
  ホストは確定された実行モードと独自のセキュリティ方針を尊重して読み込んでください。
59
63
 
64
+ 共通化した画像・サービスリンク・実行条件には `entry.metadata` と取得したソースのメタデータを使用します。
65
+ どちらか一方でも `unsandboxed: required` なら必要条件を維持し、バッジ・選択可能なモード・非対応選択の解除・
66
+ 一括初期選択で同じ判断をします。セキュリティ確認のOKは、従来どおりソースのメタデータ取得の成功/失敗が確定するまで待ちます。
67
+ それ以外の表示は今回変更せず、セキュリティ確認はソースヘッダーのバージョン・リンクのない製作者名・出所URLを維持し、
68
+ 選択一覧はカタログのバージョン・明示された製作者リンク・ドキュメントURLを維持します。
69
+ `entry.category` があれば両画面で同じ順序・日英見出しにし、カテゴリのないプロジェクトでは取得や推測をせず平坦な一覧を維持します。
70
+
71
+
60
72
  JSON契約は `schemaVersion: 1` と `extensions` 配列です。各要素の必須項目は次のとおりです。
61
73
 
62
74
  | 項目 | 内容 |
@@ -68,7 +80,7 @@ JSON契約は `schemaVersion: 1` と `extensions` 配列です。各要素の必
68
80
  | `dependencies` | 同じJSONに含まれる依存先の機能拡張ID配列。npmパッケージ名ではない |
69
81
 
70
82
  `package`・`description`・`descriptions`(`ja`等の小文字言語タグをキーとする説明)・`author`・`license`・
71
- `banner`・`repository`・`unsandboxed`(例: `required`)は任意のメタデータです。
83
+ `banner`・`repository`・`docsURL`・`authorURL`・`unsandboxed`(例: `required`)は任意のメタデータです。
72
84
  完全なJSON例は[英語版のAPI例](README.md#selecting-extensions-from-a-pages-catalog)を参照してください。
73
85
  生成側でパッケージ依存を拡張IDへ変換し、ビルド専用パッケージを除外します。
74
86
 
@@ -77,7 +89,7 @@ JSON契約は `schemaVersion: 1` と `extensions` 配列です。各要素の必
77
89
  `parseExtensionCatalog` の返り値は従来どおり配列で、カテゴリ参照は各要素の
78
90
  `category: {id, title, titleJa?, order}` へ解決します。`order` は定義の配列順です。
79
91
 
80
- `parseExtensionCatalog` は未対応schema、ID/URL重複、存在しない依存先、循環依存、HTTPS以外や認証情報付きのURLを拒否します。
92
+ `parseExtensionCatalog` は未対応schema、ID/URL重複、存在しない依存先、循環依存、HTTPS以外や認証情報付きのURLを拒否します(製作者の `authorURL` だけはHTTPも許可します)。
81
93
  `resolveExtensionSelection(entries, explicitIds, loadedIds)` はReactなしでも同じ選択集合・依存順を計算します。
82
94
  返り値の `requiredBy` は依存先IDをキー、必要としている明示選択元IDの集合を値とするMapです。
83
95
  推移的依存・共有依存を含め、選択を解除すると依存元の集合も再計算します。
@@ -161,35 +173,19 @@ flowchart TB
161
173
  1回のプロジェクト読み込みにつき1つのダイアログで、読み込み待ちの拡張URLをすべて並べます。
162
174
  各行には「拒否」「サンドボックスあり」「サンドボックスなし」の3択があり、一括選択は対応する
163
175
  拡張すべてに同じ選択を適用します。見出し・一括選択・OK/キャンセルは常に見えたままで、
164
- 一覧だけがスクロールします。下図では列見出しを「あり」「なし」と省略していますが、
165
- 実際は「サンドボックスあり」「サンドボックスなし」と表示します。
176
+ 一覧だけがスクロールします。
166
177
 
167
- ```
168
- +--------------------------------------------------------------------------+
169
- | 機能拡張セキュリティ |
170
- | 実行方法を選んでください(取得中でも選択できます) |
171
- | 機能拡張の情報:2/3 件を確認 |
172
- | |
173
- | 表示中のすべてに適用: [(x) 拒否] [(o) あり] [(!) なし] |
174
- | そのモードに対応する拡張だけに適用します。 |
175
- | +----------------------------------------------------------------------+ |
176
- | | 機能拡張 | (x) 拒否 | (o) あり | (!) なし | | <- この領域だけが
177
- | +----------------------------------------------------------------------+ | スクロールする
178
- | | [バナー] Fetch | ( ) | 非対応 | (*) | |
179
- | | 作者 ... | ID: fetch | ライセンス: MIT | 更新日時 ... | |
180
- | | (!) この拡張はサンドボックス内では動作しません。 | |
181
- | | サンドボックスなし: 動作に必要 [ 詳細 ] | |
182
- | +----------------------------------------------------------------------+ |
183
- | | [バナー] Storage | (*) | ( ) | ( ) | |
184
- | | メタデータを取得中… [ 詳細 ] | |
185
- | +----------------------------------------------------------------------+ |
186
- | メタデータは作者の自己申告です。 |
187
- | サンドボックスなしの拡張は、プロジェクトやブラウザーの |
188
- | 情報にアクセスできます。 |
189
- | 拒否 1 · あり 0 · なし 1 · 未指定 1 |
190
- | [ キャンセル ] [ OK ] |
191
- +--------------------------------------------------------------------------+
192
- ```
178
+ セキュリティ確認と選択一覧は、内部のパッケージ説明コンポーネントとカテゴリ見出しの処理を共有します。
179
+ 画像を元の縦横比のまま切り取らずに表示し、GitHub/npmリンク・実行要件バッジ・渡されたカテゴリ見出しを共通化しています。
180
+ 両画面とも「詳細」ボタン・ソースコードの一覧内表示はありません。ラジオボタンとチェックボックスは説明の外側、
181
+ 選択状態バッジは共通コンポーネントのスロットに配置します。
182
+
183
+ セキュリティ確認の上部には次の案内を1回表示し、その下にメタデータ取得状況を表示します。
184
+
185
+ > ⚠️マークの表示されている拡張を制作者の意図どおりに動作させるためには『サンドボックスなし』を選択してください。
186
+
187
+ `context="catalog"` の場合だけ、フッター内のボタンより上の独立した行に「『キャンセル』で選択一覧に戻ります。」と表示します。
188
+ プロジェクト読み込みのキャンセル確認は従来の戻り先を維持し、この案内を表示しません。
193
189
 
194
190
  続いて、2種類の確認ダイアログが出ることがあります。
195
191
 
@@ -200,7 +196,7 @@ flowchart TB
200
196
  できない可能性があります。続行しますか?」「いいえ」なら一覧に戻り、何も保存しません。
201
197
 
202
198
  バナーはホバーまたはキーボードフォーカスで原寸プレビューを表示します。画面より大きい画像は
203
- プレビュー枠内をスクロールして確認できます。ソースコードを一覧内に表示するのは「詳細」だけです。
199
+ プレビュー枠内をスクロールして確認できます。両画面のサムネイルは同じ方法で、画像を切り取らずに表示します。
204
200
 
205
201
  ```mermaid
206
202
  sequenceDiagram
@@ -217,7 +213,7 @@ sequenceDiagram
217
213
  A->>A: 保存済みの既定値を読み出す
218
214
  V->>A: 包まれた_loadExtensions
219
215
  A->>D: 読み込み待ちの全件と復元した選択
220
- D-->>U: 1つの一覧、個別と一括の3択、詳細
216
+ D-->>U: 1つの一覧、個別と一括の3択
221
217
  D->>D: メタデータとバナーを非同期に取得
222
218
  U->>D: OK
223
219
  D-->>U: 拒否があるときだけ続行確認
@@ -243,9 +239,9 @@ sequenceDiagram
243
239
 
244
240
  ## 責務
245
241
 
246
- - 一覧比較UI、個別・一括の3択、確認ダイアログ、ソースの詳細表示
242
+ - 一覧比較UI、個別・一括の3択、確認ダイアログ
247
243
  - コードを実行しないメタデータ取得、実バナー/GitHub画像の候補選択、共通バナーへの切替
248
- - 画像・メタデータの非同期更新と独立した選択・詳細の状態保持
244
+ - 画像・メタデータの非同期更新と選択状態の保持
249
245
  - プロジェクト別のIndexedDB保存と初期選択の復元
250
246
  - `./turbowarp` に分離したVM互換アダプター
251
247
 
@@ -254,19 +250,20 @@ ReactとReact DOMは任意のpeer dependencyです。React UIの利用時は両
254
250
  coreとVMアダプターの利用にReact/React DOMは必要ありません。
255
251
 
256
252
  ダイアログ全体の高さをビューポート内に制限し、見出し・一括選択・OK/キャンセルを維持したまま、
257
- 一覧(詳細と注意書きを含む)だけをスクロールします。短い画面高では余白を詰めます。
253
+ 一覧(注意書きを含む)だけをスクロールします。短い画面高では余白を詰めます。
258
254
 
259
255
  ## メタデータとプレビュー
260
256
 
261
- HTTP/HTTPSの出所URLは完全なURLを表示し、別タブで開きます。埋め込み拡張や他のスキームはリンクにしません。
257
+ セキュリティ確認ではHTTP/HTTPSの出所URLを別行に完全なURLで表示し、別タブで開きます。
258
+ 埋め込み拡張や他のスキームはリンクにしません。選択一覧は従来どおりドキュメントURLを表示し、JS配布URLは表示しません。
262
259
  ソース先頭の宣言コメントに `// Updated: 2026-10-19T10:30:00+09:00` などの更新日時がある場合、
263
260
  記載値をそのまま表示します。`Updated at` / `Updated-at` / `UpdatedAt` / `Last updated` / `Last-updated` /
264
261
  `Last modified` / `Last-modified` も同じ項目です。カスタム `loadInfo` は `metadata.updatedAt` を返せます。
265
262
  記載がなければ日時は表示せず、HTTPの取得日時やファイル時刻で補いません。
266
263
 
267
264
  バナーはホバーまたはキーボードフォーカスで原寸プレビューを表示します。画面より大きい画像は
268
- プレビュー枠内をスクロールして確認できます。ポインターを外すかEscapeで閉じ、選択や詳細状態を保持します。
269
- メタデータの非同期取得中も実行方法を選べます。ソースの一覧内表示は引き続き「詳細」を押したときだけです。
265
+ プレビュー枠内をスクロールして確認できます。ポインターを外すかEscapeで閉じ、選択状態を保持します。
266
+ メタデータの非同期取得中も実行方法を選べます。取得処理は宣言コメントの確認に使用し、コードを実行しません。
270
267
 
271
268
  ### 機能拡張の説明文の言語
272
269
 
package/README.md CHANGED
@@ -85,32 +85,17 @@ three choices — **Deny**, **With sandbox**, **Without sandbox** — and a bulk
85
85
  one choice to every listed extension that supports it. The heading, the bulk row and the
86
86
  OK/Cancel buttons stay visible; only the list scrolls, so the dialog always fits the viewport.
87
87
 
88
- ```
89
- +--------------------------------------------------------------------+
90
- | Extension Security |
91
- | Choose how to run each extension (you can choose while metadata |
92
- | loads). Extension information: 2/3 |
93
- | |
94
- | Apply to all listed: [(x) Deny] [(o) With sandbox] [(!) Without] |
95
- | Applies only to extensions that support this mode. |
96
- | +----------------------------------------------------------------+ |
97
- | | Extension | (x) Deny | (o) With | (!) Without | | <- only this
98
- | +----------------------------------------------------------------+ | area scrolls
99
- | | [banner] Fetch | ( ) | Unsupported | (*) | |
100
- | | Author: ... | ID: fetch | License: MIT | Updated: ... | |
101
- | | (!) This extension cannot run inside the sandbox. | |
102
- | | Without sandbox: Required to run [ Details ] | |
103
- | +----------------------------------------------------------------+ |
104
- | | [banner] Storage | (*) | ( ) | ( ) | |
105
- | | Getting metadata... [ Details ] | |
106
- | +----------------------------------------------------------------+ |
107
- | Metadata is provided by the author. |
108
- | Extensions without a sandbox can access your project and browser |
109
- | information. |
110
- | Deny 1 · With 0 · Without 1 · Unselected 1 |
111
- | [ Cancel ] [ OK ] |
112
- +--------------------------------------------------------------------+
113
- ```
88
+ The security dialog and selection list use the same internal package-description renderer
89
+ and category-section helper. They share uncropped images at their original aspect ratio,
90
+ GitHub/npm service links, execution-requirement badges and supplied category headings.
91
+ Neither view includes a Details button or inline source code. Radio buttons and checkboxes
92
+ remain outside the description; selection-state badges use a slot in the shared renderer.
93
+
94
+ The security dialog explains the warning badge once above the list: “To make extensions
95
+ marked ⚠️ work as their creators intended, choose Without sandbox.” Metadata progress
96
+ appears below it. With `context="catalog"`, a separate footer row above the buttons says
97
+ “Choose Cancel to return to the selection list.” Project context retains its own cancellation
98
+ confirmation and does not show that destination.
114
99
 
115
100
  Two confirmation dialogs can follow:
116
101
 
@@ -121,8 +106,7 @@ Two confirmation dialogs can follow:
121
106
  when it runs. Continue?" Answering *No* returns to the list and saves nothing.
122
107
 
123
108
  Hovering or keyboard-focusing a banner opens a full-size preview; images larger than the
124
- screen scroll inside the preview frame. *Details* is the only thing that shows extension
125
- source code inline.
109
+ screen scroll inside the preview frame. Thumbnails use the same uncropped display in both views.
126
110
 
127
111
  ```mermaid
128
112
  sequenceDiagram
@@ -139,7 +123,7 @@ sequenceDiagram
139
123
  A->>A: read saved per-project defaults
140
124
  V->>A: wrapped _loadExtensions with ids and urls
141
125
  A->>D: all pending entries plus restored choices
142
- D-->>U: one list, per-row and bulk choices, Details
126
+ D-->>U: one list, per-row and bulk choices
143
127
  D->>D: metadata and banners arrive asynchronously
144
128
  U->>D: OK
145
129
  D-->>U: continue-with-denied confirmation, only if anything is denied
@@ -165,10 +149,10 @@ implementation. The author would be glad to see it made redundant by TurboWarp E
165
149
 
166
150
  ## Responsibilities
167
151
 
168
- - Comparison list UI, per-row and bulk three-way choice, confirmation dialogs, source detail view
152
+ - Comparison list UI, per-row and bulk three-way choice, confirmation dialogs
169
153
  - Metadata retrieval that never executes extension code, candidate selection between the real
170
154
  banner and GitHub images, fallback to a common banner
171
- - Asynchronous image and metadata updates that keep selection and detail state independent
155
+ - Asynchronous image and metadata updates that preserve selection
172
156
  - Per-project IndexedDB storage and restoration of initial choices
173
157
  - A VM compatibility adapter, isolated in `./turbowarp`
174
158
 
@@ -177,13 +161,14 @@ after a cancel. React and React DOM are optional peer dependencies; supply both
177
161
  the React UI. Neither is needed for the core or the VM adapter.
178
162
 
179
163
  The dialog's total height is constrained to the viewport. The heading, bulk choice and
180
- OK/Cancel stay in place while only the list — including details and notices — scrolls.
164
+ OK/Cancel stay in place while only the list — including notices — scrolls.
181
165
  Spacing tightens on short viewports.
182
166
 
183
167
  ## Metadata and previews
184
168
 
185
- HTTP/HTTPS origin URLs are shown in full and open in a new tab. Embedded extensions and other
186
- schemes are not linked.
169
+ The security dialog retains its HTTP/HTTPS source-origin URL as a separate provenance line,
170
+ opening in a new tab. Embedded extensions and other schemes are not linked. The selection
171
+ list keeps its documentation URL instead; it never displays the JavaScript distribution URL.
187
172
 
188
173
  If the leading declaration comment in the source carries an update timestamp such as
189
174
  `// Updated: 2026-10-19T10:30:00+09:00`, the written value is displayed verbatim.
@@ -258,7 +243,7 @@ compatibility entries resolve for older Webpack 4 setups that do not read packag
258
243
 
259
244
  ## Selecting extensions from a Pages catalog
260
245
 
261
- `ExtensionSelectionList` shares the security dialog's banners, metadata and Details view,
246
+ `ExtensionSelectionList` shares the security dialog's banners and metadata,
262
247
  with one checkbox at the right of each row. This API supports the TurboWarp Plus library
263
248
  tracked in [#28](https://github.com/kubohiroya/turbowarp-extension-security-dialog/issues/28).
264
249
  The host fetches a versioned JSON catalog over HTTPS:
@@ -299,18 +284,35 @@ the dependency reason is also shown. The row's `data-selection-state` is `select
299
284
 
300
285
  Optional catalog categories render as semantic `h2` sections in dashboard order. Japanese
301
286
  uses `titleJa` when available, otherwise `title`; other locales use `title`. Search hides
302
- empty sections without changing selection or source controls. Catalogs without categories
287
+ empty sections without changing selection. Catalogs without categories
303
288
  keep a flat list. If categorized and uncategorized entries are mixed, the latter appear
304
289
  under “Other extensions” at the end. Theme colors use the host's modal background and
305
290
  foreground; accent overrides are `--twextsec-manual-accent`, `--twextsec-dependency-accent`,
306
291
  `--twextsec-unselected-accent`, and `--twextsec-loaded-accent`.
307
292
 
308
- The selection list never loads or runs extension code. Catalog metadata renders immediately;
309
- only Details fetches the source as text. Source failures leave catalog metadata and selection
310
- usable. `ExtensionSecurityDialog context="catalog"` provides permission review with cancel
293
+ Package selection states and dependency reasons appear below the preview image. The heading places the name, version, parenthesized ID and GitHub/npm icons in that order. Items after the name use regular weight and a font size 2pt smaller than the name, wrapping as needed.
294
+
295
+ The selection list never loads or runs extension code. Catalog metadata renders immediately.
296
+ It shows the verified GitHub Pages `docsURL`, and GitHub/ npm icon links to the repository
297
+ and package page. Neither screen has a Details control or inline source; the JavaScript
298
+ distribution URL remains confined to the security dialog’s provenance line.
299
+ Creator names use `authorURL` only when explicitly supplied by the published package.json
300
+ `author.url`. Every row discloses its unsandboxed requirement as required, optional, not needed
301
+ or not confirmed; required mode includes a warning icon. `ExtensionSecurityDialog context="catalog"` provides permission review with cancel
311
302
  wording that returns to this list; its default `context="project"` retains project wording.
312
303
  The host must respect the committed execution modes and its own security policy.
313
304
 
305
+ For shared images, service links and execution requirements, optional `entry.metadata`
306
+ complements the fetched source metadata. If either declares `unsandboxed: required`, the
307
+ shared badge, allowed choices, invalidation and batch defaults all honor that requirement.
308
+ Security still waits for source metadata to settle (success or failure) before accepting OK;
309
+ fetching declarations never executes the code. Its source-header version, plain creator
310
+ label and source provenance stay as before. Selection keeps catalog versions, explicit
311
+ creator links and documentation links. Those unrequested presentation differences are not
312
+ changed by the shared renderer. Supplied `entry.category` is rendered in either view;
313
+ category-free project entries stay flat without any catalog lookup or inferred category.
314
+
315
+
314
316
  The Pages document has the following contract (`schemaVersion: 1`):
315
317
 
316
318
  ```json
package/dist/index.cjs CHANGED
@@ -337,7 +337,20 @@ var preferences_default = preferences;
337
337
  // src/messages.js
338
338
  var messages = {
339
339
  "en": {
340
+ "securityInstruction": "To make extensions marked \u26A0\uFE0F work as their creators intended, choose Without sandbox.",
341
+ "catalogCancelDestination": "Choose Cancel to return to the selection list.",
340
342
  "selectionIntro": "Choose extensions to add. Required extensions are selected automatically.",
343
+ "documentationFor": "{name}: Documentation",
344
+ "repositoryFor": "{name}: GitHub repository",
345
+ "npmFor": "{name}: npm package",
346
+ "creatorLabel": "Creator:",
347
+ "creatorUnknown": "Not specified",
348
+ "licenseUnknown": "Not specified",
349
+ "unsandboxedRequirement": "Without sandbox: {requirement}",
350
+ "requirement-required": "Required",
351
+ "requirement-optional": "Optional",
352
+ "requirement-not-needed": "Not needed",
353
+ "requirement-unknown": "Not confirmed",
341
354
  "selectionRequired": "Requires running without a sandbox.",
342
355
  "selectedState": "Selected by you",
343
356
  "dependencyState": "Selected automatically",
@@ -394,7 +407,20 @@ var messages = {
394
407
  "warning": "Extensions without a sandbox can access your project and browser information."
395
408
  },
396
409
  "ja": {
410
+ "securityInstruction": "\u26A0\uFE0F\u30DE\u30FC\u30AF\u306E\u8868\u793A\u3055\u308C\u3066\u3044\u308B\u62E1\u5F35\u3092\u5236\u4F5C\u8005\u306E\u610F\u56F3\u3069\u304A\u308A\u306B\u52D5\u4F5C\u3055\u305B\u308B\u305F\u3081\u306B\u306F\u300E\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u300F\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
411
+ "catalogCancelDestination": "\u300E\u30AD\u30E3\u30F3\u30BB\u30EB\u300F\u3067\u9078\u629E\u4E00\u89A7\u306B\u623B\u308A\u307E\u3059\u3002",
397
412
  "selectionIntro": "\u8FFD\u52A0\u3059\u308B\u6A5F\u80FD\u62E1\u5F35\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002\u4F9D\u5B58\u3059\u308B\u6A5F\u80FD\u62E1\u5F35\u3082\u81EA\u52D5\u3067\u9078\u629E\u3055\u308C\u307E\u3059\u3002",
413
+ "documentationFor": "{name}: \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
414
+ "repositoryFor": "{name}: GitHub\u30EA\u30DD\u30B8\u30C8\u30EA",
415
+ "npmFor": "{name}: npm\u30D1\u30C3\u30B1\u30FC\u30B8",
416
+ "creatorLabel": "\u88FD\u4F5C\u8005\uFF1A",
417
+ "creatorUnknown": "\u672A\u8A18\u8F09",
418
+ "licenseUnknown": "\u672A\u8A18\u8F09",
419
+ "unsandboxedRequirement": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\uFF1A{requirement}",
420
+ "requirement-required": "\u5FC5\u8981",
421
+ "requirement-optional": "\u4EFB\u610F",
422
+ "requirement-not-needed": "\u4E0D\u8981",
423
+ "requirement-unknown": "\u672A\u78BA\u8A8D",
398
424
  "selectionRequired": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u3067\u306E\u5B9F\u884C\u304C\u5FC5\u8981\u3067\u3059\u3002",
399
425
  "selectedState": "\u624B\u52D5\u3067\u9078\u629E",
400
426
  "dependencyState": "\u4F9D\u5B58\u95A2\u4FC2\u3067\u81EA\u52D5\u9078\u629E",
@@ -423,7 +449,7 @@ var messages = {
423
449
  "unsandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057",
424
450
  "sandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u3042\u308A",
425
451
  "details": "\u8A73\u7D30",
426
- "author": "\u4F5C\u8005 {author}",
452
+ "author": "\u88FD\u4F5C\u8005\uFF1A{author}",
427
453
  "id": "ID: {id}",
428
454
  "license": "\u30E9\u30A4\u30BB\u30F3\u30B9: {license}",
429
455
  "updated": "\u66F4\u65B0\u65E5\u6642 {updated}",
@@ -435,7 +461,7 @@ var messages = {
435
461
  "unsupported": "\u975E\u5BFE\u5FDC",
436
462
  "choiceReset": "\u975E\u5BFE\u5FDC\u306E\u9078\u629E\u3092\u89E3\u9664\u3057\u307E\u3057\u305F\u3002\u5B9F\u884C\u65B9\u6CD5\u3092\u9078\u3073\u76F4\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
437
463
  "bulkNote": "\u305D\u306E\u30E2\u30FC\u30C9\u306B\u5BFE\u5FDC\u3059\u308B\u62E1\u5F35\u3060\u3051\u306B\u9069\u7528\u3057\u307E\u3059\u3002",
438
- "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
464
+ "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u88FD\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
439
465
  "summary": "\u62D2\u5426 {deny} \xB7 \u3042\u308A {sandboxed} \xB7 \u306A\u3057 {unsandboxed} \xB7 \u672A\u6307\u5B9A {unselected}",
440
466
  "cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
441
467
  "ok": "OK",
@@ -444,7 +470,7 @@ var messages = {
444
470
  "cancelTitle": "\u8AAD\u307F\u8FBC\u307F\u306E\u30AD\u30E3\u30F3\u30BB\u30EB",
445
471
  "cancelQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u8AAD\u307F\u8FBC\u307F\u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u3001TurboWarp Editor\u306E\u521D\u671F\u753B\u9762\u306B\u623B\u308A\u307E\u3059\u3002\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3059\u304B\uFF1F",
446
472
  "denyTitle": "\u6A5F\u80FD\u62E1\u5F35\u3092\u62D2\u5426\u3057\u3066\u7D9A\u884C",
447
- "denyQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u5B9F\u884C\u3092\u3059\u308B\u969B\u306B\u3001\u4F5C\u8005\u304C\u610F\u56F3\u3057\u305F\u52D5\u4F5C\u3092\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
473
+ "denyQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u5B9F\u884C\u3092\u3059\u308B\u969B\u306B\u3001\u88FD\u4F5C\u8005\u304C\u610F\u56F3\u3057\u305F\u52D5\u4F5C\u3092\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
448
474
  "source": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9",
449
475
  "sourceFailed": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
450
476
  "preview": "{name} \u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
@@ -464,6 +490,22 @@ var httpsURL = (value) => {
464
490
  if (url.protocol !== "https:" || url.username || url.password) throw new Error("Expected an HTTPS URL");
465
491
  return url.href;
466
492
  };
493
+ var authorURL = (value) => {
494
+ if (typeof value !== "string" || !value.trim()) throw new Error("Expected an HTTP or HTTPS author URL");
495
+ const url = new URL(value);
496
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
497
+ throw new Error("Expected an HTTP or HTTPS author URL");
498
+ }
499
+ return url.href;
500
+ };
501
+ var npmPackageName = (value) => {
502
+ if (typeof value !== "string") throw new Error("Invalid npm package name");
503
+ const name = value.trim();
504
+ if (name.length > 214 || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name) || ["node_modules", "favicon.ico"].includes(name)) {
505
+ throw new Error("Invalid npm package name");
506
+ }
507
+ return name;
508
+ };
467
509
  var resolveExtensionSelection = (entries, explicitIds = [], loadedIds = []) => {
468
510
  const byId = /* @__PURE__ */ new Map();
469
511
  for (const entry of entries) {
@@ -548,6 +590,9 @@ var parseExtensionCatalog = (data) => {
548
590
  }
549
591
  if (item.banner) metadata.banner = httpsURL(item.banner);
550
592
  if (item.repository) metadata.repository = httpsURL(item.repository);
593
+ if (item.docsURL !== void 0) metadata.docsURL = httpsURL(item.docsURL);
594
+ if (item.authorURL !== void 0) metadata.authorURL = authorURL(item.authorURL);
595
+ if (item.package !== void 0) metadata.package = npmPackageName(item.package);
551
596
  const entry = { id: item.id, ids: [item.id], url, dependencies: [...new Set(item.dependencies)], metadata };
552
597
  if (item.category !== void 0) {
553
598
  if (typeof item.category !== "string" || !categories.has(item.category)) {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  export type Decision = 'deny' | 'sandboxed' | 'unsandboxed';
2
- export interface ExtensionEntry { url: string; ids: string[]; banner?: string; }
2
+ export interface ExtensionEntry {
3
+ url: string; ids: string[]; banner?: string; metadata?: ExtensionMetadata; category?: ExtensionCategory;
4
+ }
3
5
  export interface ExtensionMetadata {
4
6
  name?: string; id?: string; description?: string; author?: string; version?: string; updatedAt?: string;
5
7
  license?: string; repository?: string; banner?: string; unsandboxed?: string;
8
+ docsURL?: string; package?: string; authorURL?: string;
6
9
  descriptions?: Record<string, string>;
7
10
  }
8
11
  export interface ExtensionInfo { source: string; metadata: ExtensionMetadata; }
package/dist/index.mjs CHANGED
@@ -288,7 +288,20 @@ var preferences_default = preferences;
288
288
  // src/messages.js
289
289
  var messages = {
290
290
  "en": {
291
+ "securityInstruction": "To make extensions marked \u26A0\uFE0F work as their creators intended, choose Without sandbox.",
292
+ "catalogCancelDestination": "Choose Cancel to return to the selection list.",
291
293
  "selectionIntro": "Choose extensions to add. Required extensions are selected automatically.",
294
+ "documentationFor": "{name}: Documentation",
295
+ "repositoryFor": "{name}: GitHub repository",
296
+ "npmFor": "{name}: npm package",
297
+ "creatorLabel": "Creator:",
298
+ "creatorUnknown": "Not specified",
299
+ "licenseUnknown": "Not specified",
300
+ "unsandboxedRequirement": "Without sandbox: {requirement}",
301
+ "requirement-required": "Required",
302
+ "requirement-optional": "Optional",
303
+ "requirement-not-needed": "Not needed",
304
+ "requirement-unknown": "Not confirmed",
292
305
  "selectionRequired": "Requires running without a sandbox.",
293
306
  "selectedState": "Selected by you",
294
307
  "dependencyState": "Selected automatically",
@@ -345,7 +358,20 @@ var messages = {
345
358
  "warning": "Extensions without a sandbox can access your project and browser information."
346
359
  },
347
360
  "ja": {
361
+ "securityInstruction": "\u26A0\uFE0F\u30DE\u30FC\u30AF\u306E\u8868\u793A\u3055\u308C\u3066\u3044\u308B\u62E1\u5F35\u3092\u5236\u4F5C\u8005\u306E\u610F\u56F3\u3069\u304A\u308A\u306B\u52D5\u4F5C\u3055\u305B\u308B\u305F\u3081\u306B\u306F\u300E\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u300F\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
362
+ "catalogCancelDestination": "\u300E\u30AD\u30E3\u30F3\u30BB\u30EB\u300F\u3067\u9078\u629E\u4E00\u89A7\u306B\u623B\u308A\u307E\u3059\u3002",
348
363
  "selectionIntro": "\u8FFD\u52A0\u3059\u308B\u6A5F\u80FD\u62E1\u5F35\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002\u4F9D\u5B58\u3059\u308B\u6A5F\u80FD\u62E1\u5F35\u3082\u81EA\u52D5\u3067\u9078\u629E\u3055\u308C\u307E\u3059\u3002",
364
+ "documentationFor": "{name}: \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
365
+ "repositoryFor": "{name}: GitHub\u30EA\u30DD\u30B8\u30C8\u30EA",
366
+ "npmFor": "{name}: npm\u30D1\u30C3\u30B1\u30FC\u30B8",
367
+ "creatorLabel": "\u88FD\u4F5C\u8005\uFF1A",
368
+ "creatorUnknown": "\u672A\u8A18\u8F09",
369
+ "licenseUnknown": "\u672A\u8A18\u8F09",
370
+ "unsandboxedRequirement": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\uFF1A{requirement}",
371
+ "requirement-required": "\u5FC5\u8981",
372
+ "requirement-optional": "\u4EFB\u610F",
373
+ "requirement-not-needed": "\u4E0D\u8981",
374
+ "requirement-unknown": "\u672A\u78BA\u8A8D",
349
375
  "selectionRequired": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u3067\u306E\u5B9F\u884C\u304C\u5FC5\u8981\u3067\u3059\u3002",
350
376
  "selectedState": "\u624B\u52D5\u3067\u9078\u629E",
351
377
  "dependencyState": "\u4F9D\u5B58\u95A2\u4FC2\u3067\u81EA\u52D5\u9078\u629E",
@@ -374,7 +400,7 @@ var messages = {
374
400
  "unsandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057",
375
401
  "sandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u3042\u308A",
376
402
  "details": "\u8A73\u7D30",
377
- "author": "\u4F5C\u8005 {author}",
403
+ "author": "\u88FD\u4F5C\u8005\uFF1A{author}",
378
404
  "id": "ID: {id}",
379
405
  "license": "\u30E9\u30A4\u30BB\u30F3\u30B9: {license}",
380
406
  "updated": "\u66F4\u65B0\u65E5\u6642 {updated}",
@@ -386,7 +412,7 @@ var messages = {
386
412
  "unsupported": "\u975E\u5BFE\u5FDC",
387
413
  "choiceReset": "\u975E\u5BFE\u5FDC\u306E\u9078\u629E\u3092\u89E3\u9664\u3057\u307E\u3057\u305F\u3002\u5B9F\u884C\u65B9\u6CD5\u3092\u9078\u3073\u76F4\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
388
414
  "bulkNote": "\u305D\u306E\u30E2\u30FC\u30C9\u306B\u5BFE\u5FDC\u3059\u308B\u62E1\u5F35\u3060\u3051\u306B\u9069\u7528\u3057\u307E\u3059\u3002",
389
- "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
415
+ "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u88FD\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
390
416
  "summary": "\u62D2\u5426 {deny} \xB7 \u3042\u308A {sandboxed} \xB7 \u306A\u3057 {unsandboxed} \xB7 \u672A\u6307\u5B9A {unselected}",
391
417
  "cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
392
418
  "ok": "OK",
@@ -395,7 +421,7 @@ var messages = {
395
421
  "cancelTitle": "\u8AAD\u307F\u8FBC\u307F\u306E\u30AD\u30E3\u30F3\u30BB\u30EB",
396
422
  "cancelQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u8AAD\u307F\u8FBC\u307F\u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u3001TurboWarp Editor\u306E\u521D\u671F\u753B\u9762\u306B\u623B\u308A\u307E\u3059\u3002\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3059\u304B\uFF1F",
397
423
  "denyTitle": "\u6A5F\u80FD\u62E1\u5F35\u3092\u62D2\u5426\u3057\u3066\u7D9A\u884C",
398
- "denyQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u5B9F\u884C\u3092\u3059\u308B\u969B\u306B\u3001\u4F5C\u8005\u304C\u610F\u56F3\u3057\u305F\u52D5\u4F5C\u3092\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
424
+ "denyQuestion": "\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u5B9F\u884C\u3092\u3059\u308B\u969B\u306B\u3001\u88FD\u4F5C\u8005\u304C\u610F\u56F3\u3057\u305F\u52D5\u4F5C\u3092\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
399
425
  "source": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9",
400
426
  "sourceFailed": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
401
427
  "preview": "{name} \u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
@@ -415,6 +441,22 @@ var httpsURL = (value) => {
415
441
  if (url.protocol !== "https:" || url.username || url.password) throw new Error("Expected an HTTPS URL");
416
442
  return url.href;
417
443
  };
444
+ var authorURL = (value) => {
445
+ if (typeof value !== "string" || !value.trim()) throw new Error("Expected an HTTP or HTTPS author URL");
446
+ const url = new URL(value);
447
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
448
+ throw new Error("Expected an HTTP or HTTPS author URL");
449
+ }
450
+ return url.href;
451
+ };
452
+ var npmPackageName = (value) => {
453
+ if (typeof value !== "string") throw new Error("Invalid npm package name");
454
+ const name = value.trim();
455
+ if (name.length > 214 || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name) || ["node_modules", "favicon.ico"].includes(name)) {
456
+ throw new Error("Invalid npm package name");
457
+ }
458
+ return name;
459
+ };
418
460
  var resolveExtensionSelection = (entries, explicitIds = [], loadedIds = []) => {
419
461
  const byId = /* @__PURE__ */ new Map();
420
462
  for (const entry of entries) {
@@ -499,6 +541,9 @@ var parseExtensionCatalog = (data) => {
499
541
  }
500
542
  if (item.banner) metadata.banner = httpsURL(item.banner);
501
543
  if (item.repository) metadata.repository = httpsURL(item.repository);
544
+ if (item.docsURL !== void 0) metadata.docsURL = httpsURL(item.docsURL);
545
+ if (item.authorURL !== void 0) metadata.authorURL = authorURL(item.authorURL);
546
+ if (item.package !== void 0) metadata.package = npmPackageName(item.package);
502
547
  const entry = { id: item.id, ids: [item.id], url, dependencies: [...new Set(item.dependencies)], metadata };
503
548
  if (item.category !== void 0) {
504
549
  if (typeof item.category !== "string" || !categories.has(item.category)) {