@inpageedit/core 0.12.0 → 0.12.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.
Files changed (63) hide show
  1. package/LICENSE +20 -20
  2. package/dist/IconQuickEdit-CMCQncyj.js.map +1 -1
  3. package/dist/InputBox-nQKtiWtZ.js +30 -0
  4. package/dist/InputBox-nQKtiWtZ.js.map +1 -0
  5. package/dist/PluginPrefSync-Ziogy_o-.js +339 -0
  6. package/dist/PluginPrefSync-Ziogy_o-.js.map +1 -0
  7. package/dist/PluginStoreApp-Bnvyl-Oc.js +452 -0
  8. package/dist/PluginStoreApp-Bnvyl-Oc.js.map +1 -0
  9. package/dist/Preferences-DS4-CFWe.js.map +1 -1
  10. package/dist/components/index.js +6 -7
  11. package/dist/components/index.js.map +1 -1
  12. package/dist/{index-CYc6LH26.js → index-B3FUDhxl.js} +2 -2
  13. package/dist/{index-CYc6LH26.js.map → index-B3FUDhxl.js.map} +1 -1
  14. package/dist/{index-DEav9Ptt.js → index-B3YJdjxo.js} +128 -132
  15. package/dist/index-B3YJdjxo.js.map +1 -0
  16. package/dist/{index-Bv7Dw5eO.js → index-BRjEc8b8.js} +2 -2
  17. package/dist/{index-Bv7Dw5eO.js.map → index-BRjEc8b8.js.map} +1 -1
  18. package/dist/{index-CZXxH2-9.js → index-Bd70aDMT.js} +6 -7
  19. package/dist/{index-CZXxH2-9.js.map → index-Bd70aDMT.js.map} +1 -1
  20. package/dist/{index-B5DtUqkK.js → index-BvuaLEFc.js} +3 -4
  21. package/dist/{index-B5DtUqkK.js.map → index-BvuaLEFc.js.map} +1 -1
  22. package/dist/index-CG38LlAh.js.map +1 -1
  23. package/dist/index-CM_6yF2v.js.map +1 -1
  24. package/dist/{index-D4uwfUZL.js → index-Ccr3YhkP.js} +2 -2
  25. package/dist/{index-D4uwfUZL.js.map → index-Ccr3YhkP.js.map} +1 -1
  26. package/dist/{index-CPoUaSMw.js → index-D5zX93bn.js} +39 -40
  27. package/dist/index-D5zX93bn.js.map +1 -0
  28. package/dist/index-DD5CVCfD.js.map +1 -1
  29. package/dist/{index-BXaiDKnr.js → index-DgQNTfPR.js} +15 -14
  30. package/dist/index-DgQNTfPR.js.map +1 -0
  31. package/dist/{index-CJFePavo.js → index-Dnmv-xDn.js} +2 -2
  32. package/dist/{index-CJFePavo.js.map → index-Dnmv-xDn.js.map} +1 -1
  33. package/dist/{index-hSl8LzNb.js → index-eARjMi7f.js} +5 -6
  34. package/dist/index-eARjMi7f.js.map +1 -0
  35. package/dist/{index-Ckozkp6W.js → index-zHTGCjfF.js} +2 -2
  36. package/dist/{index-Ckozkp6W.js.map → index-zHTGCjfF.js.map} +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/makeCallable-LDU0xZMJ.js.map +1 -1
  39. package/dist/models/WikiPage/index.d.ts +5 -1
  40. package/dist/noop-ClDc6zv4.js.map +1 -1
  41. package/dist/plugins/plugin-store/index.d.ts +9 -12
  42. package/dist/style.css +1 -1
  43. package/dist/{vueHooks-D0uVqbO-.js → vueHooks-l04s8cIl.js} +1112 -1080
  44. package/dist/{vueHooks-D0uVqbO-.js.map → vueHooks-l04s8cIl.js.map} +1 -1
  45. package/lib/index.umd.js +15 -11
  46. package/lib/index.umd.js.map +1 -1
  47. package/lib/style.css +1 -1
  48. package/package.json +1 -1
  49. package/dist/CheckBox-Bc79KBEB.js +0 -13
  50. package/dist/CheckBox-Bc79KBEB.js.map +0 -1
  51. package/dist/InputBox-DZAdyZ4B.js +0 -22
  52. package/dist/InputBox-DZAdyZ4B.js.map +0 -1
  53. package/dist/PluginPrefSync-BPQkNtX8.js +0 -292
  54. package/dist/PluginPrefSync-BPQkNtX8.js.map +0 -1
  55. package/dist/PluginStoreApp-Cxspe6t8.js +0 -158
  56. package/dist/PluginStoreApp-Cxspe6t8.js.map +0 -1
  57. package/dist/index-BXaiDKnr.js.map +0 -1
  58. package/dist/index-CB7TltEb.js +0 -297
  59. package/dist/index-CB7TltEb.js.map +0 -1
  60. package/dist/index-CPoUaSMw.js.map +0 -1
  61. package/dist/index-DEav9Ptt.js.map +0 -1
  62. package/dist/index-hSl8LzNb.js.map +0 -1
  63. package/dist/plugins/quick-delete/index.d.ts +0 -55
