@cniot/mdd-editor 0.3.1 → 0.3.4

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.
@@ -2,9 +2,19 @@ export const DEFAULT_BRIDGE_URL = 'http://127.0.0.1:17678';
2
2
 
3
3
  const trimEndSlash = (value = '') => value.replace(/\/+$/, '');
4
4
  const RELAY_CHANNEL = 'mdd-ai-bridge';
5
+ const relayCacheMap = new Map();
5
6
 
6
7
  const isLoopbackURL = (value = '') => /^http:\/\/(127(?:\.\d{1,3}){3}|localhost)(?::\d+)?/i.test(value);
7
8
 
9
+ export class BridgeRequestError extends Error {
10
+ constructor(message, options = {}) {
11
+ super(message);
12
+ this.name = 'BridgeRequestError';
13
+ this.status = options.status || 0;
14
+ this.data = options.data || null;
15
+ }
16
+ }
17
+
8
18
  export function shouldUseBridgeRelay(baseURL) {
9
19
  return typeof window !== 'undefined' && !window.isSecureContext && isLoopbackURL(baseURL);
10
20
  }
@@ -35,14 +45,22 @@ async function request(baseURL, path, options = {}) {
35
45
  }
36
46
 
37
47
  if (!res.ok) {
38
- throw new Error(data?.message || `请求本地 AI Bridge 失败: ${res.status}`);
48
+ throw new BridgeRequestError(data?.message || `请求本地 AI Bridge 失败: ${res.status}`, {
49
+ status: res.status,
50
+ data,
51
+ });
39
52
  }
40
53
  return data;
41
54
  }
42
55
 
43
- function requestViaRelay(baseURL, path, options = {}) {
44
- const relayURL = `${trimEndSlash(baseURL)}/relay`;
45
- const requestId = createRequestId();
56
+ function getRelayCache(baseURL) {
57
+ const relayOrigin = trimEndSlash(baseURL);
58
+ const relayURL = `${relayOrigin}/relay`;
59
+ const cached = relayCacheMap.get(relayOrigin);
60
+ if (cached?.window && !cached.window.closed) {
61
+ return cached;
62
+ }
63
+
46
64
  const relayWindow = window.open(
47
65
  relayURL,
48
66
  'mdd-ai-bridge-relay',
@@ -53,9 +71,23 @@ function requestViaRelay(baseURL, path, options = {}) {
53
71
  throw new Error('浏览器拦截了本地 Bridge 窗口,请允许弹窗后重试');
54
72
  }
55
73
 
74
+ const next = {
75
+ origin: relayOrigin,
76
+ window: relayWindow,
77
+ ready: false,
78
+ };
79
+ relayCacheMap.set(relayOrigin, next);
80
+ return next;
81
+ }
82
+
83
+ function requestViaRelay(baseURL, path, options = {}) {
84
+ const relayCache = getRelayCache(baseURL);
85
+ const requestId = createRequestId();
86
+
56
87
  return new Promise((resolve, reject) => {
57
88
  let settled = false;
58
- let ready = false;
89
+ let fallbackPosted = false;
90
+ let readyPosted = false;
59
91
 
60
92
  const cleanup = () => {
61
93
  window.removeEventListener('message', handleMessage);
@@ -63,8 +95,19 @@ function requestViaRelay(baseURL, path, options = {}) {
63
95
  clearTimeout(fallbackTimer);
64
96
  };
65
97
 
66
- const postRequest = () => {
67
- relayWindow.postMessage(
98
+ const postRequest = (source = 'ready') => {
99
+ if (source === 'ready') {
100
+ if (readyPosted) return;
101
+ readyPosted = true;
102
+ } else {
103
+ if (fallbackPosted) return;
104
+ fallbackPosted = true;
105
+ }
106
+ if (!relayCache.window || relayCache.window.closed) {
107
+ finish(reject, new Error('本地 Bridge relay 窗口已关闭,请重试'));
108
+ return;
109
+ }
110
+ relayCache.window.postMessage(
68
111
  {
69
112
  channel: RELAY_CHANNEL,
70
113
  type: 'request',
@@ -72,7 +115,7 @@ function requestViaRelay(baseURL, path, options = {}) {
72
115
  path,
73
116
  options,
74
117
  },
75
- trimEndSlash(baseURL),
118
+ relayCache.origin,
76
119
  );
77
120
  };
78
121
 
@@ -84,14 +127,14 @@ function requestViaRelay(baseURL, path, options = {}) {
84
127
  };
85
128
 
86
129
  const handleMessage = (event) => {
87
- if (event.origin !== trimEndSlash(baseURL)) return;
88
- if (event.source !== relayWindow) return;
130
+ if (event.origin !== relayCache.origin) return;
131
+ if (event.source !== relayCache.window) return;
89
132
  const message = event.data || {};
90
133
  if (message.channel !== RELAY_CHANNEL) return;
91
134
 
92
- if (message.type === 'ready' && !ready) {
93
- ready = true;
94
- postRequest();
135
+ if (message.type === 'ready') {
136
+ relayCache.ready = true;
137
+ postRequest('ready');
95
138
  return;
96
139
  }
97
140
 
@@ -99,7 +142,13 @@ function requestViaRelay(baseURL, path, options = {}) {
99
142
  if (!message.ok) {
100
143
  finish(
101
144
  reject,
102
- new Error(message.data?.message || `请求本地 AI Bridge 失败: ${message.status || 0}`),
145
+ new BridgeRequestError(
146
+ message.data?.message || `请求本地 AI Bridge 失败: ${message.status || 0}`,
147
+ {
148
+ status: message.status || 0,
149
+ data: message.data,
150
+ },
151
+ ),
103
152
  );
104
153
  return;
105
154
  }
@@ -110,10 +159,13 @@ function requestViaRelay(baseURL, path, options = {}) {
110
159
  finish(reject, new Error('本地 AI Bridge relay 响应超时,请确认服务已启动'));
111
160
  }, 30000);
112
161
  const fallbackTimer = setTimeout(() => {
113
- if (!ready) postRequest();
162
+ if (!relayCache.ready) postRequest('fallback');
114
163
  }, 500);
115
164
 
116
165
  window.addEventListener('message', handleMessage);
166
+ if (relayCache.ready) {
167
+ setTimeout(() => postRequest('ready'), 0);
168
+ }
117
169
  });
118
170
  }
119
171