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

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,24 @@ 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や他のスキームは出所リンクにしません。選択一覧は従来どおりドキュメントURLを表示し、JS配布URLは表示しません。
259
+ 埋め込みバンドルは先頭ヘッダーへ `// Author URL: https://example.org/creator`、
260
+ `// Docs URL: https://example.org/docs/`、`// Package: @example/bundle` を宣言できます。
261
+ セキュリティ確認にも、`By` / `Author` と組にした製作者リンク、HTTPSのドキュメントリンク、npmアイコンを表示します。
262
+ `Repository`・`Banner`・`Version`・日英説明も共通の表示部品を使用し、内包メンバーのnoticeからバンドル自身の情報を補いません。
262
263
  ソース先頭の宣言コメントに `// Updated: 2026-10-19T10:30:00+09:00` などの更新日時がある場合、
263
264
  記載値をそのまま表示します。`Updated at` / `Updated-at` / `UpdatedAt` / `Last updated` / `Last-updated` /
264
265
  `Last modified` / `Last-modified` も同じ項目です。カスタム `loadInfo` は `metadata.updatedAt` を返せます。
265
266
  記載がなければ日時は表示せず、HTTPの取得日時やファイル時刻で補いません。
266
267
 
267
268
  バナーはホバーまたはキーボードフォーカスで原寸プレビューを表示します。画面より大きい画像は
268
- プレビュー枠内をスクロールして確認できます。ポインターを外すかEscapeで閉じ、選択や詳細状態を保持します。
269
- メタデータの非同期取得中も実行方法を選べます。ソースの一覧内表示は引き続き「詳細」を押したときだけです。
269
+ プレビュー枠内をスクロールして確認できます。ポインターを外すかEscapeで閉じ、選択状態を保持します。
270
+ メタデータの非同期取得中も実行方法を選べます。取得処理は宣言コメントの確認に使用し、コードを実行しません。
270
271
 
271
272
  ### 機能拡張の説明文の言語
272
273
 
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,20 @@ 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 source URLs and other schemes are not linked. The selection
171
+ list keeps its documentation URL instead; it never displays the JavaScript distribution URL.
172
+
173
+ Embedded bundles can declare `// Author URL: https://example.org/creator`,
174
+ `// Docs URL: https://example.org/docs/` and `// Package: @example/bundle` in the leading
175
+ header. Security review displays the declared creator link (with `By`/`Author`), HTTPS
176
+ documentation link and npm icon. `Repository`, `Banner`, `Version` and localized descriptions
177
+ use the same package renderer. Bundle member notices do not supply the bundle's own fields.
187
178
 
188
179
  If the leading declaration comment in the source carries an update timestamp such as
189
180
  `// Updated: 2026-10-19T10:30:00+09:00`, the written value is displayed verbatim.
@@ -258,7 +249,7 @@ compatibility entries resolve for older Webpack 4 setups that do not read packag
258
249
 
259
250
  ## Selecting extensions from a Pages catalog
260
251
 
261
- `ExtensionSelectionList` shares the security dialog's banners, metadata and Details view,
252
+ `ExtensionSelectionList` shares the security dialog's banners and metadata,
262
253
  with one checkbox at the right of each row. This API supports the TurboWarp Plus library
