@assistant-ui/react-devtools 1.2.14 → 1.2.16
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.
|
@@ -42,7 +42,12 @@ const useDevToolsClient = (selectedApiId, t0) => {
|
|
|
42
42
|
let t4;
|
|
43
43
|
if ($[7] !== client) {
|
|
44
44
|
t4 = (apiId_0, threadId) => {
|
|
45
|
-
if (client.switchToThread) return
|
|
45
|
+
if (!client.switchToThread) return;
|
|
46
|
+
try {
|
|
47
|
+
return Promise.resolve(client.switchToThread(apiId_0, threadId)).catch(_temp);
|
|
48
|
+
} catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
46
51
|
};
|
|
47
52
|
$[7] = client;
|
|
48
53
|
$[8] = t4;
|
|
@@ -64,6 +69,7 @@ const useDevToolsClient = (selectedApiId, t0) => {
|
|
|
64
69
|
} else t5 = $[13];
|
|
65
70
|
return t5;
|
|
66
71
|
};
|
|
72
|
+
function _temp() {}
|
|
67
73
|
//#endregion
|
|
68
74
|
export { useDevToolsClient };
|
|
69
75
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDevToolsClient.js","names":["useCallback","useMemo","useSyncExternalStore","inProcessClient","EMPTY_SNAPSHOT","ApiInfo","DevToolsClient","DevToolsClientResult","apiIds","selected","clearEvents","apiId","switchToThread","threadId","Promise","useDevToolsClient","selectedApiId","t0","$","_c","client","undefined","t1","subscribe","listener","getSnapshot","getServerSnapshot","store","snapshot","t2","byId","get","t3","t4","apiId_0","t5"],"sources":["../../src/data/useDevToolsClient.ts"],"sourcesContent":["import { useCallback, useMemo, useSyncExternalStore } from \"react\";\nimport { inProcessClient } from \"./createInProcessClient\";\nimport { EMPTY_SNAPSHOT, type ApiInfo, type DevToolsClient } from \"./types\";\n\nexport interface DevToolsClientResult {\n apiIds: number[];\n selected: ApiInfo | undefined;\n clearEvents: (apiId: number) => void;\n switchToThread: (apiId: number, threadId: string) => void | Promise<void>;\n}\n\n/**\n * Reads a DevToolsClient snapshot and resolves the selected instance. The panel\n * depends only on this, so swapping the client swaps the data source without\n * touching the UI. Pass a stable client reference (the default in-process client\n * is a module singleton; memoize custom clients).\n */\nexport const useDevToolsClient = (\n selectedApiId: number | null,\n client: DevToolsClient = inProcessClient,\n): DevToolsClientResult => {\n // Bind the client methods so class-based or relay clients that rely on `this`\n // work, and keep the references stable so useSyncExternalStore does not\n // resubscribe on every render.\n const store = useMemo(\n () => ({\n subscribe: (listener: () => void) => client.subscribe(listener),\n getSnapshot: () => client.getSnapshot(),\n getServerSnapshot: () =>\n client.getServerSnapshot ? client.getServerSnapshot() : EMPTY_SNAPSHOT,\n }),\n [client],\n );\n\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getServerSnapshot,\n );\n\n const selected =\n selectedApiId !== null ? snapshot.byId.get(selectedApiId) : undefined;\n\n const clearEvents = useCallback(\n (apiId: number) => client.clearEvents(apiId),\n [client],\n );\n\n const switchToThread = useCallback(\n (apiId: number, threadId: string) => {\n if (client.switchToThread) {\n return client.switchToThread(apiId, threadId);\n }\n },\n [client],\n );\n\n return { apiIds: snapshot.apiIds, selected, clearEvents, switchToThread };\n};\n"],"mappings":";;;;;;;;;;;AAiBA,MAAae,qBAAoBC,eAAAC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAE/B,MAAAC,SAAAH,OAAAI,KAAAA,IAAAlB,kBAAAc;CAAwC,IAAAK;CAAA,IAAAJ,EAAA,OAAAE,QAAA;EAM/BE,KAAA;GAAAC,YACMC,aAA0BJ,OAAMG,UAAWC,QAAQ;GAACC,mBAC5CL,OAAMK,YAAa;GAACC,yBAErCN,OAAMM,oBAAqBN,OAAMM,kBAAoC,IAArEtB;EACJ;EAACc,EAAA,KAAAE;EAAAF,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CANH,MAAAS,QACSL;CAST,MAAAM,WAAiB1B,qBACfyB,MAAKJ,WACLI,MAAKF,aACLE,MAAKD,iBACP;CAAE,IAAAG;CAAA,IAAAX,EAAA,OAAAF,iBAAAE,EAAA,OAAAU,SAAAE,MAAA;EAGAD,KAAAb,kBAAkB,OAAOY,SAAQE,KAAKC,IAAKf,aAAyB,IAApEK,KAAAA;EAAqEH,EAAA,KAAAF;EAAAE,EAAA,KAAAU,SAAAE;EAAAZ,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CADvE,MAAAT,WACEoB;CAAsE,IAAAG;CAAA,IAAAd,EAAA,OAAAE,QAAA;EAGtEY,MAAArB,UAAmBS,OAAMV,YAAaC,KAAK;EAACO,EAAA,KAAAE;EAAAF,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAD9C,MAAAR,cAAoBsB;CAGlB,IAAAC;CAAA,IAAAf,EAAA,OAAAE,QAAA;EAGAa,MAAAC,SAAArB,aAAA;GACE,
|
|
1
|
+
{"version":3,"file":"useDevToolsClient.js","names":["useCallback","useMemo","useSyncExternalStore","inProcessClient","EMPTY_SNAPSHOT","ApiInfo","DevToolsClient","DevToolsClientResult","apiIds","selected","clearEvents","apiId","switchToThread","threadId","Promise","useDevToolsClient","selectedApiId","t0","$","_c","client","undefined","t1","subscribe","listener","getSnapshot","getServerSnapshot","store","snapshot","t2","byId","get","t3","t4","apiId_0","resolve","catch","_temp","t5"],"sources":["../../src/data/useDevToolsClient.ts"],"sourcesContent":["import { useCallback, useMemo, useSyncExternalStore } from \"react\";\nimport { inProcessClient } from \"./createInProcessClient\";\nimport { EMPTY_SNAPSHOT, type ApiInfo, type DevToolsClient } from \"./types\";\n\nexport interface DevToolsClientResult {\n apiIds: number[];\n selected: ApiInfo | undefined;\n clearEvents: (apiId: number) => void;\n switchToThread: (apiId: number, threadId: string) => void | Promise<void>;\n}\n\n/**\n * Reads a DevToolsClient snapshot and resolves the selected instance. The panel\n * depends only on this, so swapping the client swaps the data source without\n * touching the UI. Pass a stable client reference (the default in-process client\n * is a module singleton; memoize custom clients).\n */\nexport const useDevToolsClient = (\n selectedApiId: number | null,\n client: DevToolsClient = inProcessClient,\n): DevToolsClientResult => {\n // Bind the client methods so class-based or relay clients that rely on `this`\n // work, and keep the references stable so useSyncExternalStore does not\n // resubscribe on every render.\n const store = useMemo(\n () => ({\n subscribe: (listener: () => void) => client.subscribe(listener),\n getSnapshot: () => client.getSnapshot(),\n getServerSnapshot: () =>\n client.getServerSnapshot ? client.getServerSnapshot() : EMPTY_SNAPSHOT,\n }),\n [client],\n );\n\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getServerSnapshot,\n );\n\n const selected =\n selectedApiId !== null ? snapshot.byId.get(selectedApiId) : undefined;\n\n const clearEvents = useCallback(\n (apiId: number) => client.clearEvents(apiId),\n [client],\n );\n\n const switchToThread = useCallback(\n (apiId: number, threadId: string) => {\n if (!client.switchToThread) return;\n try {\n return Promise.resolve(client.switchToThread(apiId, threadId)).catch(\n () => {},\n );\n } catch {\n return;\n }\n },\n [client],\n );\n\n return { apiIds: snapshot.apiIds, selected, clearEvents, switchToThread };\n};\n"],"mappings":";;;;;;;;;;;AAiBA,MAAae,qBAAoBC,eAAAC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAE/B,MAAAC,SAAAH,OAAAI,KAAAA,IAAAlB,kBAAAc;CAAwC,IAAAK;CAAA,IAAAJ,EAAA,OAAAE,QAAA;EAM/BE,KAAA;GAAAC,YACMC,aAA0BJ,OAAMG,UAAWC,QAAQ;GAACC,mBAC5CL,OAAMK,YAAa;GAACC,yBAErCN,OAAMM,oBAAqBN,OAAMM,kBAAoC,IAArEtB;EACJ;EAACc,EAAA,KAAAE;EAAAF,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CANH,MAAAS,QACSL;CAST,MAAAM,WAAiB1B,qBACfyB,MAAKJ,WACLI,MAAKF,aACLE,MAAKD,iBACP;CAAE,IAAAG;CAAA,IAAAX,EAAA,OAAAF,iBAAAE,EAAA,OAAAU,SAAAE,MAAA;EAGAD,KAAAb,kBAAkB,OAAOY,SAAQE,KAAKC,IAAKf,aAAyB,IAApEK,KAAAA;EAAqEH,EAAA,KAAAF;EAAAE,EAAA,KAAAU,SAAAE;EAAAZ,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CADvE,MAAAT,WACEoB;CAAsE,IAAAG;CAAA,IAAAd,EAAA,OAAAE,QAAA;EAGtEY,MAAArB,UAAmBS,OAAMV,YAAaC,KAAK;EAACO,EAAA,KAAAE;EAAAF,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAD9C,MAAAR,cAAoBsB;CAGlB,IAAAC;CAAA,IAAAf,EAAA,OAAAE,QAAA;EAGAa,MAAAC,SAAArB,aAAA;GACE,IAAI,CAACO,OAAMR,gBAAe;GAC1B,IAAA;IAAA,OACSE,QAAOqB,QAASf,OAAMR,eAAgBD,SAAOE,QAAQ,CAAC,CAAC,CAAAuB,MAC5DC,KACF;GAAC,QAAA;IAAA;GAAA;EAGF;EACFnB,EAAA,KAAAE;EAAAF,EAAA,KAAAe;CAAA,OAAAA,KAAAf,EAAA;CAVH,MAAAN,iBAAuBqB;CAYrB,IAAAK;CAAA,IAAApB,EAAA,OAAAR,eAAAQ,EAAA,QAAAT,YAAAS,EAAA,QAAAU,SAAApB,UAAAU,EAAA,QAAAN,gBAAA;EAEK0B,KAAA;GAAA9B,QAAUoB,SAAQpB;GAAOC;GAAAC;GAAAE;EAAwC;EAACM,EAAA,KAAAR;EAAAQ,EAAA,MAAAT;EAAAS,EAAA,MAAAU,SAAApB;EAAAU,EAAA,MAAAN;EAAAM,EAAA,MAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAAA,OAAlEoB;AAAkE;AA7C1C,SAAAD,QAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlButton.js","names":["clsx","ButtonHTMLAttributes","ControlButton","t0","$","_c","className","props","t1","t2"],"sources":["../../../src/views/ui/ControlButton.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ButtonHTMLAttributes } from \"react\";\n\nexport const ControlButton = ({\n className,\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement>) => (\n <button\n className={clsx(\n \"text-muted-foreground hover:bg-accent hover:text-foreground border-border inline-flex h-8 items-center rounded-md border px-3 text-[13px] font-medium transition-colors\",\n className,\n )}\n {...props}\n />\n);\n"],"mappings":";;;;AAGA,MAAaE,iBAAgBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAH,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAG,
|
|
1
|
+
{"version":3,"file":"ControlButton.js","names":["clsx","ButtonHTMLAttributes","ControlButton","t0","$","_c","className","props","t1","t2"],"sources":["../../../src/views/ui/ControlButton.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ButtonHTMLAttributes } from \"react\";\n\nexport const ControlButton = ({\n className,\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement>) => (\n <button\n className={clsx(\n \"text-muted-foreground hover:bg-accent hover:text-foreground border-border inline-flex h-8 items-center rounded-md border px-3 text-[13px] font-medium transition-colors\",\n className,\n )}\n {...props}\n />\n);\n"],"mappings":";;;;AAGA,MAAaE,iBAAgBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAH,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAG,WAAA,GAAAC,SAAAJ;EAGYC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAA;EAAAD,YAAAF,EAAA;EAAAG,QAAAH,EAAA;CAAA;CAAA,IAAAI;CAAA,IAAAJ,EAAA,OAAAE,WAAA;EAE3BE,KAAAR,KACT,2KACAM,SACF;EAACF,EAAA,KAAAE;EAAAF,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAAA,IAAAK;CAAA,IAAAL,EAAA,OAAAG,SAAAH,EAAA,OAAAI,IAAA;EAJHC,KAAA,oBAAA,UAAA;GACa,WAAAD;GAGV,GACGD;EAAK,CAAA;EACTH,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;CAAA,OAAAA,KAAAL,EAAA;CAAA,OANFK;AAME"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-devtools",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.16",
|
|
4
4
|
"description": "React development tools for assistant-ui components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant-ui",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
+
"@assistant-ui/react": "0.15.17",
|
|
51
|
+
"@assistant-ui/tap": "0.9.15",
|
|
52
|
+
"@assistant-ui/x-buildutils": "0.0.24",
|
|
50
53
|
"@tailwindcss/cli": "^4.3.3",
|
|
51
54
|
"@types/node": "^26.2.0",
|
|
52
55
|
"@types/react": "^19.2.18",
|
|
53
|
-
"@types/react-dom": "^19.2.
|
|
56
|
+
"@types/react-dom": "^19.2.5",
|
|
54
57
|
"react": "^19.2.8",
|
|
55
58
|
"react-dom": "^19.2.8",
|
|
56
59
|
"tailwindcss": "^4.3.3",
|
|
57
|
-
"vitest": "^4.1.
|
|
58
|
-
"@assistant-ui/react": "0.15.15",
|
|
59
|
-
"@assistant-ui/tap": "0.9.13",
|
|
60
|
-
"@assistant-ui/x-buildutils": "0.0.23"
|
|
60
|
+
"vitest": "^4.1.11"
|
|
61
61
|
},
|
|
62
62
|
"publishConfig": {
|
|
63
63
|
"access": "public",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { EMPTY_SNAPSHOT, type DevToolsClient } from "./types";
|
|
5
|
+
import {
|
|
6
|
+
useDevToolsClient,
|
|
7
|
+
type DevToolsClientResult,
|
|
8
|
+
} from "./useDevToolsClient";
|
|
9
|
+
|
|
10
|
+
const fixtureClient = (
|
|
11
|
+
switchToThread?: DevToolsClient["switchToThread"],
|
|
12
|
+
): DevToolsClient => {
|
|
13
|
+
const client: DevToolsClient = {
|
|
14
|
+
subscribe: () => () => {},
|
|
15
|
+
getSnapshot: () => EMPTY_SNAPSHOT,
|
|
16
|
+
clearEvents: () => {},
|
|
17
|
+
};
|
|
18
|
+
if (switchToThread) client.switchToThread = switchToThread;
|
|
19
|
+
return client;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const hookResult = (client: DevToolsClient): DevToolsClientResult => {
|
|
23
|
+
let result: DevToolsClientResult | undefined;
|
|
24
|
+
const Probe = () => {
|
|
25
|
+
result = useDevToolsClient(1, client);
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
renderToStaticMarkup(createElement(Probe));
|
|
29
|
+
if (result === undefined) {
|
|
30
|
+
throw new Error("expected useDevToolsClient to render");
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const captureUnhandledRejections = async (run: () => void) => {
|
|
36
|
+
const rejections: unknown[] = [];
|
|
37
|
+
const onUnhandledRejection = (reason: unknown) => {
|
|
38
|
+
rejections.push(reason);
|
|
39
|
+
};
|
|
40
|
+
const priorListeners = process.listeners("unhandledRejection");
|
|
41
|
+
process.removeAllListeners("unhandledRejection");
|
|
42
|
+
process.on("unhandledRejection", onUnhandledRejection);
|
|
43
|
+
try {
|
|
44
|
+
run();
|
|
45
|
+
// Node emits unhandledRejection on a later event-loop turn; fake timers cannot drive that.
|
|
46
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
47
|
+
setImmediate(resolve);
|
|
48
|
+
await promise;
|
|
49
|
+
} finally {
|
|
50
|
+
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
51
|
+
for (const listener of priorListeners) {
|
|
52
|
+
process.on("unhandledRejection", listener);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return rejections;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describe("useDevToolsClient switchToThread", () => {
|
|
59
|
+
it("forwards a successful switch", async () => {
|
|
60
|
+
const switchToThread = vi.fn(async () => {});
|
|
61
|
+
const run = hookResult(fixtureClient(switchToThread)).switchToThread;
|
|
62
|
+
|
|
63
|
+
await expect(run(1, "thread-1")).resolves.toBeUndefined();
|
|
64
|
+
expect(switchToThread).toHaveBeenCalledWith(1, "thread-1");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("contains a synchronous client throw", () => {
|
|
68
|
+
const run = hookResult(
|
|
69
|
+
fixtureClient(() => {
|
|
70
|
+
throw new Error("failed");
|
|
71
|
+
}),
|
|
72
|
+
).switchToThread;
|
|
73
|
+
|
|
74
|
+
expect(() => run(1, "thread-1")).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("does not leak a rejected switch as an unhandled rejection", async () => {
|
|
78
|
+
const error = new Error("failed");
|
|
79
|
+
const run = hookResult(
|
|
80
|
+
fixtureClient(() => Promise.reject(error)),
|
|
81
|
+
).switchToThread;
|
|
82
|
+
|
|
83
|
+
const rejections = await captureUnhandledRejections(() => {
|
|
84
|
+
void run(1, "thread-1");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(rejections).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("lets an ignored rejected switch settle", async () => {
|
|
91
|
+
const run = hookResult(
|
|
92
|
+
fixtureClient(() => Promise.reject(new Error("failed"))),
|
|
93
|
+
).switchToThread;
|
|
94
|
+
|
|
95
|
+
await expect(run(1, "thread-1")).resolves.toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -48,8 +48,13 @@ export const useDevToolsClient = (
|
|
|
48
48
|
|
|
49
49
|
const switchToThread = useCallback(
|
|
50
50
|
(apiId: number, threadId: string) => {
|
|
51
|
-
if (client.switchToThread)
|
|
52
|
-
|
|
51
|
+
if (!client.switchToThread) return;
|
|
52
|
+
try {
|
|
53
|
+
return Promise.resolve(client.switchToThread(apiId, threadId)).catch(
|
|
54
|
+
() => {},
|
|
55
|
+
);
|
|
56
|
+
} catch {
|
|
57
|
+
return;
|
|
53
58
|
}
|
|
54
59
|
},
|
|
55
60
|
[client],
|