@bbki.ng/site 5.6.3 → 5.6.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.
- package/CHANGELOG.md +14 -0
- package/package.json +2 -2
- package/src/blog/utils/fingerprints.ts +8 -3
- package/src/core/bbplugin.ts +3 -2
- package/src/core/hooks/use_plugins.ts +29 -13
- package/src/core/pluginManager.ts +2 -0
- package/src/core/pluginManifestService.ts +123 -0
- package/src/core/pluginStore.ts +6 -4
- package/src/plugins/manifest.ts +1 -6
- package/src/plugins/now/context/index.ts +3 -0
- package/src/plugins/now/hooks/use_streaming.ts +12 -41
- package/src/plugins/now/index.ts +1 -0
- package/src/plugins/store/index.ts +7 -5
- package/src/types/hostApi.ts +2 -0
- package/src/types/plugin.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @bbki.ng/site
|
|
2
2
|
|
|
3
|
+
## 5.6.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fdefcf5: fix btn font
|
|
8
|
+
- Updated dependencies [fdefcf5]
|
|
9
|
+
- @bbki.ng/ui@0.2.19
|
|
10
|
+
|
|
11
|
+
## 5.6.4
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- d1b95ed: add api to fetch manifest
|
|
16
|
+
|
|
3
17
|
## 5.6.3
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbki.ng/site",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.5",
|
|
4
4
|
"description": "code behind bbki.ng",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"react-dom": "^18.0.0",
|
|
15
15
|
"react-router-dom": "6",
|
|
16
16
|
"swr": "^2.2.5",
|
|
17
|
-
"@bbki.ng/ui": "0.2.
|
|
17
|
+
"@bbki.ng/ui": "0.2.19"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@eslint/compat": "^1.0.0",
|
|
@@ -9,6 +9,8 @@ export interface FingerprintData {
|
|
|
9
9
|
generatedAt: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
type GLenum = number;
|
|
13
|
+
|
|
12
14
|
export interface FingerprintComponents {
|
|
13
15
|
// 基础环境
|
|
14
16
|
userAgent: string;
|
|
@@ -143,7 +145,7 @@ function getWebGLInfo(): WebGLInfo {
|
|
|
143
145
|
|
|
144
146
|
params.forEach(p => {
|
|
145
147
|
try {
|
|
146
|
-
const val = gl.getParameter((gl as
|
|
148
|
+
const val = gl.getParameter((gl as unknown as Record<string, GLenum>)[p] as GLenum);
|
|
147
149
|
result.params[p] = Array.isArray(val) ? val.join(',') : String(val);
|
|
148
150
|
} catch {
|
|
149
151
|
result.params[p] = 'unsupported';
|
|
@@ -220,7 +222,10 @@ function getFontList(): string[] {
|
|
|
220
222
|
// 音频指纹(频率响应差异)
|
|
221
223
|
async function getAudioFingerprint(): Promise<string | undefined> {
|
|
222
224
|
try {
|
|
223
|
-
const AudioContext =
|
|
225
|
+
const AudioContext =
|
|
226
|
+
window.OfflineAudioContext ||
|
|
227
|
+
(window as unknown as { webkitOfflineAudioContext?: typeof OfflineAudioContext })
|
|
228
|
+
.webkitOfflineAudioContext;
|
|
224
229
|
if (!AudioContext) return undefined;
|
|
225
230
|
|
|
226
231
|
const ctx = new AudioContext(1, 44100, 44100);
|
|
@@ -271,7 +276,7 @@ async function getFingerprint(): Promise<FingerprintData> {
|
|
|
271
276
|
platform: navigator.platform,
|
|
272
277
|
cookieEnabled: navigator.cookieEnabled,
|
|
273
278
|
hardwareConcurrency: navigator.hardwareConcurrency || 0,
|
|
274
|
-
deviceMemory: (navigator as
|
|
279
|
+
deviceMemory: (navigator as unknown as { deviceMemory?: number }).deviceMemory,
|
|
275
280
|
maxTouchPoints: navigator.maxTouchPoints || 0,
|
|
276
281
|
screenResolution: `${screen.width}x${screen.height}`,
|
|
277
282
|
screenColorDepth: screen.colorDepth,
|
package/src/core/bbplugin.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { ManifestMap } from '#/plugins/manifest';
|
|
2
1
|
import { IHostContext } from '#/types/hostApi';
|
|
3
2
|
import { IPlugin, IPluginManifestEntry, PluginID } from '#/types/plugin';
|
|
4
3
|
|
|
4
|
+
import { PluginManifestService } from './pluginManifestService';
|
|
5
|
+
|
|
5
6
|
export class BBPlugin implements IPlugin {
|
|
6
7
|
id: PluginID = 'default';
|
|
7
8
|
onInstall?: ((ctx: IHostContext) => void | Promise<void>) | undefined;
|
|
8
9
|
onDisable?: (() => Promise<void> | void) | undefined;
|
|
9
10
|
onDestroy?: (() => void) | undefined;
|
|
10
11
|
getMeta(): IPluginManifestEntry {
|
|
11
|
-
return
|
|
12
|
+
return PluginManifestService.getInstance().getPlugin(this.id) as IPluginManifestEntry;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { useGlobalLoading } from '@/hooks';
|
|
4
4
|
import { pluginManager } from '#/core/pluginManager';
|
|
5
5
|
import { PluginID } from '#/types/plugin';
|
|
6
6
|
|
|
7
|
+
import { PluginManifestService } from '../pluginManifestService';
|
|
7
8
|
import { PluginStore } from '../pluginStore';
|
|
8
9
|
|
|
9
10
|
const usePluginsLoading = () => {
|
|
@@ -46,26 +47,41 @@ export const usePlugins = () => {
|
|
|
46
47
|
|
|
47
48
|
usePluginsLoading();
|
|
48
49
|
|
|
49
|
-
const loadAllPlugins = useCallback(async () => {
|
|
50
|
-
try {
|
|
51
|
-
await Promise.all(pluginIds.map(id => pluginManager.loadPlugin(id)));
|
|
52
|
-
setDone(true);
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error('Error loading plugins:', error);
|
|
55
|
-
}
|
|
56
|
-
}, [pluginIds]);
|
|
57
|
-
|
|
58
50
|
useEffect(() => {
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
let cancelled = false;
|
|
52
|
+
|
|
53
|
+
PluginManifestService.getInstance()
|
|
54
|
+
.fetch()
|
|
55
|
+
.then(() => {
|
|
56
|
+
if (cancelled) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
Promise.all(pluginIds.map(id => pluginManager.loadPlugin(id)))
|
|
60
|
+
.then(() => {
|
|
61
|
+
if (!cancelled) {
|
|
62
|
+
setDone(true);
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.catch(error => {
|
|
66
|
+
if (!cancelled) {
|
|
67
|
+
console.error('Error loading plugins:', error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
})
|
|
71
|
+
.catch(error => {
|
|
72
|
+
if (!cancelled) {
|
|
73
|
+
console.error('Error fetching plugin manifest:', error);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
61
76
|
|
|
62
77
|
return () => {
|
|
78
|
+
cancelled = true;
|
|
63
79
|
// 卸载所有指定的插件
|
|
64
80
|
pluginIds.forEach(id => {
|
|
65
81
|
pluginManager.disablePlugin(id);
|
|
66
82
|
});
|
|
67
83
|
};
|
|
68
|
-
}, [
|
|
84
|
+
}, [pluginIds]);
|
|
69
85
|
|
|
70
86
|
return done;
|
|
71
87
|
};
|
|
@@ -4,6 +4,7 @@ import { IHostContext } from '#/types/hostApi';
|
|
|
4
4
|
import type { SlotName, HookPoint, IComPropsRegisteredToSlot } from '#/types/slots';
|
|
5
5
|
import { IPlugin, PluginEvents, PluginID, PluginPerm } from '#/types/plugin';
|
|
6
6
|
import { getStableDeviceId } from '@/utils/fingerprints';
|
|
7
|
+
import { cfApiFetcher } from '@/utils';
|
|
7
8
|
|
|
8
9
|
import { registry } from './registry';
|
|
9
10
|
import { createEventBus } from './utils/eventBus';
|
|
@@ -27,6 +28,7 @@ class PluginManager {
|
|
|
27
28
|
return {
|
|
28
29
|
...adminCtx,
|
|
29
30
|
api: {
|
|
31
|
+
fetch: cfApiFetcher,
|
|
30
32
|
setLoading: (id: PluginID, loading: boolean) => {
|
|
31
33
|
this.bus.emit('plugin:loading:changed', { id, loading });
|
|
32
34
|
},
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { cfApiFetcher } from '@/utils';
|
|
2
|
+
import { FALLBACK_MANIFEST } from '#/plugins/manifest';
|
|
3
|
+
import { IPluginManifestEntry, PluginID } from '#/types/plugin';
|
|
4
|
+
|
|
5
|
+
const KnownPluginIDSet = new Set<string>([
|
|
6
|
+
'sticker',
|
|
7
|
+
'xwy',
|
|
8
|
+
'extra-cd',
|
|
9
|
+
'extra-entry',
|
|
10
|
+
'store',
|
|
11
|
+
'now',
|
|
12
|
+
'default',
|
|
13
|
+
'fx',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
interface PluginsApiResponse {
|
|
17
|
+
status: string;
|
|
18
|
+
data: Array<{
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
perm?: string;
|
|
24
|
+
icon?: string;
|
|
25
|
+
dependencies?: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class PluginManifestService {
|
|
30
|
+
private static instance: PluginManifestService;
|
|
31
|
+
|
|
32
|
+
private manifestMap: Map<string, IPluginManifestEntry> = new Map();
|
|
33
|
+
|
|
34
|
+
private fetched = false;
|
|
35
|
+
|
|
36
|
+
private fetchPromise: Promise<void> | null = null;
|
|
37
|
+
|
|
38
|
+
private constructor() {
|
|
39
|
+
this.initWithFallback();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private initWithFallback = () => {
|
|
43
|
+
this.manifestMap = new Map(FALLBACK_MANIFEST.map(entry => [entry.id, entry]));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async fetch(): Promise<void> {
|
|
47
|
+
if (this.fetched) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (this.fetchPromise) {
|
|
51
|
+
return this.fetchPromise;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.fetchPromise = (async () => {
|
|
55
|
+
try {
|
|
56
|
+
const res = await cfApiFetcher<PluginsApiResponse>('plugins');
|
|
57
|
+
if (res.status === 'success' && Array.isArray(res.data)) {
|
|
58
|
+
const validated = res.data
|
|
59
|
+
.map((item): IPluginManifestEntry | null => {
|
|
60
|
+
if (!KnownPluginIDSet.has(item.id)) {
|
|
61
|
+
console.warn(`[PluginManifestService] Unknown plugin id from server: ${item.id}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let dependencies: PluginID[] | undefined;
|
|
66
|
+
if (item.dependencies) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(item.dependencies) as string[];
|
|
69
|
+
dependencies = parsed.filter((id): id is PluginID => KnownPluginIDSet.has(id));
|
|
70
|
+
} catch {
|
|
71
|
+
console.warn(
|
|
72
|
+
`[PluginManifestService] Failed to parse dependencies for plugin: ${item.id}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
id: item.id as PluginID,
|
|
79
|
+
name: item.name,
|
|
80
|
+
version: item.version,
|
|
81
|
+
description: item.description,
|
|
82
|
+
perm: (item.perm as 'guest' | 'admin' | undefined) || 'guest',
|
|
83
|
+
icon: item.icon,
|
|
84
|
+
dependencies,
|
|
85
|
+
};
|
|
86
|
+
})
|
|
87
|
+
.filter((item): item is IPluginManifestEntry => item !== null);
|
|
88
|
+
|
|
89
|
+
this.manifestMap = new Map(validated.map(entry => [entry.id, entry]));
|
|
90
|
+
}
|
|
91
|
+
this.fetched = true;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[PluginManifestService] Failed to fetch manifest:', error);
|
|
94
|
+
// Keep fallback data
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
|
|
98
|
+
return this.fetchPromise;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getPlugin(id: string): IPluginManifestEntry | undefined {
|
|
102
|
+
return this.manifestMap.get(id);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getAllPlugins(): IPluginManifestEntry[] {
|
|
106
|
+
return Array.from(this.manifestMap.values());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
reset(): void {
|
|
110
|
+
this.fetched = false;
|
|
111
|
+
this.fetchPromise = null;
|
|
112
|
+
this.initWithFallback();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static getInstance() {
|
|
116
|
+
if (!PluginManifestService.instance) {
|
|
117
|
+
PluginManifestService.instance = new PluginManifestService();
|
|
118
|
+
}
|
|
119
|
+
return PluginManifestService.instance;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { PluginManifestService };
|
package/src/core/pluginStore.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PLUGIN_MANIFEST } from '#/plugins/manifest';
|
|
2
1
|
import { IPluginStoreEntry, PluginID } from '#/types/plugin';
|
|
3
2
|
|
|
4
3
|
import { pluginManager } from './pluginManager';
|
|
4
|
+
import { PluginManifestService } from './pluginManifestService';
|
|
5
5
|
|
|
6
6
|
export class PluginStore {
|
|
7
7
|
private static instance: PluginStore;
|
|
@@ -35,13 +35,15 @@ export class PluginStore {
|
|
|
35
35
|
return this.parse();
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
getAllPlugins
|
|
38
|
+
async getAllPlugins(): Promise<Array<IPluginStoreEntry>> {
|
|
39
39
|
const installedPlugins = this.getInstalledPlugins();
|
|
40
|
-
|
|
40
|
+
await PluginManifestService.getInstance().fetch();
|
|
41
|
+
const manifest = PluginManifestService.getInstance().getAllPlugins();
|
|
42
|
+
return manifest.map(plugin => ({
|
|
41
43
|
...plugin,
|
|
42
44
|
enabled: installedPlugins.has(plugin.id),
|
|
43
45
|
}));
|
|
44
|
-
}
|
|
46
|
+
}
|
|
45
47
|
|
|
46
48
|
isPluginInstalled: (id: PluginID) => boolean = id => {
|
|
47
49
|
return this.getInstalledPlugins().has(id);
|
package/src/plugins/manifest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IPluginManifestEntry } from '#/types/plugin';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const FALLBACK_MANIFEST: Array<IPluginManifestEntry> = [
|
|
4
4
|
{
|
|
5
5
|
name: '贴纸',
|
|
6
6
|
id: 'sticker',
|
|
@@ -46,8 +46,3 @@ export const PLUGIN_MANIFEST: Array<IPluginManifestEntry> = [
|
|
|
46
46
|
description: '为小乌鸦合集实现定制功能或者样式的插件',
|
|
47
47
|
},
|
|
48
48
|
];
|
|
49
|
-
|
|
50
|
-
export const ManifestMap = PLUGIN_MANIFEST.reduce((map, entry) => {
|
|
51
|
-
map.set(entry.id, entry);
|
|
52
|
-
return map;
|
|
53
|
-
}, new Map<string, IPluginManifestEntry>());
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import useSWR from 'swr';
|
|
1
|
+
import useSWR, { type BareFetcher } from 'swr';
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import { baseFetcher } from '@/utils';
|
|
5
|
-
|
|
6
4
|
import { NowCtx } from '../context';
|
|
7
5
|
|
|
8
|
-
// In dev, use /api prefix to leverage Vite proxy to localhost:8787
|
|
9
|
-
// const isProd = typeof window !== 'undefined' && /^https:\/\/bbki.ng/.test(window.location.href);
|
|
10
|
-
const isProd = true;
|
|
11
|
-
const API_BASE = !isProd ? '/api' : 'https://cf.bbki.ng';
|
|
12
|
-
|
|
13
6
|
export type StreamingItem = {
|
|
14
7
|
id: string;
|
|
15
8
|
author: string;
|
|
@@ -32,41 +25,13 @@ export interface StreamingQueryParams {
|
|
|
32
25
|
offset?: number;
|
|
33
26
|
}
|
|
34
27
|
|
|
35
|
-
/**
|
|
36
|
-
* Build streaming API URL with query parameters
|
|
37
|
-
*/
|
|
38
|
-
function buildStreamingUrl(params: StreamingQueryParams = {}): string {
|
|
39
|
-
const url = new URL(`${API_BASE}/streaming`, !isProd ? window.location.origin : undefined);
|
|
40
|
-
|
|
41
|
-
if (params.before) {
|
|
42
|
-
url.searchParams.set('before', params.before);
|
|
43
|
-
}
|
|
44
|
-
if (params.after) {
|
|
45
|
-
url.searchParams.set('after', params.after);
|
|
46
|
-
}
|
|
47
|
-
if (params.offset) {
|
|
48
|
-
url.searchParams.set('offset', params.offset.toString());
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return !isProd ? url.pathname + url.search : url.toString();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Fetch streaming data from API
|
|
56
|
-
*/
|
|
57
|
-
async function fetchStreaming(params: StreamingQueryParams = {}): Promise<StreamingResponse> {
|
|
58
|
-
const url = buildStreamingUrl(params);
|
|
59
|
-
const response = await baseFetcher(url);
|
|
60
|
-
return response as StreamingResponse;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
28
|
// SWR key generator for streaming queries
|
|
64
29
|
const getStreamingKey = (params: StreamingQueryParams) => {
|
|
65
30
|
const parts = ['streaming'];
|
|
66
31
|
if (params.before) parts.push(`before=${params.before}`);
|
|
67
32
|
if (params.after) parts.push(`after=${params.after}`);
|
|
68
33
|
if (params.offset) parts.push(`offset=${params.offset}`);
|
|
69
|
-
return parts
|
|
34
|
+
return parts;
|
|
70
35
|
};
|
|
71
36
|
|
|
72
37
|
interface UseStreamingOptions {
|
|
@@ -83,12 +48,18 @@ interface UseStreamingOptions {
|
|
|
83
48
|
export function useStreaming(options: UseStreamingOptions = {}) {
|
|
84
49
|
const { refreshInterval = 1000, offset = 8 } = options;
|
|
85
50
|
|
|
51
|
+
const { fetch } = NowCtx.useCtx();
|
|
52
|
+
|
|
86
53
|
const key = getStreamingKey({ offset });
|
|
87
54
|
|
|
88
|
-
const { data, error, mutate } = useSWR(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
55
|
+
const { data, error, mutate } = useSWR<StreamingResponse>(
|
|
56
|
+
key,
|
|
57
|
+
fetch as BareFetcher<StreamingResponse>,
|
|
58
|
+
{
|
|
59
|
+
revalidateOnFocus: true,
|
|
60
|
+
refreshInterval,
|
|
61
|
+
}
|
|
62
|
+
);
|
|
92
63
|
|
|
93
64
|
const isLoading = !data && !error;
|
|
94
65
|
|
package/src/plugins/now/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { buildEntryCreator } from '#/utils';
|
|
|
6
6
|
import { StorePage } from './components/storePage';
|
|
7
7
|
import { StoreCtx } from './context';
|
|
8
8
|
|
|
9
|
-
export class
|
|
9
|
+
export class StorePlugin extends BBPlugin {
|
|
10
10
|
id: PluginID = 'store';
|
|
11
11
|
override onInstall = async (ctx: IHostContext): Promise<void> => {
|
|
12
12
|
const entryCreator = buildEntryCreator(ctx);
|
|
@@ -19,9 +19,11 @@ export class PluginStore extends BBPlugin {
|
|
|
19
19
|
install: ctx.store?.installPlugin || (async () => {}),
|
|
20
20
|
uninstall: ctx.store?.disablePlugin || (async () => {}),
|
|
21
21
|
isInstalled: ctx.store?.isPluginInstalled || ((_: PluginID) => false),
|
|
22
|
-
list: () => {
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
list: async () => {
|
|
23
|
+
ctx.api.setLoading('store', true);
|
|
24
|
+
const all = Array.from((await ctx.store?.getAllPlugins()) || []);
|
|
25
|
+
ctx.api.setLoading('store', false);
|
|
26
|
+
return all.filter(({ id }) => id !== this.id);
|
|
25
27
|
},
|
|
26
28
|
setLoading: (loading: boolean) => {
|
|
27
29
|
ctx.api.setLoading('store', loading);
|
|
@@ -35,4 +37,4 @@ export class PluginStore extends BBPlugin {
|
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
export default new
|
|
40
|
+
export default new StorePlugin();
|
package/src/types/hostApi.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Fetcher } from 'swr';
|
|
2
3
|
|
|
3
4
|
import { type FingerprintData } from '@/utils/fingerprints';
|
|
4
5
|
import { PluginStore } from '#/core/pluginStore';
|
|
@@ -25,6 +26,7 @@ export interface IHostApi {
|
|
|
25
26
|
pluginId: string,
|
|
26
27
|
weight?: number
|
|
27
28
|
) => void;
|
|
29
|
+
fetch: Fetcher;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export type PluginInitializer = (api: IHostApi) => void;
|
package/src/types/plugin.ts
CHANGED