@zapier/zapier-sdk 0.32.5 → 0.33.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +1 -0
  4. package/dist/constants.d.ts +0 -14
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/constants.js +0 -14
  7. package/dist/credentials.d.ts.map +1 -1
  8. package/dist/credentials.js +19 -20
  9. package/dist/index.cjs +22 -37
  10. package/dist/index.d.mts +1 -15
  11. package/dist/index.mjs +23 -30
  12. package/package.json +2 -2
  13. package/dist/api/auth.test.d.ts +0 -2
  14. package/dist/api/auth.test.d.ts.map +0 -1
  15. package/dist/api/auth.test.js +0 -220
  16. package/dist/api/client.test.d.ts +0 -2
  17. package/dist/api/client.test.d.ts.map +0 -1
  18. package/dist/api/client.test.js +0 -611
  19. package/dist/api/debug.test.d.ts +0 -2
  20. package/dist/api/debug.test.d.ts.map +0 -1
  21. package/dist/api/debug.test.js +0 -59
  22. package/dist/api/polling.test.d.ts +0 -2
  23. package/dist/api/polling.test.d.ts.map +0 -1
  24. package/dist/api/polling.test.js +0 -360
  25. package/dist/auth.test.d.ts +0 -2
  26. package/dist/auth.test.d.ts.map +0 -1
  27. package/dist/auth.test.js +0 -480
  28. package/dist/plugins/eventEmission/builders.test.d.ts +0 -2
  29. package/dist/plugins/eventEmission/builders.test.d.ts.map +0 -1
  30. package/dist/plugins/eventEmission/builders.test.js +0 -138
  31. package/dist/plugins/eventEmission/index.test.d.ts +0 -5
  32. package/dist/plugins/eventEmission/index.test.d.ts.map +0 -1
  33. package/dist/plugins/eventEmission/index.test.js +0 -704
  34. package/dist/plugins/eventEmission/transport.test.d.ts +0 -5
  35. package/dist/plugins/eventEmission/transport.test.d.ts.map +0 -1
  36. package/dist/plugins/eventEmission/transport.test.js +0 -164
  37. package/dist/plugins/fetch/index.test.d.ts +0 -2
  38. package/dist/plugins/fetch/index.test.d.ts.map +0 -1
  39. package/dist/plugins/fetch/index.test.js +0 -428
  40. package/dist/plugins/findFirstConnection/index.test.d.ts +0 -2
  41. package/dist/plugins/findFirstConnection/index.test.d.ts.map +0 -1
  42. package/dist/plugins/findFirstConnection/index.test.js +0 -177
  43. package/dist/plugins/findUniqueConnection/index.test.d.ts +0 -2
  44. package/dist/plugins/findUniqueConnection/index.test.d.ts.map +0 -1
  45. package/dist/plugins/findUniqueConnection/index.test.js +0 -159
  46. package/dist/plugins/getAction/index.test.d.ts +0 -2
  47. package/dist/plugins/getAction/index.test.d.ts.map +0 -1
  48. package/dist/plugins/getAction/index.test.js +0 -211
  49. package/dist/plugins/getApp/index.test.d.ts +0 -2
  50. package/dist/plugins/getApp/index.test.d.ts.map +0 -1
  51. package/dist/plugins/getApp/index.test.js +0 -157
  52. package/dist/plugins/getConnection/index.test.d.ts +0 -2
  53. package/dist/plugins/getConnection/index.test.d.ts.map +0 -1
  54. package/dist/plugins/getConnection/index.test.js +0 -124
  55. package/dist/plugins/getInputFieldsSchema/index.test.d.ts +0 -2
  56. package/dist/plugins/getInputFieldsSchema/index.test.d.ts.map +0 -1
  57. package/dist/plugins/getInputFieldsSchema/index.test.js +0 -291
  58. package/dist/plugins/listActions/index.test.d.ts +0 -2
  59. package/dist/plugins/listActions/index.test.d.ts.map +0 -1
  60. package/dist/plugins/listActions/index.test.js +0 -454
  61. package/dist/plugins/listApps/index.test.d.ts +0 -2
  62. package/dist/plugins/listApps/index.test.d.ts.map +0 -1
  63. package/dist/plugins/listApps/index.test.js +0 -124
  64. package/dist/plugins/listConnections/index.test.d.ts +0 -2
  65. package/dist/plugins/listConnections/index.test.d.ts.map +0 -1
  66. package/dist/plugins/listConnections/index.test.js +0 -920
  67. package/dist/plugins/listInputFieldChoices/index.test.d.ts +0 -2
  68. package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +0 -1
  69. package/dist/plugins/listInputFieldChoices/index.test.js +0 -717
  70. package/dist/plugins/listInputFields/index.test.d.ts +0 -2
  71. package/dist/plugins/listInputFields/index.test.d.ts.map +0 -1
  72. package/dist/plugins/listInputFields/index.test.js +0 -359
  73. package/dist/plugins/manifest/index.test.d.ts +0 -2
  74. package/dist/plugins/manifest/index.test.d.ts.map +0 -1
  75. package/dist/plugins/manifest/index.test.js +0 -1179
  76. package/dist/plugins/request/index.test.d.ts +0 -2
  77. package/dist/plugins/request/index.test.d.ts.map +0 -1
  78. package/dist/plugins/request/index.test.js +0 -458
  79. package/dist/plugins/runAction/index.test.d.ts +0 -2
  80. package/dist/plugins/runAction/index.test.d.ts.map +0 -1
  81. package/dist/plugins/runAction/index.test.js +0 -350
  82. package/dist/resolvers/connectionId.test.d.ts +0 -2
  83. package/dist/resolvers/connectionId.test.d.ts.map +0 -1
  84. package/dist/resolvers/connectionId.test.js +0 -61
  85. package/dist/sdk.test.d.ts +0 -2
  86. package/dist/sdk.test.d.ts.map +0 -1
  87. package/dist/sdk.test.js +0 -260
  88. package/dist/types/domain.test.d.ts +0 -2
  89. package/dist/types/domain.test.d.ts.map +0 -1
  90. package/dist/types/domain.test.js +0 -39
  91. package/dist/utils/array-utils.test.d.ts +0 -2
  92. package/dist/utils/array-utils.test.d.ts.map +0 -1
  93. package/dist/utils/array-utils.test.js +0 -107
  94. package/dist/utils/batch-utils.test.d.ts +0 -2
  95. package/dist/utils/batch-utils.test.d.ts.map +0 -1
  96. package/dist/utils/batch-utils.test.js +0 -476
  97. package/dist/utils/domain-utils.test.d.ts +0 -2
  98. package/dist/utils/domain-utils.test.d.ts.map +0 -1
  99. package/dist/utils/domain-utils.test.js +0 -346
  100. package/dist/utils/file-utils.test.d.ts +0 -2
  101. package/dist/utils/file-utils.test.d.ts.map +0 -1
  102. package/dist/utils/file-utils.test.js +0 -51
  103. package/dist/utils/function-utils.test.d.ts +0 -2
  104. package/dist/utils/function-utils.test.d.ts.map +0 -1
  105. package/dist/utils/function-utils.test.js +0 -188
  106. package/dist/utils/id-utils.test.d.ts +0 -2
  107. package/dist/utils/id-utils.test.d.ts.map +0 -1
  108. package/dist/utils/id-utils.test.js +0 -22
  109. package/dist/utils/pagination-utils.test.d.ts +0 -17
  110. package/dist/utils/pagination-utils.test.d.ts.map +0 -1
  111. package/dist/utils/pagination-utils.test.js +0 -461
  112. package/dist/utils/retry-utils.test.d.ts +0 -2
  113. package/dist/utils/retry-utils.test.d.ts.map +0 -1
  114. package/dist/utils/retry-utils.test.js +0 -90
  115. package/dist/utils/string-utils.test.d.ts +0 -2
  116. package/dist/utils/string-utils.test.d.ts.map +0 -1
  117. package/dist/utils/string-utils.test.js +0 -59
  118. package/dist/utils/telemetry-context.test.d.ts +0 -2
  119. package/dist/utils/telemetry-context.test.d.ts.map +0 -1
  120. package/dist/utils/telemetry-context.test.js +0 -154
  121. package/dist/utils/telemetry-utils.test.d.ts +0 -2
  122. package/dist/utils/telemetry-utils.test.d.ts.map +0 -1
  123. package/dist/utils/telemetry-utils.test.js +0 -155
  124. package/dist/utils/url-utils.test.d.ts +0 -2
  125. package/dist/utils/url-utils.test.d.ts.map +0 -1
  126. package/dist/utils/url-utils.test.js +0 -103
  127. package/dist/utils/validation.test.d.ts +0 -2
  128. package/dist/utils/validation.test.d.ts.map +0 -1
  129. package/dist/utils/validation.test.js +0 -44