263
254
  tracked in [#28](https://github.com/kubohiroya/turbowarp-extension-security-dialog/issues/28).
264
255
  The host fetches a versioned JSON catalog over HTTPS:
@@ -299,18 +290,34 @@ the dependency reason is also shown. The row's `data-selection-state` is `select
299
290
 
300
291
  Optional catalog categories render as semantic `h2` sections in dashboard order. Japanese
301
292
  uses `titleJa` when available, otherwise `title`; other locales use `title`. Search hides
302
- empty sections without changing selection or source controls. Catalogs without categories
293
+ empty sections without changing selection. Catalogs without categories
303
294
  keep a flat list. If categorized and uncategorized entries are mixed, the latter appear
304
295
  under “Other extensions” at the end. Theme colors use the host's modal background and
305
296
  foreground; accent overrides are `--twextsec-manual-accent`, `--twextsec-dependency-accent`,
306
297
  `--twextsec-unselected-accent`, and `--twextsec-loaded-accent`.
307
298
 
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
299
+ 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.
300
+
301
+ The selection list never loads or runs extension code. Catalog metadata renders immediately.
302
+ It shows the verified GitHub Pages `docsURL`, and GitHub/ npm icon links to the repository
303
+ and package page. Neither screen has a Details control or inline source; the JavaScript
304
+ distribution URL remains confined to the security dialog’s provenance line.
305
+ Creator names use `authorURL` only when explicitly supplied by the published package.json
306
+ `author.url`. Every row discloses its unsandboxed requirement as required, optional, not needed
307
+ or not confirmed; required mode includes a warning icon. `ExtensionSecurityDialog context="catalog"` provides permission review with cancel
311
308
  wording that returns to this list; its default `context="project"` retains project wording.
312
309
  The host must respect the committed execution modes and its own security policy.
313
310
 
311
+ For shared images, service links and execution requirements, optional `entry.metadata`
312
+ complements the fetched source metadata. If either declares `unsandboxed: required`, the
313
+ shared badge, allowed choices, invalidation and batch defaults all honor that requirement.
314
+ Security still waits for source metadata to settle (success or failure) before accepting OK;
315
+ fetching declarations never executes the code. Security uses the source-header version,
316
+ explicit creator/documentation links and source provenance; a creator without a URL stays
317
+ plain text. Selection keeps catalog versions and links. Supplied `entry.category` is rendered in either view;
318
+ category-free project entries stay flat without any catalog lookup or inferred category.
319
+
320
+
314
321
  The Pages document has the following contract (`schemaVersion: 1`):
315
322
 
316
323
  ```json
package/dist/index.cjs CHANGED
@@ -110,6 +110,9 @@ var parseExtensionMetadata = (source) => {
110
110
  description: "description",
111
111
  author: "author",
112
112
  by: "author",
113
+ authorurl: "authorURL",
114
+ "author url": "authorURL",
115
+ "author-url": "authorURL",
113
116
  version: "version",
114
117
  updated: "updatedAt",
115
118
  updatedat: "updatedAt",
@@ -122,6 +125,10 @@ var parseExtensionMetadata = (source) => {
122
125
  license: "license",
123
126
  repository: "repository",
124
127
  source: "repository",
128
+ docsurl: "docsURL",
129
+ "docs url": "docsURL",
130
+ "docs-url": "docsURL",
131
+ package: "package",
125
132
  banner: "banner",
126
133
  thumbnail: "banner",
127
134
  image: "banner",
@@ -337,7 +344,20 @@ var preferences_default = preferences;
337
344
  // src/messages.js
338
345
  var messages = {
339
346
  "en": {
347
+ "securityInstruction": "To make extensions marked \u26A0\uFE0F work as their creators intended, choose Without sandbox.",
348
+ "catalogCancelDestination": "Choose Cancel to return to the selection list.",
340
349
  "selectionIntro": "Choose extensions to add. Required extensions are selected automatically.",
350
+ "documentationFor": "{name}: Documentation",
351
+ "repositoryFor": "{name}: GitHub repository",
352
+ "npmFor": "{name}: npm package",
353
+ "creatorLabel": "Creator:",
354
+ "creatorUnknown": "Not specified",
355
+ "licenseUnknown": "Not specified",
356
+ "unsandboxedRequirement": "Without sandbox: {requirement}",
357
+ "requirement-required": "Required",
358
+ "requirement-optional": "Optional",
359
+ "requirement-not-needed": "Not needed",
360
+ "requirement-unknown": "Not confirmed",
341
361
  "selectionRequired": "Requires running without a sandbox.",
342
362
  "selectedState": "Selected by you",
343
363
  "dependencyState": "Selected automatically",
@@ -394,7 +414,20 @@ var messages = {
394
414
  "warning": "Extensions without a sandbox can access your project and browser information."
395
415
  },
396
416
  "ja": {
417
+ "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",
418
+ "catalogCancelDestination": "\u300E\u30AD\u30E3\u30F3\u30BB\u30EB\u300F\u3067\u9078\u629E\u4E00\u89A7\u306B\u623B\u308A\u307E\u3059\u3002",
397
419
  "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",
420
+ "documentationFor": "{name}: \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
421
+ "repositoryFor": "{name}: GitHub\u30EA\u30DD\u30B8\u30C8\u30EA",
422
+ "npmFor": "{name}: npm\u30D1\u30C3\u30B1\u30FC\u30B8",
423
+ "creatorLabel": "\u88FD\u4F5C\u8005\uFF1A",
424
+ "creatorUnknown": "\u672A\u8A18\u8F09",
425
+ "licenseUnknown": "\u672A\u8A18\u8F09",
426
+ "unsandboxedRequirement": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\uFF1A{requirement}",
427
+ "requirement-required": "\u5FC5\u8981",
428
+ "requirement-optional": "\u4EFB\u610F",
429
+ "requirement-not-needed": "\u4E0D\u8981",
430
+ "requirement-unknown": "\u672A\u78BA\u8A8D",
398
431
  "selectionRequired": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u3067\u306E\u5B9F\u884C\u304C\u5FC5\u8981\u3067\u3059\u3002",
399
432
  "selectedState": "\u624B\u52D5\u3067\u9078\u629E",
400
433
  "dependencyState": "\u4F9D\u5B58\u95A2\u4FC2\u3067\u81EA\u52D5\u9078\u629E",
@@ -423,7 +456,7 @@ var messages = {
423
456
  "unsandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057",
424
457
  "sandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u3042\u308A",
425
458
  "details": "\u8A73\u7D30",
426
- "author": "\u4F5C\u8005 {author}",
459
+ "author": "\u88FD\u4F5C\u8005\uFF1A{author}",
427
460
  "id": "ID: {id}",
428
461
  "license": "\u30E9\u30A4\u30BB\u30F3\u30B9: {license}",
429
462
  "updated": "\u66F4\u65B0\u65E5\u6642 {updated}",
@@ -435,7 +468,7 @@ var messages = {
435
468
  "unsupported": "\u975E\u5BFE\u5FDC",
436
469
  "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
470
  "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",
471
+ "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u88FD\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
439
472
  "summary": "\u62D2\u5426 {deny} \xB7 \u3042\u308A {sandboxed} \xB7 \u306A\u3057 {unsandboxed} \xB7 \u672A\u6307\u5B9A {unselected}",
440
473
  "cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
441
474
  "ok": "OK",
@@ -444,7 +477,7 @@ var messages = {
444
477
  "cancelTitle": "\u8AAD\u307F\u8FBC\u307F\u306E\u30AD\u30E3\u30F3\u30BB\u30EB",
445
478
  "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
479
  "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",
480
+ "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
481
  "source": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9",
449
482
  "sourceFailed": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
450
483
  "preview": "{name} \u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
@@ -464,6 +497,22 @@ var httpsURL = (value) => {
464
497
  if (url.protocol !== "https:" || url.username || url.password) throw new Error("Expected an HTTPS URL");
465
498
  return url.href;
466
499
  };
500
+ var authorURL = (value) => {
501
+ if (typeof value !== "string" || !value.trim()) throw new Error("Expected an HTTP or HTTPS author URL");
502
+ const url = new URL(value);
503
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
504
+ throw new Error("Expected an HTTP or HTTPS author URL");
505
+ }
506
+ return url.href;
507
+ };
508
+ var npmPackageName = (value) => {
509
+ if (typeof value !== "string") throw new Error("Invalid npm package name");
510
+ const name = value.trim();
511
+ if (name.length > 214 || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name) || ["node_modules", "favicon.ico"].includes(name)) {
512
+ throw new Error("Invalid npm package name");
513
+ }
514
+ return name;
515
+ };
467
516
  var resolveExtensionSelection = (entries, explicitIds = [], loadedIds = []) => {
468
517
  const byId = /* @__PURE__ */ new Map();
469
518
  for (const entry of entries) {
@@ -548,6 +597,9 @@ var parseExtensionCatalog = (data) => {
548
597
  }
549
598
  if (item.banner) metadata.banner = httpsURL(item.banner);
550
599
  if (item.repository) metadata.repository = httpsURL(item.repository);
600
+ if (item.docsURL !== void 0) metadata.docsURL = httpsURL(item.docsURL);
601
+ if (item.authorURL !== void 0) metadata.authorURL = authorURL(item.authorURL);
602
+ if (item.package !== void 0) metadata.package = npmPackageName(item.package);
551
603
  const entry = { id: item.id, ids: [item.id], url, dependencies: [...new Set(item.dependencies)], metadata };
552
604
  if (item.category !== void 0) {
553
605
  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
@@ -61,6 +61,9 @@ var parseExtensionMetadata = (source) => {
61
61
  description: "description",
62
62
  author: "author",
63
63
  by: "author",
64
+ authorurl: "authorURL",
65
+ "author url": "authorURL",
66
+ "author-url": "authorURL",
64
67
  version: "version",
65
68
  updated: "updatedAt",
66
69
  updatedat: "updatedAt",
@@ -73,6 +76,10 @@ var parseExtensionMetadata = (source) => {
73
76
  license: "license",
74
77
  repository: "repository",
75
78
  source: "repository",
79
+ docsurl: "docsURL",
80
+ "docs url": "docsURL",
81
+ "docs-url": "docsURL",
82
+ package: "package",
76
83
  banner: "banner",
77
84
  thumbnail: "banner",
78
85
  image: "banner",
@@ -288,7 +295,20 @@ var preferences_default = preferences;
288
295
  // src/messages.js
289
296
  var messages = {
290
297
  "en": {
298
+ "securityInstruction": "To make extensions marked \u26A0\uFE0F work as their creators intended, choose Without sandbox.",
299
+ "catalogCancelDestination": "Choose Cancel to return to the selection list.",
291
300
  "selectionIntro": "Choose extensions to add. Required extensions are selected automatically.",
301
+ "documentationFor": "{name}: Documentation",
302
+ "repositoryFor": "{name}: GitHub repository",
303
+ "npmFor": "{name}: npm package",
304
+ "creatorLabel": "Creator:",
305
+ "creatorUnknown": "Not specified",
306
+ "licenseUnknown": "Not specified",
307
+ "unsandboxedRequirement": "Without sandbox: {requirement}",
308
+ "requirement-required": "Required",
309
+ "requirement-optional": "Optional",
310
+ "requirement-not-needed": "Not needed",
311
+ "requirement-unknown": "Not confirmed",
292
312
  "selectionRequired": "Requires running without a sandbox.",
293
313
  "selectedState": "Selected by you",
294
314
  "dependencyState": "Selected automatically",
@@ -345,7 +365,20 @@ var messages = {
345
365
  "warning": "Extensions without a sandbox can access your project and browser information."
346
366
  },
347
367
  "ja": {
368
+ "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",
369
+ "catalogCancelDestination": "\u300E\u30AD\u30E3\u30F3\u30BB\u30EB\u300F\u3067\u9078\u629E\u4E00\u89A7\u306B\u623B\u308A\u307E\u3059\u3002",
348
370
  "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",
371
+ "documentationFor": "{name}: \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
372
+ "repositoryFor": "{name}: GitHub\u30EA\u30DD\u30B8\u30C8\u30EA",
373
+ "npmFor": "{name}: npm\u30D1\u30C3\u30B1\u30FC\u30B8",
374
+ "creatorLabel": "\u88FD\u4F5C\u8005\uFF1A",
375
+ "creatorUnknown": "\u672A\u8A18\u8F09",
376
+ "licenseUnknown": "\u672A\u8A18\u8F09",
377
+ "unsandboxedRequirement": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\uFF1A{requirement}",
378
+ "requirement-required": "\u5FC5\u8981",
379
+ "requirement-optional": "\u4EFB\u610F",
380
+ "requirement-not-needed": "\u4E0D\u8981",
381
+ "requirement-unknown": "\u672A\u78BA\u8A8D",
349
382
  "selectionRequired": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057\u3067\u306E\u5B9F\u884C\u304C\u5FC5\u8981\u3067\u3059\u3002",
350
383
  "selectedState": "\u624B\u52D5\u3067\u9078\u629E",
351
384
  "dependencyState": "\u4F9D\u5B58\u95A2\u4FC2\u3067\u81EA\u52D5\u9078\u629E",
@@ -374,7 +407,7 @@ var messages = {
374
407
  "unsandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306A\u3057",
375
408
  "sandboxed": "\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u3042\u308A",
376
409
  "details": "\u8A73\u7D30",
377
- "author": "\u4F5C\u8005 {author}",
410
+ "author": "\u88FD\u4F5C\u8005\uFF1A{author}",
378
411
  "id": "ID: {id}",
379
412
  "license": "\u30E9\u30A4\u30BB\u30F3\u30B9: {license}",
380
413
  "updated": "\u66F4\u65B0\u65E5\u6642 {updated}",
@@ -386,7 +419,7 @@ var messages = {
386
419
  "unsupported": "\u975E\u5BFE\u5FDC",
387
420
  "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
421
  "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",
422
+ "note": "\u30E1\u30BF\u30C7\u30FC\u30BF\u306F\u88FD\u4F5C\u8005\u306E\u81EA\u5DF1\u7533\u544A\u3067\u3059\u3002",
390
423
  "summary": "\u62D2\u5426 {deny} \xB7 \u3042\u308A {sandboxed} \xB7 \u306A\u3057 {unsandboxed} \xB7 \u672A\u6307\u5B9A {unselected}",
391
424
  "cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
392
425
  "ok": "OK",
@@ -395,7 +428,7 @@ var messages = {
395
428
  "cancelTitle": "\u8AAD\u307F\u8FBC\u307F\u306E\u30AD\u30E3\u30F3\u30BB\u30EB",
396
429
  "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
430
  "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",
431
+ "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
432
  "source": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9",
400
433
  "sourceFailed": "\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
401
434
  "preview": "{name} \u306E\u30D7\u30EC\u30D3\u30E5\u30FC",
@@ -415,6 +448,22 @@ var httpsURL = (value) => {
415
448
  if (url.protocol !== "https:" || url.username || url.password) throw new Error("Expected an HTTPS URL");
416
449
  return url.href;
417
450
  };
451
+ var authorURL = (value) => {
452
+ if (typeof value !== "string" || !value.trim()) throw new Error("Expected an HTTP or HTTPS author URL");
453
+ const url = new URL(value);
454
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
455
+ throw new Error("Expected an HTTP or HTTPS author URL");
456
+ }
457
+ return url.href;
458
+ };
459
+ var npmPackageName = (value) => {
460
+ if (typeof value !== "string") throw new Error("Invalid npm package name");
461
+ const name = value.trim();
462
+ if (name.length > 214 || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name) || ["node_modules", "favicon.ico"].includes(name)) {
463
+ throw new Error("Invalid npm package name");
464
+ }
465
+ return name;
466
+ };
418
467
  var resolveExtensionSelection = (entries, explicitIds = [], loadedIds = []) => {
419
468
  const byId = /* @__PURE__ */ new Map();
420
469
  for (const entry of entries) {
@@ -499,6 +548,9 @@ var parseExtensionCatalog = (data) => {
499
548
  }
500
549
  if (item.banner) metadata.banner = httpsURL(item.banner);
501
550
  if (item.repository) metadata.repository = httpsURL(item.repository);
551
+ if (item.docsURL !== void 0) metadata.docsURL = httpsURL(item.docsURL);
552
+ if (item.authorURL !== void 0) metadata.authorURL = authorURL(item.authorURL);
553
+ if (item.package !== void 0) metadata.package = npmPackageName(item.package);
502
554
  const entry = { id: item.id, ids: [item.id], url, dependencies: [...new Set(item.dependencies)], metadata };
503
555
  if (item.category !== void 0) {
504
556
  if (typeof item.category !== "string" || !categories.has(item.category)) {