@delta-comic/plugin 2.2.0 → 3.0.0-next.5

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.
Files changed (62) hide show
  1. package/dist/global-3ZqxvD1L.mjs +1446 -0
  2. package/dist/global-3ZqxvD1L.mjs.map +1 -0
  3. package/dist/lib/index.d.mts +927 -0
  4. package/dist/lib/index.mjs +535 -0
  5. package/dist/lib/index.mjs.map +1 -0
  6. package/dist/native-CHH-Olpj.mjs +63 -0
  7. package/dist/native-CHH-Olpj.mjs.map +1 -0
  8. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  9. package/dist/runtime-C6MmbYYH.mjs +663 -0
  10. package/dist/runtime-C6MmbYYH.mjs.map +1 -0
  11. package/dist/storage-DMKDlPzl.mjs +351 -0
  12. package/dist/storage-DMKDlPzl.mjs.map +1 -0
  13. package/dist/vite/index.d.mts +33 -0
  14. package/dist/vite/index.mjs +81 -0
  15. package/dist/vite/index.mjs.map +1 -0
  16. package/package.json +30 -37
  17. package/README.md +0 -42
  18. package/dist/core-BF7YwnuZ.js +0 -2
  19. package/dist/core-Czlf2w3A.js +0 -521
  20. package/dist/core-Czlf2w3A.js.map +0 -1
  21. package/dist/index.css +0 -3
  22. package/dist/index.js +0 -6764
  23. package/dist/index.js.map +0 -1
  24. package/dist/lib/config.d.ts +0 -71
  25. package/dist/lib/depends.d.ts +0 -10
  26. package/dist/lib/driver/booter.d.ts +0 -4
  27. package/dist/lib/driver/core.d.ts +0 -67
  28. package/dist/lib/driver/icon.d.ts +0 -3
  29. package/dist/lib/driver/index.d.ts +0 -4
  30. package/dist/lib/driver/init/booter/0_configSetter.d.ts +0 -8
  31. package/dist/lib/driver/init/booter/10_apiTest.d.ts +0 -9
  32. package/dist/lib/driver/init/booter/20_resourceTest.d.ts +0 -8
  33. package/dist/lib/driver/init/booter/30_boot.d.ts +0 -8
  34. package/dist/lib/driver/init/booter/40_auth.d.ts +0 -8
  35. package/dist/lib/driver/init/booter/50_otherProcess.d.ts +0 -8
  36. package/dist/lib/driver/init/booter/utils.d.ts +0 -3
  37. package/dist/lib/driver/init/installer/10_normalUrl.d.ts +0 -12
  38. package/dist/lib/driver/init/installer/20_local.d.ts +0 -12
  39. package/dist/lib/driver/init/installer/30_dev.d.ts +0 -12
  40. package/dist/lib/driver/init/installer/40_github.d.ts +0 -12
  41. package/dist/lib/driver/init/installer/9999_awesome.d.ts +0 -12
  42. package/dist/lib/driver/init/loader/1_userscript.d.ts +0 -11
  43. package/dist/lib/driver/init/loader/2_zip.d.ts +0 -11
  44. package/dist/lib/driver/init/utils.d.ts +0 -29
  45. package/dist/lib/driver/install.d.ts +0 -14
  46. package/dist/lib/driver/loader.d.ts +0 -5
  47. package/dist/lib/driver/store.d.ts +0 -37
  48. package/dist/lib/env/Inject.vue.d.ts +0 -18
  49. package/dist/lib/env/index.d.ts +0 -12
  50. package/dist/lib/global.d.ts +0 -26
  51. package/dist/lib/index.d.ts +0 -6
  52. package/dist/lib/plugin/api.d.ts +0 -8
  53. package/dist/lib/plugin/auth.d.ts +0 -15
  54. package/dist/lib/plugin/content.d.ts +0 -8
  55. package/dist/lib/plugin/index.d.ts +0 -58
  56. package/dist/lib/plugin/otherProgress.d.ts +0 -4
  57. package/dist/lib/plugin/resource.d.ts +0 -5
  58. package/dist/lib/plugin/search.d.ts +0 -75
  59. package/dist/lib/plugin/share.d.ts +0 -26
  60. package/dist/lib/plugin/subscribe.d.ts +0 -15
  61. package/dist/lib/plugin/user.d.ts +0 -47
  62. package/dist/pack.tgz +0 -0
