mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
Restore provider readiness for mobile plugins (#6760)
* fix(client): restore provider readiness on mobile React Native uses an AbortSignal without throwIfAborted. Check aborted with the existing API disposal error so readiness works while preserving cleanup and cancellation. Co-authored-by: GeorgeR <897457+GeorgeR@users.noreply.github.com> * test(plugins): run mobile readiness regression in CI --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> Co-authored-by: GeorgeR <897457+GeorgeR@users.noreply.github.com>
This commit is contained in:
co-authored by
GeorgeR
parent
ec6393f31f
commit
1839de5b1d
@@ -1051,7 +1051,7 @@ async function waitForProvidersReady(
|
||||
|
||||
try {
|
||||
await observation.ready;
|
||||
signal.throwIfAborted();
|
||||
if (signal.aborted) throw new Error("Paseo API is disposed");
|
||||
return await new Promise<PaseoProviderSnapshotResult>((resolve, reject) => {
|
||||
let settled = false;
|
||||
let requestId: string | null = null;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { runInNewContext } from "node:vm";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { build } from "esbuild";
|
||||
import { EventEmitter, once } from "node:events";
|
||||
import { resolveDaemonVersion } from "../daemon-version.js";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
@@ -15,6 +19,59 @@ afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
test.each(["React Native", "native"])(
|
||||
"provider readiness works with %s AbortController",
|
||||
async (runtime) => {
|
||||
const require = createRequire(import.meta.url);
|
||||
const reactNativeRequire = createRequire(require.resolve("react-native/package.json"));
|
||||
const { AbortController: ReactNativeAbortController } = reactNativeRequire(
|
||||
"abort-controller/dist/abort-controller",
|
||||
);
|
||||
const bundle = await build({
|
||||
entryPoints: [fileURLToPath(new URL("../../../../client/src/index.ts", import.meta.url))],
|
||||
bundle: true,
|
||||
write: false,
|
||||
format: "iife",
|
||||
globalName: "paseoSdk",
|
||||
platform: "browser",
|
||||
});
|
||||
// A separate runtime starts with React Native's real abort implementation.
|
||||
// Node's globals and the daemon's runtime remain unchanged.
|
||||
const sdk: typeof import("@getpaseo/client") = runInNewContext(
|
||||
`${bundle.outputFiles[0]!.text}\npaseoSdk`,
|
||||
{
|
||||
AbortController: runtime === "React Native" ? ReactNativeAbortController : AbortController,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
queueMicrotask,
|
||||
TextEncoder,
|
||||
TextDecoder,
|
||||
},
|
||||
);
|
||||
const daemon = await createTestPaseoDaemon({ mcpEnabled: false });
|
||||
const client = new DaemonClient({ url: `ws://127.0.0.1:${daemon.port}/ws` });
|
||||
const api = sdk.createPaseoApi(client);
|
||||
try {
|
||||
await client.connect();
|
||||
const snapshot = await api.providers.waitForReady({ cwd: daemon.staticDir });
|
||||
expect(snapshot.entries.length).toBeGreaterThan(0);
|
||||
expect(snapshot.entries.every((entry) => entry.status !== "loading")).toBe(true);
|
||||
const waiting = api.providers.waitForReady({ cwd: daemon.staticDir });
|
||||
const canceled = expect(waiting).rejects.toThrow(/disposed|released/);
|
||||
await api.dispose();
|
||||
await canceled;
|
||||
const { diagnostic } = await client.collectDiagnostics();
|
||||
expect(diagnostic).toContain("Source registrations: 0");
|
||||
expect(diagnostic).toContain("Producer listeners: 0");
|
||||
expect(() => api.providers.waitForReady()).toThrow("disposed");
|
||||
} finally {
|
||||
await api.dispose();
|
||||
await client.close();
|
||||
await daemon.close();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test("plugin handlers create workspaces and agents through their Paseo API", async () => {
|
||||
const pluginDirectory = await mkdtemp(path.join(tmpdir(), "paseo-api-plugin-"));
|
||||
const workspaceDirectory = await mkdtemp(path.join(tmpdir(), "paseo-api-workspace-"));
|
||||
|
||||
Reference in New Issue
Block a user