fix: lazy-load zca-js at the zalouser runtime boundary

This commit is contained in:
Peter Steinberger
2026-03-27 01:04:48 +00:00
parent b9c60fd37a
commit 5e35e6a95f
3 changed files with 48 additions and 6 deletions

View File

@@ -26,7 +26,7 @@ import {
type LoginQRCallbackEvent,
type Message,
type User,
Zalo,
createZalo,
} from "./zca-client.js";
import { LoginQRCallbackEventType, ThreadType } from "./zca-constants.js";
@@ -619,7 +619,7 @@ async function ensureApi(
if (!stored) {
throw new Error(`No saved Zalo session for profile \"${profile}\"`);
}
const zalo = new Zalo({
const zalo = await createZalo({
logging: false,
selfListen: false,
});
@@ -1293,7 +1293,7 @@ export async function startZaloQrLogin(params: {
let capturedCredentials: Omit<StoredZaloCredentials, "createdAt" | "lastUsedAt"> | null =
null;
try {
const zalo = new Zalo({ logging: false, selfListen: false });
const zalo = await createZalo({ logging: false, selfListen: false });
const api = await zalo.loginQR(undefined, (event: LoginQRCallbackEvent) => {
const current = activeQrLogins.get(profile);
if (!current || current.id !== login.id) {

View File

@@ -0,0 +1,28 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
describe("zca-client runtime loading", () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
});
it("does not import zca-js until a session is created", async () => {
const runtimeFactory = vi.fn(() => ({
Zalo: class MockZalo {
constructor(public readonly options?: { logging?: boolean; selfListen?: boolean }) {}
},
}));
vi.doMock("zca-js", runtimeFactory);
const zcaClient = await import("./zca-client.js");
expect(runtimeFactory).not.toHaveBeenCalled();
const client = await zcaClient.createZalo({ logging: false, selfListen: true });
expect(runtimeFactory).toHaveBeenCalledTimes(1);
expect(client).toMatchObject({
options: { logging: false, selfListen: true },
});
});
});

View File

@@ -1,4 +1,3 @@
import * as zcaJsRuntime from "zca-js";
import {
LoginQRCallbackEventType,
Reactions,
@@ -7,9 +6,18 @@ import {
type Style,
} from "./zca-constants.js";
const zcaJs = zcaJsRuntime as unknown as {
type ZcaJsRuntime = {
Zalo: unknown;
};
let zcaJsRuntimePromise: Promise<ZcaJsRuntime> | null = null;
async function loadZcaJsRuntime(): Promise<ZcaJsRuntime> {
// Keep zca-js behind a runtime boundary so bundled metadata/contracts can load
// without resolving its optional WebSocket dependency tree.
zcaJsRuntimePromise ??= import("zca-js").then((mod) => mod as unknown as ZcaJsRuntime);
return await zcaJsRuntimePromise;
}
export { LoginQRCallbackEventType, Reactions, TextStyle, ThreadType };
export type { Style };
@@ -242,4 +250,10 @@ type ZaloCtor = new (options?: { logging?: boolean; selfListen?: boolean }) => {
): Promise<API>;
};
export const Zalo = zcaJs.Zalo as unknown as ZaloCtor;
export async function createZalo(
options?: ConstructorParameters<ZaloCtor>[0],
): Promise<InstanceType<ZaloCtor>> {
const zcaJs = await loadZcaJsRuntime();
const Zalo = zcaJs.Zalo as unknown as ZaloCtor;
return new Zalo(options);
}