@@ -1,704 +0,0 @@
1
- /**
2
- * Tests for Event Emission Plugin
3
- */
4
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
5
- import { eventEmissionPlugin, cleanupEventListeners } from "./index";
6
- import { createTransport } from "./transport";
7
- import { clearTokenCache } from "../../auth";
8
- const { mockTransport, mockGetToken } = vi.hoisted(() => ({
9
- mockTransport: {
10
- emit: vi.fn().mockResolvedValue(undefined),
11
- close: vi.fn().mockResolvedValue(undefined),
12
- },
13
- mockGetToken: vi.fn().mockResolvedValue(undefined),
14
- }));
15
- vi.mock("./transport", () => ({
16
- createTransport: vi.fn(() => mockTransport),
17
- }));
18
- // Mock both CLI login import paths (SDK tries @zapier/zapier-sdk-cli/login
19
- // first, then falls back to @zapier/zapier-sdk-cli-login)
20
- vi.mock("@zapier/zapier-sdk-cli/login", () => ({
21
- getToken: mockGetToken,
22
- }));
23
- vi.mock("@zapier/zapier-sdk-cli-login", () => ({
24
- getToken: mockGetToken,
25
- }));
26
- describe("eventEmissionPlugin", () => {
27
- beforeEach(() => {
28
- vi.clearAllMocks();
29
- clearTokenCache();
30
- // Reset to default behavior - no token available
31
- mockGetToken.mockResolvedValue(undefined);
32
- });
33
- it("should create plugin with default configuration", () => {
34
- const plugin = eventEmissionPlugin({
35
- sdk: {},
36
- context: {
37
- meta: {},
38
- options: {},
39
- },
40
- });
41
- expect(plugin.context.eventEmission).toBeDefined();
42
- expect(plugin.context.eventEmission.emit).toBeDefined();
43
- expect(plugin.context.eventEmission.transport).toBeDefined();
44
- expect(plugin.context.eventEmission.config).toBeDefined();
45
- });
46
- it("should create noop implementations when disabled", () => {
47
- const config = { enabled: false };
48
- const plugin = eventEmissionPlugin({
49
- sdk: {},
50
- context: {
51
- meta: {},
52
- options: { eventEmission: config },
53
- },
54
- });
55
- // Should not emit any events when disabled
56
- plugin.context.eventEmission.emit("platform.sdk.TestEvent", {
57
- test_event: "data",
58
- });
59
- expect(mockTransport.emit).not.toHaveBeenCalled();
60
- });
61
- it("should emit events using generic emit method", async () => {
62
- const plugin = eventEmissionPlugin({
63
- sdk: {},
64
- context: {
65
- meta: {},
66
- options: {
67
- eventEmission: {
68
- enabled: true,
69
- transport: { type: "console" },
70
- },
71
- },
72
- },
73
- });
74
- await plugin.context.eventEmission.flush();
75
- // Clear startup event calls so we only assert on our test event
76
- mockTransport.emit.mockClear();
77
- const testEvent = {
78
- test_data: "example",
79
- value: 123,
80
- };
81
- const testSubject = "test.event.TestEvent";
82
- plugin.context.eventEmission.emit(testSubject, testEvent);
83
- await plugin.context.eventEmission.flush();
84
- // The event will be enriched with user context (null values)
85
- expect(mockTransport.emit).toHaveBeenCalledWith(testSubject, {
86
- ...testEvent,
87
- customuser_id: null,
88
- account_id: null,
89
- });
90
- });
91
- it("should handle transport creation failures silently", () => {
92
- // Mock createTransport to throw an error
93
- vi.mocked(createTransport).mockImplementationOnce(() => {
94
- throw new Error("Transport creation failed");
95
- });
96
- expect(() => {
97
- eventEmissionPlugin({
98
- sdk: {},
99
- context: {
100
- meta: {},
101
- options: {
102
- eventEmission: {
103
- enabled: true,
104
- transport: { type: "http", endpoint: "invalid-url" },
105
- },
106
- },
107
- },
108
- });
109
- }).not.toThrow();
110
- });
111
- it("should handle event emission failures silently", async () => {
112
- // Mock transport to throw error
113
- const failingTransport = {
114
- emit: vi.fn().mockRejectedValue(new Error("Network error")),
115
- close: vi.fn().mockResolvedValue(undefined),
116
- };
117
- vi.mocked(createTransport).mockReturnValueOnce(failingTransport);
118
- const plugin = eventEmissionPlugin({
119
- sdk: {},
120
- context: {
121
- meta: {},
122
- options: {
123
- eventEmission: {
124
- enabled: true,
125
- transport: {
126
- type: "http",
127
- endpoint: "https://example.com",
128
- },
129
- },
130
- },
131
- },
132
- });
133
- await plugin.context.eventEmission.flush();
134
- // Should not throw even if transport fails
135
- expect(() => {
136
- plugin.context.eventEmission.emit("test.event.TestEvent", {
137
- test_event: "data",
138
- });
139
- }).not.toThrow();
140
- await plugin.context.eventEmission.flush();
141
- expect(failingTransport.emit).toHaveBeenCalled();
142
- });
143
- it("should merge options with defaults", () => {
144
- // Override env var to ensure proper transport is used
145
- vi.stubEnv("ZAPIER_SDK_TELEMETRY_TRANSPORT", undefined);
146
- const plugin = eventEmissionPlugin({
147
- sdk: {},
148
- context: {
149
- meta: {},
150
- options: {
151
- eventEmission: {
152
- transport: {
153
- type: "http",
154
- endpoint: "https://example.com",
155
- },
156
- },
157
- },
158
- },
159
- });
160
- expect(plugin.context.eventEmission.config.enabled).toBe(true);
161
- expect(plugin.context.eventEmission.config.transport).toEqual({
162
- type: "http",
163
- endpoint: "https://example.com",
164
- });
165
- vi.unstubAllEnvs();
166
- });
167
- it("should extract user IDs from JWT token and include in events", async () => {
168
- // Create a test JWT token with user data
169
- // JWT format: header.payload.signature
170
- const header = { alg: "HS256", typ: "JWT" };
171
- const payload = {
172
- "zap:acc": "12345",
173
- sub: "67890",
174
- sub_type: "customuser",
175
- "zap:uname": "test@example.com",
176
- };
177
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
178
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
179
- const testJwt = `${encodedHeader}.${encodedPayload}.fake-signature`;
180
- // Mock getToken to return the JWT
181
- mockGetToken.mockResolvedValue(testJwt);
182
- const plugin = eventEmissionPlugin({
183
- sdk: {},
184
- context: {
185
- meta: {},
186
- options: {
187
- eventEmission: {
188
- enabled: true,
189
- transport: { type: "console" },
190
- },
191
- },
192
- },
193
- });
194
- // Test that createBaseEvent includes the extracted user IDs
195
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
196
- expect(baseEvent.customuser_id).toBe(67890);
197
- expect(baseEvent.account_id).toBe(12345);
198
- });
199
- it("should handle service tokens with nested JWT", async () => {
200
- // Create a nested JWT for service token testing
201
- const nestedHeader = { alg: "HS256", typ: "JWT" };
202
- const nestedPayload = {
203
- "zap:acc": "99999",
204
- sub: "88888",
205
- sub_type: "customuser",
206
- };
207
- const nestedEncodedHeader = Buffer.from(JSON.stringify(nestedHeader)).toString("base64url");
208
- const nestedEncodedPayload = Buffer.from(JSON.stringify(nestedPayload)).toString("base64url");
209
- const nestedJwt = `${nestedEncodedHeader}.${nestedEncodedPayload}.nested-signature`;
210
- // Create the service token that wraps the nested JWT
211
- const serviceHeader = { alg: "HS256", typ: "JWT" };
212
- const servicePayload = {
213
- "zap:acc": "11111",
214
- sub: "22222",
215
- sub_type: "service",
216
- njwt: nestedJwt,
217
- };
218
- const serviceEncodedHeader = Buffer.from(JSON.stringify(serviceHeader)).toString("base64url");
219
- const serviceEncodedPayload = Buffer.from(JSON.stringify(servicePayload)).toString("base64url");
220
- const serviceJwt = `${serviceEncodedHeader}.${serviceEncodedPayload}.service-signature`;
221
- // Mock getToken to return the service JWT
222
- mockGetToken.mockResolvedValue(serviceJwt);
223
- const plugin = eventEmissionPlugin({
224
- sdk: {},
225
- context: {
226
- meta: {},
227
- options: {
228
- eventEmission: {
229
- enabled: true,
230
- transport: { type: "console" },
231
- },
232
- },
233
- },
234
- });
235
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
236
- // Should extract from nested JWT, not the service token
237
- expect(baseEvent.customuser_id).toBe(88888);
238
- expect(baseEvent.account_id).toBe(99999);
239
- });
240
- it("should handle invalid JWT tokens gracefully", async () => {
241
- // Mock getToken to return an invalid JWT
242
- mockGetToken.mockResolvedValue("not-a-valid-jwt-token");
243
- const plugin = eventEmissionPlugin({
244
- sdk: {},
245
- context: {
246
- meta: {},
247
- options: {
248
- eventEmission: {
249
- enabled: true,
250
- transport: { type: "console" },
251
- },
252
- },
253
- },
254
- });
255
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
256
- // Should default to null when JWT is invalid
257
- expect(baseEvent.customuser_id).toBe(null);
258
- expect(baseEvent.account_id).toBe(null);
259
- });
260
- it("should handle missing token gracefully", async () => {
261
- // mockGetToken defaults to returning undefined (no token)
262
- const plugin = eventEmissionPlugin({
263
- sdk: {},
264
- context: {
265
- meta: {},
266
- options: {
267
- eventEmission: {
268
- enabled: true,
269
- transport: { type: "console" },
270
- },
271
- },
272
- },
273
- });
274
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
275
- // Should default to null when no token is provided
276
- expect(baseEvent.customuser_id).toBe(null);
277
- expect(baseEvent.account_id).toBe(null);
278
- });
279
- it("should extract user IDs when getToken returns valid JWT", async () => {
280
- // Create a test JWT token
281
- const header = { alg: "HS256", typ: "JWT" };
282
- const payload = {
283
- "zap:acc": "98765",
284
- sub: "54321",
285
- sub_type: "customuser",
286
- };
287
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
288
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
289
- const testJwt = `${encodedHeader}.${encodedPayload}.test-signature`;
290
- // Mock getToken to return the JWT
291
- mockGetToken.mockResolvedValue(testJwt);
292
- const plugin = eventEmissionPlugin({
293
- sdk: {},
294
- context: {
295
- meta: {},
296
- options: {
297
- eventEmission: {
298
- enabled: true,
299
- transport: { type: "console" },
300
- },
301
- },
302
- },
303
- });
304
- // Test that createBaseEvent includes the extracted user IDs
305
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
306
- expect(baseEvent.customuser_id).toBe(54321);
307
- expect(baseEvent.account_id).toBe(98765);
308
- });
309
- it("should handle getToken failures gracefully", async () => {
310
- // Mock getToken to reject/throw
311
- mockGetToken.mockRejectedValue(new Error("Token fetch failed"));
312
- const plugin = eventEmissionPlugin({
313
- sdk: {},
314
- context: {
315
- meta: {},
316
- options: {
317
- eventEmission: {
318
- enabled: true,
319
- transport: { type: "console" },
320
- },
321
- },
322
- },
323
- });
324
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
325
- // Should gracefully fall back to null context
326
- expect(baseEvent.customuser_id).toBe(null);
327
- expect(baseEvent.account_id).toBe(null);
328
- });
329
- it("should extract user IDs from static token in SDK options", async () => {
330
- // Create a test JWT token
331
- const header = { alg: "HS256", typ: "JWT" };
332
- const payload = {
333
- "zap:acc": "11111",
334
- sub: "22222",
335
- sub_type: "customuser",
336
- };
337
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
338
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
339
- const testJwt = `${encodedHeader}.${encodedPayload}.static-signature`;
340
- const plugin = eventEmissionPlugin({
341
- sdk: {},
342
- context: {
343
- meta: {},
344
- options: {
345
- token: testJwt,
346
- eventEmission: {
347
- enabled: true,
348
- transport: { type: "console" },
349
- },
350
- },
351
- },
352
- });
353
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
354
- // Should extract from static token in options
355
- expect(baseEvent.customuser_id).toBe(22222);
356
- expect(baseEvent.account_id).toBe(11111);
357
- // CLI login package should not be called when token is in options
358
- expect(mockGetToken).not.toHaveBeenCalled();
359
- });
360
- it("should extract user IDs from credentials function in SDK options", async () => {
361
- // Create a test JWT token
362
- const header = { alg: "HS256", typ: "JWT" };
363
- const payload = {
364
- "zap:acc": "33333",
365
- sub: "44444",
366
- sub_type: "customuser",
367
- };
368
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
369
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
370
- const testJwt = `${encodedHeader}.${encodedPayload}.custom-signature`;
371
- const credentialsFn = vi.fn().mockResolvedValue(testJwt);
372
- const plugin = eventEmissionPlugin({
373
- sdk: {},
374
- context: {
375
- meta: {},
376
- options: {
377
- credentials: credentialsFn,
378
- eventEmission: {
379
- enabled: true,
380
- transport: { type: "console" },
381
- },
382
- },
383
- },
384
- });
385
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
386
- // Should extract from custom credentials function
387
- expect(baseEvent.customuser_id).toBe(44444);
388
- expect(baseEvent.account_id).toBe(33333);
389
- expect(credentialsFn).toHaveBeenCalled();
390
- // CLI login package should not be called when credentials is in options
391
- expect(mockGetToken).not.toHaveBeenCalled();
392
- });
393
- it("should prioritize string credentials over credentials function", async () => {
394
- // Create test JWT tokens
395
- const staticHeader = { alg: "HS256", typ: "JWT" };
396
- const staticPayload = {
397
- "zap:acc": "55555",
398
- sub: "66666",
399
- sub_type: "customuser",
400
- };
401
- const staticEncodedHeader = Buffer.from(JSON.stringify(staticHeader)).toString("base64url");
402
- const staticEncodedPayload = Buffer.from(JSON.stringify(staticPayload)).toString("base64url");
403
- const staticJwt = `${staticEncodedHeader}.${staticEncodedPayload}.static-sig`;
404
- const plugin = eventEmissionPlugin({
405
- sdk: {},
406
- context: {
407
- meta: {},
408
- options: {
409
- credentials: staticJwt,
410
- eventEmission: {
411
- enabled: true,
412
- transport: { type: "console" },
413
- },
414
- },
415
- },
416
- });
417
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
418
- // Should use string credentials
419
- expect(baseEvent.customuser_id).toBe(66666);
420
- expect(baseEvent.account_id).toBe(55555);
421
- expect(mockGetToken).not.toHaveBeenCalled();
422
- });
423
- it("should fall back to CLI login when SDK options have no token", async () => {
424
- // Create a test JWT token for CLI login
425
- const header = { alg: "HS256", typ: "JWT" };
426
- const payload = {
427
- "zap:acc": "77777",
428
- sub: "88888",
429
- sub_type: "customuser",
430
- };
431
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
432
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
433
- const testJwt = `${encodedHeader}.${encodedPayload}.cli-signature`;
434
- mockGetToken.mockResolvedValue(testJwt);
435
- const plugin = eventEmissionPlugin({
436
- sdk: {},
437
- context: {
438
- meta: {},
439
- options: {
440
- // No token or getToken in options
441
- eventEmission: {
442
- enabled: true,
443
- transport: { type: "console" },
444
- },
445
- },
446
- },
447
- });
448
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
449
- // Should fall back to CLI login package
450
- expect(baseEvent.customuser_id).toBe(88888);
451
- expect(baseEvent.account_id).toBe(77777);
452
- expect(mockGetToken).toHaveBeenCalled();
453
- });
454
- it("should handle credentials function returning undefined and fall back to CLI login", async () => {
455
- const credentialsFn = vi.fn().mockResolvedValue(undefined);
456
- // Also mock CLI login to return a token
457
- const header = { alg: "HS256", typ: "JWT" };
458
- const payload = {
459
- "zap:acc": "99999",
460
- sub: "10101",
461
- sub_type: "customuser",
462
- };
463
- const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
464
- const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
465
- const testJwt = `${encodedHeader}.${encodedPayload}.fallback-signature`;
466
- mockGetToken.mockResolvedValue(testJwt);
467
- const plugin = eventEmissionPlugin({
468
- sdk: {},
469
- context: {
470
- meta: {},
471
- options: {
472
- credentials: credentialsFn,
473
- eventEmission: {
474
- enabled: true,
475
- transport: { type: "console" },
476
- },
477
- },
478
- },
479
- });
480
- const baseEvent = await plugin.context.eventEmission.createBaseEvent();
481
- // Since credentials function returns undefined, SDK falls back to CLI login
482
- expect(baseEvent.customuser_id).toBe(10101);
483
- expect(baseEvent.account_id).toBe(99999);
484
- expect(credentialsFn).toHaveBeenCalled();
485
- expect(mockGetToken).toHaveBeenCalled();
486
- });
487
- });
488
- describe("emitMethodCalled call_context", () => {
489
- beforeEach(() => {
490
- vi.clearAllMocks();
491
- mockGetToken.mockResolvedValue(undefined);
492
- });
493
- it("should default call_context to 'sdk' when callContext is not configured", async () => {
494
- const plugin = eventEmissionPlugin({
495
- sdk: {},
496
- context: {
497
- meta: {},
498
- options: {
499
- eventEmission: {
500
- enabled: true,
501
- transport: { type: "console" },
502
- },
503
- },
504
- },
505
- });
506
- plugin.context.eventEmission.emitMethodCalled({
507
- method_name: "listApps",
508
- execution_duration_ms: 50,
509
- success_flag: true,
510
- argument_count: 0,
511
- });
512
- await plugin.context.eventEmission.flush();
513
- expect(mockTransport.emit).toHaveBeenCalledWith("platform.sdk.MethodCalledEvent", expect.objectContaining({ call_context: "sdk" }));
514
- });
515
- it("should set call_context to 'cli' when callContext is 'cli'", async () => {
516
- const plugin = eventEmissionPlugin({
517
- sdk: {},
518
- context: {
519
- meta: {},
520
- options: {
521
- eventEmission: {
522
- enabled: true,
523
- transport: { type: "console" },
524
- callContext: "cli",
525
- },
526
- },
527
- },
528
- });
529
- plugin.context.eventEmission.emitMethodCalled({
530
- method_name: "listApps",
531
- execution_duration_ms: 50,
532
- success_flag: true,
533
- argument_count: 0,
534
- });
535
- await plugin.context.eventEmission.flush();
536
- expect(mockTransport.emit).toHaveBeenCalledWith("platform.sdk.MethodCalledEvent", expect.objectContaining({ call_context: "cli" }));
537
- });
538
- it("should set call_context to 'mcp' when callContext is 'mcp'", async () => {
539
- const plugin = eventEmissionPlugin({
540
- sdk: {},
541
- context: {
542
- meta: {},
543
- options: {
544
- eventEmission: {
545
- enabled: true,
546
- transport: { type: "console" },
547
- callContext: "mcp",
548
- },
549
- },
550
- },
551
- });
552
- plugin.context.eventEmission.emitMethodCalled({
553
- method_name: "listApps",
554
- execution_duration_ms: 50,
555
- success_flag: true,
556
- argument_count: 0,
557
- });
558
- await plugin.context.eventEmission.flush();
559
- expect(mockTransport.emit).toHaveBeenCalledWith("platform.sdk.MethodCalledEvent", expect.objectContaining({ call_context: "mcp" }));
560
- });
561
- });
562
- describe("close()", () => {
563
- beforeEach(() => {
564
- vi.clearAllMocks();
565
- cleanupEventListeners();
566
- mockGetToken.mockResolvedValue(undefined);
567
- });
568
- afterEach(() => {
569
- cleanupEventListeners();
570
- });
571
- it("should emit exit lifecycle event", async () => {
572
- const plugin = eventEmissionPlugin({
573
- sdk: {},
574
- context: {
575
- meta: {},
576
- options: {
577
- eventEmission: {
578
- enabled: true,
579
- transport: { type: "console" },
580
- },
581
- },
582
- },
583
- });
584
- await plugin.context.eventEmission.close(0);
585
- expect(mockTransport.emit).toHaveBeenCalledWith("platform.sdk.ApplicationLifecycleEvent", expect.objectContaining({
586
- lifecycle_event_type: "exit",
587
- exit_code: 0,
588
- is_graceful_shutdown: true,
589
- }));
590
- });
591
- it("should be idempotent — second call is a no-op", async () => {
592
- const plugin = eventEmissionPlugin({
593
- sdk: {},
594
- context: {
595
- meta: {},
596
- options: {
597
- eventEmission: {
598
- enabled: true,
599
- transport: { type: "console" },
600
- },
601
- },
602
- },
603
- });
604
- await plugin.context.eventEmission.close(0);
605
- const callCountAfterFirst = mockTransport.emit.mock.calls.filter((call) => call[0] === "platform.sdk.ApplicationLifecycleEvent" &&
606
- call[1]?.lifecycle_event_type === "exit").length;
607
- await plugin.context.eventEmission.close(0);
608
- const callCountAfterSecond = mockTransport.emit.mock.calls.filter((call) => call[0] === "platform.sdk.ApplicationLifecycleEvent" &&
609
- call[1]?.lifecycle_event_type === "exit").length;
610
- expect(callCountAfterSecond).toBe(callCountAfterFirst);
611
- });
612
- it("should remove process listeners after close", async () => {
613
- const initialExitCount = process.listenerCount("exit");
614
- const plugin = eventEmissionPlugin({
615
- sdk: {},
616
- context: {
617
- meta: {},
618
- options: {
619
- eventEmission: {
620
- enabled: true,
621
- transport: { type: "console" },
622
- },
623
- },
624
- },
625
- });
626
- expect(process.listenerCount("exit")).toBe(initialExitCount + 1);
627
- await plugin.context.eventEmission.close(0);
628
- expect(process.listenerCount("exit")).toBe(initialExitCount);
629
- });
630
- it("should handle transport failures silently", async () => {
631
- const failingTransport = {
632
- emit: vi.fn().mockRejectedValue(new Error("Network error")),
633
- close: vi.fn().mockResolvedValue(undefined),
634
- };
635
- vi.mocked(createTransport).mockReturnValueOnce(failingTransport);
636
- const plugin = eventEmissionPlugin({
637
- sdk: {},
638
- context: {
639
- meta: {},
640
- options: {
641
- eventEmission: {
642
- enabled: true,
643
- transport: {
644
- type: "http",
645
- endpoint: "https://example.com",
646
- },
647
- },
648
- },
649
- },
650
- });
651
- await expect(plugin.context.eventEmission.close(1)).resolves.toBeUndefined();
652
- });
653
- });
654
- describe("Process Listener Cleanup", () => {
655
- beforeEach(() => {
656
- // Clean up any listeners from previous tests
657
- cleanupEventListeners();
658
- });
659
- afterEach(() => {
660
- cleanupEventListeners();
661
- });
662
- it("should not accumulate process listeners when creating multiple SDK instances", () => {
663
- const initialExitListenerCount = process.listenerCount("exit");
664
- // Create multiple SDK instances
665
- for (let i = 0; i < 5; i++) {
666
- eventEmissionPlugin({
667
- sdk: {},
668
- context: { meta: {}, options: {} },
669
- });
670
- }
671
- // Should only have added 1 listener, not 5
672
- expect(process.listenerCount("exit")).toBe(initialExitListenerCount + 1);
673
- });
674
- it("should remove existing listeners before registering new ones", () => {
675
- const removeListenerSpy = vi.spyOn(process, "removeListener");
676
- // Create first plugin instance
677
- eventEmissionPlugin({
678
- sdk: {},
679
- context: { meta: {}, options: {} },
680
- });
681
- // Create second plugin instance (should remove listeners from first)
682
- eventEmissionPlugin({
683
- sdk: {},
684
- context: { meta: {}, options: {} },
685
- });
686
- // Verify cleanup happened for all event types
687
- expect(removeListenerSpy).toHaveBeenCalledWith("exit", expect.any(Function));
688
- expect(removeListenerSpy).toHaveBeenCalledWith("uncaughtException", expect.any(Function));
689
- expect(removeListenerSpy).toHaveBeenCalledWith("unhandledRejection", expect.any(Function));
690
- expect(removeListenerSpy).toHaveBeenCalledWith("SIGINT", expect.any(Function));
691
- expect(removeListenerSpy).toHaveBeenCalledWith("SIGTERM", expect.any(Function));
692
- removeListenerSpy.mockRestore();
693
- });
694
- it("should allow explicit cleanup via cleanupEventListeners", () => {
695
- const initialExitListenerCount = process.listenerCount("exit");
696
- eventEmissionPlugin({
697
- sdk: {},
698
- context: { meta: {}, options: {} },
699
- });
700
- expect(process.listenerCount("exit")).toBe(initialExitListenerCount + 1);
701
- cleanupEventListeners();
702
- expect(process.listenerCount("exit")).toBe(initialExitListenerCount);
703
- });
704
- });