@@ -0,0 +1,535 @@
1
+ import { A as pluginI18n, B as require, C as findCyclePaths, D as pluginStore, E as PluginStore, F as declareDepType, G as definePlugin, H as useConfig, I as defaultDependencyRegistry, L as exposeSymbol, M as translatePluginText, N as DependencyRegistry, O as usePluginStore, P as createDependencyRegistry, R as pluginExposes, S as synchronizeBuiltInPlugins, T as planPluginLoadOrder, U as ConfigPointer, V as ConfigStore, W as defineInnerPlugin, _ as runtimeExtensions, a as PluginLoader, b as isBuiltInPlugin, c as filterPluginsBySelection, d as loader_exports, f as booter_exports, g as registerLoader, h as registerInstaller, i as PluginInstaller, j as pluginMessageKey, k as PluginI18nRegistry, l as pluginKind, m as registerBooter, n as install_exports, o as pluginRuntime, p as PluginRuntimeExtensions, r as PluginBooter, s as failedDependencies, t as Global, u as selectPluginsForPhase, v as BUILT_IN_PLUGIN_LOADER, w as formatPluginLoadPlanError, x as isBuiltInPluginName, y as builtInDefinitionToArchive, z as provide } from "../global-3ZqxvD1L.mjs";
2
+ import { t as createPluginAssetUrl } from "../storage-DMKDlPzl.mjs";
3
+ import { logger } from "@delta-comic/logger";
4
+ import semver from "semver";
5
+ import ky from "ky";
6
+ //#region lib/manifest.ts
7
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8
+ const requiredRecord = (value, path) => {
9
+ if (!isRecord$1(value)) throw new PluginManifestError(`${path} must be an object`);
10
+ return value;
11
+ };
12
+ const requiredString = (value, path) => {
13
+ if (typeof value !== "string" || value.length === 0) throw new PluginManifestError(`${path} must be a non-empty string`);
14
+ return value;
15
+ };
16
+ const optionalPath = (value, path) => {
17
+ const text = requiredString(value, path);
18
+ const normalized = text.replaceAll("\\", "/");
19
+ if (normalized.startsWith("/") || normalized.split("/").includes("..")) throw new PluginManifestError(`${path} must be a safe relative path`);
20
+ return text;
21
+ };
22
+ const decodeLocalIconPath = (value, path) => {
23
+ let decoded = value;
24
+ for (let index = 0; index < 5; index += 1) {
25
+ let next;
26
+ try {
27
+ next = decodeURIComponent(decoded);
28
+ } catch {
29
+ throw new PluginManifestError(`${path} must be a valid URL path`);
30
+ }
31
+ if (next === decoded) break;
32
+ decoded = next;
33
+ }
34
+ const normalized = decoded.replaceAll("\\", "/");
35
+ const segments = normalized.split("/");
36
+ if (!normalized || normalized.startsWith("/") || /^[a-z]:($|\/)/i.test(normalized) || normalized.includes("\0") || segments.some((segment) => segment === "..")) throw new PluginManifestError(`${path} must be a safe relative path`);
37
+ return segments.filter((segment) => segment && segment !== ".").join("/");
38
+ };
39
+ /**
40
+ * Accepts a credential-free HTTP(S) URL or a safe path inside the plugin archive.
41
+ * Query strings and fragments are kept for both forms.
42
+ */
43
+ const parsePluginIconReference = (value, path = "manifest.icon") => {
44
+ const text = requiredString(value, path).trim();
45
+ if (!text) throw new PluginManifestError(`${path} must be a non-empty string`);
46
+ if (/^[a-z][a-z\d+.-]*:/i.test(text) || text.startsWith("//")) {
47
+ let url;
48
+ try {
49
+ url = new URL(text);
50
+ } catch {
51
+ throw new PluginManifestError(`${path} must be an HTTP(S) URL or a safe relative path`);
52
+ }
53
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new PluginManifestError(`${path} must be a credential-free HTTP(S) URL or a safe relative path`);
54
+ return {
55
+ type: "remote",
56
+ url: text
57
+ };
58
+ }
59
+ const rawPath = text.split(/[?#]/, 1)[0] ?? "";
60
+ const resolvedPath = decodeLocalIconPath(rawPath, path);
61
+ if (!resolvedPath) throw new PluginManifestError(`${path} must be a safe relative path`);
62
+ const fragmentIndex = text.indexOf("#");
63
+ return {
64
+ fragment: fragmentIndex < 0 ? "" : text.slice(fragmentIndex),
65
+ path: resolvedPath,
66
+ type: "local"
67
+ };
68
+ };
69
+ var PluginManifestError = class extends Error {
70
+ constructor(message) {
71
+ super(`Invalid Delta Comic manifest: ${message}`);
72
+ this.name = "PluginManifestError";
73
+ }
74
+ };
75
+ /**
76
+ * Validates the exact manifest format emitted by the `deltaComic` Vite plugin.
77
+ * The returned value is safe to use as the persisted plugin metadata shape.
78
+ */
79
+ const parsePluginManifest = (value) => {
80
+ const manifest = requiredRecord(value, "manifest");
81
+ const name = requiredRecord(manifest.name, "manifest.name");
82
+ const version = requiredRecord(manifest.version, "manifest.version");
83
+ const id = requiredString(name.id, "manifest.name.id");
84
+ if (id === "." || id === ".." || /[\\/]/.test(id)) throw new PluginManifestError("manifest.name.id contains an unsafe path segment");
85
+ const requireValue = manifest.require;
86
+ if (!Array.isArray(requireValue)) throw new PluginManifestError("manifest.require must be an array");
87
+ const require = requireValue.map((dependency, index) => {
88
+ const record = requiredRecord(dependency, `manifest.require[${index}]`);
89
+ const download = record.download;
90
+ if (download !== void 0 && typeof download !== "string") throw new PluginManifestError(`manifest.require[${index}].download must be a string`);
91
+ return {
92
+ id: requiredString(record.id, `manifest.require[${index}].id`),
93
+ ...download === void 0 ? {} : { download }
94
+ };
95
+ });
96
+ const result = {
97
+ author: requiredString(manifest.author, "manifest.author"),
98
+ description: requiredString(manifest.description, "manifest.description"),
99
+ name: {
100
+ display: requiredString(name.display, "manifest.name.display"),
101
+ id
102
+ },
103
+ require,
104
+ version: {
105
+ plugin: requiredString(version.plugin, "manifest.version.plugin"),
106
+ supportCore: requiredString(version.supportCore, "manifest.version.supportCore")
107
+ }
108
+ };
109
+ if (manifest.icon !== void 0) {
110
+ const icon = parsePluginIconReference(manifest.icon);
111
+ result.icon = icon.type === "remote" ? icon.url : requiredString(manifest.icon, "manifest.icon").trim();
112
+ }
113
+ if (manifest.entry !== void 0) {
114
+ const entry = requiredRecord(manifest.entry, "manifest.entry");
115
+ result.entry = {
116
+ jsPath: optionalPath(entry.jsPath, "manifest.entry.jsPath"),
117
+ ...entry.cssPath === void 0 ? {} : { cssPath: optionalPath(entry.cssPath, "manifest.entry.cssPath") }
118
+ };
119
+ }
120
+ if (manifest.kind !== void 0) {
121
+ if (manifest.kind !== "normal" && manifest.kind !== "preboot") throw new PluginManifestError("manifest.kind must be \"normal\" or \"preboot\"");
122
+ result.kind = manifest.kind;
123
+ }
124
+ if (manifest.integrity !== void 0) {
125
+ const integrity = requiredRecord(manifest.integrity, "manifest.integrity");
126
+ if (integrity.algorithm !== "blake3" && integrity.algorithm !== "sha256") throw new PluginManifestError("manifest.integrity.algorithm is unsupported");
127
+ result.integrity = {
128
+ algorithm: integrity.algorithm,
129
+ digest: requiredString(integrity.digest, "manifest.integrity.digest")
130
+ };
131
+ }
132
+ return result;
133
+ };
134
+ const isPluginManifestCompatible = (manifest, coreVersion) => semver.satisfies(coreVersion, manifest.version.supportCore);
135
+ //#endregion
136
+ //#region lib/marketplace/types.ts
137
+ const AWESOME_REGISTRY_BASE_URL = "https://raw.githubusercontent.com/delta-comic/awesome-plugins/main/";
138
+ const AWESOME_REGISTRY_INDEX_PATH = "registry/index.json";
139
+ const AWESOME_REGISTRY_SCHEMA_VERSION = 1;
140
+ //#endregion
141
+ //#region lib/marketplace/validation.ts
142
+ const PAGE_PATH_PATTERN = /^registry\/pages\/[1-9][0-9]*\.json$/;
143
+ const PLUGIN_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
144
+ const GITHUB_LOGIN_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
145
+ const GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
146
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
147
+ const record = (value, path) => {
148
+ if (!isRecord(value)) throw new AwesomeRegistryValidationError(`${path} must be an object`);
149
+ return value;
150
+ };
151
+ const exactKeys = (value, allowed, path) => {
152
+ const extras = Object.keys(value).filter((key) => !allowed.includes(key));
153
+ if (extras.length > 0) throw new AwesomeRegistryValidationError(`${path} has unknown fields: ${extras.join(", ")}`);
154
+ };
155
+ const string = (value, path) => {
156
+ if (typeof value !== "string" || value.length === 0) throw new AwesomeRegistryValidationError(`${path} must be a non-empty string`);
157
+ return value;
158
+ };
159
+ const integer = (value, path, minimum, maximum = Infinity) => {
160
+ if (!Number.isInteger(value) || value < minimum || value > maximum) throw new AwesomeRegistryValidationError(`${path} must be an integer between ${minimum} and ${maximum}`);
161
+ return value;
162
+ };
163
+ const schemaVersion = (value, path) => {
164
+ if (value !== 1) throw new AwesomeRegistryVersionError(value, path);
165
+ return 1;
166
+ };
167
+ const path = (value, field) => {
168
+ const result = string(value, field);
169
+ if (!PAGE_PATH_PATTERN.test(result)) throw new AwesomeRegistryValidationError(`${field} must be a registry page path`);
170
+ return result;
171
+ };
172
+ const httpUrl = (value, field) => {
173
+ const result = string(value, field);
174
+ let parsed;
175
+ try {
176
+ parsed = new URL(result);
177
+ } catch {
178
+ throw new AwesomeRegistryValidationError(`${field} must be an absolute URL`);
179
+ }
180
+ if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password) throw new AwesomeRegistryValidationError(`${field} must be a credential-free HTTP(S) URL`);
181
+ return result;
182
+ };
183
+ const dateTime = (value, field) => {
184
+ const result = string(value, field);
185
+ if (!/^\d{4}-\d{2}-\d{2}T/.test(result) || Number.isNaN(Date.parse(result))) throw new AwesomeRegistryValidationError(`${field} must be an ISO date-time`);
186
+ return result;
187
+ };
188
+ const parsePageReference = (value, field) => {
189
+ const item = record(value, field);
190
+ exactKeys(item, [
191
+ "page",
192
+ "items",
193
+ "path"
194
+ ], field);
195
+ return {
196
+ page: integer(item.page, `${field}.page`, 1),
197
+ items: integer(item.items, `${field}.items`, 1),
198
+ path: path(item.path, `${field}.path`)
199
+ };
200
+ };
201
+ const parseDownload = (value, field) => {
202
+ const download = record(value, field);
203
+ if (download.type === "github") {
204
+ exactKeys(download, ["type", "repository"], field);
205
+ const repository = string(download.repository, `${field}.repository`);
206
+ if (!GITHUB_REPOSITORY_PATTERN.test(repository)) throw new AwesomeRegistryValidationError(`${field}.repository is invalid`);
207
+ return {
208
+ type: "github",
209
+ repository
210
+ };
211
+ }
212
+ if (download.type === "url") {
213
+ exactKeys(download, ["type", "url"], field);
214
+ return {
215
+ type: "url",
216
+ url: httpUrl(download.url, `${field}.url`)
217
+ };
218
+ }
219
+ throw new AwesomeRegistryValidationError(`${field}.type is unsupported`);
220
+ };
221
+ const parseRepository = (value, field) => {
222
+ const repository = record(value, field);
223
+ exactKeys(repository, [
224
+ "owner",
225
+ "name",
226
+ "url",
227
+ "defaultBranch",
228
+ "lastCommitAt",
229
+ "readmeUrl"
230
+ ], field);
231
+ return {
232
+ owner: string(repository.owner, `${field}.owner`),
233
+ name: string(repository.name, `${field}.name`),
234
+ url: httpUrl(repository.url, `${field}.url`),
235
+ defaultBranch: string(repository.defaultBranch, `${field}.defaultBranch`),
236
+ lastCommitAt: dateTime(repository.lastCommitAt, `${field}.lastCommitAt`),
237
+ ...repository.readmeUrl === void 0 ? {} : { readmeUrl: httpUrl(repository.readmeUrl, `${field}.readmeUrl`) }
238
+ };
239
+ };
240
+ const parseRelease = (value, field) => {
241
+ const release = record(value, field);
242
+ exactKeys(release, [
243
+ "version",
244
+ "url",
245
+ "publishedAt",
246
+ "manifestUrl"
247
+ ], field);
248
+ return {
249
+ version: string(release.version, `${field}.version`),
250
+ url: httpUrl(release.url, `${field}.url`),
251
+ publishedAt: dateTime(release.publishedAt, `${field}.publishedAt`),
252
+ manifestUrl: release.manifestUrl === null ? null : httpUrl(release.manifestUrl, `${field}.manifestUrl`)
253
+ };
254
+ };
255
+ var AwesomeRegistryValidationError = class extends Error {
256
+ constructor(message) {
257
+ super(`Invalid awesome-plugins registry: ${message}`);
258
+ this.name = "AwesomeRegistryValidationError";
259
+ }
260
+ };
261
+ var AwesomeRegistryVersionError = class extends AwesomeRegistryValidationError {
262
+ received;
263
+ constructor(received, path) {
264
+ super(`${path} has unsupported schemaVersion ${String(received)}`);
265
+ this.received = received;
266
+ this.name = "AwesomeRegistryVersionError";
267
+ }
268
+ };
269
+ const parseAwesomeRegistryIndex = (value) => {
270
+ const index = record(value, "index");
271
+ exactKeys(index, [
272
+ "schemaVersion",
273
+ "pageSize",
274
+ "totalItems",
275
+ "totalPages",
276
+ "pages"
277
+ ], "index");
278
+ if (!Array.isArray(index.pages)) throw new AwesomeRegistryValidationError("index.pages must be an array");
279
+ const result = {
280
+ schemaVersion: schemaVersion(index.schemaVersion, "index"),
281
+ pageSize: integer(index.pageSize, "index.pageSize", 1, 100),
282
+ totalItems: integer(index.totalItems, "index.totalItems", 0),
283
+ totalPages: integer(index.totalPages, "index.totalPages", 0),
284
+ pages: index.pages.map((item, itemIndex) => parsePageReference(item, `index.pages[${itemIndex}]`))
285
+ };
286
+ if (result.pages.length !== result.totalPages) throw new AwesomeRegistryValidationError("index.pages length must equal index.totalPages");
287
+ if (result.pages.reduce((total, page) => total + page.items, 0) !== result.totalItems) throw new AwesomeRegistryValidationError("index page item counts must equal index.totalItems");
288
+ result.pages.forEach((page, itemIndex) => {
289
+ if (page.page !== itemIndex + 1 || page.items > result.pageSize) throw new AwesomeRegistryValidationError("index pages must be ordered and respect pageSize");
290
+ });
291
+ return result;
292
+ };
293
+ const parseAwesomePluginListing = (value, field = "listing") => {
294
+ const listing = record(value, field);
295
+ exactKeys(listing, [
296
+ "schemaVersion",
297
+ "id",
298
+ "authors",
299
+ "download",
300
+ "repository",
301
+ "release"
302
+ ], field);
303
+ const id = string(listing.id, `${field}.id`);
304
+ if (!PLUGIN_ID_PATTERN.test(id)) throw new AwesomeRegistryValidationError(`${field}.id is invalid`);
305
+ if (!Array.isArray(listing.authors) || listing.authors.length === 0) throw new AwesomeRegistryValidationError(`${field}.authors must be a non-empty array`);
306
+ const authors = listing.authors.map((author, index) => {
307
+ const login = string(author, `${field}.authors[${index}]`);
308
+ if (!GITHUB_LOGIN_PATTERN.test(login)) throw new AwesomeRegistryValidationError(`${field}.authors[${index}] is invalid`);
309
+ return login;
310
+ });
311
+ if (new Set(authors).size !== authors.length) throw new AwesomeRegistryValidationError(`${field}.authors must be unique`);
312
+ return {
313
+ schemaVersion: schemaVersion(listing.schemaVersion, field),
314
+ id,
315
+ authors,
316
+ download: parseDownload(listing.download, `${field}.download`),
317
+ ...listing.repository === void 0 ? {} : { repository: parseRepository(listing.repository, `${field}.repository`) },
318
+ ...listing.release === void 0 ? {} : { release: parseRelease(listing.release, `${field}.release`) }
319
+ };
320
+ };
321
+ const parsePagination = (value, field) => {
322
+ const pagination = record(value, field);
323
+ exactKeys(pagination, [
324
+ "page",
325
+ "pageSize",
326
+ "totalItems",
327
+ "totalPages",
328
+ "previous",
329
+ "next"
330
+ ], field);
331
+ return {
332
+ page: integer(pagination.page, `${field}.page`, 1),
333
+ pageSize: integer(pagination.pageSize, `${field}.pageSize`, 1, 100),
334
+ totalItems: integer(pagination.totalItems, `${field}.totalItems`, 0),
335
+ totalPages: integer(pagination.totalPages, `${field}.totalPages`, 1),
336
+ previous: pagination.previous === null ? null : path(pagination.previous, `${field}.previous`),
337
+ next: pagination.next === null ? null : path(pagination.next, `${field}.next`)
338
+ };
339
+ };
340
+ const parseAwesomeRegistryPage = (value) => {
341
+ const page = record(value, "page");
342
+ exactKeys(page, [
343
+ "schemaVersion",
344
+ "pagination",
345
+ "items"
346
+ ], "page");
347
+ if (!Array.isArray(page.items)) throw new AwesomeRegistryValidationError("page.items must be an array");
348
+ const result = {
349
+ schemaVersion: schemaVersion(page.schemaVersion, "page"),
350
+ pagination: parsePagination(page.pagination, "page.pagination"),
351
+ items: page.items.map((item, index) => parseAwesomePluginListing(item, `page.items[${index}]`))
352
+ };
353
+ if (result.items.length > result.pagination.pageSize) throw new AwesomeRegistryValidationError("page.items exceeds pageSize");
354
+ const expectedPrevious = result.pagination.page === 1 ? null : result.pagination.page - 1;
355
+ const expectedNext = result.pagination.page === result.pagination.totalPages ? null : result.pagination.page + 1;
356
+ if (expectedPrevious === null !== (result.pagination.previous === null) || expectedNext === null !== (result.pagination.next === null)) throw new AwesomeRegistryValidationError("page pagination links are inconsistent");
357
+ return result;
358
+ };
359
+ const assertAwesomeRegistryPagePath = (value) => path(value, "page path");
360
+ //#endregion
361
+ //#region lib/marketplace/cache.ts
362
+ const parseEnvelope = (value) => {
363
+ try {
364
+ const envelope = JSON.parse(value);
365
+ if (typeof envelope.cachedAt !== "string" || !("data" in envelope)) return void 0;
366
+ return {
367
+ cachedAt: envelope.cachedAt,
368
+ data: envelope.data
369
+ };
370
+ } catch {
371
+ return;
372
+ }
373
+ };
374
+ var AwesomeRegistryCache = class {
375
+ storage;
376
+ prefix;
377
+ constructor(storage, prefix = "delta-comic:awesome-registry:v1") {
378
+ this.storage = storage;
379
+ this.prefix = prefix;
380
+ }
381
+ readIndex() {
382
+ return this.read(`${this.prefix}:index`, parseAwesomeRegistryIndex);
383
+ }
384
+ writeIndex(data) {
385
+ return this.write(`${this.prefix}:index`, data);
386
+ }
387
+ readPage(path) {
388
+ return this.read(`${this.prefix}:page:${path}`, parseAwesomeRegistryPage);
389
+ }
390
+ writePage(path, data) {
391
+ return this.write(`${this.prefix}:page:${path}`, data);
392
+ }
393
+ read(key, parse) {
394
+ if (!this.storage) return void 0;
395
+ let stored;
396
+ try {
397
+ stored = this.storage.getItem(key);
398
+ } catch {
399
+ return;
400
+ }
401
+ if (!stored) return void 0;
402
+ const envelope = parseEnvelope(stored);
403
+ if (!envelope) {
404
+ this.remove(key);
405
+ return;
406
+ }
407
+ try {
408
+ return {
409
+ cachedAt: envelope.cachedAt,
410
+ data: parse(envelope.data)
411
+ };
412
+ } catch {
413
+ this.remove(key);
414
+ return;
415
+ }
416
+ }
417
+ remove(key) {
418
+ try {
419
+ this.storage?.removeItem(key);
420
+ } catch {}
421
+ }
422
+ write(key, data) {
423
+ const cachedAt = (/* @__PURE__ */ new Date()).toISOString();
424
+ try {
425
+ this.storage?.setItem(key, JSON.stringify({
426
+ cachedAt,
427
+ data
428
+ }));
429
+ } catch {}
430
+ return cachedAt;
431
+ }
432
+ };
433
+ //#endregion
434
+ //#region lib/marketplace/client.ts
435
+ const marketplaceLogger = logger.scoped("plugin:marketplace");
436
+ const defaultRequestJson = async (url) => await ky.get(url, {
437
+ retry: 2,
438
+ timeout: 3e4
439
+ }).json();
440
+ const defaultStorage = () => {
441
+ try {
442
+ return globalThis.localStorage;
443
+ } catch {
444
+ return;
445
+ }
446
+ };
447
+ var AwesomeRegistryNetworkError = class extends Error {
448
+ cause;
449
+ constructor(message, cause) {
450
+ super(message);
451
+ this.cause = cause;
452
+ this.name = "AwesomeRegistryNetworkError";
453
+ }
454
+ };
455
+ var AwesomeRegistryClient = class {
456
+ baseUrl;
457
+ cache;
458
+ requestJson;
459
+ constructor(options = {}) {
460
+ this.baseUrl = new URL(options.baseUrl ?? "https://raw.githubusercontent.com/delta-comic/awesome-plugins/main/").href;
461
+ this.cache = options.cache ?? new AwesomeRegistryCache(options.storage ?? defaultStorage());
462
+ this.requestJson = options.requestJson ?? defaultRequestJson;
463
+ }
464
+ async loadIndex() {
465
+ return await this.load(AWESOME_REGISTRY_INDEX_PATH, parseAwesomeRegistryIndex, () => this.cache.readIndex(), (data) => this.cache.writeIndex(data));
466
+ }
467
+ async loadPage(path) {
468
+ const safePath = assertAwesomeRegistryPagePath(path);
469
+ return await this.load(safePath, parseAwesomeRegistryPage, () => this.cache.readPage(safePath), (data) => this.cache.writePage(safePath, data));
470
+ }
471
+ async findListing(id) {
472
+ marketplaceLogger.debug("searching marketplace listing", { plugin: id });
473
+ const { data: index } = await this.loadIndex();
474
+ for (const pageReference of index.pages) {
475
+ const { data: page } = await this.loadPage(pageReference.path);
476
+ const listing = page.items.find((item) => item.id === id);
477
+ if (listing) {
478
+ marketplaceLogger.debug("marketplace listing found", { plugin: id });
479
+ return listing;
480
+ }
481
+ }
482
+ throw new Error(`Plugin "${id}" is not registered in awesome-plugins`);
483
+ }
484
+ async loadManifest(listing) {
485
+ const manifestUrl = listing.release?.manifestUrl;
486
+ if (!manifestUrl) return void 0;
487
+ const manifest = parsePluginManifest(await this.requestJson(manifestUrl));
488
+ if (manifest.name.id !== listing.id) throw new AwesomeRegistryValidationError(`listing ${listing.id} points to manifest for ${manifest.name.id}`);
489
+ return manifest;
490
+ }
491
+ async load(path, parse, readCache, writeCache) {
492
+ let payload;
493
+ try {
494
+ payload = await this.requestJson(new URL(path, this.baseUrl).href);
495
+ } catch (error) {
496
+ if (error instanceof AwesomeRegistryValidationError || error instanceof SyntaxError) throw error;
497
+ const cached = readCache();
498
+ if (cached) {
499
+ marketplaceLogger.warn("marketplace request failed; using stale cache", { path }, error);
500
+ return {
501
+ ...cached,
502
+ stale: true
503
+ };
504
+ }
505
+ marketplaceLogger.error("marketplace request failed without cache", { path }, error);
506
+ throw new AwesomeRegistryNetworkError(`Failed to request awesome-plugins ${path}`, error);
507
+ }
508
+ const data = parse(payload);
509
+ marketplaceLogger.debug("marketplace response cached", { path });
510
+ return {
511
+ cachedAt: writeCache(data),
512
+ data,
513
+ stale: false
514
+ };
515
+ }
516
+ };
517
+ //#endregion
518
+ //#region lib/marketplace/index.ts
519
+ const marketplaceDownloadToInstallInput = (download) => download.type === "github" ? `gh:${download.repository}` : download.url;
520
+ const marketplaceListingInstallId = (listing) => `ap:${listing.id}`;
521
+ const marketplaceListingSource = (listing) => listing.download.type === "github" ? `https://github.com/${listing.download.repository}` : listing.download.url;
522
+ //#endregion
523
+ //#region lib/pluginIcon.ts
524
+ /** Resolves persisted plugin icon metadata into a URL that an `<img>` can consume. */
525
+ const resolvePluginIconUrl = async (pluginId, icon) => {
526
+ if (icon === void 0) return void 0;
527
+ const reference = parsePluginIconReference(icon, "plugin icon");
528
+ if (reference.type === "remote") return reference.url;
529
+ if (!pluginId) throw new Error("A plugin id is required to resolve a local plugin icon");
530
+ return `${await createPluginAssetUrl(pluginId, reference.path)}${reference.fragment}`;
531
+ };
532
+ //#endregion
533
+ export { AWESOME_REGISTRY_BASE_URL, AWESOME_REGISTRY_INDEX_PATH, AWESOME_REGISTRY_SCHEMA_VERSION, AwesomeRegistryCache, AwesomeRegistryClient, AwesomeRegistryNetworkError, AwesomeRegistryValidationError, AwesomeRegistryVersionError, BUILT_IN_PLUGIN_LOADER, booter_exports as Booter, ConfigPointer, ConfigStore, DependencyRegistry, Global, install_exports as Install, loader_exports as Loader, PluginBooter, PluginI18nRegistry, PluginInstaller, PluginLoader, PluginManifestError, PluginRuntimeExtensions, PluginStore, assertAwesomeRegistryPagePath, builtInDefinitionToArchive, createDependencyRegistry, declareDepType, defaultDependencyRegistry, defineInnerPlugin, definePlugin, exposeSymbol, failedDependencies, filterPluginsBySelection, findCyclePaths, formatPluginLoadPlanError, isBuiltInPlugin, isBuiltInPluginName, isPluginManifestCompatible, marketplaceDownloadToInstallInput, marketplaceListingInstallId, marketplaceListingSource, parseAwesomePluginListing, parseAwesomeRegistryIndex, parseAwesomeRegistryPage, parsePluginIconReference, parsePluginManifest, planPluginLoadOrder, pluginExposes, pluginI18n, pluginKind, pluginMessageKey, pluginRuntime, pluginStore, provide, registerBooter, registerInstaller, registerLoader, require, resolvePluginIconUrl, runtimeExtensions, selectPluginsForPhase, synchronizeBuiltInPlugins, translatePluginText, useConfig, usePluginStore };
534
+
535
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["isRecord"],"sources":["../../lib/manifest.ts","../../lib/marketplace/types.ts","../../lib/marketplace/validation.ts","../../lib/marketplace/cache.ts","../../lib/marketplace/client.ts","../../lib/marketplace/index.ts","../../lib/pluginIcon.ts"],"sourcesContent":["import type { PluginArchiveDB } from '@delta-comic/db'\nimport semver from 'semver'\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst requiredRecord = (value: unknown, path: string) => {\n if (!isRecord(value)) throw new PluginManifestError(`${path} must be an object`)\n return value\n}\n\nconst requiredString = (value: unknown, path: string) => {\n if (typeof value !== 'string' || value.length === 0) {\n throw new PluginManifestError(`${path} must be a non-empty string`)\n }\n return value\n}\n\nconst optionalPath = (value: unknown, path: string) => {\n const text = requiredString(value, path)\n const normalized = text.replaceAll('\\\\', '/')\n if (normalized.startsWith('/') || normalized.split('/').includes('..')) {\n throw new PluginManifestError(`${path} must be a safe relative path`)\n }\n return text\n}\n\nexport type PluginIconReference =\n | { type: 'local'; path: string; fragment: string }\n | { type: 'remote'; url: string }\n\nconst decodeLocalIconPath = (value: string, path: string) => {\n let decoded = value\n for (let index = 0; index < 5; index += 1) {\n let next: string\n try {\n next = decodeURIComponent(decoded)\n } catch {\n throw new PluginManifestError(`${path} must be a valid URL path`)\n }\n if (next === decoded) break\n decoded = next\n }\n\n const normalized = decoded.replaceAll('\\\\', '/')\n const segments = normalized.split('/')\n if (\n !normalized ||\n normalized.startsWith('/') ||\n /^[a-z]:($|\\/)/i.test(normalized) ||\n normalized.includes('\\0') ||\n segments.some(segment => segment === '..')\n ) {\n throw new PluginManifestError(`${path} must be a safe relative path`)\n }\n return segments.filter(segment => segment && segment !== '.').join('/')\n}\n\n/**\n * Accepts a credential-free HTTP(S) URL or a safe path inside the plugin archive.\n * Query strings and fragments are kept for both forms.\n */\nexport const parsePluginIconReference = (\n value: unknown,\n path = 'manifest.icon',\n): PluginIconReference => {\n const text = requiredString(value, path).trim()\n if (!text) throw new PluginManifestError(`${path} must be a non-empty string`)\n\n if (/^[a-z][a-z\\d+.-]*:/i.test(text) || text.startsWith('//')) {\n let url: URL\n try {\n url = new URL(text)\n } catch {\n throw new PluginManifestError(`${path} must be an HTTP(S) URL or a safe relative path`)\n }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) {\n throw new PluginManifestError(\n `${path} must be a credential-free HTTP(S) URL or a safe relative path`,\n )\n }\n return { type: 'remote', url: text }\n }\n\n const rawPath = text.split(/[?#]/, 1)[0] ?? ''\n const resolvedPath = decodeLocalIconPath(rawPath, path)\n if (!resolvedPath) throw new PluginManifestError(`${path} must be a safe relative path`)\n\n const fragmentIndex = text.indexOf('#')\n return {\n fragment: fragmentIndex < 0 ? '' : text.slice(fragmentIndex),\n path: resolvedPath,\n type: 'local',\n }\n}\n\nexport class PluginManifestError extends Error {\n public constructor(message: string) {\n super(`Invalid Delta Comic manifest: ${message}`)\n this.name = 'PluginManifestError'\n }\n}\n\n/**\n * Validates the exact manifest format emitted by the `deltaComic` Vite plugin.\n * The returned value is safe to use as the persisted plugin metadata shape.\n */\nexport const parsePluginManifest = (value: unknown): PluginArchiveDB.Meta => {\n const manifest = requiredRecord(value, 'manifest')\n const name = requiredRecord(manifest.name, 'manifest.name')\n const version = requiredRecord(manifest.version, 'manifest.version')\n const id = requiredString(name.id, 'manifest.name.id')\n if (id === '.' || id === '..' || /[\\\\/]/.test(id)) {\n throw new PluginManifestError('manifest.name.id contains an unsafe path segment')\n }\n\n const requireValue = manifest.require\n if (!Array.isArray(requireValue)) {\n throw new PluginManifestError('manifest.require must be an array')\n }\n const require = requireValue.map((dependency, index) => {\n const record = requiredRecord(dependency, `manifest.require[${index}]`)\n const download = record.download\n if (download !== undefined && typeof download !== 'string') {\n throw new PluginManifestError(`manifest.require[${index}].download must be a string`)\n }\n return {\n id: requiredString(record.id, `manifest.require[${index}].id`),\n ...(download === undefined ? {} : { download }),\n }\n })\n\n const result: PluginArchiveDB.Meta = {\n author: requiredString(manifest.author, 'manifest.author'),\n description: requiredString(manifest.description, 'manifest.description'),\n name: { display: requiredString(name.display, 'manifest.name.display'), id },\n require,\n version: {\n plugin: requiredString(version.plugin, 'manifest.version.plugin'),\n supportCore: requiredString(version.supportCore, 'manifest.version.supportCore'),\n },\n }\n\n if (manifest.icon !== undefined) {\n const icon = parsePluginIconReference(manifest.icon)\n result.icon =\n icon.type === 'remote' ? icon.url : requiredString(manifest.icon, 'manifest.icon').trim()\n }\n\n if (manifest.entry !== undefined) {\n const entry = requiredRecord(manifest.entry, 'manifest.entry')\n result.entry = {\n jsPath: optionalPath(entry.jsPath, 'manifest.entry.jsPath'),\n ...(entry.cssPath === undefined\n ? {}\n : { cssPath: optionalPath(entry.cssPath, 'manifest.entry.cssPath') }),\n }\n }\n\n if (manifest.kind !== undefined) {\n if (manifest.kind !== 'normal' && manifest.kind !== 'preboot') {\n throw new PluginManifestError('manifest.kind must be \"normal\" or \"preboot\"')\n }\n result.kind = manifest.kind\n }\n\n if (manifest.integrity !== undefined) {\n const integrity = requiredRecord(manifest.integrity, 'manifest.integrity')\n if (integrity.algorithm !== 'blake3' && integrity.algorithm !== 'sha256') {\n throw new PluginManifestError('manifest.integrity.algorithm is unsupported')\n }\n result.integrity = {\n algorithm: integrity.algorithm,\n digest: requiredString(integrity.digest, 'manifest.integrity.digest'),\n }\n }\n\n return result\n}\n\nexport const isPluginManifestCompatible = (manifest: PluginArchiveDB.Meta, coreVersion: string) =>\n semver.satisfies(coreVersion, manifest.version.supportCore)","import type { PluginArchiveDB } from '@delta-comic/db'\n\nexport const AWESOME_REGISTRY_BASE_URL =\n 'https://raw.githubusercontent.com/delta-comic/awesome-plugins/main/'\nexport const AWESOME_REGISTRY_INDEX_PATH = 'registry/index.json'\nexport const AWESOME_REGISTRY_SCHEMA_VERSION = 1 as const\n\nexport interface AwesomeRegistryPageReference {\n page: number\n items: number\n path: string\n}\n\nexport interface AwesomeRegistryIndex {\n schemaVersion: typeof AWESOME_REGISTRY_SCHEMA_VERSION\n pageSize: number\n totalItems: number\n totalPages: number\n pages: AwesomeRegistryPageReference[]\n}\n\nexport interface AwesomeRegistryPagination {\n page: number\n pageSize: number\n totalItems: number\n totalPages: number\n previous: string | null\n next: string | null\n}\n\nexport type AwesomePluginDownload =\n | { type: 'github'; repository: string }\n | { type: 'url'; url: string }\n\nexport interface AwesomePluginRepository {\n owner: string\n name: string\n url: string\n defaultBranch: string\n lastCommitAt: string\n readmeUrl?: string\n}\n\nexport interface AwesomePluginRelease {\n version: string\n url: string\n publishedAt: string\n manifestUrl: string | null\n}\n\nexport interface AwesomePluginListing {\n schemaVersion: typeof AWESOME_REGISTRY_SCHEMA_VERSION\n id: string\n authors: string[]\n download: AwesomePluginDownload\n repository?: AwesomePluginRepository\n release?: AwesomePluginRelease\n}\n\nexport interface AwesomeRegistryPage {\n schemaVersion: typeof AWESOME_REGISTRY_SCHEMA_VERSION\n pagination: AwesomeRegistryPagination\n items: AwesomePluginListing[]\n}\n\nexport interface AwesomeRegistryResult<T> {\n data: T\n cachedAt: string\n stale: boolean\n}\n\nexport interface AwesomeMarketplaceEntry {\n listing: AwesomePluginListing\n manifest?: PluginArchiveDB.Meta\n manifestError?: string\n}\n\nexport interface MarketplaceStorage {\n getItem(key: string): string | null\n removeItem(key: string): void\n setItem(key: string, value: string): void\n}","import {\n AWESOME_REGISTRY_SCHEMA_VERSION,\n type AwesomePluginDownload,\n type AwesomePluginListing,\n type AwesomePluginRelease,\n type AwesomePluginRepository,\n type AwesomeRegistryIndex,\n type AwesomeRegistryPage,\n type AwesomeRegistryPagination,\n type AwesomeRegistryPageReference,\n} from './types'\n\nconst PAGE_PATH_PATTERN = /^registry\\/pages\\/[1-9][0-9]*\\.json$/\nconst PLUGIN_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/\nconst GITHUB_LOGIN_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/\nconst GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst record = (value: unknown, path: string) => {\n if (!isRecord(value)) throw new AwesomeRegistryValidationError(`${path} must be an object`)\n return value\n}\n\nconst exactKeys = (value: Record<string, unknown>, allowed: readonly string[], path: string) => {\n const extras = Object.keys(value).filter(key => !allowed.includes(key))\n if (extras.length > 0) {\n throw new AwesomeRegistryValidationError(`${path} has unknown fields: ${extras.join(', ')}`)\n }\n}\n\nconst string = (value: unknown, path: string) => {\n if (typeof value !== 'string' || value.length === 0) {\n throw new AwesomeRegistryValidationError(`${path} must be a non-empty string`)\n }\n return value\n}\n\nconst integer = (value: unknown, path: string, minimum: number, maximum = Infinity) => {\n if (!Number.isInteger(value) || (value as number) < minimum || (value as number) > maximum) {\n throw new AwesomeRegistryValidationError(\n `${path} must be an integer between ${minimum} and ${maximum}`,\n )\n }\n return value as number\n}\n\nconst schemaVersion = (value: unknown, path: string) => {\n if (value !== AWESOME_REGISTRY_SCHEMA_VERSION) {\n throw new AwesomeRegistryVersionError(value, path)\n }\n return AWESOME_REGISTRY_SCHEMA_VERSION\n}\n\nconst path = (value: unknown, field: string) => {\n const result = string(value, field)\n if (!PAGE_PATH_PATTERN.test(result)) {\n throw new AwesomeRegistryValidationError(`${field} must be a registry page path`)\n }\n return result\n}\n\nconst httpUrl = (value: unknown, field: string) => {\n const result = string(value, field)\n let parsed: URL\n try {\n parsed = new URL(result)\n } catch {\n throw new AwesomeRegistryValidationError(`${field} must be an absolute URL`)\n }\n if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password) {\n throw new AwesomeRegistryValidationError(`${field} must be a credential-free HTTP(S) URL`)\n }\n return result\n}\n\nconst dateTime = (value: unknown, field: string) => {\n const result = string(value, field)\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(result) || Number.isNaN(Date.parse(result))) {\n throw new AwesomeRegistryValidationError(`${field} must be an ISO date-time`)\n }\n return result\n}\n\nconst parsePageReference = (value: unknown, field: string): AwesomeRegistryPageReference => {\n const item = record(value, field)\n exactKeys(item, ['page', 'items', 'path'], field)\n return {\n page: integer(item.page, `${field}.page`, 1),\n items: integer(item.items, `${field}.items`, 1),\n path: path(item.path, `${field}.path`),\n }\n}\n\nconst parseDownload = (value: unknown, field: string): AwesomePluginDownload => {\n const download = record(value, field)\n if (download.type === 'github') {\n exactKeys(download, ['type', 'repository'], field)\n const repository = string(download.repository, `${field}.repository`)\n if (!GITHUB_REPOSITORY_PATTERN.test(repository)) {\n throw new AwesomeRegistryValidationError(`${field}.repository is invalid`)\n }\n return { type: 'github', repository }\n }\n if (download.type === 'url') {\n exactKeys(download, ['type', 'url'], field)\n return { type: 'url', url: httpUrl(download.url, `${field}.url`) }\n }\n throw new AwesomeRegistryValidationError(`${field}.type is unsupported`)\n}\n\nconst parseRepository = (value: unknown, field: string): AwesomePluginRepository => {\n const repository = record(value, field)\n exactKeys(\n repository,\n ['owner', 'name', 'url', 'defaultBranch', 'lastCommitAt', 'readmeUrl'],\n field,\n )\n return {\n owner: string(repository.owner, `${field}.owner`),\n name: string(repository.name, `${field}.name`),\n url: httpUrl(repository.url, `${field}.url`),\n defaultBranch: string(repository.defaultBranch, `${field}.defaultBranch`),\n lastCommitAt: dateTime(repository.lastCommitAt, `${field}.lastCommitAt`),\n ...(repository.readmeUrl === undefined\n ? {}\n : { readmeUrl: httpUrl(repository.readmeUrl, `${field}.readmeUrl`) }),\n }\n}\n\nconst parseRelease = (value: unknown, field: string): AwesomePluginRelease => {\n const release = record(value, field)\n exactKeys(release, ['version', 'url', 'publishedAt', 'manifestUrl'], field)\n return {\n version: string(release.version, `${field}.version`),\n url: httpUrl(release.url, `${field}.url`),\n publishedAt: dateTime(release.publishedAt, `${field}.publishedAt`),\n manifestUrl:\n release.manifestUrl === null ? null : httpUrl(release.manifestUrl, `${field}.manifestUrl`),\n }\n}\n\nexport class AwesomeRegistryValidationError extends Error {\n public constructor(message: string) {\n super(`Invalid awesome-plugins registry: ${message}`)\n this.name = 'AwesomeRegistryValidationError'\n }\n}\n\nexport class AwesomeRegistryVersionError extends AwesomeRegistryValidationError {\n public constructor(\n public readonly received: unknown,\n path: string,\n ) {\n super(`${path} has unsupported schemaVersion ${String(received)}`)\n this.name = 'AwesomeRegistryVersionError'\n }\n}\n\nexport const parseAwesomeRegistryIndex = (value: unknown): AwesomeRegistryIndex => {\n const index = record(value, 'index')\n exactKeys(index, ['schemaVersion', 'pageSize', 'totalItems', 'totalPages', 'pages'], 'index')\n if (!Array.isArray(index.pages)) {\n throw new AwesomeRegistryValidationError('index.pages must be an array')\n }\n const result: AwesomeRegistryIndex = {\n schemaVersion: schemaVersion(index.schemaVersion, 'index'),\n pageSize: integer(index.pageSize, 'index.pageSize', 1, 100),\n totalItems: integer(index.totalItems, 'index.totalItems', 0),\n totalPages: integer(index.totalPages, 'index.totalPages', 0),\n pages: index.pages.map((item, itemIndex) =>\n parsePageReference(item, `index.pages[${itemIndex}]`),\n ),\n }\n if (result.pages.length !== result.totalPages) {\n throw new AwesomeRegistryValidationError('index.pages length must equal index.totalPages')\n }\n if (result.pages.reduce((total, page) => total + page.items, 0) !== result.totalItems) {\n throw new AwesomeRegistryValidationError('index page item counts must equal index.totalItems')\n }\n result.pages.forEach((page, itemIndex) => {\n if (page.page !== itemIndex + 1 || page.items > result.pageSize) {\n throw new AwesomeRegistryValidationError('index pages must be ordered and respect pageSize')\n }\n })\n return result\n}\n\nexport const parseAwesomePluginListing = (\n value: unknown,\n field = 'listing',\n): AwesomePluginListing => {\n const listing = record(value, field)\n exactKeys(listing, ['schemaVersion', 'id', 'authors', 'download', 'repository', 'release'], field)\n const id = string(listing.id, `${field}.id`)\n if (!PLUGIN_ID_PATTERN.test(id)) {\n throw new AwesomeRegistryValidationError(`${field}.id is invalid`)\n }\n if (!Array.isArray(listing.authors) || listing.authors.length === 0) {\n throw new AwesomeRegistryValidationError(`${field}.authors must be a non-empty array`)\n }\n const authors = listing.authors.map((author, index) => {\n const login = string(author, `${field}.authors[${index}]`)\n if (!GITHUB_LOGIN_PATTERN.test(login)) {\n throw new AwesomeRegistryValidationError(`${field}.authors[${index}] is invalid`)\n }\n return login\n })\n if (new Set(authors).size !== authors.length) {\n throw new AwesomeRegistryValidationError(`${field}.authors must be unique`)\n }\n return {\n schemaVersion: schemaVersion(listing.schemaVersion, field),\n id,\n authors,\n download: parseDownload(listing.download, `${field}.download`),\n ...(listing.repository === undefined\n ? {}\n : { repository: parseRepository(listing.repository, `${field}.repository`) }),\n ...(listing.release === undefined\n ? {}\n : { release: parseRelease(listing.release, `${field}.release`) }),\n }\n}\n\nconst parsePagination = (value: unknown, field: string): AwesomeRegistryPagination => {\n const pagination = record(value, field)\n exactKeys(pagination, ['page', 'pageSize', 'totalItems', 'totalPages', 'previous', 'next'], field)\n return {\n page: integer(pagination.page, `${field}.page`, 1),\n pageSize: integer(pagination.pageSize, `${field}.pageSize`, 1, 100),\n totalItems: integer(pagination.totalItems, `${field}.totalItems`, 0),\n totalPages: integer(pagination.totalPages, `${field}.totalPages`, 1),\n previous: pagination.previous === null ? null : path(pagination.previous, `${field}.previous`),\n next: pagination.next === null ? null : path(pagination.next, `${field}.next`),\n }\n}\n\nexport const parseAwesomeRegistryPage = (value: unknown): AwesomeRegistryPage => {\n const page = record(value, 'page')\n exactKeys(page, ['schemaVersion', 'pagination', 'items'], 'page')\n if (!Array.isArray(page.items)) {\n throw new AwesomeRegistryValidationError('page.items must be an array')\n }\n const result: AwesomeRegistryPage = {\n schemaVersion: schemaVersion(page.schemaVersion, 'page'),\n pagination: parsePagination(page.pagination, 'page.pagination'),\n items: page.items.map((item, index) => parseAwesomePluginListing(item, `page.items[${index}]`)),\n }\n if (result.items.length > result.pagination.pageSize) {\n throw new AwesomeRegistryValidationError('page.items exceeds pageSize')\n }\n const expectedPrevious = result.pagination.page === 1 ? null : result.pagination.page - 1\n const expectedNext =\n result.pagination.page === result.pagination.totalPages ? null : result.pagination.page + 1\n if (\n (expectedPrevious === null) !== (result.pagination.previous === null) ||\n (expectedNext === null) !== (result.pagination.next === null)\n ) {\n throw new AwesomeRegistryValidationError('page pagination links are inconsistent')\n }\n return result\n}\n\nexport const assertAwesomeRegistryPagePath = (value: string) => path(value, 'page path')","import type { AwesomeRegistryIndex, AwesomeRegistryPage, MarketplaceStorage } from './types'\nimport { parseAwesomeRegistryIndex, parseAwesomeRegistryPage } from './validation'\n\ninterface CacheEnvelope {\n cachedAt: string\n data: unknown\n}\n\nconst parseEnvelope = (value: string): CacheEnvelope | undefined => {\n try {\n const envelope = JSON.parse(value) as Partial<CacheEnvelope>\n if (typeof envelope.cachedAt !== 'string' || !('data' in envelope)) return undefined\n return { cachedAt: envelope.cachedAt, data: envelope.data }\n } catch {\n return undefined\n }\n}\n\nexport class AwesomeRegistryCache {\n public constructor(\n private readonly storage?: MarketplaceStorage,\n private readonly prefix = 'delta-comic:awesome-registry:v1',\n ) {}\n\n public readIndex() {\n return this.read(`${this.prefix}:index`, parseAwesomeRegistryIndex)\n }\n\n public writeIndex(data: AwesomeRegistryIndex) {\n return this.write(`${this.prefix}:index`, data)\n }\n\n public readPage(path: string) {\n return this.read(`${this.prefix}:page:${path}`, parseAwesomeRegistryPage)\n }\n\n public writePage(path: string, data: AwesomeRegistryPage) {\n return this.write(`${this.prefix}:page:${path}`, data)\n }\n\n private read<T>(key: string, parse: (value: unknown) => T) {\n if (!this.storage) return undefined\n let stored: string | null\n try {\n stored = this.storage.getItem(key)\n } catch {\n return undefined\n }\n if (!stored) return undefined\n const envelope = parseEnvelope(stored)\n if (!envelope) {\n this.remove(key)\n return undefined\n }\n try {\n return { cachedAt: envelope.cachedAt, data: parse(envelope.data) }\n } catch {\n this.remove(key)\n return undefined\n }\n }\n\n private remove(key: string) {\n try {\n this.storage?.removeItem(key)\n } catch {}\n }\n\n private write(key: string, data: AwesomeRegistryIndex | AwesomeRegistryPage) {\n const cachedAt = new Date().toISOString()\n try {\n this.storage?.setItem(key, JSON.stringify({ cachedAt, data } satisfies CacheEnvelope))\n } catch {}\n return cachedAt\n }\n}","import type { PluginArchiveDB } from '@delta-comic/db'\nimport { logger } from '@delta-comic/logger'\nimport ky from 'ky'\n\nimport { parsePluginManifest } from '../manifest'\n\nimport { AwesomeRegistryCache } from './cache'\nimport {\n AWESOME_REGISTRY_BASE_URL,\n AWESOME_REGISTRY_INDEX_PATH,\n type AwesomePluginListing,\n type AwesomeRegistryIndex,\n type AwesomeRegistryPage,\n type AwesomeRegistryResult,\n type MarketplaceStorage,\n} from './types'\nimport {\n assertAwesomeRegistryPagePath,\n parseAwesomeRegistryIndex,\n parseAwesomeRegistryPage,\n AwesomeRegistryValidationError,\n} from './validation'\n\nconst marketplaceLogger = logger.scoped('plugin:marketplace')\n\nexport interface AwesomeRegistryClientOptions {\n baseUrl?: string\n cache?: AwesomeRegistryCache\n requestJson?: (url: string) => Promise<unknown>\n storage?: MarketplaceStorage\n}\n\nconst defaultRequestJson = async (url: string) =>\n await ky.get(url, { retry: 2, timeout: 30_000 }).json<unknown>()\n\nconst defaultStorage = () => {\n try {\n return globalThis.localStorage\n } catch {\n return undefined\n }\n}\n\nexport class AwesomeRegistryNetworkError extends Error {\n public constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message)\n this.name = 'AwesomeRegistryNetworkError'\n }\n}\n\nexport class AwesomeRegistryClient {\n private readonly baseUrl: string\n private readonly cache: AwesomeRegistryCache\n private readonly requestJson: (url: string) => Promise<unknown>\n\n public constructor(options: AwesomeRegistryClientOptions = {}) {\n this.baseUrl = new URL(options.baseUrl ?? AWESOME_REGISTRY_BASE_URL).href\n this.cache = options.cache ?? new AwesomeRegistryCache(options.storage ?? defaultStorage())\n this.requestJson = options.requestJson ?? defaultRequestJson\n }\n\n public async loadIndex(): Promise<AwesomeRegistryResult<AwesomeRegistryIndex>> {\n return await this.load(\n AWESOME_REGISTRY_INDEX_PATH,\n parseAwesomeRegistryIndex,\n () => this.cache.readIndex(),\n data => this.cache.writeIndex(data),\n )\n }\n\n public async loadPage(path: string): Promise<AwesomeRegistryResult<AwesomeRegistryPage>> {\n const safePath = assertAwesomeRegistryPagePath(path)\n return await this.load(\n safePath,\n parseAwesomeRegistryPage,\n () => this.cache.readPage(safePath),\n data => this.cache.writePage(safePath, data),\n )\n }\n\n public async findListing(id: string): Promise<AwesomePluginListing> {\n marketplaceLogger.debug('searching marketplace listing', { plugin: id })\n const { data: index } = await this.loadIndex()\n for (const pageReference of index.pages) {\n const { data: page } = await this.loadPage(pageReference.path)\n const listing = page.items.find(item => item.id === id)\n if (listing) {\n marketplaceLogger.debug('marketplace listing found', { plugin: id })\n return listing\n }\n }\n throw new Error(`Plugin \"${id}\" is not registered in awesome-plugins`)\n }\n\n public async loadManifest(\n listing: AwesomePluginListing,\n ): Promise<PluginArchiveDB.Meta | undefined> {\n const manifestUrl = listing.release?.manifestUrl\n if (!manifestUrl) return undefined\n const manifest = parsePluginManifest(await this.requestJson(manifestUrl))\n if (manifest.name.id !== listing.id) {\n throw new AwesomeRegistryValidationError(\n `listing ${listing.id} points to manifest for ${manifest.name.id}`,\n )\n }\n return manifest\n }\n\n private async load<T>(\n path: string,\n parse: (value: unknown) => T,\n readCache: () => { data: T; cachedAt: string } | undefined,\n writeCache: (data: T) => string,\n ): Promise<AwesomeRegistryResult<T>> {\n let payload: unknown\n try {\n payload = await this.requestJson(new URL(path, this.baseUrl).href)\n } catch (error) {\n if (error instanceof AwesomeRegistryValidationError || error instanceof SyntaxError)\n throw error\n const cached = readCache()\n if (cached) {\n marketplaceLogger.warn('marketplace request failed; using stale cache', { path }, error)\n return { ...cached, stale: true }\n }\n marketplaceLogger.error('marketplace request failed without cache', { path }, error)\n throw new AwesomeRegistryNetworkError(`Failed to request awesome-plugins ${path}`, error)\n }\n const data = parse(payload)\n marketplaceLogger.debug('marketplace response cached', { path })\n return { cachedAt: writeCache(data), data, stale: false }\n }\n}","export * from './cache'\nexport * from './client'\nexport * from './types'\nexport * from './validation'\n\nimport type { AwesomePluginDownload, AwesomePluginListing } from './types'\n\nexport const marketplaceDownloadToInstallInput = (download: AwesomePluginDownload) =>\n download.type === 'github' ? `gh:${download.repository}` : download.url\n\nexport const marketplaceListingInstallId = (listing: AwesomePluginListing) => `ap:${listing.id}`\n\nexport const marketplaceListingSource = (listing: AwesomePluginListing) =>\n listing.download.type === 'github'\n ? `https://github.com/${listing.download.repository}`\n : listing.download.url","import { createPluginAssetUrl } from './driver/init/storage'\nimport { parsePluginIconReference } from './manifest'\n\n/** Resolves persisted plugin icon metadata into a URL that an `<img>` can consume. */\nexport const resolvePluginIconUrl = async (\n pluginId: string | undefined,\n icon: string | undefined,\n): Promise<string | undefined> => {\n if (icon === undefined) return undefined\n const reference = parsePluginIconReference(icon, 'plugin icon')\n if (reference.type === 'remote') return reference.url\n if (!pluginId) throw new Error('A plugin id is required to resolve a local plugin icon')\n return `${await createPluginAssetUrl(pluginId, reference.path)}${reference.fragment}`\n}"],"mappings":"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"}