@vustcc/suite-sdk 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ 格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),并遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
+
5
+ ## [0.1.0-alpha.1] - 2026-09-10
6
+
7
+ ### Added
8
+
9
+ - 首次发布 `@vustcc/suite-sdk` 套件集成 SDK。
10
+ - 提供套件端桥接、主控端桥接、主题同步、语言同步和消息订阅能力。
11
+ - 支持套件导航请求、统一通知和独立运行时降级。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gwj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # @vustcc/suite-sdk
2
+
3
+ VUST 套件集成 SDK,为运行在 iframe 中的套件 Web 前端提供统一的主控通信协议。
4
+
5
+ ## 能力
6
+
7
+ - 套件向主控报告就绪状态。
8
+ - 主控向套件同步主题。
9
+ - 主控向套件同步国际化语言。
10
+ - 套件请求主控展示统一通知。
11
+ - 套件请求主控执行导航动作,例如打开主控应用、跳转外链或切换主控页面。
12
+ - 套件请求主控聚焦承载窗口。
13
+ - 套件独立运行时自动使用系统主题和浏览器语言。
14
+ - 提供无框架 TypeScript API,Vue、React 或原生前端都可以使用。
15
+
16
+ 设计约定由 `vust-docs` 统一维护,见 [VUST 套件 SDK 设计规范](https://github.com/vustcc/vust-docs/blob/main/suites/VUST%E5%A5%97%E4%BB%B6SDK%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83.md)。
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ pnpm add @vustcc/suite-sdk
22
+ ```
23
+
24
+ ## 套件端使用
25
+
26
+ ```ts
27
+ import { createSuiteBridge } from "@vustcc/suite-sdk";
28
+
29
+ const bridge = createSuiteBridge();
30
+ bridge.ready();
31
+ ```
32
+
33
+ SDK 会把最终主题写入:
34
+
35
+ ```ts
36
+ document.documentElement.dataset.theme;
37
+ document.documentElement.style.colorScheme;
38
+ ```
39
+
40
+ 如果需要监听主题变化:
41
+
42
+ ```ts
43
+ const unsubscribe = bridge.subscribeTheme((theme) => {
44
+ console.log(theme.resolvedTheme);
45
+ });
46
+
47
+ unsubscribe();
48
+ ```
49
+
50
+ 如果套件已经接入国际化,可以声明并监听语言变化:
51
+
52
+ ```ts
53
+ const bridge = createSuiteBridge({
54
+ capabilities: ["theme", "locale"],
55
+ supportedLocales: ["zh-CN", "en-US"],
56
+ defaultLocale: "zh-CN",
57
+ });
58
+
59
+ bridge.subscribeLocale((locale) => {
60
+ console.log(locale.locale);
61
+ });
62
+ ```
63
+
64
+ 如果套件需要统一通知,声明 `notification` 能力后调用 `notify()`。主控可用时由主控弹窗;独立运行或主控不可用时返回 `false`,套件应使用自己的本地 toast 降级。
65
+
66
+ ```ts
67
+ const bridge = createSuiteBridge({
68
+ capabilities: ["theme", "locale", "notification"],
69
+ });
70
+
71
+ const delivered = bridge.notify({
72
+ type: "success",
73
+ title: "上传成功",
74
+ message: "PCAP 文件已进入解析队列",
75
+ });
76
+
77
+ if (!delivered) {
78
+ showLocalToast("success", "上传成功", "PCAP 文件已进入解析队列");
79
+ }
80
+ ```
81
+
82
+ 如果套件需要通过主控打开应用或跳转页面,声明 `navigation` 能力后调用 `navigate()`。主控可用并且套件处于 iframe 承载环境时返回 `true`;独立运行或主控不可用时返回 `false`,套件应按业务需要降级处理。
83
+
84
+ ```ts
85
+ const bridge = createSuiteBridge({
86
+ capabilities: ["theme", "locale", "navigation"],
87
+ });
88
+
89
+ const delivered = bridge.navigate({
90
+ target: "suite-center",
91
+ });
92
+
93
+ if (!delivered) {
94
+ showLocalNavigationHint();
95
+ }
96
+ ```
97
+
98
+ 当前 `navigate()` payload 使用统一结构:
99
+
100
+ ```ts
101
+ interface SuiteNavigationPayload {
102
+ target: "app" | "url" | "suite-center" | "desktop";
103
+ value?: string;
104
+ payload?: Record<string, unknown>;
105
+ external?: boolean;
106
+ }
107
+ ```
108
+
109
+ SDK 只负责发送通用导航请求,具体 `target` 的执行策略由主控承载层决定。
110
+
111
+ ## 主控端使用
112
+
113
+ ```ts
114
+ import { createSuiteHostBridge } from "@vustcc/suite-sdk";
115
+
116
+ const bridge = createSuiteHostBridge({
117
+ iframe: () => iframeElement,
118
+ theme: () => "light",
119
+ locale: () => "zh-CN",
120
+ });
121
+
122
+ bridge.sendTheme();
123
+ bridge.sendLocale();
124
+ ```
125
+
126
+ 主控收到 `suite:lifecycle:ready` 后会自动补发主题。套件声明 `locale` 能力时,主控也会补发语言。
127
+
128
+ ## 协议
129
+
130
+ 所有消息都使用统一 envelope:
131
+
132
+ ```ts
133
+ interface SuiteMessage<TPayload = unknown> {
134
+ protocolVersion: 1;
135
+ source: "vust-suite" | "vust-host";
136
+ type: string;
137
+ id?: string;
138
+ requestId?: string;
139
+ payload?: TPayload;
140
+ error?: {
141
+ code: string;
142
+ message: string;
143
+ details?: unknown;
144
+ };
145
+ }
146
+ ```
147
+
148
+ 当前内置消息:
149
+
150
+ - `suite:lifecycle:ready`
151
+ - `host:theme:update`
152
+ - `host:locale:update`
153
+ - `suite:notification:show`
154
+ - `suite:navigation:open`
155
+ - `suite:window:focus`
156
+
157
+ 已占位但暂未实现运行逻辑的蓝图消息:
158
+
159
+ - `host:context:update`
160
+ - `host:permission:grant`
161
+ - `host:permission:deny`
162
+ - `suite:dialog:confirm`
163
+ - `suite:window:title:update`
164
+ - `suite:window:dirty:update`
165
+ - `suite:file:open`
166
+ - `suite:file:save`
167
+ - `suite:log:event`
168
+ - `suite:error:report`
169
+ - `suite:lifecycle:heartbeat`
170
+ - `suite:lifecycle:status`
171
+
172
+ ## API
173
+
174
+ ```ts
175
+ createSuiteBridge(options?: CreateSuiteBridgeOptions): SuiteBridge
176
+ createSuiteHostBridge(options: CreateSuiteHostBridgeOptions): SuiteHostBridge
177
+ ```
178
+
179
+ 声明 `window` capability 后,套件 SDK 会在套件内 `pointerdown` 和 `focusin` 时自动发送 `suite:window:focus`,主控可据此聚焦承载窗口。特殊场景也可以调用 `bridge.requestWindowFocus()` 手动请求聚焦。
180
+
181
+ 声明 `notification` capability 后,套件可以调用 `bridge.notify(payload)` 请求主控展示统一通知。该方法返回布尔值,便于套件在独立运行时切回本地通知。
182
+
183
+ 声明 `navigation` capability 后,套件可以调用 `bridge.navigate(payload)` 请求主控执行导航动作。该方法返回布尔值,便于套件在独立运行时使用本地窗口、链接或提示降级。
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @file environment.ts
3
+ * @description 浏览器运行环境辅助函数。
4
+ */
5
+ export declare function isBrowser(): boolean;
6
+ export declare function resolveIframe(iframe: HTMLIFrameElement | (() => HTMLIFrameElement | null)): HTMLIFrameElement | null;
@@ -0,0 +1,5 @@
1
+ import { CreateSuiteHostBridgeOptions, SuiteHostBridge } from './types';
2
+ /**
3
+ * @description 创建主控端 Bridge,负责向套件 iframe 推送主控主题与语言。
4
+ */
5
+ export declare function createSuiteHostBridge(options: CreateSuiteHostBridgeOptions): SuiteHostBridge;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description VUST 套件 SDK 公开入口。
4
+ */
5
+ export { createSuiteHostBridge } from './host-bridge';
6
+ export { createSuiteBridge } from './suite-bridge';
7
+ export { SECLAB_SUITE_PROTOCOL_VERSION, SUITE_MESSAGE_TYPES } from './protocol';
8
+ export type * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,206 @@
1
+ //#region src/environment.ts
2
+ function e() {
3
+ return typeof window < "u" && typeof document < "u";
4
+ }
5
+ function t(e) {
6
+ return typeof e == "function" ? e() : e;
7
+ }
8
+ //#endregion
9
+ //#region src/protocol.ts
10
+ var n = 1, r = {
11
+ lifecycleReady: "suite:lifecycle:ready",
12
+ hostThemeUpdate: "host:theme:update",
13
+ hostLocaleUpdate: "host:locale:update",
14
+ hostContextUpdate: "host:context:update",
15
+ hostPermissionGrant: "host:permission:grant",
16
+ hostPermissionDeny: "host:permission:deny",
17
+ suiteNotificationShow: "suite:notification:show",
18
+ suiteDialogConfirm: "suite:dialog:confirm",
19
+ suiteNavigationOpen: "suite:navigation:open",
20
+ suiteWindowFocus: "suite:window:focus",
21
+ suiteWindowTitleUpdate: "suite:window:title:update",
22
+ suiteWindowDirtyUpdate: "suite:window:dirty:update",
23
+ suiteFileOpen: "suite:file:open",
24
+ suiteFileSave: "suite:file:save",
25
+ suiteLogEvent: "suite:log:event",
26
+ suiteErrorReport: "suite:error:report",
27
+ suiteLifecycleHeartbeat: "suite:lifecycle:heartbeat",
28
+ suiteLifecycleStatus: "suite:lifecycle:status"
29
+ };
30
+ function i(e) {
31
+ if (!e || typeof e != "object") return !1;
32
+ let t = e;
33
+ return t.protocolVersion === 1 && (t.source === "vust-suite" || t.source === "vust-host") && typeof t.type == "string";
34
+ }
35
+ function a(e, t, n) {
36
+ return {
37
+ protocolVersion: 1,
38
+ source: e,
39
+ type: t,
40
+ payload: n
41
+ };
42
+ }
43
+ //#endregion
44
+ //#region src/theme.ts
45
+ function o() {
46
+ return !e() || typeof window.matchMedia != "function" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
47
+ }
48
+ function s(e) {
49
+ return e === "light" || e === "dark" || e === "auto";
50
+ }
51
+ function c(e) {
52
+ return typeof e == "string" ? {
53
+ theme: s(e) ? e : "auto",
54
+ resolvedTheme: e === "auto" ? o() : e
55
+ } : !e || typeof e != "object" ? {
56
+ theme: "auto",
57
+ resolvedTheme: o()
58
+ } : {
59
+ theme: s(e.theme) ? e.theme : "auto",
60
+ resolvedTheme: e.resolvedTheme === "dark" || e.resolvedTheme === "light" ? e.resolvedTheme : e.theme === "dark" ? "dark" : "light"
61
+ };
62
+ }
63
+ function l(e, t) {
64
+ return {
65
+ ...c(e),
66
+ source: t
67
+ };
68
+ }
69
+ function u(e, t) {
70
+ let n = "documentElement" in e ? e.documentElement : e;
71
+ n.dataset.theme = t.resolvedTheme, n.style.colorScheme = t.resolvedTheme;
72
+ }
73
+ //#endregion
74
+ //#region src/host-bridge.ts
75
+ function d(n) {
76
+ let o = n.targetOrigin ?? "*", s = !1, l = () => c(typeof n.theme == "function" ? n.theme() : n.theme), u = () => {
77
+ if (n.locale === void 0) return null;
78
+ let e = typeof n.locale == "function" ? n.locale() : n.locale;
79
+ return typeof e == "string" ? { locale: e } : e;
80
+ }, d = (e, r) => {
81
+ s || t(n.iframe)?.contentWindow?.postMessage(a("vust-host", e, r), o);
82
+ }, f = () => {
83
+ d(r.hostThemeUpdate, l());
84
+ }, p = () => {
85
+ let e = u();
86
+ e && d(r.hostLocaleUpdate, e);
87
+ }, m = (e) => {
88
+ let a = t(n.iframe);
89
+ if (!a || e.source !== a.contentWindow || !i(e.data)) return;
90
+ let o = e.data;
91
+ if (o.source === "vust-suite") {
92
+ if (o.type === r.lifecycleReady) {
93
+ let t = o.payload;
94
+ n.onReady?.(t, e), f(), t.capabilities.includes("locale") && p();
95
+ }
96
+ n.onMessage?.(o, e);
97
+ }
98
+ };
99
+ return e() && window.addEventListener("message", m), {
100
+ sendTheme: f,
101
+ sendLocale: p,
102
+ postMessage: d,
103
+ destroy() {
104
+ s = !0, e() && window.removeEventListener("message", m);
105
+ }
106
+ };
107
+ }
108
+ //#endregion
109
+ //#region src/locale.ts
110
+ function f(e) {
111
+ return e.trim() || void 0;
112
+ }
113
+ function p(e, t) {
114
+ let n = f(e);
115
+ if (!n) return;
116
+ if (t.length === 0 || t.includes(n)) return n;
117
+ let r = n.split("-")[0];
118
+ return t.find((e) => e.split("-")[0] === r);
119
+ }
120
+ function m(t, n) {
121
+ if (!e()) return {
122
+ locale: n,
123
+ source: "default"
124
+ };
125
+ let r = [...navigator.languages, navigator.language].filter(Boolean);
126
+ for (let e of r) {
127
+ let n = p(e, t);
128
+ if (n) return {
129
+ locale: n,
130
+ source: "browser"
131
+ };
132
+ }
133
+ return {
134
+ locale: n,
135
+ source: "default"
136
+ };
137
+ }
138
+ function h(e, t, n) {
139
+ let r = typeof e == "string" ? e : e?.locale;
140
+ return { locale: (typeof r == "string" ? p(r, t) : void 0) ?? n };
141
+ }
142
+ //#endregion
143
+ //#region src/suite-bridge.ts
144
+ function g(t = {}) {
145
+ let n = t.capabilities ?? ["theme"], o = t.target ?? (e() ? document : void 0), s = t.parentWindow ?? (e() ? window.parent : void 0), c = t.targetOrigin ?? "*", d = t.applyTheme ?? !0, f = t.supportedLocales ?? [], p = t.defaultLocale ?? "zh-CN", g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), v = /* @__PURE__ */ new Set(), y = !1, b = l("auto", "system"), x = m(f, p), S = 0, C = () => {
146
+ for (let e of _) e(b);
147
+ }, w = () => {
148
+ for (let e of v) e(x);
149
+ }, T = (e) => {
150
+ b = e, d && o && u(o, b), C();
151
+ }, E = (e) => {
152
+ x = e, w();
153
+ }, D = (e, t) => {
154
+ y || !s || s.postMessage(a("vust-suite", e, t), c);
155
+ }, O = () => !y && !!s && (!e() || s !== window), k = (e) => !n.includes("notification") || !O() ? !1 : (D(r.suiteNotificationShow, e), !0), A = (e) => !n.includes("navigation") || !O() ? !1 : (D(r.suiteNavigationOpen, e), !0), j = (e = "manual") => {
156
+ let t = Date.now();
157
+ t - S < 300 || (S = t, D(r.suiteWindowFocus, { reason: e }));
158
+ }, M = (e) => {
159
+ if (y || !i(e.data) || e.data.source !== "vust-host") return;
160
+ let t = e.data;
161
+ t.type === r.hostThemeUpdate && T(l(t.payload, "host")), t.type === r.hostLocaleUpdate && E({
162
+ locale: h(t.payload, f, p).locale,
163
+ source: "host"
164
+ });
165
+ let n = g.get(t.type);
166
+ if (n) for (let r of n) r(t, e);
167
+ }, N = e() && typeof window.matchMedia == "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null, P = () => {
168
+ b.source !== "host" && T(l("auto", "system"));
169
+ }, F = () => j("pointer"), I = () => j("focus");
170
+ return e() && (window.addEventListener("message", M), N?.addEventListener("change", P), n.includes("window") && (document.addEventListener("pointerdown", F, !0), document.addEventListener("focusin", I, !0))), T(b), t.onThemeChange && _.add(t.onThemeChange), t.onLocaleChange && v.add(t.onLocaleChange), {
171
+ ready() {
172
+ D(r.lifecycleReady, { capabilities: n });
173
+ },
174
+ notify: k,
175
+ navigate: A,
176
+ requestWindowFocus: j,
177
+ postMessage: D,
178
+ onMessage(e, t) {
179
+ let n = g.get(e) ?? /* @__PURE__ */ new Set();
180
+ return n.add(t), g.set(e, n), () => {
181
+ n.delete(t), n.size === 0 && g.delete(e);
182
+ };
183
+ },
184
+ subscribeTheme(e) {
185
+ return _.add(e), e(b), () => {
186
+ _.delete(e);
187
+ };
188
+ },
189
+ subscribeLocale(e) {
190
+ return v.add(e), e(x), () => {
191
+ v.delete(e);
192
+ };
193
+ },
194
+ getThemeSnapshot() {
195
+ return b;
196
+ },
197
+ getLocaleSnapshot() {
198
+ return x;
199
+ },
200
+ destroy() {
201
+ y = !0, g.clear(), _.clear(), v.clear(), e() && (window.removeEventListener("message", M), N?.removeEventListener("change", P), document.removeEventListener("pointerdown", F, !0), document.removeEventListener("focusin", I, !0));
202
+ }
203
+ };
204
+ }
205
+ //#endregion
206
+ export { n as SECLAB_SUITE_PROTOCOL_VERSION, r as SUITE_MESSAGE_TYPES, g as createSuiteBridge, d as createSuiteHostBridge };
@@ -0,0 +1,3 @@
1
+ import { SuiteLocalePayload, SuiteLocaleState } from './types';
2
+ export declare function resolveBrowserLocale(supportedLocales: string[], defaultLocale: string): SuiteLocaleState;
3
+ export declare function normalizeLocale(input: string | SuiteLocalePayload, supportedLocales: string[], defaultLocale: string): SuiteLocalePayload;
@@ -0,0 +1,24 @@
1
+ import { SuiteMessage, SuiteMessageSource } from './types';
2
+ export declare const SECLAB_SUITE_PROTOCOL_VERSION: 1;
3
+ export declare const SUITE_MESSAGE_TYPES: {
4
+ readonly lifecycleReady: "suite:lifecycle:ready";
5
+ readonly hostThemeUpdate: "host:theme:update";
6
+ readonly hostLocaleUpdate: "host:locale:update";
7
+ readonly hostContextUpdate: "host:context:update";
8
+ readonly hostPermissionGrant: "host:permission:grant";
9
+ readonly hostPermissionDeny: "host:permission:deny";
10
+ readonly suiteNotificationShow: "suite:notification:show";
11
+ readonly suiteDialogConfirm: "suite:dialog:confirm";
12
+ readonly suiteNavigationOpen: "suite:navigation:open";
13
+ readonly suiteWindowFocus: "suite:window:focus";
14
+ readonly suiteWindowTitleUpdate: "suite:window:title:update";
15
+ readonly suiteWindowDirtyUpdate: "suite:window:dirty:update";
16
+ readonly suiteFileOpen: "suite:file:open";
17
+ readonly suiteFileSave: "suite:file:save";
18
+ readonly suiteLogEvent: "suite:log:event";
19
+ readonly suiteErrorReport: "suite:error:report";
20
+ readonly suiteLifecycleHeartbeat: "suite:lifecycle:heartbeat";
21
+ readonly suiteLifecycleStatus: "suite:lifecycle:status";
22
+ };
23
+ export declare function isSuiteMessage(value: unknown): value is SuiteMessage;
24
+ export declare function createMessage<TPayload>(source: SuiteMessageSource, type: string, payload?: TPayload): SuiteMessage<TPayload>;
@@ -0,0 +1,5 @@
1
+ import { CreateSuiteBridgeOptions, SuiteBridge } from './types';
2
+ /**
3
+ * @description 创建套件端 Bridge,负责向主控报告就绪状态并接收主控主题与语言。
4
+ */
5
+ export declare function createSuiteBridge(options?: CreateSuiteBridgeOptions): SuiteBridge;
@@ -0,0 +1,5 @@
1
+ import { SuiteResolvedTheme, SuiteThemeMode, SuiteThemePayload, SuiteThemeSource, SuiteThemeState } from './types';
2
+ export declare function resolveSystemTheme(): SuiteResolvedTheme;
3
+ export declare function normalizeTheme(input: SuiteThemeMode | SuiteThemePayload): SuiteThemePayload;
4
+ export declare function resolveThemeState(input: SuiteThemeMode | SuiteThemePayload, source: SuiteThemeSource): SuiteThemeState;
5
+ export declare function applyThemeToTarget(target: Document | HTMLElement, theme: SuiteThemeState): void;
@@ -0,0 +1,150 @@
1
+ import { SECLAB_SUITE_PROTOCOL_VERSION, SUITE_MESSAGE_TYPES } from './protocol';
2
+ export type SuiteProtocolVersion = typeof SECLAB_SUITE_PROTOCOL_VERSION;
3
+ export type SuiteMessageSource = "vust-suite" | "vust-host";
4
+ export type SuiteResolvedTheme = "light" | "dark";
5
+ export type SuiteThemeMode = SuiteResolvedTheme | "auto";
6
+ export type SuiteThemeSource = "host" | "system";
7
+ export type SuiteLocaleSource = "host" | "browser" | "default";
8
+ export type SuiteRunMode = "hosted" | "standalone";
9
+ export type SuiteNotificationType = "info" | "success" | "warning" | "error";
10
+ export type SuiteCapability = "theme" | "locale" | "context" | "notification" | "dialog" | "navigation" | "window" | "file" | "diagnostics" | "heartbeat";
11
+ export type SuiteMessageType = (typeof SUITE_MESSAGE_TYPES)[keyof typeof SUITE_MESSAGE_TYPES];
12
+ export type SuiteMessageHandler<TPayload = unknown> = (message: SuiteMessage<TPayload>, event: MessageEvent) => void;
13
+ export type SuiteThemeSubscriber = (theme: SuiteThemeState) => void;
14
+ export type SuiteLocaleSubscriber = (locale: SuiteLocaleState) => void;
15
+ export type SuiteUnsubscribe = () => void;
16
+ export interface SuiteMessage<TPayload = unknown> {
17
+ protocolVersion: SuiteProtocolVersion;
18
+ source: SuiteMessageSource;
19
+ type: string;
20
+ id?: string;
21
+ requestId?: string;
22
+ payload?: TPayload;
23
+ error?: SuiteMessageError;
24
+ }
25
+ export interface SuiteMessageError {
26
+ code: string;
27
+ message: string;
28
+ details?: unknown;
29
+ }
30
+ export interface SuiteThemeState {
31
+ theme: SuiteThemeMode;
32
+ resolvedTheme: SuiteResolvedTheme;
33
+ source: SuiteThemeSource;
34
+ }
35
+ export interface SuiteThemePayload {
36
+ theme: SuiteThemeMode;
37
+ resolvedTheme: SuiteResolvedTheme;
38
+ }
39
+ export interface SuiteLocaleState {
40
+ locale: string;
41
+ source: SuiteLocaleSource;
42
+ }
43
+ export interface SuiteLocalePayload {
44
+ locale: string;
45
+ }
46
+ export interface SuiteContextPayload {
47
+ suiteId: string;
48
+ instanceId?: string;
49
+ appId?: string;
50
+ runMode: SuiteRunMode;
51
+ basePath?: string;
52
+ user?: {
53
+ id: string;
54
+ name?: string;
55
+ };
56
+ node?: {
57
+ id: string;
58
+ name?: string;
59
+ };
60
+ }
61
+ export interface SuiteNotificationPayload {
62
+ type?: SuiteNotificationType;
63
+ title: string;
64
+ message?: string;
65
+ duration?: number;
66
+ }
67
+ export interface SuiteConfirmDialogPayload {
68
+ title: string;
69
+ message?: string;
70
+ confirmText?: string;
71
+ cancelText?: string;
72
+ danger?: boolean;
73
+ }
74
+ export interface SuiteNavigationPayload {
75
+ target: "app" | "url" | "suite-center" | "desktop";
76
+ value?: string;
77
+ payload?: Record<string, unknown>;
78
+ external?: boolean;
79
+ }
80
+ export interface SuiteWindowTitlePayload {
81
+ title: string;
82
+ }
83
+ /**
84
+ * 套件请求主控聚焦承载窗口的原因,用于后续诊断或策略分流。
85
+ */
86
+ export interface SuiteWindowFocusPayload {
87
+ reason: "pointer" | "focus" | "manual";
88
+ }
89
+ export interface SuiteWindowDirtyPayload {
90
+ dirty: boolean;
91
+ }
92
+ export interface SuiteLogEventPayload {
93
+ level: "debug" | "info" | "warn" | "error";
94
+ message: string;
95
+ details?: unknown;
96
+ }
97
+ export interface SuiteLifecycleStatusPayload {
98
+ status: "ready" | "busy" | "idle" | "error";
99
+ message?: string;
100
+ }
101
+ export interface SuiteReadyPayload {
102
+ capabilities: SuiteCapability[];
103
+ }
104
+ export interface CreateSuiteBridgeOptions {
105
+ capabilities?: SuiteCapability[];
106
+ target?: Document | HTMLElement;
107
+ parentWindow?: Window;
108
+ targetOrigin?: string;
109
+ applyTheme?: boolean;
110
+ supportedLocales?: string[];
111
+ defaultLocale?: string;
112
+ onThemeChange?: SuiteThemeSubscriber;
113
+ onLocaleChange?: SuiteLocaleSubscriber;
114
+ }
115
+ export interface SuiteBridge {
116
+ ready: () => void;
117
+ /**
118
+ * 请求主控展示统一通知;独立运行或主控不可用时返回 false,套件应使用本地通知降级。
119
+ */
120
+ notify: (payload: SuiteNotificationPayload) => boolean;
121
+ /**
122
+ * 请求主控执行导航动作,例如打开主控内置应用或跳转外链。
123
+ */
124
+ navigate: (payload: SuiteNavigationPayload) => boolean;
125
+ /**
126
+ * 手动请求主控聚焦承载窗口;常规点击和焦点变化由 SDK 自动上报。
127
+ */
128
+ requestWindowFocus: () => void;
129
+ postMessage: <TPayload = unknown>(type: string, payload?: TPayload) => void;
130
+ onMessage: <TPayload = unknown>(type: string, handler: SuiteMessageHandler<TPayload>) => SuiteUnsubscribe;
131
+ subscribeTheme: (subscriber: SuiteThemeSubscriber) => SuiteUnsubscribe;
132
+ subscribeLocale: (subscriber: SuiteLocaleSubscriber) => SuiteUnsubscribe;
133
+ getThemeSnapshot: () => SuiteThemeState;
134
+ getLocaleSnapshot: () => SuiteLocaleState;
135
+ destroy: () => void;
136
+ }
137
+ export interface CreateSuiteHostBridgeOptions {
138
+ iframe: HTMLIFrameElement | (() => HTMLIFrameElement | null);
139
+ theme: SuiteThemeMode | SuiteThemePayload | (() => SuiteThemeMode | SuiteThemePayload);
140
+ locale?: string | SuiteLocalePayload | (() => string | SuiteLocalePayload);
141
+ targetOrigin?: string;
142
+ onReady?: (payload: SuiteReadyPayload, event: MessageEvent) => void;
143
+ onMessage?: SuiteMessageHandler;
144
+ }
145
+ export interface SuiteHostBridge {
146
+ sendTheme: () => void;
147
+ sendLocale: () => void;
148
+ postMessage: <TPayload = unknown>(type: string, payload?: TPayload) => void;
149
+ destroy: () => void;
150
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@vustcc/suite-sdk",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "VUST Suite Integration SDK",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/vustcc/vust-ui.git",
23
+ "directory": "packages/suite-sdk"
24
+ },
25
+ "publishConfig": {
26
+ "registry": "https://registry.npmjs.org",
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "vite build",
31
+ "type-check": "tsc --noEmit -p tsconfig.json"
32
+ }
33
+ }