@kodelyth/zalouser 2026.5.42 → 2026.6.2

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.
Files changed (71) hide show
  1. package/klaw.plugin.json +286 -3
  2. package/package.json +19 -6
  3. package/api.ts +0 -9
  4. package/channel-plugin-api.ts +0 -3
  5. package/contract-api.ts +0 -2
  6. package/doctor-contract-api.ts +0 -1
  7. package/index.ts +0 -34
  8. package/runtime-api.ts +0 -62
  9. package/secret-contract-api.ts +0 -4
  10. package/setup-entry.ts +0 -9
  11. package/setup-plugin-api.ts +0 -2
  12. package/src/accounts.runtime.ts +0 -1
  13. package/src/accounts.test-mocks.ts +0 -14
  14. package/src/accounts.test.ts +0 -298
  15. package/src/accounts.ts +0 -136
  16. package/src/channel-api.ts +0 -16
  17. package/src/channel.adapters.ts +0 -432
  18. package/src/channel.directory.test.ts +0 -59
  19. package/src/channel.runtime.ts +0 -12
  20. package/src/channel.sendpayload.test.ts +0 -311
  21. package/src/channel.setup.test.ts +0 -30
  22. package/src/channel.setup.ts +0 -12
  23. package/src/channel.test.ts +0 -424
  24. package/src/channel.ts +0 -221
  25. package/src/config-schema.ts +0 -33
  26. package/src/directory.ts +0 -54
  27. package/src/doctor-contract.ts +0 -156
  28. package/src/doctor.test.ts +0 -87
  29. package/src/doctor.ts +0 -37
  30. package/src/group-policy.test.ts +0 -61
  31. package/src/group-policy.ts +0 -83
  32. package/src/message-sid.test.ts +0 -66
  33. package/src/message-sid.ts +0 -80
  34. package/src/monitor.account-scope.test.ts +0 -122
  35. package/src/monitor.group-gating.test.ts +0 -967
  36. package/src/monitor.send-mocks.ts +0 -20
  37. package/src/monitor.ts +0 -1057
  38. package/src/probe.test.ts +0 -60
  39. package/src/probe.ts +0 -35
  40. package/src/qr-temp-file.ts +0 -19
  41. package/src/reaction.test.ts +0 -19
  42. package/src/reaction.ts +0 -32
  43. package/src/runtime.ts +0 -9
  44. package/src/security-audit.test.ts +0 -83
  45. package/src/security-audit.ts +0 -71
  46. package/src/send-receipt.ts +0 -31
  47. package/src/send.test.ts +0 -424
  48. package/src/send.ts +0 -280
  49. package/src/session-route.ts +0 -121
  50. package/src/setup-core.ts +0 -36
  51. package/src/setup-surface.test.ts +0 -367
  52. package/src/setup-surface.ts +0 -481
  53. package/src/setup-test-helpers.ts +0 -42
  54. package/src/shared.ts +0 -92
  55. package/src/status-issues.test.ts +0 -31
  56. package/src/status-issues.ts +0 -55
  57. package/src/test-helpers.ts +0 -26
  58. package/src/text-styles.test.ts +0 -203
  59. package/src/text-styles.ts +0 -540
  60. package/src/tool.test.ts +0 -212
  61. package/src/tool.ts +0 -200
  62. package/src/types.ts +0 -127
  63. package/src/zalo-js.credentials.test.ts +0 -465
  64. package/src/zalo-js.test-mocks.ts +0 -89
  65. package/src/zalo-js.ts +0 -1889
  66. package/src/zca-client.test.ts +0 -27
  67. package/src/zca-client.ts +0 -259
  68. package/src/zca-constants.ts +0 -55
  69. package/src/zca-js-exports.d.ts +0 -22
  70. package/test-api.ts +0 -21
  71. package/tsconfig.json +0 -16
