@zhin.js/client 2.0.6 → 2.1.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/README.md +7 -1
- package/client/bootstrap/loadConsoleEntries.test.ts +28 -2
- package/client/bootstrap/loadConsoleEntries.ts +39 -1
- package/client/console-utils/remoteApi.test.ts +68 -0
- package/client/console-utils/remoteApi.ts +19 -1
- package/client/index.ts +16 -0
- package/client/mediaSrc.test.ts +34 -0
- package/client/mediaSrc.ts +16 -1
- package/client/persistence/idb-store.test.ts +128 -0
- package/client/persistence/idb-store.ts +16 -16
- package/client/websocket/hooks.test.ts +21 -0
- package/client/websocket/hooks.ts +56 -27
- package/client/websocket/manager.test.ts +216 -0
- package/client/websocket/manager.ts +57 -28
- package/client/websocket/remote-settings.ts +13 -1
- package/dist/bootstrap/loadConsoleEntries.d.ts +9 -0
- package/dist/bootstrap/loadConsoleEntries.d.ts.map +1 -1
- package/dist/bootstrap/loadConsoleEntries.js +36 -1
- package/dist/bootstrap/loadConsoleEntries.js.map +1 -1
- package/dist/console-utils/remoteApi.d.ts.map +1 -1
- package/dist/console-utils/remoteApi.js +22 -1
- package/dist/console-utils/remoteApi.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mediaSrc.d.ts.map +1 -1
- package/dist/mediaSrc.js +17 -1
- package/dist/mediaSrc.js.map +1 -1
- package/dist/persistence/idb-store.d.ts.map +1 -1
- package/dist/persistence/idb-store.js +15 -17
- package/dist/persistence/idb-store.js.map +1 -1
- package/dist/websocket/hooks.d.ts +9 -0
- package/dist/websocket/hooks.d.ts.map +1 -1
- package/dist/websocket/hooks.js +52 -31
- package/dist/websocket/hooks.js.map +1 -1
- package/dist/websocket/manager.d.ts.map +1 -1
- package/dist/websocket/manager.js +47 -27
- package/dist/websocket/manager.js.map +1 -1
- package/dist/websocket/remote-settings.d.ts.map +1 -1
- package/dist/websocket/remote-settings.js +16 -1
- package/dist/websocket/remote-settings.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const mockApplyConsoleEvent = vi.fn(async () => undefined);
|
|
4
|
+
|
|
5
|
+
vi.mock("../persistence/idb-store.js", () => ({
|
|
6
|
+
applyConsoleEvent: (...args: unknown[]) => mockApplyConsoleEvent(...args),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
import { WebSocketManager } from "./manager.js";
|
|
10
|
+
import { ConnectionState, type WebSocketMessage } from "./types.js";
|
|
11
|
+
|
|
12
|
+
function mockStorage(map: Record<string, string> = {}): Storage {
|
|
13
|
+
return {
|
|
14
|
+
getItem: (key: string) => map[key] ?? null,
|
|
15
|
+
setItem: (key: string, value: string) => {
|
|
16
|
+
map[key] = value;
|
|
17
|
+
},
|
|
18
|
+
removeItem: (key: string) => {
|
|
19
|
+
delete map[key];
|
|
20
|
+
},
|
|
21
|
+
clear: () => {
|
|
22
|
+
for (const key of Object.keys(map)) delete map[key];
|
|
23
|
+
},
|
|
24
|
+
key: () => null,
|
|
25
|
+
length: 0,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function installBrowserGlobals(storage: Storage = mockStorage()) {
|
|
30
|
+
const win = {
|
|
31
|
+
location: { origin: "http://localhost:5173", protocol: "http:", host: "localhost:5173" },
|
|
32
|
+
__ZHIN_API_TOKEN: undefined as string | undefined,
|
|
33
|
+
dispatchEvent: vi.fn(),
|
|
34
|
+
};
|
|
35
|
+
vi.stubGlobal("window", win);
|
|
36
|
+
vi.stubGlobal("localStorage", storage);
|
|
37
|
+
vi.stubGlobal("sessionStorage", mockStorage());
|
|
38
|
+
return { win, storage };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sseResponse(status = 200, body?: ReadableStream<Uint8Array> | null) {
|
|
42
|
+
return {
|
|
43
|
+
ok: status >= 200 && status < 300,
|
|
44
|
+
status,
|
|
45
|
+
body: body === undefined
|
|
46
|
+
? new ReadableStream<Uint8Array>({
|
|
47
|
+
start(controller) {
|
|
48
|
+
// Keep the stream open until abort; tests control lifecycle via disconnect().
|
|
49
|
+
void controller;
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
: body,
|
|
53
|
+
json: async () => ({}),
|
|
54
|
+
} as Response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe("WebSocketManager REST/SSE transport", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.useFakeTimers();
|
|
60
|
+
mockApplyConsoleEvent.mockClear();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
vi.useRealTimers();
|
|
65
|
+
vi.unstubAllGlobals();
|
|
66
|
+
vi.restoreAllMocks();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("connects SSE without a token (local Host with empty TokenRegistry)", async () => {
|
|
70
|
+
installBrowserGlobals();
|
|
71
|
+
const fetchMock = vi.fn(async () => sseResponse(200));
|
|
72
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
73
|
+
|
|
74
|
+
const manager = new WebSocketManager({ reconnectInterval: 100, maxReconnectAttempts: 3 });
|
|
75
|
+
const states: boolean[] = [];
|
|
76
|
+
manager.onConnectionChange((c) => states.push(c));
|
|
77
|
+
manager.connect();
|
|
78
|
+
|
|
79
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
80
|
+
await Promise.resolve();
|
|
81
|
+
await Promise.resolve();
|
|
82
|
+
|
|
83
|
+
expect(fetchMock).toHaveBeenCalled();
|
|
84
|
+
const init = fetchMock.mock.calls[0]![1] as RequestInit;
|
|
85
|
+
const headers = init.headers as Record<string, string>;
|
|
86
|
+
expect(headers.Accept).toBe("text/event-stream");
|
|
87
|
+
expect(headers.Authorization).toBeUndefined();
|
|
88
|
+
expect(manager.getState()).toBe(ConnectionState.CONNECTED);
|
|
89
|
+
expect(manager.isConnected()).toBe(true);
|
|
90
|
+
manager.disconnect();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("sends Authorization when a token is stored", async () => {
|
|
94
|
+
installBrowserGlobals(mockStorage({ zhin_api_token: "secret" }));
|
|
95
|
+
const fetchMock = vi.fn(async () => sseResponse(200));
|
|
96
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
97
|
+
|
|
98
|
+
const manager = new WebSocketManager();
|
|
99
|
+
manager.connect();
|
|
100
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
101
|
+
await Promise.resolve();
|
|
102
|
+
await Promise.resolve();
|
|
103
|
+
|
|
104
|
+
const init = fetchMock.mock.calls[0]![1] as RequestInit;
|
|
105
|
+
expect((init.headers as Record<string, string>).Authorization).toBe("Bearer secret");
|
|
106
|
+
manager.disconnect();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("reconnects after a transient SSE failure (no ERROR deadlock)", async () => {
|
|
110
|
+
installBrowserGlobals();
|
|
111
|
+
let calls = 0;
|
|
112
|
+
const fetchMock = vi.fn(async () => {
|
|
113
|
+
calls += 1;
|
|
114
|
+
if (calls === 1) return sseResponse(503, null);
|
|
115
|
+
return sseResponse(200);
|
|
116
|
+
});
|
|
117
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
118
|
+
|
|
119
|
+
const manager = new WebSocketManager({
|
|
120
|
+
reconnectInterval: 100,
|
|
121
|
+
maxReconnectAttempts: 5,
|
|
122
|
+
});
|
|
123
|
+
manager.connect();
|
|
124
|
+
|
|
125
|
+
// First attempt fails → RECONNECTING
|
|
126
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
127
|
+
await Promise.resolve();
|
|
128
|
+
await Promise.resolve();
|
|
129
|
+
expect(manager.getState()).toBe(ConnectionState.RECONNECTING);
|
|
130
|
+
expect(calls).toBe(1);
|
|
131
|
+
|
|
132
|
+
// scheduleReconnect delay = 100 * attempt(1) = 100ms
|
|
133
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
134
|
+
await Promise.resolve();
|
|
135
|
+
await Promise.resolve();
|
|
136
|
+
|
|
137
|
+
expect(calls).toBeGreaterThanOrEqual(2);
|
|
138
|
+
expect(manager.getState()).toBe(ConnectionState.CONNECTED);
|
|
139
|
+
manager.disconnect();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("sendRequest posts without Authorization when no token is stored", async () => {
|
|
143
|
+
installBrowserGlobals();
|
|
144
|
+
const fetchMock = vi.fn(async () => ({
|
|
145
|
+
ok: true,
|
|
146
|
+
status: 200,
|
|
147
|
+
json: async () => ({ success: true, data: { yaml: "http:\n port: 1\n" } }),
|
|
148
|
+
} as Response));
|
|
149
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
150
|
+
|
|
151
|
+
const manager = new WebSocketManager();
|
|
152
|
+
const data = await manager.getConfigYaml();
|
|
153
|
+
expect(data.yaml).toContain("port");
|
|
154
|
+
const init = fetchMock.mock.calls[0]![1] as RequestInit;
|
|
155
|
+
expect((init.headers as Record<string, string>).Authorization).toBeUndefined();
|
|
156
|
+
expect(init.method).toBe("POST");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("sendRequest includes Bearer token when present", async () => {
|
|
160
|
+
installBrowserGlobals(mockStorage({ zhin_api_token: "t" }));
|
|
161
|
+
const fetchMock = vi.fn(async () => ({
|
|
162
|
+
ok: true,
|
|
163
|
+
status: 200,
|
|
164
|
+
json: async () => ({ success: true, data: { ok: true } }),
|
|
165
|
+
} as Response));
|
|
166
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
167
|
+
|
|
168
|
+
const manager = new WebSocketManager();
|
|
169
|
+
await manager.setConfig("sandbox", { endpoints: [] });
|
|
170
|
+
const init = fetchMock.mock.calls[0]![1] as RequestInit;
|
|
171
|
+
expect((init.headers as Record<string, string>).Authorization).toBe("Bearer t");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("normalizes legacy endpoint push names and payload aliases before callbacks", async () => {
|
|
175
|
+
installBrowserGlobals();
|
|
176
|
+
const received: WebSocketMessage[] = [];
|
|
177
|
+
const manager = new WebSocketManager({}, {
|
|
178
|
+
onMessage: (message) => received.push(message),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
(manager as unknown as { handleMessage(event: MessageEvent): void }).handleMessage({
|
|
182
|
+
data: '{"type":"endpoint:message","data":{"$adapter":"sandbox","endpoint":"bot"}}',
|
|
183
|
+
} as MessageEvent);
|
|
184
|
+
await Promise.resolve();
|
|
185
|
+
|
|
186
|
+
expect(received).toEqual([expect.objectContaining({
|
|
187
|
+
type: "message.receive",
|
|
188
|
+
data: expect.objectContaining({ adapter: "sandbox", endpointId: "bot" }),
|
|
189
|
+
})]);
|
|
190
|
+
expect(mockApplyConsoleEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
191
|
+
type: "message.receive",
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("stops reconnecting after maxReconnectAttempts", async () => {
|
|
196
|
+
installBrowserGlobals();
|
|
197
|
+
const fetchMock = vi.fn(async () => sseResponse(500, null));
|
|
198
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
199
|
+
|
|
200
|
+
const manager = new WebSocketManager({
|
|
201
|
+
reconnectInterval: 10,
|
|
202
|
+
maxReconnectAttempts: 2,
|
|
203
|
+
});
|
|
204
|
+
manager.connect();
|
|
205
|
+
|
|
206
|
+
for (let i = 0; i < 6; i++) {
|
|
207
|
+
await vi.advanceTimersByTimeAsync(50);
|
|
208
|
+
await Promise.resolve();
|
|
209
|
+
await Promise.resolve();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
expect(manager.getState()).toBe(ConnectionState.ERROR);
|
|
213
|
+
expect(fetchMock.mock.calls.length).toBeLessThanOrEqual(3);
|
|
214
|
+
manager.disconnect();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -2,6 +2,10 @@ import { ConnectionState, WebSocketError, ConnectionError, MessageError, Request
|
|
|
2
2
|
|
|
3
3
|
import { getApiBase, getStoredToken, resolveApiUrl } from "./remote-settings.js";
|
|
4
4
|
import { applyConsoleEvent } from "../persistence/idb-store.js";
|
|
5
|
+
import {
|
|
6
|
+
SIDE_EVENT_PUSH,
|
|
7
|
+
normalizeConsolePushMessage,
|
|
8
|
+
} from "@zhin.js/console-protocol";
|
|
5
9
|
|
|
6
10
|
export class WebSocketManager {
|
|
7
11
|
private ws: WebSocket | null = null;
|
|
@@ -50,25 +54,27 @@ export class WebSocketManager {
|
|
|
50
54
|
this.attachEventHandlers();
|
|
51
55
|
} catch (error) {
|
|
52
56
|
this.handleConnectionError(new ConnectionError("Failed to create WebSocket", error as Error));
|
|
57
|
+
this.setState(ConnectionState.RECONNECTING);
|
|
58
|
+
this.notifyConnection(false);
|
|
59
|
+
this.scheduleReconnect();
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
private async connectRestSse(): Promise<void> {
|
|
64
|
+
// Host without http.token allows unauthenticated SSE (authenticateHttp
|
|
65
|
+
// returns full when TokenRegistry is empty). Still open the stream so
|
|
66
|
+
// local dev Console receives hmr:reload / message.receive broadcasts.
|
|
57
67
|
const token = getStoredToken();
|
|
58
|
-
if (!token) {
|
|
59
|
-
this.setState(ConnectionState.DISCONNECTED);
|
|
60
|
-
this.notifyConnection(false);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
68
|
this.setState(ConnectionState.CONNECTING);
|
|
64
69
|
this.sseAbort?.abort();
|
|
65
70
|
this.sseAbort = new AbortController();
|
|
66
71
|
try {
|
|
72
|
+
const headers: Record<string, string> = {
|
|
73
|
+
Accept: "text/event-stream",
|
|
74
|
+
};
|
|
75
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
67
76
|
const res = await fetch(resolveApiUrl("/api/events"), {
|
|
68
|
-
headers
|
|
69
|
-
Authorization: `Bearer ${token}`,
|
|
70
|
-
Accept: "text/event-stream",
|
|
71
|
-
},
|
|
77
|
+
headers,
|
|
72
78
|
signal: this.sseAbort.signal,
|
|
73
79
|
});
|
|
74
80
|
if (!res.ok || !res.body) {
|
|
@@ -81,9 +87,13 @@ export class WebSocketManager {
|
|
|
81
87
|
void this.pumpSse(res.body);
|
|
82
88
|
} catch (error) {
|
|
83
89
|
if ((error as Error).name === "AbortError") return;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
// Must set RECONNECTING (not ERROR) before scheduleReconnect — the timer
|
|
91
|
+
// only fires when state === RECONNECTING; handleConnectionError(ERROR)
|
|
92
|
+
// previously deadlocked reconnect after any transient SSE failure.
|
|
93
|
+
this.callbacks.onError?.(error as unknown as Event);
|
|
94
|
+
console.error("[Console transport] SSE connect error:", error);
|
|
95
|
+
this.setState(ConnectionState.RECONNECTING);
|
|
96
|
+
this.notifyConnection(false);
|
|
87
97
|
this.scheduleReconnect();
|
|
88
98
|
}
|
|
89
99
|
}
|
|
@@ -104,7 +114,9 @@ export class WebSocketManager {
|
|
|
104
114
|
if (!line) continue;
|
|
105
115
|
const json = line.slice(6);
|
|
106
116
|
try {
|
|
107
|
-
const message =
|
|
117
|
+
const message = normalizeConsolePushMessage(
|
|
118
|
+
JSON.parse(json) as WebSocketMessage,
|
|
119
|
+
) as WebSocketMessage;
|
|
108
120
|
void applyConsoleEvent(message);
|
|
109
121
|
this.handleBroadcast(message);
|
|
110
122
|
this.callbacks.onMessage?.(message);
|
|
@@ -174,24 +186,23 @@ export class WebSocketManager {
|
|
|
174
186
|
}
|
|
175
187
|
|
|
176
188
|
private async sendRestRequest<T>(message: unknown): Promise<T> {
|
|
189
|
+
// Host may run without http.token (local smoke). Only require a token when
|
|
190
|
+
// one is configured client-side; otherwise POST without Authorization and
|
|
191
|
+
// let the Host accept (TokenRegistry empty → full scope).
|
|
177
192
|
const token = getStoredToken();
|
|
178
|
-
if (!token) throw new WebSocketError("Not authenticated", "NOT_CONNECTED");
|
|
179
193
|
const requestId = ++this.requestId;
|
|
180
194
|
const body = { ...(message as Record<string, unknown>), requestId };
|
|
181
|
-
const timer = setTimeout(() => {
|
|
182
|
-
/* handled below */
|
|
183
|
-
}, this.config.requestTimeout);
|
|
184
195
|
try {
|
|
196
|
+
const headers: Record<string, string> = {
|
|
197
|
+
"Content-Type": "application/json",
|
|
198
|
+
};
|
|
199
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
185
200
|
const res = await fetch(resolveApiUrl("/api/console/request"), {
|
|
186
201
|
method: "POST",
|
|
187
|
-
headers
|
|
188
|
-
Authorization: `Bearer ${token}`,
|
|
189
|
-
"Content-Type": "application/json",
|
|
190
|
-
},
|
|
202
|
+
headers,
|
|
191
203
|
body: JSON.stringify(body),
|
|
192
204
|
signal: AbortSignal.timeout(this.config.requestTimeout),
|
|
193
205
|
});
|
|
194
|
-
clearTimeout(timer);
|
|
195
206
|
const json = (await res.json()) as {
|
|
196
207
|
success?: boolean;
|
|
197
208
|
data?: T;
|
|
@@ -203,7 +214,6 @@ export class WebSocketManager {
|
|
|
203
214
|
}
|
|
204
215
|
return json.data as T;
|
|
205
216
|
} catch (error) {
|
|
206
|
-
clearTimeout(timer);
|
|
207
217
|
if (error instanceof WebSocketError) throw error;
|
|
208
218
|
throw new MessageError("REST request failed", error as Error);
|
|
209
219
|
}
|
|
@@ -325,7 +335,9 @@ export class WebSocketManager {
|
|
|
325
335
|
|
|
326
336
|
private handleMessage(event: MessageEvent): void {
|
|
327
337
|
try {
|
|
328
|
-
const message =
|
|
338
|
+
const message = normalizeConsolePushMessage(
|
|
339
|
+
JSON.parse(event.data) as WebSocketMessage,
|
|
340
|
+
) as WebSocketMessage;
|
|
329
341
|
if (message.requestId && this.pendingRequests.has(message.requestId)) {
|
|
330
342
|
this.handleRequestResponse(message);
|
|
331
343
|
return;
|
|
@@ -340,7 +352,11 @@ export class WebSocketManager {
|
|
|
340
352
|
|
|
341
353
|
private handleBroadcast(message: WebSocketMessage): void {
|
|
342
354
|
const t = message.type;
|
|
343
|
-
if (
|
|
355
|
+
if (
|
|
356
|
+
t === SIDE_EVENT_PUSH.REQUEST_RECEIVE
|
|
357
|
+
|| t === SIDE_EVENT_PUSH.NOTICE_RECEIVE
|
|
358
|
+
|| t === SIDE_EVENT_PUSH.MESSAGE_RECEIVE
|
|
359
|
+
) {
|
|
344
360
|
if (typeof window !== "undefined") {
|
|
345
361
|
window.dispatchEvent(new CustomEvent("zhin-console-bot-push", { detail: message }));
|
|
346
362
|
}
|
|
@@ -381,8 +397,10 @@ export class WebSocketManager {
|
|
|
381
397
|
}
|
|
382
398
|
|
|
383
399
|
private handleConnectionError(error: Error): void {
|
|
384
|
-
|
|
385
|
-
|
|
400
|
+
// Used by the legacy WS path. Do NOT clear the reconnect timer here —
|
|
401
|
+
// callers that still want to retry should set RECONNECTING then call
|
|
402
|
+
// scheduleReconnect(). Terminal failures go through scheduleReconnect's
|
|
403
|
+
// maxAttempts branch which sets ERROR.
|
|
386
404
|
console.error("[Console transport] Connection error:", error);
|
|
387
405
|
this.callbacks.onError?.(error as unknown as Event);
|
|
388
406
|
}
|
|
@@ -390,12 +408,23 @@ export class WebSocketManager {
|
|
|
390
408
|
private scheduleReconnect(): void {
|
|
391
409
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
392
410
|
this.setState(ConnectionState.ERROR);
|
|
411
|
+
this.notifyConnection(false);
|
|
393
412
|
return;
|
|
394
413
|
}
|
|
395
414
|
this.reconnectAttempts++;
|
|
415
|
+
// Keep / restore RECONNECTING so the timer callback's state guard passes.
|
|
416
|
+
if (this.state !== ConnectionState.RECONNECTING) {
|
|
417
|
+
this.setState(ConnectionState.RECONNECTING);
|
|
418
|
+
}
|
|
396
419
|
const delay = this.config.reconnectInterval * this.reconnectAttempts;
|
|
420
|
+
this.clearReconnectTimer();
|
|
397
421
|
this.reconnectTimer = setTimeout(() => {
|
|
398
|
-
if (
|
|
422
|
+
if (
|
|
423
|
+
this.state === ConnectionState.RECONNECTING
|
|
424
|
+
|| this.state === ConnectionState.DISCONNECTED
|
|
425
|
+
) {
|
|
426
|
+
this.connect();
|
|
427
|
+
}
|
|
399
428
|
}, delay);
|
|
400
429
|
}
|
|
401
430
|
|
|
@@ -15,7 +15,19 @@ export function setApiBase(base: string): void {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function getStoredToken(): string | null {
|
|
18
|
-
|
|
18
|
+
const runtime = getRuntimeToken();
|
|
19
|
+
if (runtime) return runtime;
|
|
20
|
+
if (typeof localStorage !== "undefined") {
|
|
21
|
+
const local = localStorage.getItem(TOKEN_KEY)?.trim();
|
|
22
|
+
if (local) return local;
|
|
23
|
+
}
|
|
24
|
+
// Some login flows only persist to sessionStorage; keep transport in sync
|
|
25
|
+
// with sandbox page / agent-stream which already read both stores.
|
|
26
|
+
if (typeof sessionStorage !== "undefined") {
|
|
27
|
+
const session = sessionStorage.getItem(TOKEN_KEY)?.trim();
|
|
28
|
+
if (session) return session;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
export function resolveApiUrl(path: string): string {
|
|
@@ -21,6 +21,15 @@ export type CreatePluginRegisterHostApiOptions = {
|
|
|
21
21
|
export declare function createPluginRegisterHostApi(options: CreatePluginRegisterHostApiOptions): PluginRegisterHostApi;
|
|
22
22
|
export declare function fetchConsoleEntries(options?: FetchConsoleEntriesOptions): Promise<ConsoleEntriesResponse>;
|
|
23
23
|
export declare function getRegisterFn(mod: Record<string, unknown> | null | undefined): ConsolePluginRegister | null;
|
|
24
|
+
/**
|
|
25
|
+
* Convention pages (ADR 0046) export a React component as `default` + optional `meta`,
|
|
26
|
+
* without a legacy `register(api)`. Synthesize register from entry.route / entry meta
|
|
27
|
+
* so Remote Console can still mount them.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveEntryRegister(mod: Record<string, unknown> | null | undefined, entry: ConsoleClientEntry & {
|
|
30
|
+
readonly route?: string;
|
|
31
|
+
readonly title?: string;
|
|
32
|
+
}): ConsolePluginRegister | null;
|
|
24
33
|
export declare function registerConsolePluginsFromEntries(entries: ConsoleClientEntry[], hostApi: PluginRegisterHostApi, onEntryError?: (entry: ConsoleClientEntry, error: unknown) => void, assetOrigin?: string): Promise<void>;
|
|
25
34
|
export declare function loadConsoleEntries(options: LoadConsoleEntriesOptions): Promise<void>;
|
|
26
35
|
//# sourceMappingURL=loadConsoleEntries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadConsoleEntries.d.ts","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"loadConsoleEntries.d.ts","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG5K,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,GAAG;IACnE,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpE,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,qBAAqB,CAQvB;AAoCD,wBAAsB,mBAAmB,CACvC,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAUjC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,qBAAqB,GAAG,IAAI,CAO3G;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/C,KAAK,EAAE,kBAAkB,GAAG;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,qBAAqB,GAAG,IAAI,CA2B9B;AAED,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,EAAE,qBAAqB,EAC9B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,EAClE,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB1F"}
|
|
@@ -65,6 +65,41 @@ export function getRegisterFn(mod) {
|
|
|
65
65
|
return d["register"];
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Convention pages (ADR 0046) export a React component as `default` + optional `meta`,
|
|
70
|
+
* without a legacy `register(api)`. Synthesize register from entry.route / entry meta
|
|
71
|
+
* so Remote Console can still mount them.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveEntryRegister(mod, entry) {
|
|
74
|
+
const named = getRegisterFn(mod);
|
|
75
|
+
if (named)
|
|
76
|
+
return named;
|
|
77
|
+
if (!mod)
|
|
78
|
+
return null;
|
|
79
|
+
const Component = mod["default"];
|
|
80
|
+
if (typeof Component !== "function" && !(Component && typeof Component === "object")) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const path = typeof entry.route === "string" && entry.route
|
|
84
|
+
? entry.route
|
|
85
|
+
: `/${entry.id}`;
|
|
86
|
+
const meta = mod["meta"] && typeof mod["meta"] === "object"
|
|
87
|
+
? mod["meta"]
|
|
88
|
+
: undefined;
|
|
89
|
+
const name = (typeof meta?.title === "string" && meta.title)
|
|
90
|
+
|| (typeof entry.title === "string" && entry.title)
|
|
91
|
+
|| entry.meta?.name
|
|
92
|
+
|| entry.id;
|
|
93
|
+
return (api) => {
|
|
94
|
+
api.addRoute({
|
|
95
|
+
path,
|
|
96
|
+
name,
|
|
97
|
+
element: api.React.createElement(Component),
|
|
98
|
+
...(meta?.icon != null ? { icon: meta.icon } : {}),
|
|
99
|
+
meta: { hideInMenu: meta?.hideInNav === true },
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
}
|
|
68
103
|
export async function registerConsolePluginsFromEntries(entries, hostApi, onEntryError, assetOrigin) {
|
|
69
104
|
if (!entries.length)
|
|
70
105
|
return;
|
|
@@ -72,7 +107,7 @@ export async function registerConsolePluginsFromEntries(entries, hostApi, onEntr
|
|
|
72
107
|
try {
|
|
73
108
|
const specifier = resolvePluginImportUrl(entry.resolvedModule, assetOrigin);
|
|
74
109
|
const mod = (await import(/* @vite-ignore */ specifier));
|
|
75
|
-
const register =
|
|
110
|
+
const register = resolveEntryRegister(mod, entry);
|
|
76
111
|
if (register)
|
|
77
112
|
await register(hostApi);
|
|
78
113
|
else
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadConsoleEntries.js","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAgH,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"loadConsoleEntries.js","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteApi.d.ts","sourceRoot":"","sources":["../../client/console-utils/remoteApi.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAED,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"remoteApi.d.ts","sourceRoot":"","sources":["../../client/console-utils/remoteApi.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAED,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAYxC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,kFAAkF;AAClF,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,QAAQ,CAAC,CAoBnB"}
|
|
@@ -9,7 +9,20 @@ export function getApiBase() {
|
|
|
9
9
|
return "";
|
|
10
10
|
}
|
|
11
11
|
export function getToken() {
|
|
12
|
-
|
|
12
|
+
const runtime = getRuntimeToken();
|
|
13
|
+
if (runtime)
|
|
14
|
+
return runtime;
|
|
15
|
+
if (typeof localStorage !== "undefined") {
|
|
16
|
+
const local = localStorage.getItem(TOKEN_KEY)?.trim();
|
|
17
|
+
if (local)
|
|
18
|
+
return local;
|
|
19
|
+
}
|
|
20
|
+
if (typeof sessionStorage !== "undefined") {
|
|
21
|
+
const session = sessionStorage.getItem(TOKEN_KEY)?.trim();
|
|
22
|
+
if (session)
|
|
23
|
+
return session;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
13
26
|
}
|
|
14
27
|
export function resolveApiUrl(path) {
|
|
15
28
|
const base = getApiBase();
|
|
@@ -33,6 +46,9 @@ export async function apiFetch(input, init) {
|
|
|
33
46
|
: input;
|
|
34
47
|
const res = await fetch(url, { ...init, headers });
|
|
35
48
|
if (res.status === 401) {
|
|
49
|
+
// runtime token(Demo 预置)过期后也必须失效,否则 getToken() 永远优先命中它,
|
|
50
|
+
// local/sessionStorage 清理不掉,造成 401 死循环。
|
|
51
|
+
clearRuntimeToken();
|
|
36
52
|
sessionStorage.removeItem(TOKEN_KEY);
|
|
37
53
|
localStorage.removeItem(TOKEN_KEY);
|
|
38
54
|
window.dispatchEvent(new CustomEvent("zhin:auth-required"));
|
|
@@ -48,4 +64,9 @@ function getRuntimeToken() {
|
|
|
48
64
|
const token = window.__ZHIN_API_TOKEN;
|
|
49
65
|
return token?.trim() || null;
|
|
50
66
|
}
|
|
67
|
+
function clearRuntimeToken() {
|
|
68
|
+
if (typeof window === "undefined")
|
|
69
|
+
return;
|
|
70
|
+
delete window.__ZHIN_API_TOKEN;
|
|
71
|
+
}
|
|
51
72
|
//# sourceMappingURL=remoteApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteApi.js","sourceRoot":"","sources":["../../client/console-utils/remoteApi.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAAG,gBAAgB,CAAC;AACnC,MAAM,YAAY,GAAG,eAAe,CAAC;AAErC,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAI,MAAM;QAAE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IACjE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,eAAe,EAAE,IAAI,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"remoteApi.js","sourceRoot":"","sources":["../../client/console-utils/remoteApi.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
+
export { ENDPOINT_RPC, ENDPOINT_MANAGEMENT_CAPABILITIES, INBOX_RPC, SIDE_EVENT_PUSH, SIDE_EVENT_RPC, normalizeConsolePushMessage, normalizeConsolePushType, parseConsoleInboxEvent, type ConsoleInboxEvent, type ConsoleInboxEventKind, type ConsoleEndpointPhase, type ConsoleEndpointSummary, type EndpointManagementCapability, } from "@zhin.js/console-protocol";
|
|
2
3
|
export { resolveMediaSrc, pickMediaRawUrl, type MediaKind } from "./mediaSrc";
|
|
3
4
|
export { segmentsForImDelivery, segmentsForAgentPanel } from "./segments.js";
|
|
4
5
|
export { app, type ConsoleApp, type AddRouteInput, type AddToolInput, type ConsoleRouteRecord, type RouteTreeNode, type ToolTreeNode, type ConsoleRouteRenderer, } from "./app";
|
|
@@ -7,7 +8,7 @@ export { configureConsole, getRuntimeEnv } from "./runtime/index.js";
|
|
|
7
8
|
export { createRegistryStore, useRegistry, type RegistryStore, } from "./store/createRegistryStore.js";
|
|
8
9
|
export { cn } from "./console-utils/cn.js";
|
|
9
10
|
export { apiFetch, getApiBase, getToken, resolveApiUrl, resolveWebSocketUrl, } from "./console-utils/remoteApi.js";
|
|
10
|
-
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, type CreatePluginRegisterHostApiOptions, type FetchConsoleEntriesOptions, type LoadConsoleEntriesOptions, } from "./bootstrap/loadConsoleEntries.js";
|
|
11
|
+
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, resolveEntryRegister, type CreatePluginRegisterHostApiOptions, type FetchConsoleEntriesOptions, type LoadConsoleEntriesOptions, } from "./bootstrap/loadConsoleEntries.js";
|
|
11
12
|
export { registerOrchestrationConsole } from "./orchestration/registerOrchestrationConsole.js";
|
|
12
13
|
export { default as OrchestrationRunsPage } from "./orchestration/OrchestrationRunsPage.js";
|
|
13
14
|
export { startAgentSession, continueAgentSession, subscribeAgentStream, iterateAgentStreamNdjson, foldAgentStreamNdjson, type AgentStreamClientOptions, type SubscribeAgentStreamOptions, } from "./agent-stream.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,GAClC,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG7E,OAAO,EACL,GAAG,EACH,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,OAAO,CAAC;AAGf,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,EACpB,KAAK,kCAAkC,EACvC,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,GAC/B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAE5F,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,GACjC,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
export * from "./types";
|
|
3
|
+
export { ENDPOINT_RPC, ENDPOINT_MANAGEMENT_CAPABILITIES, INBOX_RPC, SIDE_EVENT_PUSH, SIDE_EVENT_RPC, normalizeConsolePushMessage, normalizeConsolePushType, parseConsoleInboxEvent, } from "@zhin.js/console-protocol";
|
|
3
4
|
// Media URL resolution
|
|
4
5
|
export { resolveMediaSrc, pickMediaRawUrl } from "./mediaSrc";
|
|
5
6
|
// Segment IM visibility (inbox vs agent panel)
|
|
@@ -12,7 +13,7 @@ export { configureConsole, getRuntimeEnv } from "./runtime/index.js";
|
|
|
12
13
|
export { createRegistryStore, useRegistry, } from "./store/createRegistryStore.js";
|
|
13
14
|
export { cn } from "./console-utils/cn.js";
|
|
14
15
|
export { apiFetch, getApiBase, getToken, resolveApiUrl, resolveWebSocketUrl, } from "./console-utils/remoteApi.js";
|
|
15
|
-
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, } from "./bootstrap/loadConsoleEntries.js";
|
|
16
|
+
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, resolveEntryRegister, } from "./bootstrap/loadConsoleEntries.js";
|
|
16
17
|
export { registerOrchestrationConsole } from "./orchestration/registerOrchestrationConsole.js";
|
|
17
18
|
export { default as OrchestrationRunsPage } from "./orchestration/OrchestrationRunsPage.js";
|
|
18
19
|
export { startAgentSession, continueAgentSession, subscribeAgentStream, iterateAgentStreamNdjson, foldAgentStreamNdjson, } from "./agent-stream.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,GAMvB,MAAM,2BAA2B,CAAC;AAEnC,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAC;AAE9E,+CAA+C;AAC/C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE7E,uDAAuD;AACvD,OAAO,EACL,GAAG,GAQJ,MAAM,OAAO,CAAC;AAEf,iCAAiC;AACjC,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,GAEZ,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,GAIrB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAE5F,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,GAGtB,MAAM,mBAAmB,CAAC"}
|
package/dist/mediaSrc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaSrc.d.ts","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAInD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,IAAI,GAAE,SAAmB,GACxB,MAAM,GAAG,SAAS,CAyBpB;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"mediaSrc.d.ts","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAInD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,IAAI,GAAE,SAAmB,GACxB,MAAM,GAAG,SAAS,CAyBpB;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAwB7F"}
|
package/dist/mediaSrc.js
CHANGED
|
@@ -43,6 +43,22 @@ export function pickMediaRawUrl(data) {
|
|
|
43
43
|
return value.trim();
|
|
44
44
|
}
|
|
45
45
|
const v = data.url ?? data.file ?? data.src ?? data.href;
|
|
46
|
-
|
|
46
|
+
if (typeof v === 'string' && v.trim())
|
|
47
|
+
return v.trim();
|
|
48
|
+
// agent 出站媒体段(media-publisher)直接携带纯 base64,无 url/file;
|
|
49
|
+
// 有 mime 时拼成 data: URL 透传,否则走 base64:// 让 resolveMediaSrc 按 kind 补默认 MIME
|
|
50
|
+
const b64 = data.base64;
|
|
51
|
+
if (typeof b64 === 'string' && b64.trim()) {
|
|
52
|
+
const payload = b64.trim();
|
|
53
|
+
if (payload.startsWith('data:') || payload.startsWith('base64://')
|
|
54
|
+
|| payload.startsWith('http://') || payload.startsWith('https://')) {
|
|
55
|
+
return payload;
|
|
56
|
+
}
|
|
57
|
+
const mime = typeof data.mime === 'string' && data.mime.trim() ? data.mime.trim() : '';
|
|
58
|
+
return mime
|
|
59
|
+
? `data:${mime};base64,${payload.replace(/\s/g, '')}`
|
|
60
|
+
: `base64://${payload}`;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
47
63
|
}
|
|
48
64
|
//# sourceMappingURL=mediaSrc.js.map
|