@zapier/zapier-sdk 0.18.3 → 1.0.0
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.
- package/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +11 -24
- package/dist/api/client.test.js +82 -27
- package/dist/api/index.d.ts +3 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -3
- package/dist/api/schemas.d.ts +5 -114
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +0 -67
- package/dist/api/types.d.ts +10 -4
- package/dist/api/types.d.ts.map +1 -1
- package/dist/auth.d.ts +54 -26
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +211 -39
- package/dist/auth.test.js +338 -64
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +14 -0
- package/dist/credentials.d.ts +57 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +174 -0
- package/dist/index.cjs +644 -685
- package/dist/index.d.mts +265 -134
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.mjs +624 -684
- package/dist/plugins/api/index.d.ts +2 -0
- package/dist/plugins/api/index.d.ts.map +1 -1
- package/dist/plugins/api/index.js +8 -4
- package/dist/plugins/eventEmission/index.d.ts.map +1 -1
- package/dist/plugins/eventEmission/index.js +1 -3
- package/dist/plugins/eventEmission/index.test.js +14 -17
- package/dist/plugins/getAction/schemas.d.ts +1 -1
- package/dist/plugins/getInputFieldsSchema/schemas.d.ts +1 -1
- package/dist/plugins/listActions/index.test.js +1 -0
- package/dist/plugins/listActions/schemas.d.ts +1 -1
- package/dist/plugins/listApps/index.d.ts +2 -8
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +4 -6
- package/dist/plugins/listApps/index.test.js +62 -82
- package/dist/plugins/listApps/schemas.d.ts +35 -14
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +44 -14
- package/dist/plugins/listAuthentications/index.test.js +16 -0
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +1 -1
- package/dist/plugins/listInputFields/schemas.d.ts +1 -1
- package/dist/plugins/runAction/schemas.d.ts +1 -1
- package/dist/schemas/Action.d.ts +1 -1
- package/dist/schemas/App.d.ts +28 -28
- package/dist/schemas/App.d.ts.map +1 -1
- package/dist/schemas/App.js +3 -8
- package/dist/sdk.d.ts +2 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.test.js +17 -13
- package/dist/types/credentials.d.ts +65 -0
- package/dist/types/credentials.d.ts.map +1 -0
- package/dist/types/credentials.js +42 -0
- package/dist/types/properties.d.ts +1 -1
- package/dist/types/sdk.d.ts +12 -3
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/utils/logging.d.ts +13 -0
- package/dist/utils/logging.d.ts.map +1 -0
- package/dist/utils/logging.js +20 -0
- package/package.json +2 -2
- package/dist/api/client.integration.test.d.ts +0 -5
- package/dist/api/client.integration.test.d.ts.map +0 -1
- package/dist/api/client.integration.test.js +0 -318
- package/dist/api/client.methods.test.d.ts +0 -2
- package/dist/api/client.methods.test.d.ts.map +0 -1
- package/dist/api/client.methods.test.js +0 -158
- package/dist/api/router.d.ts +0 -16
- package/dist/api/router.d.ts.map +0 -1
- package/dist/api/router.js +0 -31
- package/dist/api/router.test.d.ts +0 -2
- package/dist/api/router.test.d.ts.map +0 -1
- package/dist/api/router.test.js +0 -103
- package/dist/temporary-internal-core/handlers/listApps.d.ts +0 -67
- package/dist/temporary-internal-core/handlers/listApps.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.js +0 -134
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts +0 -2
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.test.js +0 -367
- package/dist/temporary-internal-core/index.d.ts +0 -18
- package/dist/temporary-internal-core/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/index.js +0 -18
- package/dist/temporary-internal-core/schemas/apps/index.d.ts +0 -175
- package/dist/temporary-internal-core/schemas/apps/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/apps/index.js +0 -97
- package/dist/temporary-internal-core/schemas/errors/index.d.ts +0 -139
- package/dist/temporary-internal-core/schemas/errors/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/errors/index.js +0 -129
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts +0 -127
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/implementations/index.js +0 -79
- package/dist/temporary-internal-core/types/handler.d.ts +0 -51
- package/dist/temporary-internal-core/types/handler.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/handler.js +0 -8
- package/dist/temporary-internal-core/types/index.d.ts +0 -5
- package/dist/temporary-internal-core/types/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/index.js +0 -4
- package/dist/temporary-internal-core/utils/app-locators.d.ts +0 -34
- package/dist/temporary-internal-core/utils/app-locators.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/app-locators.js +0 -39
- package/dist/temporary-internal-core/utils/string-utils.d.ts +0 -28
- package/dist/temporary-internal-core/utils/string-utils.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/string-utils.js +0 -52
- package/dist/temporary-internal-core/utils/transformations.d.ts +0 -18
- package/dist/temporary-internal-core/utils/transformations.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/transformations.js +0 -36
package/dist/auth.test.js
CHANGED
|
@@ -1,102 +1,376 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import * as auth from "./auth";
|
|
3
|
+
import { resetDeprecationWarnings } from "./utils/logging";
|
|
3
4
|
// Mock the CLI login package
|
|
4
5
|
const mockGetToken = vi.fn();
|
|
5
6
|
vi.mock("@zapier/zapier-sdk-cli-login", () => ({
|
|
6
7
|
getToken: mockGetToken,
|
|
7
8
|
}));
|
|
9
|
+
// Mock fetch for client credentials tests
|
|
10
|
+
const mockFetch = vi.fn();
|
|
8
11
|
describe("auth", () => {
|
|
9
12
|
beforeEach(() => {
|
|
10
13
|
vi.clearAllMocks();
|
|
14
|
+
auth.clearTokenCache();
|
|
15
|
+
resetDeprecationWarnings();
|
|
16
|
+
// Clear all ZAPIER_* env vars
|
|
17
|
+
delete process.env.ZAPIER_CREDENTIALS;
|
|
18
|
+
delete process.env.ZAPIER_CREDENTIALS_CLIENT_ID;
|
|
19
|
+
delete process.env.ZAPIER_CREDENTIALS_CLIENT_SECRET;
|
|
20
|
+
delete process.env.ZAPIER_CREDENTIALS_BASE_URL;
|
|
11
21
|
delete process.env.ZAPIER_TOKEN;
|
|
22
|
+
delete process.env.ZAPIER_AUTH_CLIENT_ID;
|
|
23
|
+
delete process.env.ZAPIER_AUTH_BASE_URL;
|
|
12
24
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
process.env.ZAPIER_TOKEN = "test-token";
|
|
16
|
-
expect(auth.getTokenFromEnv()).toBe("test-token");
|
|
17
|
-
});
|
|
18
|
-
it("should return undefined when ZAPIER_TOKEN is not set", () => {
|
|
19
|
-
delete process.env.ZAPIER_TOKEN;
|
|
20
|
-
expect(auth.getTokenFromEnv()).toBeUndefined();
|
|
21
|
-
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
auth.clearTokenCache();
|
|
22
27
|
});
|
|
23
28
|
describe("getTokenFromCliLogin", () => {
|
|
24
|
-
it("should pass
|
|
29
|
+
it("should pass options to CLI login package getToken", async () => {
|
|
25
30
|
mockGetToken.mockResolvedValue("cli-token");
|
|
26
31
|
const options = {
|
|
27
32
|
baseUrl: "https://api.custom.zapier.com",
|
|
28
|
-
authBaseUrl: "https://auth.custom.zapier.com",
|
|
29
33
|
onEvent: vi.fn(),
|
|
30
34
|
fetch: vi.fn(),
|
|
31
35
|
};
|
|
32
36
|
const result = await auth.getTokenFromCliLogin(options);
|
|
33
37
|
expect(result).toBe("cli-token");
|
|
34
|
-
expect(mockGetToken).
|
|
35
|
-
});
|
|
36
|
-
it("should pass empty options when none provided", async () => {
|
|
37
|
-
mockGetToken.mockResolvedValue("cli-token");
|
|
38
|
-
const result = await auth.getTokenFromCliLogin();
|
|
39
|
-
expect(result).toBe("cli-token");
|
|
40
|
-
expect(mockGetToken).toHaveBeenCalledWith({});
|
|
38
|
+
expect(mockGetToken).toHaveBeenCalled();
|
|
41
39
|
});
|
|
42
40
|
it("should return undefined when CLI login package throws error", async () => {
|
|
43
41
|
mockGetToken.mockRejectedValue(new Error("CLI not available"));
|
|
44
|
-
const result = await auth.getTokenFromCliLogin();
|
|
42
|
+
const result = await auth.getTokenFromCliLogin({});
|
|
45
43
|
expect(result).toBeUndefined();
|
|
46
44
|
});
|
|
47
45
|
});
|
|
48
|
-
describe("
|
|
49
|
-
it("should
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
describe("resolveAuthToken", () => {
|
|
47
|
+
it("should return string credentials directly", async () => {
|
|
48
|
+
const result = await auth.resolveAuthToken({
|
|
49
|
+
credentials: "my-token",
|
|
50
|
+
});
|
|
51
|
+
expect(result).toBe("my-token");
|
|
52
|
+
});
|
|
53
|
+
it("should use deprecated token option with warning", async () => {
|
|
54
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
55
|
+
const result = await auth.resolveAuthToken({
|
|
56
|
+
token: "deprecated-token",
|
|
57
|
+
});
|
|
58
|
+
expect(result).toBe("deprecated-token");
|
|
59
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("deprecated"));
|
|
60
|
+
warnSpy.mockRestore();
|
|
61
|
+
});
|
|
62
|
+
it("should resolve credentials from env var (string token)", async () => {
|
|
63
|
+
// Set env var and re-import to pick up the new value
|
|
64
|
+
process.env.ZAPIER_CREDENTIALS = "env-token";
|
|
65
|
+
vi.resetModules();
|
|
66
|
+
const freshAuth = await import("./auth");
|
|
67
|
+
const result = await freshAuth.resolveAuthToken({});
|
|
53
68
|
expect(result).toBe("env-token");
|
|
54
|
-
expect(mockGetToken).not.toHaveBeenCalled();
|
|
55
69
|
});
|
|
56
|
-
it("should
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
expect(result).toBe("cli-token");
|
|
67
|
-
expect(mockGetToken).toHaveBeenCalledWith(options);
|
|
70
|
+
it("should resolve credentials from ZAPIER_TOKEN with deprecation warning", async () => {
|
|
71
|
+
// Set env var and re-import to pick up the new value
|
|
72
|
+
process.env.ZAPIER_TOKEN = "legacy-token";
|
|
73
|
+
vi.resetModules();
|
|
74
|
+
const freshAuth = await import("./auth");
|
|
75
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
76
|
+
const result = await freshAuth.resolveAuthToken({});
|
|
77
|
+
expect(result).toBe("legacy-token");
|
|
78
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("ZAPIER_TOKEN is deprecated"));
|
|
79
|
+
warnSpy.mockRestore();
|
|
68
80
|
});
|
|
69
|
-
it("should
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
it("should fall back to CLI login when no credentials provided", async () => {
|
|
82
|
+
mockGetToken.mockResolvedValue("cli-stored-token");
|
|
83
|
+
const result = await auth.resolveAuthToken({});
|
|
84
|
+
expect(result).toBe("cli-stored-token");
|
|
85
|
+
expect(mockGetToken).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
it("should return undefined when no credentials found", async () => {
|
|
88
|
+
mockGetToken.mockResolvedValue(undefined);
|
|
89
|
+
const result = await auth.resolveAuthToken({});
|
|
73
90
|
expect(result).toBeUndefined();
|
|
74
91
|
});
|
|
75
|
-
it("should
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
authBaseUrl: "https://custom-auth.zapier.dev",
|
|
80
|
-
baseUrl: "https://api.zapier.dev",
|
|
81
|
-
};
|
|
82
|
-
await auth.getTokenFromEnvOrConfig(options);
|
|
83
|
-
expect(mockGetToken).toHaveBeenCalledWith({
|
|
84
|
-
authBaseUrl: "https://custom-auth.zapier.dev",
|
|
85
|
-
baseUrl: "https://api.zapier.dev",
|
|
92
|
+
it("should call credentials function and use result", async () => {
|
|
93
|
+
const credentialsFn = vi.fn().mockResolvedValue("dynamic-token");
|
|
94
|
+
const result = await auth.resolveAuthToken({
|
|
95
|
+
credentials: credentialsFn,
|
|
86
96
|
});
|
|
97
|
+
expect(result).toBe("dynamic-token");
|
|
98
|
+
expect(credentialsFn).toHaveBeenCalled();
|
|
87
99
|
});
|
|
88
|
-
it("should
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
it("should throw if credentials function returns another function", async () => {
|
|
101
|
+
const credentialsFn = vi.fn().mockResolvedValue(() => "nested");
|
|
102
|
+
await expect(auth.resolveAuthToken({
|
|
103
|
+
credentials: credentialsFn,
|
|
104
|
+
})).rejects.toThrow("Credentials function returned another function");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("client credentials flow", () => {
|
|
108
|
+
it("should exchange client credentials for token with scope and audience", async () => {
|
|
109
|
+
mockFetch.mockResolvedValue({
|
|
110
|
+
ok: true,
|
|
111
|
+
json: () => Promise.resolve({
|
|
112
|
+
access_token: "exchanged-token",
|
|
113
|
+
expires_in: 3600,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
const result = await auth.resolveAuthToken({
|
|
117
|
+
credentials: {
|
|
118
|
+
type: "client_credentials",
|
|
119
|
+
clientId: "test-client-id",
|
|
120
|
+
clientSecret: "test-client-secret",
|
|
121
|
+
},
|
|
122
|
+
fetch: mockFetch,
|
|
123
|
+
});
|
|
124
|
+
expect(result).toBe("exchanged-token");
|
|
125
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining("/oauth/token/"), expect.objectContaining({
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: expect.any(URLSearchParams),
|
|
128
|
+
}));
|
|
129
|
+
// Verify the body includes required parameters
|
|
130
|
+
const callArgs = mockFetch.mock.calls[0];
|
|
131
|
+
const body = callArgs[1].body;
|
|
132
|
+
expect(body.get("grant_type")).toBe("client_credentials");
|
|
133
|
+
expect(body.get("client_id")).toBe("test-client-id");
|
|
134
|
+
expect(body.get("client_secret")).toBe("test-client-secret");
|
|
135
|
+
expect(body.get("scope")).toBe("external");
|
|
136
|
+
expect(body.get("audience")).toBe("zapier.com");
|
|
137
|
+
});
|
|
138
|
+
it("should cache token from client credentials exchange", async () => {
|
|
139
|
+
mockFetch.mockResolvedValue({
|
|
140
|
+
ok: true,
|
|
141
|
+
json: () => Promise.resolve({
|
|
142
|
+
access_token: "cached-token",
|
|
143
|
+
expires_in: 3600,
|
|
144
|
+
}),
|
|
145
|
+
});
|
|
146
|
+
// First call - should exchange
|
|
147
|
+
const result1 = await auth.resolveAuthToken({
|
|
148
|
+
credentials: {
|
|
149
|
+
type: "client_credentials",
|
|
150
|
+
clientId: "cache-test-client",
|
|
151
|
+
clientSecret: "secret",
|
|
152
|
+
},
|
|
153
|
+
fetch: mockFetch,
|
|
154
|
+
});
|
|
155
|
+
// Second call - should use cache
|
|
156
|
+
const result2 = await auth.resolveAuthToken({
|
|
157
|
+
credentials: {
|
|
158
|
+
type: "client_credentials",
|
|
159
|
+
clientId: "cache-test-client",
|
|
160
|
+
clientSecret: "secret",
|
|
161
|
+
},
|
|
162
|
+
fetch: mockFetch,
|
|
99
163
|
});
|
|
164
|
+
expect(result1).toBe("cached-token");
|
|
165
|
+
expect(result2).toBe("cached-token");
|
|
166
|
+
expect(mockFetch).toHaveBeenCalledTimes(1); // Only called once due to cache
|
|
167
|
+
});
|
|
168
|
+
it("should deduplicate concurrent client credentials exchanges", async () => {
|
|
169
|
+
let resolveExchange;
|
|
170
|
+
const exchangePromise = new Promise((resolve) => {
|
|
171
|
+
resolveExchange = resolve;
|
|
172
|
+
});
|
|
173
|
+
mockFetch.mockReturnValue(exchangePromise);
|
|
174
|
+
// Start two concurrent requests - they will both start executing
|
|
175
|
+
const promise1 = auth.resolveAuthToken({
|
|
176
|
+
credentials: {
|
|
177
|
+
type: "client_credentials",
|
|
178
|
+
clientId: "concurrent-test-client",
|
|
179
|
+
clientSecret: "secret",
|
|
180
|
+
},
|
|
181
|
+
fetch: mockFetch,
|
|
182
|
+
});
|
|
183
|
+
const promise2 = auth.resolveAuthToken({
|
|
184
|
+
credentials: {
|
|
185
|
+
type: "client_credentials",
|
|
186
|
+
clientId: "concurrent-test-client",
|
|
187
|
+
clientSecret: "secret",
|
|
188
|
+
},
|
|
189
|
+
fetch: mockFetch,
|
|
190
|
+
});
|
|
191
|
+
// Let microtasks run so both promises start
|
|
192
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
193
|
+
// Now resolve the exchange
|
|
194
|
+
resolveExchange({
|
|
195
|
+
ok: true,
|
|
196
|
+
json: () => Promise.resolve({
|
|
197
|
+
access_token: "concurrent-token",
|
|
198
|
+
expires_in: 3600,
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
201
|
+
const [result1, result2] = await Promise.all([promise1, promise2]);
|
|
202
|
+
expect(result1).toBe("concurrent-token");
|
|
203
|
+
expect(result2).toBe("concurrent-token");
|
|
204
|
+
expect(mockFetch).toHaveBeenCalledTimes(1); // Only one exchange despite two concurrent calls
|
|
205
|
+
});
|
|
206
|
+
it("should throw on client credentials exchange failure", async () => {
|
|
207
|
+
mockFetch.mockResolvedValue({
|
|
208
|
+
ok: false,
|
|
209
|
+
status: 401,
|
|
210
|
+
statusText: "Unauthorized",
|
|
211
|
+
text: () => Promise.resolve("Invalid credentials"),
|
|
212
|
+
});
|
|
213
|
+
await expect(auth.resolveAuthToken({
|
|
214
|
+
credentials: {
|
|
215
|
+
type: "client_credentials",
|
|
216
|
+
clientId: "bad-client",
|
|
217
|
+
clientSecret: "bad-secret",
|
|
218
|
+
},
|
|
219
|
+
fetch: mockFetch,
|
|
220
|
+
})).rejects.toThrow("Client credentials exchange failed");
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe("PKCE credentials", () => {
|
|
224
|
+
it("should delegate PKCE credentials to CLI login", async () => {
|
|
225
|
+
mockGetToken.mockResolvedValue("pkce-stored-token");
|
|
226
|
+
const result = await auth.resolveAuthToken({
|
|
227
|
+
credentials: {
|
|
228
|
+
type: "pkce",
|
|
229
|
+
clientId: "pkce-client",
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
expect(result).toBe("pkce-stored-token");
|
|
233
|
+
expect(mockGetToken).toHaveBeenCalled();
|
|
234
|
+
});
|
|
235
|
+
it("should throw when PKCE credentials have no stored token", async () => {
|
|
236
|
+
mockGetToken.mockResolvedValue(undefined);
|
|
237
|
+
await expect(auth.resolveAuthToken({
|
|
238
|
+
credentials: {
|
|
239
|
+
type: "pkce",
|
|
240
|
+
clientId: "pkce-client",
|
|
241
|
+
},
|
|
242
|
+
})).rejects.toThrow("PKCE credentials require interactive login");
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
describe("token cache management", () => {
|
|
246
|
+
it("should invalidate cached token", async () => {
|
|
247
|
+
mockFetch.mockResolvedValue({
|
|
248
|
+
ok: true,
|
|
249
|
+
json: () => Promise.resolve({
|
|
250
|
+
access_token: "will-be-invalidated",
|
|
251
|
+
expires_in: 3600,
|
|
252
|
+
}),
|
|
253
|
+
});
|
|
254
|
+
// First call - cache the token
|
|
255
|
+
await auth.resolveAuthToken({
|
|
256
|
+
credentials: {
|
|
257
|
+
type: "client_credentials",
|
|
258
|
+
clientId: "invalidate-test",
|
|
259
|
+
clientSecret: "secret",
|
|
260
|
+
},
|
|
261
|
+
fetch: mockFetch,
|
|
262
|
+
});
|
|
263
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
264
|
+
// Invalidate the cache
|
|
265
|
+
auth.invalidateCachedToken("invalidate-test");
|
|
266
|
+
// Next call should fetch again
|
|
267
|
+
mockFetch.mockResolvedValue({
|
|
268
|
+
ok: true,
|
|
269
|
+
json: () => Promise.resolve({
|
|
270
|
+
access_token: "new-token-after-invalidation",
|
|
271
|
+
expires_in: 3600,
|
|
272
|
+
}),
|
|
273
|
+
});
|
|
274
|
+
const result = await auth.resolveAuthToken({
|
|
275
|
+
credentials: {
|
|
276
|
+
type: "client_credentials",
|
|
277
|
+
clientId: "invalidate-test",
|
|
278
|
+
clientSecret: "secret",
|
|
279
|
+
},
|
|
280
|
+
fetch: mockFetch,
|
|
281
|
+
});
|
|
282
|
+
expect(result).toBe("new-token-after-invalidation");
|
|
283
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
284
|
+
});
|
|
285
|
+
it("should clear all cached tokens", async () => {
|
|
286
|
+
mockFetch.mockResolvedValue({
|
|
287
|
+
ok: true,
|
|
288
|
+
json: () => Promise.resolve({
|
|
289
|
+
access_token: "token",
|
|
290
|
+
expires_in: 3600,
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
// Cache a token
|
|
294
|
+
await auth.resolveAuthToken({
|
|
295
|
+
credentials: {
|
|
296
|
+
type: "client_credentials",
|
|
297
|
+
clientId: "clear-test",
|
|
298
|
+
clientSecret: "secret",
|
|
299
|
+
},
|
|
300
|
+
fetch: mockFetch,
|
|
301
|
+
});
|
|
302
|
+
// Clear all caches
|
|
303
|
+
auth.clearTokenCache();
|
|
304
|
+
// Should fetch again
|
|
305
|
+
await auth.resolveAuthToken({
|
|
306
|
+
credentials: {
|
|
307
|
+
type: "client_credentials",
|
|
308
|
+
clientId: "clear-test",
|
|
309
|
+
clientSecret: "secret",
|
|
310
|
+
},
|
|
311
|
+
fetch: mockFetch,
|
|
312
|
+
});
|
|
313
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
describe("invalidateCredentialsToken", () => {
|
|
317
|
+
it("should invalidate cached token for client credentials", async () => {
|
|
318
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
319
|
+
ok: true,
|
|
320
|
+
json: () => Promise.resolve({
|
|
321
|
+
access_token: "cached-token",
|
|
322
|
+
expires_in: 3600,
|
|
323
|
+
}),
|
|
324
|
+
});
|
|
325
|
+
// First, get a token to populate the cache
|
|
326
|
+
await auth.resolveAuthToken({
|
|
327
|
+
credentials: {
|
|
328
|
+
type: "client_credentials",
|
|
329
|
+
clientId: "invalidate-test-client",
|
|
330
|
+
clientSecret: "test-secret",
|
|
331
|
+
},
|
|
332
|
+
fetch: mockFetch,
|
|
333
|
+
});
|
|
334
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
335
|
+
// Verify token is cached (no new fetch)
|
|
336
|
+
const cachedToken = await auth.resolveAuthToken({
|
|
337
|
+
credentials: {
|
|
338
|
+
type: "client_credentials",
|
|
339
|
+
clientId: "invalidate-test-client",
|
|
340
|
+
clientSecret: "test-secret",
|
|
341
|
+
},
|
|
342
|
+
fetch: mockFetch,
|
|
343
|
+
});
|
|
344
|
+
expect(cachedToken).toBe("cached-token");
|
|
345
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
346
|
+
// Invalidate the token
|
|
347
|
+
await auth.invalidateCredentialsToken({
|
|
348
|
+
credentials: {
|
|
349
|
+
type: "client_credentials",
|
|
350
|
+
clientId: "invalidate-test-client",
|
|
351
|
+
clientSecret: "test-secret",
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
// Next request should fetch a new token
|
|
355
|
+
await auth.resolveAuthToken({
|
|
356
|
+
credentials: {
|
|
357
|
+
type: "client_credentials",
|
|
358
|
+
clientId: "invalidate-test-client",
|
|
359
|
+
clientSecret: "test-secret",
|
|
360
|
+
},
|
|
361
|
+
fetch: mockFetch,
|
|
362
|
+
});
|
|
363
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
364
|
+
});
|
|
365
|
+
it("should do nothing for string credentials", async () => {
|
|
366
|
+
// Should not throw
|
|
367
|
+
await auth.invalidateCredentialsToken({
|
|
368
|
+
credentials: "string-token",
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
it("should do nothing when no credentials provided", async () => {
|
|
372
|
+
// Should not throw
|
|
373
|
+
await auth.invalidateCredentialsToken({});
|
|
100
374
|
});
|
|
101
375
|
});
|
|
102
376
|
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -11,4 +11,18 @@ export declare const ZAPIER_BASE_URL: string;
|
|
|
11
11
|
* Maximum number of items that can be requested per page across all paginated functions
|
|
12
12
|
*/
|
|
13
13
|
export declare const MAX_PAGE_LIMIT = 10000;
|
|
14
|
+
/**
|
|
15
|
+
* Credentials from environment variables
|
|
16
|
+
*/
|
|
17
|
+
export declare const ZAPIER_CREDENTIALS: string | undefined;
|
|
18
|
+
export declare const ZAPIER_CREDENTIALS_CLIENT_ID: string | undefined;
|
|
19
|
+
export declare const ZAPIER_CREDENTIALS_CLIENT_SECRET: string | undefined;
|
|
20
|
+
export declare const ZAPIER_CREDENTIALS_BASE_URL: string | undefined;
|
|
21
|
+
export declare const ZAPIER_CREDENTIALS_SCOPE: string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Deprecated environment variables (kept for backwards compatibility)
|
|
24
|
+
*/
|
|
25
|
+
export declare const ZAPIER_TOKEN: string | undefined;
|
|
26
|
+
export declare const ZAPIER_AUTH_BASE_URL: string | undefined;
|
|
27
|
+
export declare const ZAPIER_AUTH_CLIENT_ID: string | undefined;
|
|
14
28
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe,QACyB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,cAAc,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe,QACyB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,cAAc,QAAQ,CAAC;AAEpC;;GAEG;AACH,eAAO,MAAM,kBAAkB,oBAAiC,CAAC;AACjE,eAAO,MAAM,4BAA4B,oBACC,CAAC;AAC3C,eAAO,MAAM,gCAAgC,oBACC,CAAC;AAC/C,eAAO,MAAM,2BAA2B,oBACC,CAAC;AAC1C,eAAO,MAAM,wBAAwB,oBAAuC,CAAC;AAE7E;;GAEG;AACH,eAAO,MAAM,YAAY,oBAA2B,CAAC;AACrD,eAAO,MAAM,oBAAoB,oBAAmC,CAAC;AACrE,eAAO,MAAM,qBAAqB,oBAAoC,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -11,3 +11,17 @@ export const ZAPIER_BASE_URL = process.env.ZAPIER_BASE_URL || "https://zapier.co
|
|
|
11
11
|
* Maximum number of items that can be requested per page across all paginated functions
|
|
12
12
|
*/
|
|
13
13
|
export const MAX_PAGE_LIMIT = 10000;
|
|
14
|
+
/**
|
|
15
|
+
* Credentials from environment variables
|
|
16
|
+
*/
|
|
17
|
+
export const ZAPIER_CREDENTIALS = process.env.ZAPIER_CREDENTIALS;
|
|
18
|
+
export const ZAPIER_CREDENTIALS_CLIENT_ID = process.env.ZAPIER_CREDENTIALS_CLIENT_ID;
|
|
19
|
+
export const ZAPIER_CREDENTIALS_CLIENT_SECRET = process.env.ZAPIER_CREDENTIALS_CLIENT_SECRET;
|
|
20
|
+
export const ZAPIER_CREDENTIALS_BASE_URL = process.env.ZAPIER_CREDENTIALS_BASE_URL;
|
|
21
|
+
export const ZAPIER_CREDENTIALS_SCOPE = process.env.ZAPIER_CREDENTIALS_SCOPE;
|
|
22
|
+
/**
|
|
23
|
+
* Deprecated environment variables (kept for backwards compatibility)
|
|
24
|
+
*/
|
|
25
|
+
export const ZAPIER_TOKEN = process.env.ZAPIER_TOKEN;
|
|
26
|
+
export const ZAPIER_AUTH_BASE_URL = process.env.ZAPIER_AUTH_BASE_URL;
|
|
27
|
+
export const ZAPIER_AUTH_CLIENT_ID = process.env.ZAPIER_AUTH_CLIENT_ID;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials Resolution
|
|
3
|
+
*
|
|
4
|
+
* This module provides the core logic for resolving credentials from various sources:
|
|
5
|
+
* - Explicit credentials option
|
|
6
|
+
* - Deprecated token option
|
|
7
|
+
* - Environment variables (new and deprecated)
|
|
8
|
+
* - CLI login stored tokens
|
|
9
|
+
*/
|
|
10
|
+
import type { Credentials, ResolvedCredentials } from "./types/credentials";
|
|
11
|
+
/**
|
|
12
|
+
* Options for resolving credentials.
|
|
13
|
+
*/
|
|
14
|
+
export interface ResolveCredentialsOptions {
|
|
15
|
+
credentials?: Credentials;
|
|
16
|
+
/** @deprecated Use `credentials` instead */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** SDK base URL - used to derive auth base URL if not specified in credentials */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve credentials from environment variables.
|
|
23
|
+
*
|
|
24
|
+
* Precedence:
|
|
25
|
+
* 1. ZAPIER_CREDENTIALS (string token)
|
|
26
|
+
* 2. ZAPIER_CREDENTIALS_CLIENT_ID + ZAPIER_CREDENTIALS_CLIENT_SECRET (client credentials)
|
|
27
|
+
* 3. ZAPIER_CREDENTIALS_CLIENT_ID alone (PKCE)
|
|
28
|
+
* 4. Deprecated ZAPIER_TOKEN, ZAPIER_AUTH_* vars (with warnings)
|
|
29
|
+
*
|
|
30
|
+
* @param sdkBaseUrl - SDK base URL used to derive auth base URL if not specified
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveCredentialsFromEnv(sdkBaseUrl?: string): ResolvedCredentials | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve credentials from all possible sources.
|
|
35
|
+
*
|
|
36
|
+
* Precedence:
|
|
37
|
+
* 1. Explicit credentials option
|
|
38
|
+
* 2. Deprecated token option (with warning)
|
|
39
|
+
* 3. Environment variables
|
|
40
|
+
* 4. CLI login stored token (handled separately in auth.ts)
|
|
41
|
+
*
|
|
42
|
+
* If credentials is a function, it is called and must return
|
|
43
|
+
* a string or credentials object (not another function).
|
|
44
|
+
*
|
|
45
|
+
* The baseUrl option is used to derive the auth base URL if not
|
|
46
|
+
* specified in credentials.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveCredentials(options?: ResolveCredentialsOptions): Promise<ResolvedCredentials | undefined>;
|
|
49
|
+
/**
|
|
50
|
+
* Extract the base URL from credentials for use in auth flows.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getBaseUrlFromCredentials(credentials: ResolvedCredentials | undefined): string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Extract the client ID from credentials for use in auth flows.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getClientIdFromCredentials(credentials: ResolvedCredentials | undefined): string | undefined;
|
|
57
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EAEpB,MAAM,qBAAqB,CAAC;AAkB7B;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA2CD;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,CAAC,EAAE,MAAM,GAClB,mBAAmB,GAAG,SAAS,CAyDjC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAkD1C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,mBAAmB,GAAG,SAAS,GAC3C,MAAM,GAAG,SAAS,CAKpB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,mBAAmB,GAAG,SAAS,GAC3C,MAAM,GAAG,SAAS,CAKpB"}
|