@@ -1,311 +0,0 @@
1
- import {
2
- installChannelOutboundPayloadContractSuite,
3
- primeChannelOutboundSendMock,
4
- type OutboundPayloadHarnessParams,
5
- } from "klaw/plugin-sdk/channel-contract-testing";
6
- import {
7
- createMessageReceiptFromOutboundResults,
8
- verifyChannelMessageAdapterCapabilityProofs,
9
- } from "klaw/plugin-sdk/channel-message";
10
- import { beforeEach, describe, expect, it, vi } from "vitest";
11
- import "./accounts.test-mocks.js";
12
- import "./zalo-js.test-mocks.js";
13
- import type { ReplyPayload } from "../runtime-api.js";
14
- import { zalouserPlugin } from "./channel.js";
15
- import { setZalouserRuntime } from "./runtime.js";
16
- import * as sendModule from "./send.js";
17
-
18
- vi.mock("./send.js", () => ({
19
- sendMessageZalouser: vi.fn().mockResolvedValue({ ok: true, messageId: "zlu-1" } as never),
20
- sendReactionZalouser: vi.fn().mockResolvedValue({ ok: true } as never),
21
- }));
22
-
23
- function baseCtx(payload: ReplyPayload) {
24
- return {
25
- cfg: {},
26
- to: "user:987654321",
27
- text: "",
28
- payload,
29
- };
30
- }
31
-
32
- type ZalouserOutbound = NonNullable<typeof zalouserPlugin.outbound>;
33
- type ZalouserSendPayload = NonNullable<ZalouserOutbound["sendPayload"]>;
34
- type ZalouserMessageAdapter = NonNullable<typeof zalouserPlugin.message>;
35
- type ZalouserMessageSender = NonNullable<ZalouserMessageAdapter["send"]>;
36
-
37
- function requireZalouserSendPayload(): ZalouserSendPayload {
38
- const sendPayload = zalouserPlugin.outbound?.sendPayload;
39
- if (!sendPayload) {
40
- throw new Error("Expected Zalouser outbound sendPayload");
41
- }
42
- return sendPayload;
43
- }
44
-
45
- function requireZalouserMessageAdapter(): ZalouserMessageAdapter {
46
- const adapter = zalouserPlugin.message;
47
- if (!adapter) {
48
- throw new Error("Expected Zalouser message adapter");
49
- }
50
- return adapter;
51
- }
52
-
53
- function requireZalouserTextSender(
54
- adapter: ZalouserMessageAdapter,
55
- ): NonNullable<ZalouserMessageSender["text"]> {
56
- const text = adapter.send?.text;
57
- if (!text) {
58
- throw new Error("Expected Zalouser message adapter text sender");
59
- }
60
- return text;
61
- }
62
-
63
- function requireZalouserMediaSender(
64
- adapter: ZalouserMessageAdapter,
65
- ): NonNullable<ZalouserMessageSender["media"]> {
66
- const media = adapter.send?.media;
67
- if (!media) {
68
- throw new Error("Expected Zalouser message adapter media sender");
69
- }
70
- return media;
71
- }
72
-
73
- function requireRecord(value: unknown, label: string): Record<string, unknown> {
74
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
75
- throw new Error(`expected ${label} to be a record`);
76
- }
77
- return value as Record<string, unknown>;
78
- }
79
-
80
- function requireSendOptions(
81
- mockedSend: ReturnType<typeof vi.mocked<(typeof import("./send.js"))["sendMessageZalouser"]>>,
82
- ): Record<string, unknown> {
83
- return requireRecord(requireSendCall(mockedSend)[2], "Zalouser send options");
84
- }
85
-
86
- function requireSendCall(
87
- mockedSend: ReturnType<typeof vi.mocked<(typeof import("./send.js"))["sendMessageZalouser"]>>,
88
- ): unknown[] {
89
- const [call] = mockedSend.mock.calls as unknown[][];
90
- if (!call) {
91
- throw new Error("expected Zalouser send call");
92
- }
93
- return call;
94
- }
95
-
96
- describe("zalouserPlugin outbound sendPayload", () => {
97
- let mockedSend: ReturnType<typeof vi.mocked<(typeof import("./send.js"))["sendMessageZalouser"]>>;
98
-
99
- beforeEach(() => {
100
- setZalouserRuntime({
101
- channel: {
102
- text: {
103
- resolveChunkMode: vi.fn(() => "length"),
104
- resolveTextChunkLimit: vi.fn(() => 1200),
105
- },
106
- },
107
- } as never);
108
- mockedSend = vi.mocked(sendModule.sendMessageZalouser);
109
- primeChannelOutboundSendMock(mockedSend, { ok: true, messageId: "zlu-1" });
110
- });
111
-
112
- it("group target delegates with isGroup=true and stripped threadId", async () => {
113
- mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-g1" } as never);
114
- const sendPayload = requireZalouserSendPayload();
115
-
116
- const result = await sendPayload({
117
- ...baseCtx({ text: "hello group" }),
118
- to: "group:1471383327500481391",
119
- });
120
-
121
- expect(mockedSend).toHaveBeenCalledOnce();
122
- const sendCall = requireSendCall(mockedSend);
123
- expect(sendCall[0]).toBe("1471383327500481391");
124
- expect(sendCall[1]).toBe("hello group");
125
- const options = requireSendOptions(mockedSend);
126
- expect(options.isGroup).toBe(true);
127
- expect(options.textMode).toBe("markdown");
128
- expect(result.channel).toBe("zalouser");
129
- expect(result.messageId).toBe("zlu-g1");
130
- });
131
-
132
- it("treats bare numeric targets as direct chats for backward compatibility", async () => {
133
- mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-d1" } as never);
134
- const sendPayload = requireZalouserSendPayload();
135
-
136
- const result = await sendPayload({
137
- ...baseCtx({ text: "hello" }),
138
- to: "987654321",
139
- });
140
-
141
- expect(mockedSend).toHaveBeenCalledOnce();
142
- const sendCall = requireSendCall(mockedSend);
143
- expect(sendCall[0]).toBe("987654321");
144
- expect(sendCall[1]).toBe("hello");
145
- const options = requireSendOptions(mockedSend);
146
- expect(options.isGroup).toBe(false);
147
- expect(options.textMode).toBe("markdown");
148
- expect(result.channel).toBe("zalouser");
149
- expect(result.messageId).toBe("zlu-d1");
150
- });
151
-
152
- it("preserves provider-native group ids when sending to raw g- targets", async () => {
153
- mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-g-native" } as never);
154
- const sendPayload = requireZalouserSendPayload();
155
-
156
- const result = await sendPayload({
157
- ...baseCtx({ text: "hello native group" }),
158
- to: "g-1471383327500481391",
159
- });
160
-
161
- expect(mockedSend).toHaveBeenCalledOnce();
162
- const sendCall = requireSendCall(mockedSend);
163
- expect(sendCall[0]).toBe("g-1471383327500481391");
164
- expect(sendCall[1]).toBe("hello native group");
165
- const options = requireSendOptions(mockedSend);
166
- expect(options.isGroup).toBe(true);
167
- expect(options.textMode).toBe("markdown");
168
- expect(result.channel).toBe("zalouser");
169
- expect(result.messageId).toBe("zlu-g-native");
170
- });
171
-
172
- it("passes long markdown through once so formatting happens before chunking", async () => {
173
- const text = `**${"a".repeat(2501)}**`;
174
- mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-code" } as never);
175
- const sendPayload = requireZalouserSendPayload();
176
-
177
- const result = await sendPayload({
178
- ...baseCtx({ text }),
179
- to: "987654321",
180
- });
181
-
182
- expect(mockedSend).toHaveBeenCalledTimes(1);
183
- const sendCall = requireSendCall(mockedSend);
184
- expect(sendCall[0]).toBe("987654321");
185
- expect(sendCall[1]).toBe(text);
186
- const options = requireSendOptions(mockedSend);
187
- expect(options.isGroup).toBe(false);
188
- expect(options.textMode).toBe("markdown");
189
- expect(options.textChunkMode).toBe("length");
190
- expect(options.textChunkLimit).toBe(1200);
191
- expect(result.channel).toBe("zalouser");
192
- expect(result.messageId).toBe("zlu-code");
193
- });
194
-
195
- it("declares message adapter durable text and media with receipt proofs", async () => {
196
- mockedSend.mockImplementation(async (_threadId, _text, opts: { mediaUrl?: string } = {}) =>
197
- opts.mediaUrl
198
- ? {
199
- ok: true,
200
- messageId: "zlu-media-1",
201
- receipt: createMessageReceiptFromOutboundResults({
202
- results: [{ channel: "zalouser", messageId: "zlu-media-1" }],
203
- kind: "media",
204
- }),
205
- }
206
- : {
207
- ok: true,
208
- messageId: "zlu-text-1",
209
- receipt: createMessageReceiptFromOutboundResults({
210
- results: [{ channel: "zalouser", messageId: "zlu-text-1" }],
211
- kind: "text",
212
- }),
213
- },
214
- );
215
- const adapter = requireZalouserMessageAdapter();
216
- const sendText = requireZalouserTextSender(adapter);
217
- const sendMedia = requireZalouserMediaSender(adapter);
218
-
219
- const proofs = await verifyChannelMessageAdapterCapabilityProofs({
220
- adapterName: "zalouser",
221
- adapter,
222
- proofs: {
223
- text: async () => {
224
- const result = await sendText({
225
- cfg: {},
226
- to: "user:987654321",
227
- text: "hello",
228
- });
229
- expect(result.receipt.platformMessageIds).toEqual(["zlu-text-1"]);
230
- },
231
- media: async () => {
232
- const result = await sendMedia({
233
- cfg: {},
234
- to: "user:987654321",
235
- text: "image",
236
- mediaUrl: "https://example.com/image.png",
237
- });
238
- expect(result.receipt.platformMessageIds).toEqual(["zlu-media-1"]);
239
- },
240
- messageSendingHooks: () => {
241
- expect(adapter.durableFinal?.capabilities?.messageSendingHooks).toBe(true);
242
- },
243
- },
244
- });
245
- const proofStatusByCapability = new Map(
246
- proofs.map((proof) => [proof.capability, proof.status] as const),
247
- );
248
- expect(proofStatusByCapability.get("text")).toBe("verified");
249
- expect(proofStatusByCapability.get("media")).toBe("verified");
250
- expect(proofStatusByCapability.get("messageSendingHooks")).toBe("verified");
251
- });
252
- });
253
-
254
- describe("zalouserPlugin outbound payload contract", () => {
255
- function createZalouserHarness(params: OutboundPayloadHarnessParams) {
256
- const mockedSend = vi.mocked(sendModule.sendMessageZalouser);
257
- setZalouserRuntime({
258
- channel: {
259
- text: {
260
- resolveChunkMode: vi.fn(() => "length"),
261
- resolveTextChunkLimit: vi.fn(() => 1200),
262
- },
263
- },
264
- } as never);
265
- primeChannelOutboundSendMock(mockedSend, { ok: true, messageId: "zlu-1" }, params.sendResults);
266
- const ctx = {
267
- cfg: {},
268
- to: "user:987654321",
269
- text: "",
270
- payload: params.payload,
271
- };
272
- const sendPayload = requireZalouserSendPayload();
273
- return {
274
- run: async () => await sendPayload(ctx),
275
- sendMock: mockedSend,
276
- to: "987654321",
277
- };
278
- }
279
-
280
- installChannelOutboundPayloadContractSuite({
281
- channel: "zalouser",
282
- chunking: { mode: "passthrough", longTextLength: 3000 },
283
- createHarness: createZalouserHarness,
284
- });
285
- });
286
-
287
- describe("zalouserPlugin messaging target normalization", () => {
288
- it("normalizes user/group aliases to canonical targets", () => {
289
- const normalize = zalouserPlugin.messaging?.normalizeTarget;
290
- if (!normalize) {
291
- throw new Error("normalizeTarget unavailable");
292
- }
293
- expect(normalize("zlu:g:30003")).toBe("group:30003");
294
- expect(normalize("zalouser:u:20002")).toBe("user:20002");
295
- expect(normalize("zlu:g-30003")).toBe("group:g-30003");
296
- expect(normalize("zalouser:u-20002")).toBe("user:u-20002");
297
- expect(normalize("20002")).toBe("20002");
298
- });
299
-
300
- it("treats canonical and provider-native user/group targets as ids", () => {
301
- const looksLikeId = zalouserPlugin.messaging?.targetResolver?.looksLikeId;
302
- if (!looksLikeId) {
303
- throw new Error("looksLikeId unavailable");
304
- }
305
- expect(looksLikeId("user:20002")).toBe(true);
306
- expect(looksLikeId("group:30003")).toBe(true);
307
- expect(looksLikeId("g-30003")).toBe(true);
308
- expect(looksLikeId("u-20002")).toBe(true);
309
- expect(looksLikeId("Alice Nguyen")).toBe(false);
310
- });
311
- });
@@ -1,30 +0,0 @@
1
- import { mkdtemp, rm } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { createPluginSetupWizardStatus } from "klaw/plugin-sdk/plugin-test-runtime";
5
- import { withEnvAsync } from "klaw/plugin-sdk/test-env";
6
- import { describe, expect, it } from "vitest";
7
- import "./zalo-js.test-mocks.js";
8
- import { zalouserSetupPlugin } from "./setup-test-helpers.js";
9
-
10
- const zalouserSetupGetStatus = createPluginSetupWizardStatus(zalouserSetupPlugin);
11
-
12
- describe("zalouser setup plugin", () => {
13
- it("builds setup status without an initialized runtime", async () => {
14
- const stateDir = await mkdtemp(path.join(os.tmpdir(), "klaw-zalouser-setup-"));
15
-
16
- try {
17
- await withEnvAsync({ KLAW_STATE_DIR: stateDir }, async () => {
18
- const status = await zalouserSetupGetStatus({
19
- cfg: {},
20
- accountOverrides: {},
21
- });
22
- expect(status.channel).toBe("zalouser");
23
- expect(status.configured).toBe(false);
24
- expect(status.statusLines).toEqual(["Zalo Personal: needs QR login"]);
25
- });
26
- } finally {
27
- await rm(stateDir, { recursive: true, force: true });
28
- }
29
- });
30
- });
@@ -1,12 +0,0 @@
1
- import type { ResolvedZalouserAccount } from "./accounts.js";
2
- import type { ChannelPlugin } from "./channel-api.js";
3
- import { zalouserSetupAdapter } from "./setup-core.js";
4
- import { zalouserSetupWizard } from "./setup-surface.js";
5
- import { createZalouserPluginBase } from "./shared.js";
6
-
7
- export const zalouserSetupPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
8
- ...createZalouserPluginBase({
9
- setupWizard: zalouserSetupWizard,
10
- setup: zalouserSetupAdapter,
11
- }),
12
- };