@@ -1 +1 @@
1
- {"version":3,"file":"index-CYc6LH26.js","sources":["../src/plugins/analytics/index.tsx"],"sourcesContent":["import { Endpoints } from '@/constants/endpoints.js'\nimport { Inject, InPageEdit, Schema } from '@/InPageEdit'\n\ndeclare module '@/InPageEdit' {\n interface InPageEdit {\n analytics: PluginAnalytics\n }\n}\n\nexport interface IPEBeaconPayload {\n siteApi: string\n siteName?: string\n userId: number\n userName: string\n version?: string\n usages: IPEBeaconUsage[]\n}\n\nexport interface IPEBeaconUsage {\n ts: number\n feature: string\n subtype?: string\n page?: string\n}\n\n@Inject(['wiki', 'preferences'])\nexport class PluginAnalytics extends BasePlugin {\n private _usages: IPEBeaconUsage[] = []\n private _timer: ReturnType<typeof setInterval> | null = null\n private readonly MAX_QUEUE_SIZE = 50\n private readonly INTERVAL_MS = 60 * 1000 // 1分钟\n\n constructor(public ctx: InPageEdit) {\n super(ctx, {}, 'analytics')\n this._setupTimer()\n this._registerUnloadHandler()\n this._showConfirmNotify()\n this._initPluginListeners()\n ctx.set('analytics', this)\n }\n\n protected start(): Promise<void> | void {\n const ctx = this.ctx\n ctx.preferences.registerCustomConfig(\n 'analytics',\n Schema.object({\n 'analytics._intro': Schema.const(\n <section>\n <h3>InPageEdit Analytics</h3>\n <p>\n InPageEdit Analytics is the companion analytics platform for the InPageEdit NEXT\n project. By collecting and displaying usage data from around the world, it helps\n developers and the community better understand how the tool is used, optimize feature\n design, and enhance user experience.\n </p>\n <h4>What data will be collected?</h4>\n <ol style={{ listStyle: 'number', paddingLeft: '1em' }}>\n <li>\n <strong>Usage data</strong>: When and which features you use, what pages you edit,\n etc.\n </li>\n <li>\n <strong>User information</strong>: Your user name and user ID.\n </li>\n <li>\n <strong>Site information</strong>: This wiki's url and site name.\n </li>\n </ol>\n <p>\n <strong>NO sensitive data will be collected.</strong>\n </p>\n <div style={{ display: 'grid', gap: '0.5rem' }}>\n <a href={this.analyticsDashUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <button className=\"btn\" style={{ width: '100%' }}>\n Analytics Platform\n </button>\n </a>\n <a\n href={`${this.analyticsDashUrl}/_redirect/user?${new URLSearchParams({\n siteApi: this.ctx.wiki.getSciprtUrl('api'),\n mwUserId: this.ctx.wiki.userInfo.id.toString(),\n }).toString()}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <button className=\"btn\" style={{ width: '100%' }}>\n View My Data\n </button>\n </a>\n </div>\n </section>\n ).role('raw-html'),\n 'analytics.enabled': Schema.boolean()\n .description('Share my usage data with the community.')\n .default(false),\n }).description('Analytics settings'),\n 'general'\n )\n }\n\n private get analyticsDashUrl() {\n return import.meta.env.PROD ? Endpoints.ANALYTICS_DASH_URL : 'http://localhost:20105'\n }\n private get analyticsApiBase() {\n return import.meta.env.PROD ? Endpoints.ANALYTICS_API_BASE : 'http://localhost:20105/api/v6'\n }\n\n private _setupTimer() {\n this._timer = setInterval(() => {\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }, this.INTERVAL_MS)\n }\n\n private _registerUnloadHandler() {\n const handleUnload = () => {\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }\n\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n handleUnload()\n }\n })\n\n window.addEventListener('pagehide', handleUnload)\n window.addEventListener('beforeunload', handleUnload)\n }\n\n private async _showConfirmNotify() {\n this.ctx.inject(['modal', 'storage'], async (ctx) => {\n const key = 'analytics/confirm-shown'\n const shown = await ctx.storage.simpleKV.get(key)\n const enabled = await ctx.preferences.get('analytics.enabled')\n if (shown || enabled) {\n return\n }\n ctx.modal.notify(\n 'confirm',\n {\n title: 'Enable Analytics',\n content: (\n <div>\n <p>Help us improve InPageEdit by sharing your usage data with us.</p>\n <p>What data will be collected?</p>\n <ul style={{ listStyle: 'auto', paddingLeft: '1.5em' }}>\n <li>Usage data: What features you use, what pages you edit, etc.</li>\n <li>User information: Your username, user ID.</li>\n <li>Site information: The wiki you are editing.</li>\n </ul>\n <p>\n <strong>NO sensitive data will be collected.</strong>\n </p>\n </div>\n ),\n okBtn: {\n label: 'Enable',\n },\n cancelBtn: {\n label: 'Disable',\n },\n closeAfter: 0,\n onClose: () => {\n this.ctx.storage.simpleKV.set(key, 1)\n },\n },\n (result) => {\n ctx.preferences.set('analytics.enabled', result)\n if (result) {\n this.addEvent('analytics', 'enabled')\n }\n }\n )\n })\n }\n\n private _initPluginListeners() {\n const ctx = this.ctx\n ctx.on('in-article-links/anchor-clicked', (payload) => {\n this.addEvent('in-article-links', paramCase(payload.action))\n })\n ctx.on('quick-delete/submit', (payload) => {\n this.addEvent('quick-delete', 'submit', payload.wikiPage.title)\n })\n ctx.on('quick-diff/loaded', (payload) => {\n this.addEvent('quick-diff', 'loaded', payload.compare.fromtitle)\n })\n ctx.on('quick-redirect/submit', (payload) => {\n this.addEvent('quick-redirect', 'submit', payload.payload.to || undefined)\n })\n ctx.on('quick-preview/loaded', (payload) => {\n this.addEvent('quick-preview', 'loaded', payload.wikiPage.title)\n })\n ctx.on('quick-edit/wiki-page', (payload) => {\n this.addEvent('quick-edit', undefined, payload.wikiPage.title)\n })\n ctx.on('quick-edit/submit', (payload) => {\n this.addEvent('quick-edit', 'submit', payload.wikiPage.title)\n })\n ctx.on('quick-move/submit', (payload) => {\n this.addEvent('quick-move', 'submit')\n })\n ctx.on('toolbox/button-clicked', (payload) => {\n this.addEvent('toolbox', `button-clicked#${payload.id || 'unknown'}`)\n })\n }\n\n public addEvent(feature: string, subtype?: string, page?: string) {\n const usage: IPEBeaconUsage = {\n ts: Date.now(),\n feature,\n subtype,\n page,\n }\n this._usages.push(usage)\n\n // 如果usages数组超过50个,立即发送\n if (this._usages.length >= this.MAX_QUEUE_SIZE) {\n this.sendBeacon()\n }\n\n return this\n }\n\n private async sendBeacon() {\n if (this._usages.length === 0) {\n return true\n }\n\n const enabled = await this.ctx.preferences.get<boolean>('analytics.enabled', false)\n if (!enabled) {\n this.logger.debug('Analytics disabled, skipping')\n return true\n }\n\n const usages = this._usages.splice(0, this._usages.length)\n const payload: IPEBeaconPayload = {\n siteApi: this.ctx.wiki.getSciprtUrl('api'),\n siteName: this.ctx.wiki.siteInfo.general.sitename,\n userId: this.ctx.wiki.userInfo.id,\n userName: this.ctx.wiki.userInfo.name,\n version: this.ctx.version.split('-')[0],\n usages,\n }\n const body = JSON.stringify(payload)\n\n const endpoint = `${this.analyticsApiBase}/submit`\n\n const beaconOK = navigator?.sendBeacon?.(endpoint, body)\n if (beaconOK) {\n this.logger.debug('Beacon sent successfully', payload)\n return true\n } else {\n this.logger.debug('Beacon failed, sending via XMLHttpRequest')\n const { promise, resolve, reject } = promiseWithResolvers<boolean>()\n try {\n const xhr = new XMLHttpRequest()\n xhr.open('POST', endpoint, true)\n xhr.setRequestHeader('Content-Type', 'application/json')\n xhr.send(body)\n xhr.onload = () => {\n this.logger.debug('Beacon sent successfully via XMLHttpRequest', payload)\n resolve(xhr.status >= 200 && xhr.status < 300)\n }\n xhr.onerror = () => {\n reject(new Error('Failed to send beacon'))\n }\n } catch (error) {\n reject(error)\n }\n return promise\n }\n }\n\n /**\n * 清理资源\n */\n protected stop() {\n if (this._timer !== null) {\n clearInterval(this._timer)\n this._timer = null\n }\n // 在销毁前发送剩余的数据\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }\n}\n"],"names":["_PluginAnalytics_decorators","_init","_a","Inject","PluginAnalytics","BasePlugin","ctx","Schema","jsx","jsxs","Endpoints","handleUnload","key","shown","enabled","result","payload","paramCase","feature","subtype","page","usage","usages","body","endpoint","promise","resolve","reject","promiseWithResolvers","xhr","error","__decoratorStart","__decorateElement","__runInitializers"],"mappings":"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"}
1
+ {"version":3,"file":"index-B3FUDhxl.js","sources":["../src/plugins/analytics/index.tsx"],"sourcesContent":["import { Endpoints } from '@/constants/endpoints.js'\nimport { Inject, InPageEdit, Schema } from '@/InPageEdit'\n\ndeclare module '@/InPageEdit' {\n interface InPageEdit {\n analytics: PluginAnalytics\n }\n}\n\nexport interface IPEBeaconPayload {\n siteApi: string\n siteName?: string\n userId: number\n userName: string\n version?: string\n usages: IPEBeaconUsage[]\n}\n\nexport interface IPEBeaconUsage {\n ts: number\n feature: string\n subtype?: string\n page?: string\n}\n\n@Inject(['wiki', 'preferences'])\nexport class PluginAnalytics extends BasePlugin {\n private _usages: IPEBeaconUsage[] = []\n private _timer: ReturnType<typeof setInterval> | null = null\n private readonly MAX_QUEUE_SIZE = 50\n private readonly INTERVAL_MS = 60 * 1000 // 1分钟\n\n constructor(public ctx: InPageEdit) {\n super(ctx, {}, 'analytics')\n this._setupTimer()\n this._registerUnloadHandler()\n this._showConfirmNotify()\n this._initPluginListeners()\n ctx.set('analytics', this)\n }\n\n protected start(): Promise<void> | void {\n const ctx = this.ctx\n ctx.preferences.registerCustomConfig(\n 'analytics',\n Schema.object({\n 'analytics._intro': Schema.const(\n <section>\n <h3>InPageEdit Analytics</h3>\n <p>\n InPageEdit Analytics is the companion analytics platform for the InPageEdit NEXT\n project. By collecting and displaying usage data from around the world, it helps\n developers and the community better understand how the tool is used, optimize feature\n design, and enhance user experience.\n </p>\n <h4>What data will be collected?</h4>\n <ol style={{ listStyle: 'number', paddingLeft: '1em' }}>\n <li>\n <strong>Usage data</strong>: When and which features you use, what pages you edit,\n etc.\n </li>\n <li>\n <strong>User information</strong>: Your user name and user ID.\n </li>\n <li>\n <strong>Site information</strong>: This wiki's url and site name.\n </li>\n </ol>\n <p>\n <strong>NO sensitive data will be collected.</strong>\n </p>\n <div style={{ display: 'grid', gap: '0.5rem' }}>\n <a href={this.analyticsDashUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <button className=\"btn\" style={{ width: '100%' }}>\n Analytics Platform\n </button>\n </a>\n <a\n href={`${this.analyticsDashUrl}/_redirect/user?${new URLSearchParams({\n siteApi: this.ctx.wiki.getSciprtUrl('api'),\n mwUserId: this.ctx.wiki.userInfo.id.toString(),\n }).toString()}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <button className=\"btn\" style={{ width: '100%' }}>\n View My Data\n </button>\n </a>\n </div>\n </section>\n ).role('raw-html'),\n 'analytics.enabled': Schema.boolean()\n .description('Share my usage data with the community.')\n .default(false),\n }).description('Analytics settings'),\n 'general'\n )\n }\n\n private get analyticsDashUrl() {\n return import.meta.env.PROD ? Endpoints.ANALYTICS_DASH_URL : 'http://localhost:20105'\n }\n private get analyticsApiBase() {\n return import.meta.env.PROD ? Endpoints.ANALYTICS_API_BASE : 'http://localhost:20105/api/v6'\n }\n\n private _setupTimer() {\n this._timer = setInterval(() => {\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }, this.INTERVAL_MS)\n }\n\n private _registerUnloadHandler() {\n const handleUnload = () => {\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }\n\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n handleUnload()\n }\n })\n\n window.addEventListener('pagehide', handleUnload)\n window.addEventListener('beforeunload', handleUnload)\n }\n\n private async _showConfirmNotify() {\n this.ctx.inject(['modal', 'storage'], async (ctx) => {\n const key = 'analytics/confirm-shown'\n const shown = await ctx.storage.simpleKV.get(key)\n const enabled = await ctx.preferences.get('analytics.enabled')\n if (shown || enabled) {\n return\n }\n ctx.modal.notify(\n 'confirm',\n {\n title: 'Enable Analytics',\n content: (\n <div>\n <p>Help us improve InPageEdit by sharing your usage data with us.</p>\n <p>What data will be collected?</p>\n <ul style={{ listStyle: 'auto', paddingLeft: '1.5em' }}>\n <li>Usage data: What features you use, what pages you edit, etc.</li>\n <li>User information: Your username, user ID.</li>\n <li>Site information: The wiki you are editing.</li>\n </ul>\n <p>\n <strong>NO sensitive data will be collected.</strong>\n </p>\n </div>\n ),\n okBtn: {\n label: 'Enable',\n },\n cancelBtn: {\n label: 'Disable',\n },\n closeAfter: 0,\n onClose: () => {\n this.ctx.storage.simpleKV.set(key, 1)\n },\n },\n (result) => {\n ctx.preferences.set('analytics.enabled', result)\n if (result) {\n this.addEvent('analytics', 'enabled')\n }\n }\n )\n })\n }\n\n private _initPluginListeners() {\n const ctx = this.ctx\n ctx.on('in-article-links/anchor-clicked', (payload) => {\n this.addEvent('in-article-links', paramCase(payload.action))\n })\n ctx.on('quick-delete/submit', (payload) => {\n this.addEvent('quick-delete', 'submit', payload.wikiPage.title)\n })\n ctx.on('quick-diff/loaded', (payload) => {\n this.addEvent('quick-diff', 'loaded', payload.compare.fromtitle)\n })\n ctx.on('quick-redirect/submit', (payload) => {\n this.addEvent('quick-redirect', 'submit', payload.payload.to || undefined)\n })\n ctx.on('quick-preview/loaded', (payload) => {\n this.addEvent('quick-preview', 'loaded', payload.wikiPage.title)\n })\n ctx.on('quick-edit/wiki-page', (payload) => {\n this.addEvent('quick-edit', undefined, payload.wikiPage.title)\n })\n ctx.on('quick-edit/submit', (payload) => {\n this.addEvent('quick-edit', 'submit', payload.wikiPage.title)\n })\n ctx.on('quick-move/submit', (payload) => {\n this.addEvent('quick-move', 'submit')\n })\n ctx.on('toolbox/button-clicked', (payload) => {\n this.addEvent('toolbox', `button-clicked#${payload.id || 'unknown'}`)\n })\n }\n\n public addEvent(feature: string, subtype?: string, page?: string) {\n const usage: IPEBeaconUsage = {\n ts: Date.now(),\n feature,\n subtype,\n page,\n }\n this._usages.push(usage)\n\n // 如果usages数组超过50个,立即发送\n if (this._usages.length >= this.MAX_QUEUE_SIZE) {\n this.sendBeacon()\n }\n\n return this\n }\n\n private async sendBeacon() {\n if (this._usages.length === 0) {\n return true\n }\n\n const enabled = await this.ctx.preferences.get<boolean>('analytics.enabled', false)\n if (!enabled) {\n this.logger.debug('Analytics disabled, skipping')\n return true\n }\n\n const usages = this._usages.splice(0, this._usages.length)\n const payload: IPEBeaconPayload = {\n siteApi: this.ctx.wiki.getSciprtUrl('api'),\n siteName: this.ctx.wiki.siteInfo.general.sitename,\n userId: this.ctx.wiki.userInfo.id,\n userName: this.ctx.wiki.userInfo.name,\n version: this.ctx.version.split('-')[0],\n usages,\n }\n const body = JSON.stringify(payload)\n\n const endpoint = `${this.analyticsApiBase}/submit`\n\n const beaconOK = navigator?.sendBeacon?.(endpoint, body)\n if (beaconOK) {\n this.logger.debug('Beacon sent successfully', payload)\n return true\n } else {\n this.logger.debug('Beacon failed, sending via XMLHttpRequest')\n const { promise, resolve, reject } = promiseWithResolvers<boolean>()\n try {\n const xhr = new XMLHttpRequest()\n xhr.open('POST', endpoint, true)\n xhr.setRequestHeader('Content-Type', 'application/json')\n xhr.send(body)\n xhr.onload = () => {\n this.logger.debug('Beacon sent successfully via XMLHttpRequest', payload)\n resolve(xhr.status >= 200 && xhr.status < 300)\n }\n xhr.onerror = () => {\n reject(new Error('Failed to send beacon'))\n }\n } catch (error) {\n reject(error)\n }\n return promise\n }\n }\n\n /**\n * 清理资源\n */\n protected stop() {\n if (this._timer !== null) {\n clearInterval(this._timer)\n this._timer = null\n }\n // 在销毁前发送剩余的数据\n if (this._usages.length > 0) {\n this.sendBeacon()\n }\n }\n}\n"],"names":["_PluginAnalytics_decorators","_init","_a","Inject","PluginAnalytics","BasePlugin","ctx","Schema","jsx","jsxs","Endpoints","handleUnload","key","shown","enabled","result","payload","paramCase","feature","subtype","page","usage","usages","body","endpoint","promise","resolve","reject","promiseWithResolvers","xhr","error","__decoratorStart","__decorateElement","__runInitializers"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { j as y } from "./index-CM_6yF2v.js";
2
- import { S as i, B as L, E as j, I as A } from "./index-BXaiDKnr.js";
3
- import { c as T, d as D } from "./vueHooks-D0uVqbO-.js";
4
- const O = 1;
5
- var w;
1
+ import { j as R } from "./index-CM_6yF2v.js";
2
+ import { S as i, B as T, E as C, I as j } from "./index-DgQNTfPR.js";
3
+ import { c as L, d as O } from "./vueHooks-l04s8cIl.js";
4
+ const F = 1;
5
+ var m;
6
6
  ((n) => {
7
7
  n.PackageLoader = new i(
8
8
  i.object({
@@ -33,7 +33,7 @@ var w;
33
33
  })
34
34
  ), n.Registry = new i(
35
35
  i.object({
36
- manifest_version: i.number().min(O).required(),
36
+ manifest_version: i.number().min(F).required(),
37
37
  name: i.string().required(),
38
38
  base_url: i.string().required(),
39
39
  homepage: i.string(),
@@ -42,19 +42,19 @@ var w;
42
42
  packages: i.array(n.Package).required()
43
43
  })
44
44
  );
45
- })(w || (w = {}));
46
- var U = Object.create, v = Object.defineProperty, B = Object.getOwnPropertyDescriptor, b = (n, e) => (e = Symbol[n]) ? e : Symbol.for("Symbol." + n), C = (n) => {
45
+ })(m || (m = {}));
46
+ var G = Object.create, x = Object.defineProperty, B = Object.getOwnPropertyDescriptor, S = (n, e) => (e = Symbol[n]) ? e : Symbol.for("Symbol." + n), b = (n) => {
47
47
  throw TypeError(n);
48
- }, q = (n, e, t) => e in n ? v(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, M = (n, e) => v(n, "name", { value: e, configurable: !0 }), N = (n) => [, , , U(n?.[b("metadata")] ?? null)], F = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"], k = (n) => n !== void 0 && typeof n != "function" ? C("Function expected") : n, G = (n, e, t, r, s) => ({ kind: F[n], name: e, metadata: r, addInitializer: (a) => t._ ? C("Already initialized") : s.push(k(a || null)) }), z = (n, e) => q(e, b("metadata"), n[3]), V = (n, e, t, r) => {
49
- for (var s = 0, a = n[e >> 1], o = a && a.length; s < o; s++) a[s].call(t);
48
+ }, M = (n, e, t) => e in n ? x(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, q = (n, e) => x(n, "name", { value: e, configurable: !0 }), N = (n) => [, , , G(n?.[S("metadata")] ?? null)], D = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"], $ = (n) => n !== void 0 && typeof n != "function" ? b("Function expected") : n, U = (n, e, t, r, s) => ({ kind: D[n], name: e, metadata: r, addInitializer: (a) => t._ ? b("Already initialized") : s.push($(a || null)) }), Y = (n, e) => M(e, S("metadata"), n[3]), z = (n, e, t, r) => {
49
+ for (var s = 0, a = n[e >> 1], l = a && a.length; s < l; s++) a[s].call(t);
50
50
  return r;
51
- }, K = (n, e, t, r, s, a) => {
52
- var o, h, f, g = e & 7, p = !1, c = 0, m = n[c] || (n[c] = []), d = g && (s = s.prototype, g < 5 && (g > 3 || !p) && B(s, t));
53
- M(s, t);
54
- for (var l = r.length - 1; l >= 0; l--)
55
- f = G(g, t, h = {}, n[3], m), o = (0, r[l])(s, f), h._ = 1, k(o) && (s = o);
56
- return z(n, s), d && v(s, t, d), p ? g ^ 4 ? a : d : s;
57
- }, S, x, $;
51
+ }, Q = (n, e, t, r, s, a) => {
52
+ var l, f, h, g = e & 7, p = !1, c = 0, y = n[c] || (n[c] = []), d = g && (s = s.prototype, g < 5 && (g > 3 || !p) && B(s, t));
53
+ q(s, t);
54
+ for (var o = r.length - 1; o >= 0; o--)
55
+ h = U(g, t, f = {}, n[3], y), l = (0, r[o])(s, h), f._ = 1, $(l) && (s = l);
56
+ return Y(n, s), d && x(s, t, d), p ? g ^ 4 ? a : d : s;
57
+ }, k, I, A;
58
58
  const P = (n) => {
59
59
  if (!n) return null;
60
60
  try {
@@ -62,12 +62,12 @@ const P = (n) => {
62
62
  } catch {
63
63
  return null;
64
64
  }
65
- }, I = (n, e, t) => {
65
+ }, E = (n, e, t) => {
66
66
  if (/^https?:\/\//i.test(n)) return n;
67
- const r = (o) => o.endsWith("/") ? o : o + "/";
67
+ const r = (l) => l.endsWith("/") ? l : l + "/";
68
68
  if (/^https?:\/\//i.test(e)) {
69
- const o = r(e);
70
- return new URL(n, o).href;
69
+ const l = r(e);
70
+ return new URL(n, l).href;
71
71
  }
72
72
  const s = (() => {
73
73
  try {
@@ -78,20 +78,27 @@ const P = (n) => {
78
78
  })(), a = new URL(r(e), s).href;
79
79
  return new URL(n, a).href;
80
80
  };
81
- S = [A(["storage", "preferences", "resourceLoader"])];
82
- let R = class E extends ($ = L) {
81
+ k = [j(["storage", "preferences", "resourceLoader"])];
82
+ let w = class v extends (A = T) {
83
83
  constructor(e) {
84
- super(e, {}, "plugin-store"), this.ctx = e, this.registryInfoDB = void 0, this._installedPlugins = /* @__PURE__ */ new Map(), this._registryUpdateCheckCaches = /* @__PURE__ */ new Map(), e.set("store", this), this.registryInfoDB = e.storage.createDatabase(
85
- "plugin-store-registry",
86
- E.REGISTRY_CACHE_TTL,
87
- 1
84
+ super(e, {}, "plugin-store"), this.ctx = e, this.regInfoDB = void 0, this._installedPlugins = /* @__PURE__ */ new Map(), this._onlineRegistryQueries = /* @__PURE__ */ new Map(), e.set("store", this), this.regInfoDB = e.storage.createDatabase(
85
+ v.REGISTRY_INFO_STORAGE_NAME,
86
+ v.REGISTRY_INFO_CACHE_TTL,
87
+ 1,
88
+ "indexedDB"
88
89
  );
89
90
  }
90
91
  static {
91
- this.PluginStoreSchemas = w;
92
+ this.PluginStoreSchemas = m;
92
93
  }
93
94
  static {
94
- this.REGISTRY_CACHE_TTL = 1e3 * 60 * 60 * 24;
95
+ this.REGISTRY_INFO_CACHE_TTL = 1e3 * 60 * 60 * 24;
96
+ }
97
+ static {
98
+ this.REGISTRY_INFO_STORAGE_NAME = "plugin-store-registry";
99
+ }
100
+ static {
101
+ this.REGISTRY_ETAG_STORAGE_NAME = "psreg-etag";
95
102
  }
96
103
  async start() {
97
104
  this._installUserPlugins(), this._injectPreferenceUI();
@@ -103,8 +110,8 @@ let R = class E extends ($ = L) {
103
110
  this.install(t.registry, t.id, t.source);
104
111
  }
105
112
  async _createManagementApp() {
106
- const e = await this.ctx.withInject(["store"]), t = D(() => import("./PluginStoreApp-Cxspe6t8.js"));
107
- return T(e, t);
113
+ const e = await this.ctx.withInject(["store"]), t = O(() => import("./PluginStoreApp-Bnvyl-Oc.js"));
114
+ return L(e, t);
108
115
  }
109
116
  async _injectPreferenceUI() {
110
117
  const e = this.ctx;
@@ -113,25 +120,20 @@ let R = class E extends ($ = L) {
113
120
  label: "Plugin Store",
114
121
  description: "Plugin Store",
115
122
  index: 90,
123
+ autoGenerateForm: !1,
116
124
  customRenderer: async ({ onUnmount: t }) => {
117
- const r = /* @__PURE__ */ y("div", { id: "ipe-plugin-store-preferences-vue" }), s = await this._createManagementApp();
125
+ const r = /* @__PURE__ */ R("div", { id: "ipe-plugin-store-preferences-vue" }), s = await this._createManagementApp();
118
126
  return s.mount(r), t(() => {
119
127
  s.unmount(), this.ctx.logger.debug("Plugin Store preferences app unmounted");
120
- }), /* @__PURE__ */ y("section", { children: [
121
- r,
122
- /* @__PURE__ */ y("div", { className: "theme-ipe-prose", children: [
123
- /* @__PURE__ */ y("hr", {}),
124
- /* @__PURE__ */ y("div", { style: { textAlign: "center", marginBottom: "1em", fontSize: "0.8em" }, children: "🚫 DO NOT edit fields below manually 🚫" })
125
- ] })
126
- ] });
128
+ }), /* @__PURE__ */ R("section", { children: r });
127
129
  }
128
130
  }), e.preferences.registerCustomConfig(
129
131
  "plugin-store",
130
132
  i.object({
131
133
  "pluginStore.registries": i.array(i.string()).default([
132
- j.PLUGIN_REGISTRY_URL
133
- ]).description("Registry URLs"),
134
- "pluginStore.cdnForNpm": i.string().description("CDN to install packages from npm").default('https://cdn.jsdelivr.net/npm/{{ package }}{{ version ? "@" + version : "" }}'),
134
+ C.PLUGIN_REGISTRY_URL
135
+ ]).description("Registry URLs").hidden(),
136
+ "pluginStore.cdnForNpm": i.string().description("CDN to install packages from npm").default('https://cdn.jsdelivr.net/npm/{{ package }}{{ version ? "@" + version : "" }}').hidden(),
135
137
  "pluginStore.plugins": i.array(
136
138
  i.object({
137
139
  source: i.union(["online_manifest", "npm"]).default("online_manifest"),
@@ -147,7 +149,7 @@ let R = class E extends ($ = L) {
147
149
  const e = this.ctx.modal.show({
148
150
  title: "Plugin Store",
149
151
  sizeClass: "small"
150
- }), t = /* @__PURE__ */ y("section", { id: "ipe-plugin-store-vue" });
152
+ }), t = /* @__PURE__ */ R("section", { id: "ipe-plugin-store-vue" });
151
153
  e.setContent(t);
152
154
  const r = await this._createManagementApp();
153
155
  return r.mount(t), e.on(e.Event.Close, () => {
@@ -155,17 +157,20 @@ let R = class E extends ($ = L) {
155
157
  }), e;
156
158
  }
157
159
  async install(e, t, r = "online_manifest") {
158
- const s = await this.getRegistryInfo(e), a = `${e}:${t}`;
160
+ const s = await this.getRegistryInfo(e, r);
161
+ if (!s)
162
+ return this.ctx.logger.warn(`Registry ${e} not found`), null;
163
+ const a = `${e}#${t}`;
159
164
  if (this._installedPlugins.has(a))
160
165
  return await this._installedPlugins.get(a) ?? null;
161
- const o = this._installOneByRegistryInfo(e, s, t);
162
- return this._installedPlugins.set(a, o), await o;
166
+ const l = this._installOneByRegistryInfo(e, s, t);
167
+ return this._installedPlugins.set(a, l), await l;
163
168
  }
164
169
  async uninstall(e, t) {
165
- const r = this._installedPlugins.get(`${e}:${t}`);
170
+ const r = this._installedPlugins.get(`${e}#${t}`);
166
171
  if (r === void 0)
167
172
  return !0;
168
- this._installedPlugins.delete(`${e}:${t}`);
173
+ this._installedPlugins.delete(`${e}#${t}`);
169
174
  const s = await r;
170
175
  return s ? s.dispose?.() ?? !0 : !0;
171
176
  }
@@ -185,35 +190,35 @@ let R = class E extends ($ = L) {
185
190
  }
186
191
  // 3) 增加 registryUrl 参数
187
192
  async _installOneByRegistryInfo(e, t, r) {
188
- const s = t.base_url, a = t.packages.find((l) => l.id === r);
193
+ const s = t.base_url, a = t.packages.find((o) => o.id === r);
189
194
  if (!a)
190
195
  return this.ctx.logger.warn(`Package ${r} not found in registry ${e}`), null;
191
- const o = a.loader, { kind: h, entry: f = "index.js", styles: g = [], main_export: p = null } = o;
196
+ const l = a.loader, { kind: f, entry: h = "index.js", styles: g = [], main_export: p = null } = l;
192
197
  let c = null;
193
- if (h !== "styles") {
194
- if (!f)
195
- return this.ctx.logger.warn(`Entry url missing for ${r}`, o), null;
198
+ if (f !== "styles") {
199
+ if (!h)
200
+ return this.ctx.logger.warn(`Entry url missing for ${r}`, l), null;
196
201
  try {
197
- c = I(f, s, e);
198
- } catch (l) {
202
+ c = E(h, s, e);
203
+ } catch (o) {
199
204
  return this.ctx.logger.warn(
200
- `Failed to resolve entry "${f}" with base "${s}" and registry "${e}"`,
201
- l
205
+ `Failed to resolve entry "${h}" with base "${s}" and registry "${e}"`,
206
+ o
202
207
  ), null;
203
208
  }
204
209
  }
205
- const m = {
210
+ const y = {
206
211
  "data-plugin-registry": e,
207
212
  "data-plugin-id": r
208
213
  };
209
214
  let d = null;
210
- switch (h) {
215
+ switch (f) {
211
216
  case "autoload": {
212
217
  d = this.ctx.plugin({
213
218
  inject: ["resourceLoader"],
214
219
  name: `plugin-store-${e}-${r}`,
215
- apply: (l) => {
216
- c && l.resourceLoader.loadScript(c, { ...m });
220
+ apply: (o) => {
221
+ c && o.resourceLoader.loadScript(c, { ...y });
217
222
  }
218
223
  });
219
224
  break;
@@ -221,36 +226,36 @@ let R = class E extends ($ = L) {
221
226
  case "module": {
222
227
  if (!c)
223
228
  return this.ctx.logger.warn(`Entry url missing for module kind, package ${r}`), null;
224
- const l = await import(
229
+ const o = await import(
225
230
  /* @vite-ignore */
226
231
  c
227
232
  ).then(
228
233
  (u) => (p ? u[p] : u.default) ?? u
229
234
  );
230
- if (!l)
235
+ if (!o)
231
236
  return this.ctx.logger.warn(`Main export ${p} not found in module ${c}`), null;
232
- d = this.ctx.plugin(l);
237
+ d = this.ctx.plugin(o);
233
238
  break;
234
239
  }
235
240
  case "umd": {
236
- let l = P(p);
237
- if (!l) {
241
+ let o = P(p);
242
+ if (!o) {
238
243
  if (!c)
239
244
  return this.ctx.logger.warn(`Entry url missing for umd kind, package ${r}`), null;
240
- await this.ctx.resourceLoader.loadScript(c, { ...m }), l = P(p);
245
+ await this.ctx.resourceLoader.loadScript(c, { ...y }), o = P(p);
241
246
  }
242
- if (!l)
247
+ if (!o)
243
248
  return this.ctx.logger.warn(
244
249
  `Main export ${p} not found on globalThis after loading ${c}`
245
250
  ), null;
246
- d = this.ctx.plugin(l);
251
+ d = this.ctx.plugin(o);
247
252
  break;
248
253
  }
249
254
  }
250
255
  if (g && g.length > 0) {
251
- let l = [];
256
+ let o = [];
252
257
  try {
253
- l = g.map((u) => I(u, s, e)).filter(Boolean);
258
+ o = g.map((u) => E(u, s, e)).filter(Boolean);
254
259
  } catch (u) {
255
260
  this.ctx.logger.warn(`Failed to resolve styles for ${r}`, g, u);
256
261
  }
@@ -259,9 +264,9 @@ let R = class E extends ($ = L) {
259
264
  inject: ["resourceLoader"],
260
265
  name: `plugin-store-${e}-${r}-styles`,
261
266
  apply: (u) => {
262
- l.forEach((_) => u.resourceLoader.loadStyle(_, { ...m })), u.on("dispose", () => {
267
+ o.forEach((_) => u.resourceLoader.loadStyle(_, { ...y })), u.on("dispose", () => {
263
268
  try {
264
- l.forEach((_) => u.resourceLoader.removeStyle(_));
269
+ o.forEach((_) => u.resourceLoader.removeStyle(_));
265
270
  } catch {
266
271
  }
267
272
  });
@@ -271,95 +276,86 @@ let R = class E extends ($ = L) {
271
276
  return d;
272
277
  }
273
278
  validateRegistry(e) {
274
- return w.Registry(e);
275
- }
276
- async getRegistryInfo(e) {
277
- const t = await this.getRegistryCache(e);
278
- if (t) return t;
279
- const r = await this._fetchOnlineRegistryInfo(e);
280
- return await this.setRegistryCache(e, r), r;
281
- }
282
- async _fetchOnlineRegistryInfo(e) {
283
- const r = await (await fetch(e)).json(), s = this.validateRegistry(r);
284
- return this.ctx.storage.simpleKV.set(`plugin-store-last-fetch/${e}`, Date.now()), s;
279
+ return m.Registry(e);
285
280
  }
286
- /**
287
- * 检查自从上次缓存后,registry 是否有更新
288
- * @param registry
289
- * @returns
290
- */
291
- async _checkIfOnlineRegistryUpdated(e) {
292
- if (this._registryUpdateCheckCaches.has(e))
293
- return this._registryUpdateCheckCaches.get(e);
294
- const t = (async () => {
295
- try {
296
- const s = await fetch(e, { method: "HEAD" });
297
- if (!s.ok)
298
- throw new Error(s.statusText, { cause: s });
299
- if (s.status === 304)
300
- return this.ctx.logger.debug("Registry not modified:", e), !1;
301
- const a = s.headers.get("Last-Modified");
302
- if (a) {
303
- const o = Date.parse(a);
304
- if (!isNaN(o))
305
- return !1;
306
- const h = await this.ctx.storage.simpleKV.get(
307
- `plugin-store-last-fetch/${e}`
308
- );
309
- return !h || o > h ? (await this.ctx.storage.simpleKV.set(`plugin-store-last-fetch/${e}`, o), !0) : !1;
281
+ async getRegistryInfo(e, t = "online_manifest", r = !1) {
282
+ try {
283
+ let a;
284
+ switch (t) {
285
+ case "online_manifest": {
286
+ a = await this._fetchOnlineRegistryInfo(e, r), this.logger.debug("Fetched registry info from online manifest", a), this.setRegistryCache(e, a);
287
+ break;
310
288
  }
311
- } catch (s) {
312
- this.ctx.logger.warn("Failed to fetch registry HEAD for last-modified", e, s);
289
+ default:
290
+ throw new Error(`Unsupported registry source: ${t}`);
313
291
  }
314
- return !1;
292
+ return a;
293
+ } catch (a) {
294
+ this.ctx.logger.warn("Failed to fetch registry info", a);
295
+ }
296
+ const s = await this.getRegistryCache(e);
297
+ if (!s)
298
+ throw new Error(`Failed to fetch registry info: ${e}`);
299
+ return s;
300
+ }
301
+ async _fetchOnlineRegistryInfo(e, t = !1) {
302
+ if (!t && this._onlineRegistryQueries.has(e))
303
+ return await this._onlineRegistryQueries.get(e);
304
+ const s = (async () => {
305
+ const a = {
306
+ method: "GET"
307
+ };
308
+ t && (a.cache = "no-store");
309
+ const f = await (await fetch(e, a)).json();
310
+ return this.validateRegistry(f);
315
311
  })();
316
- return this._registryUpdateCheckCaches.set(e, t), await t;
312
+ return this._onlineRegistryQueries.set(e, s), await s;
317
313
  }
318
314
  async getRegistryCache(e) {
319
- const t = await this.registryInfoDB.get(e);
315
+ const t = await this.regInfoDB.get(e);
320
316
  if (t)
321
317
  try {
322
- return await this._checkIfOnlineRegistryUpdated(e) ? (this.ctx.logger.debug("Registry updated online, ignoring cache:", e), this.deleteRegistryCache(e), null) : this.validateRegistry(t);
318
+ return this.validateRegistry(t);
323
319
  } catch (r) {
324
- this.ctx.logger.warn("Invalid cached registry", r, t), this.registryInfoDB.delete(e);
320
+ this.ctx.logger.warn("Invalid cached registry", r, t), this.regInfoDB.delete(e);
325
321
  }
326
322
  return null;
327
323
  }
328
324
  async setRegistryCache(e, t) {
329
- return this.registryInfoDB.set(e, t);
325
+ return this.regInfoDB.set(e, t);
330
326
  }
331
327
  async deleteRegistryCache(e) {
332
- await Promise.all([
333
- this.registryInfoDB.delete(e),
334
- this.ctx.storage.simpleKV.delete(`plugin-store-last-fetch/${e}`)
335
- ]);
328
+ await this.regInfoDB.delete(e);
336
329
  }
337
330
  async clearAllRegistryCaches() {
338
- await Promise.all([this.registryInfoDB.clear(), this.ctx.storage.simpleKV.clear()]), this.ctx.logger.debug("All registry caches cleared");
331
+ await this.regInfoDB.clear(), this.ctx.logger.debug("All registry caches cleared");
339
332
  }
340
333
  /**
341
334
  * 刷新指定 registry 的缓存(重新从网络获取)
342
335
  */
343
336
  async refreshRegistryCache(e) {
344
- await this.deleteRegistryCache(e);
345
- const t = await this._fetchOnlineRegistryInfo(e);
346
- return await this.setRegistryCache(e, t), this.ctx.logger.debug("Registry cache refreshed:", e), t;
337
+ const t = await this.getRegistryInfo(e, "online_manifest", !0);
338
+ if (!t)
339
+ throw new Error(`Failed to refresh registry cache: ${e}`);
340
+ return this.ctx.logger.debug("Registry cache refreshed:", e), t;
347
341
  }
348
342
  /**
349
343
  * 刷新所有已配置的 registry 缓存
350
344
  */
351
345
  async refreshAllRegistryCaches() {
352
- const e = await this.ctx.preferences.get("pluginStore.registries") || [], r = (await Promise.allSettled(
346
+ const e = await this.ctx.preferences.get("pluginStore.registries") || [], t = await Promise.allSettled(
353
347
  e.map((s) => this.refreshRegistryCache(s))
354
- )).filter((s) => s.status === "fulfilled").map((s) => s.value);
355
- return this.ctx.logger.debug(`Refreshed ${r.length} registry caches`), r;
348
+ ), r = {};
349
+ for (const [s, a] of t.entries())
350
+ a.status === "fulfilled" ? r[e[s]] = a.value : r[e[s]] = null;
351
+ return r;
356
352
  }
357
353
  };
358
- x = N($);
359
- R = K(x, 0, "PluginPluginStore", S, R);
360
- V(x, 1, R);
361
- let J = R;
354
+ I = N(A);
355
+ w = Q(I, 0, "PluginPluginStore", k, w);
356
+ z(I, 1, w);
357
+ let J = w;
362
358
  export {
363
359
  J as PluginPluginStore
364
360
  };
365
- //# sourceMappingURL=index-DEav9Ptt.js.map
361
+ //# sourceMappingURL=index-B3YJdjxo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B3YJdjxo.js","sources":["../src/plugins/plugin-store/schema.ts","../src/plugins/plugin-store/index.tsx"],"sourcesContent":["import Schema from 'schemastery'\n\nexport const PLUGIN_STORE_REGISTRY_MANIFEST_VERSION = 1\n\nexport interface PluginStoreRegistry {\n manifest_version: number\n name: string\n base_url: string\n homepage?: string\n maintainers?: PluginStoreRegistryMaintainer[]\n repository?: PluginStoreRegistryRepository\n packages: PluginStorePackage[]\n}\n\nexport interface PluginStoreRegistryMaintainer {\n name: string\n email?: string\n}\n\nexport interface PluginStoreRegistryRepository {\n type: 'git'\n url: string\n}\n\nexport interface PluginStorePackage {\n id: string\n name: string\n version: string\n description?: string\n author?: string\n license?: string\n loader: PluginStorePackageLoader\n}\n\nexport interface PluginStorePackageLoader {\n kind: 'autoload' | 'module' | 'umd' | 'styles'\n entry?: string\n styles?: string[]\n main_export?: string\n}\n\nexport namespace PluginStoreSchemas {\n export const PackageLoader = new Schema<PluginStorePackageLoader>(\n Schema.object({\n kind: Schema.union(['autoload', 'module', 'umd', 'styles']).required().default('autoload'),\n entry: Schema.string(),\n styles: Schema.array(Schema.string()),\n main_export: Schema.string(),\n })\n )\n\n export const Package = new Schema<PluginStorePackage>(\n Schema.object({\n id: Schema.string().required(),\n name: Schema.string().required(),\n version: Schema.string().required(),\n description: Schema.string(),\n author: Schema.string(),\n license: Schema.string(),\n loader: PackageLoader.required(),\n })\n )\n\n export const RegistryMaintainer = new Schema<PluginStoreRegistryMaintainer>(\n Schema.object({\n name: Schema.string().required(),\n email: Schema.string(),\n })\n )\n\n export const RegistryRepository = new Schema<PluginStoreRegistryRepository>(\n Schema.object({\n type: Schema.union(['git']).required().default('git'),\n url: Schema.string().required(),\n })\n )\n\n export const Registry = new Schema<PluginStoreRegistry>(\n Schema.object({\n manifest_version: Schema.number().min(PLUGIN_STORE_REGISTRY_MANIFEST_VERSION).required(),\n name: Schema.string().required(),\n base_url: Schema.string().required(),\n homepage: Schema.string(),\n maintainers: Schema.array(RegistryMaintainer),\n repository: RegistryRepository,\n packages: Schema.array(Package).required(),\n })\n )\n}\n","import { InPageEdit, Schema } from '@/InPageEdit'\nimport { ForkScope, Inject } from '@cordisjs/core'\nimport { defineAsyncComponent, type App as VueApp } from 'vue'\nimport { PluginStoreRegistry, PluginStoreSchemas } from './schema'\nimport { AbstractIPEStorageManager } from '@/services/storage/index.js'\n\ndeclare module '@/InPageEdit' {\n interface InPageEdit {\n store: PluginPluginStore\n }\n interface PreferencesMap {\n 'pluginStore.registries': string[]\n 'pluginStore.plugins': { source?: 'online_manifest' | 'npm'; registry: string; id: string }[]\n 'pluginStore.cdnForNpm': string\n }\n}\n\nexport type PluginStoreRegistrySource = 'online_manifest' | 'npm'\n\nconst tryGetGlobalPlugin = (main_export: string) => {\n if (!main_export) return null\n try {\n return main_export\n .split('.')\n .reduce<any>((acc, key) => (acc == null ? acc : acc[key]), globalThis as any)\n } catch {\n return null\n }\n}\n\n/**\n * 解析资源URL\n * @param resourcePath - entry或style路径,可能是相对路径或完整URL\n * @param baseUrl - registry中的base_url,可能是完整URL或相对路径\n * @param registryUrl - registry的URL(必须是绝对URL,作为相对baseUrl时的基准)\n * @returns 解析后的完整URL\n */\nconst resolveResourceUrl = (resourcePath: string, baseUrl: string, registryUrl: string): string => {\n // 完整URL:直接返回\n if (/^https?:\\/\\//i.test(resourcePath)) return resourcePath\n\n // 兜底:规范化一个“带尾斜杠”的 helper\n const ensureSlash = (s: string) => (s.endsWith('/') ? s : s + '/')\n\n // 如果 baseUrl 是完整URL:直接用它当基准(并确保目录语义)\n if (/^https?:\\/\\//i.test(baseUrl)) {\n const normalizedBase = ensureSlash(baseUrl)\n return new URL(resourcePath, normalizedBase).href\n }\n\n // baseUrl 是相对路径(如 './' 或 '/plugins')\n // 先把 baseUrl 相对于 registryUrl 解析成绝对URL\n // 注意:当 baseUrl 是 '/plugins' 这种根相对路径时,new URL 会以 registryUrl 的 origin 作为根。\n const registryAbs = (() => {\n try {\n return new URL(registryUrl).href\n } catch {\n // 极端情况(开发环境传了相对 registry),用页面 origin 兜底\n return new URL(registryUrl, location.origin).href\n }\n })()\n\n const resolvedBaseUrl = new URL(ensureSlash(baseUrl), registryAbs).href\n return new URL(resourcePath, resolvedBaseUrl).href\n}\n\n@Inject(['storage', 'preferences', 'resourceLoader'])\nexport class PluginPluginStore extends BasePlugin {\n // re-export for external usage\n static readonly PluginStoreSchemas = PluginStoreSchemas\n static REGISTRY_INFO_CACHE_TTL = 1000 * 60 * 60 * 24 // 1 day\n static REGISTRY_INFO_STORAGE_NAME = 'plugin-store-registry'\n static REGISTRY_ETAG_STORAGE_NAME = 'psreg-etag'\n private regInfoDB: AbstractIPEStorageManager<PluginStoreRegistry>\n\n constructor(public ctx: InPageEdit) {\n super(ctx, {}, 'plugin-store')\n ctx.set('store', this)\n this.regInfoDB = ctx.storage.createDatabase<PluginStoreRegistry>(\n PluginPluginStore.REGISTRY_INFO_STORAGE_NAME,\n PluginPluginStore.REGISTRY_INFO_CACHE_TTL,\n 1,\n 'indexedDB'\n )\n }\n\n protected async start() {\n this._installUserPlugins()\n this._injectPreferenceUI()\n }\n\n private async _installUserPlugins() {\n const prefs = await this.ctx.preferences.get('pluginStore.plugins', [])\n if (!prefs?.length) {\n return\n }\n for (const pref of prefs) {\n this.install(pref.registry, pref.id, pref.source)\n }\n }\n\n private async _createManagementApp() {\n const ctx = await this.ctx.withInject(['store'])\n const PluginStoreApp = defineAsyncComponent(() => import('./components/PluginStoreApp.vue'))\n const app = createVueAppWithIPE(ctx, PluginStoreApp)\n return app\n }\n\n private async _injectPreferenceUI() {\n const ctx = this.ctx\n\n ctx.preferences.defineCategory({\n name: 'plugin-store',\n label: 'Plugin Store',\n description: 'Plugin Store',\n index: 90,\n autoGenerateForm: false,\n customRenderer: async ({ onUnmount }) => {\n const container = <div id=\"ipe-plugin-store-preferences-vue\"></div>\n const app = await this._createManagementApp()\n app.mount(container)\n\n onUnmount(() => {\n app.unmount()\n this.ctx.logger.debug('Plugin Store preferences app unmounted')\n })\n\n return (\n <section>\n {container}\n {/* <div className=\"theme-ipe-prose\">\n <hr />\n <div style={{ textAlign: 'center', marginBottom: '1em', fontSize: '0.8em' }}>\n 🚫 DO NOT edit fields below manually 🚫\n </div>\n </div> */}\n </section>\n )\n },\n })\n\n ctx.preferences.registerCustomConfig(\n 'plugin-store',\n Schema.object({\n 'pluginStore.registries': Schema.array(Schema.string())\n .default([\n import.meta.env.PROD\n ? Endpoints.PLUGIN_REGISTRY_URL\n : 'http://127.0.0.1:1005/src/__test__/plugin-registry/index.json',\n ])\n .description('Registry URLs')\n .hidden(),\n 'pluginStore.cdnForNpm': Schema.string()\n .description('CDN to install packages from npm')\n .default('https://cdn.jsdelivr.net/npm/{{ package }}{{ version ? \"@\" + version : \"\" }}')\n .hidden(),\n 'pluginStore.plugins': Schema.array(\n Schema.object({\n source: Schema.union(['online_manifest', 'npm']).default('online_manifest'),\n registry: Schema.string().required(),\n id: Schema.string().required(),\n })\n )\n .description('Installed plugins')\n .default([])\n .hidden(),\n }),\n 'plugin-store'\n )\n }\n\n async showModal() {\n const modal = this.ctx.modal.show({\n title: 'Plugin Store',\n sizeClass: 'small',\n })\n const container = <section id=\"ipe-plugin-store-vue\"></section>\n modal.setContent(container)\n const app = await this._createManagementApp()\n app.mount(container)\n modal.on(modal.Event.Close, () => {\n app.unmount()\n })\n return modal\n }\n\n private _installedPlugins = new Map<string, Promise<ForkScope<InPageEdit> | null>>()\n async install(\n registry: string,\n id: string,\n source: PluginStoreRegistrySource = 'online_manifest'\n ): Promise<ForkScope<InPageEdit> | null> {\n const registryInfo = await this.getRegistryInfo(registry, source)\n if (!registryInfo) {\n this.ctx.logger.warn(`Registry ${registry} not found`)\n return null\n }\n const key = `${registry}#${id}`\n if (this._installedPlugins.has(key)) {\n return (await this._installedPlugins.get(key)) ?? null\n }\n // 2) 把 registry 原始 URL 传进去,供 URL 解析使用\n const scope = this._installOneByRegistryInfo(registry, registryInfo, id)\n this._installedPlugins.set(key, scope)\n return await scope\n }\n async uninstall(registry: string, id: string): Promise<boolean> {\n const promise = this._installedPlugins.get(`${registry}#${id}`)\n if (promise === void 0) {\n return true // not installed or already uninstalled\n }\n this._installedPlugins.delete(`${registry}#${id}`)\n const scope = await promise\n if (scope) {\n return scope.dispose?.() ?? true // disposed successfully\n }\n return true // not a plugin, just removed from the list\n }\n\n async addToPreferences(registry: string, id: string) {\n let prefs =\n (await this.ctx.preferences.get<{ registry: string; id: string }[]>('pluginStore.plugins')) ||\n []\n const existed = prefs.some((p) => p.registry === registry && p.id === id)\n if (existed) {\n return true\n }\n prefs.push({ registry, id })\n await this.ctx.preferences.set('pluginStore.plugins', prefs)\n return true\n }\n async removeFromPreferences(registry: string, id: string) {\n let prefs =\n (await this.ctx.preferences.get<{ registry: string; id: string }[]>('pluginStore.plugins')) ||\n []\n prefs = prefs.filter((p) => p.registry !== registry || p.id !== id)\n await this.ctx.preferences.set('pluginStore.plugins', prefs)\n return true\n }\n\n async installAndSetPreference(registry: string, id: string) {\n await this.addToPreferences(registry, id)\n return this.install(registry, id)\n }\n async uninstallAndRemovePreference(registry: string, id: string) {\n await this.removeFromPreferences(registry, id)\n return this.uninstall(registry, id)\n }\n\n // 3) 增加 registryUrl 参数\n private async _installOneByRegistryInfo(\n registryUrl: string,\n registryInfo: PluginStoreRegistry,\n id: string\n ): Promise<ForkScope<InPageEdit> | null> {\n const baseUrl = registryInfo.base_url\n const pkg = registryInfo.packages.find((p) => p.id === id)\n if (!pkg) {\n this.ctx.logger.warn(`Package ${id} not found in registry ${registryUrl}`)\n return null\n }\n\n const loader = pkg.loader\n const { kind, entry = 'index.js', styles = [], main_export = null } = loader\n\n // 4) 统一用 resolveResourceUrl 解析入口\n let entryUrl: string | null = null\n if (kind !== 'styles') {\n if (!entry) {\n this.ctx.logger.warn(`Entry url missing for ${id}`, loader)\n return null\n }\n try {\n entryUrl = resolveResourceUrl(entry, baseUrl, registryUrl)\n } catch (e) {\n this.ctx.logger.warn(\n `Failed to resolve entry \"${entry}\" with base \"${baseUrl}\" and registry \"${registryUrl}\"`,\n e\n )\n return null\n }\n }\n\n const datasets = {\n 'data-plugin-registry': registryUrl,\n 'data-plugin-id': id,\n }\n\n let fork: ForkScope<InPageEdit> | null = null\n\n switch (kind) {\n case 'autoload': {\n fork = this.ctx.plugin({\n inject: ['resourceLoader'],\n name: `plugin-store-${registryUrl}-${id}`,\n apply: (ctx) => {\n if (!entryUrl) return\n ctx.resourceLoader.loadScript(entryUrl, { ...datasets })\n },\n })\n break\n }\n case 'module': {\n if (!entryUrl) {\n this.ctx.logger.warn(`Entry url missing for module kind, package ${id}`)\n return null\n }\n const apply = await import(/* @vite-ignore */ entryUrl).then(\n (m) => (main_export ? m[main_export] : m.default) ?? m\n )\n if (!apply) {\n this.ctx.logger.warn(`Main export ${main_export} not found in module ${entryUrl}`)\n return null\n }\n fork = this.ctx.plugin(apply)\n break\n }\n case 'umd': {\n let apply = tryGetGlobalPlugin(main_export!)\n if (!apply) {\n if (!entryUrl) {\n this.ctx.logger.warn(`Entry url missing for umd kind, package ${id}`)\n return null\n }\n await this.ctx.resourceLoader.loadScript(entryUrl, { ...datasets })\n apply = tryGetGlobalPlugin(main_export!)\n }\n if (!apply) {\n this.ctx.logger.warn(\n `Main export ${main_export} not found on globalThis after loading ${entryUrl}`\n )\n return null\n }\n fork = this.ctx.plugin(apply)\n break\n }\n case 'styles': {\n // 没有脚本,仅样式。下面统一样式注入逻辑会覆盖\n break\n }\n }\n\n // 5) 统一解析并注入样式(兼容绝对URL与相对 + base_url 的组合)\n if (styles && styles.length > 0) {\n let urls: string[] = []\n try {\n urls = styles.map((u) => resolveResourceUrl(u, baseUrl, registryUrl)).filter(Boolean)\n } catch (e) {\n this.ctx.logger.warn(`Failed to resolve styles for ${id}`, styles, e)\n }\n\n // 确保存在父 fork\n fork ||= this.ctx.plugin({ name: `plugin-store-${registryUrl}-${id}`, apply() {} })\n\n // 在子插件里加载样式,并在卸载时清理\n fork.ctx.plugin({\n inject: ['resourceLoader'],\n name: `plugin-store-${registryUrl}-${id}-styles`,\n apply: (ctx) => {\n urls.forEach((u) => ctx.resourceLoader.loadStyle(u, { ...datasets }))\n ctx.on('dispose', () => {\n try {\n urls.forEach((u) => ctx.resourceLoader.removeStyle(u))\n } catch (e) {\n console.info('styles cleanup failed', e)\n }\n })\n },\n })\n }\n\n return fork\n }\n\n validateRegistry(data: any): PluginStoreRegistry {\n return PluginStoreSchemas.Registry(data)\n }\n\n async getRegistryInfo(\n registry: string,\n source: PluginStoreRegistrySource = 'online_manifest',\n noCache = false\n ): Promise<PluginStoreRegistry> {\n try {\n let info: PluginStoreRegistry\n switch (source) {\n case 'online_manifest': {\n info = await this._fetchOnlineRegistryInfo(registry, noCache)\n this.logger.debug('Fetched registry info from online manifest', info)\n this.setRegistryCache(registry, info)\n break\n }\n default: {\n throw new Error(`Unsupported registry source: ${source}`)\n }\n }\n\n return info\n } catch (e) {\n this.ctx.logger.warn('Failed to fetch registry info', e)\n }\n\n const info = await this.getRegistryCache(registry)\n if (!info) {\n throw new Error(`Failed to fetch registry info: ${registry}`)\n }\n return info\n }\n\n private _onlineRegistryQueries = new Map<string, Promise<PluginStoreRegistry>>()\n private async _fetchOnlineRegistryInfo(\n registry: string,\n noCache = false\n ): Promise<PluginStoreRegistry> {\n if (!noCache && this._onlineRegistryQueries.has(registry)) {\n return await this._onlineRegistryQueries.get(registry)!\n }\n const task = async () => {\n const payload: RequestInit = {\n method: 'GET',\n }\n if (noCache) {\n payload.cache = 'no-store'\n }\n const response = await fetch(registry, payload)\n const data = await response.json()\n const validated = this.validateRegistry(data)\n return validated\n }\n const promise = task()\n this._onlineRegistryQueries.set(registry, promise)\n return await promise\n }\n\n private async getRegistryCache(registry: string) {\n const data = await this.regInfoDB.get(registry)\n if (data) {\n try {\n const validated = this.validateRegistry(data)\n return validated\n } catch (e) {\n this.ctx.logger.warn('Invalid cached registry', e, data)\n this.regInfoDB.delete(registry)\n }\n }\n return null\n }\n private async setRegistryCache(registry: string, data: PluginStoreRegistry) {\n return this.regInfoDB.set(registry, data)\n }\n private async deleteRegistryCache(registry: string) {\n await this.regInfoDB.delete(registry)\n }\n async clearAllRegistryCaches() {\n await this.regInfoDB.clear()\n this.ctx.logger.debug('All registry caches cleared')\n }\n\n /**\n * 刷新指定 registry 的缓存(重新从网络获取)\n */\n async refreshRegistryCache(registry: string): Promise<PluginStoreRegistry> {\n const data = await this.getRegistryInfo(registry, 'online_manifest', true)\n if (!data) {\n throw new Error(`Failed to refresh registry cache: ${registry}`)\n }\n this.ctx.logger.debug('Registry cache refreshed:', registry)\n return data\n }\n\n /**\n * 刷新所有已配置的 registry 缓存\n */\n async refreshAllRegistryCaches(): Promise<Record<string, PluginStoreRegistry | null>> {\n const registryUrls = (await this.ctx.preferences.get('pluginStore.registries')) || []\n const responses = await Promise.allSettled(\n registryUrls.map((url) => this.refreshRegistryCache(url))\n )\n const results: Record<string, PluginStoreRegistry | null> = {}\n for (const [index, response] of responses.entries()) {\n if (response.status === 'fulfilled') {\n results[registryUrls[index]] = response.value\n } else {\n results[registryUrls[index]] = null\n }\n }\n return results\n }\n}\n"],"names":["PLUGIN_STORE_REGISTRY_MANIFEST_VERSION","PluginStoreSchemas","Schema","_PluginPluginStore_decorators","_init","_a","tryGetGlobalPlugin","main_export","acc","key","resolveResourceUrl","resourcePath","baseUrl","registryUrl","ensureSlash","s","normalizedBase","registryAbs","resolvedBaseUrl","Inject","_PluginPluginStore","BasePlugin","ctx","prefs","pref","PluginStoreApp","defineAsyncComponent","createVueAppWithIPE","onUnmount","container","jsx","app","Endpoints","modal","registry","id","source","registryInfo","scope","promise","p","pkg","loader","kind","entry","styles","entryUrl","e","datasets","fork","apply","m","urls","u","data","noCache","info","payload","registryUrls","responses","url","results","index","response","__decoratorStart","__decorateElement","__runInitializers","PluginPluginStore"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { j as c } from "./index-CM_6yF2v.js";
2
- import { b as D, I as C, S as f } from "./index-BXaiDKnr.js";
2
+ import { b as D, I as C, S as f } from "./index-DgQNTfPR.js";
3
3
  import { R as E } from "./Preferences-DS4-CFWe.js";
4
4
  var I = Object.create, v = Object.defineProperty, T = Object.getOwnPropertyDescriptor, y = (e, t) => (t = Symbol[e]) ? t : Symbol.for("Symbol." + e), _ = (e) => {
5
5
  throw TypeError(e);
@@ -143,4 +143,4 @@ A(m, 1, x);
143
143
  export {
144
144
  x as PluginToolbox
145
145
  };
146
- //# sourceMappingURL=index-Bv7Dw5eO.js.map
146
+ //# sourceMappingURL=index-BRjEc8b8.js.map