zapier-platform-core 17.7.0 → 17.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapier-platform-core",
3
- "version": "17.7.0",
3
+ "version": "17.7.2",
4
4
  "description": "The core SDK for CLI apps in the Zapier Developer Platform.",
5
5
  "repository": "zapier/zapier-platform",
6
6
  "homepage": "https://platform.zapier.com/",
@@ -63,7 +63,7 @@
63
63
  "node-fetch": "2.7.0",
64
64
  "oauth-sign": "0.9.0",
65
65
  "semver": "7.7.1",
66
- "zapier-platform-schema": "17.7.0"
66
+ "zapier-platform-schema": "17.7.2"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@types/node-fetch": "^2.6.11",
@@ -2,19 +2,44 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const querystring = require('querystring');
5
+
6
+ const { scrub, findSensitiveValues } = require('@zapier/secret-scrubber');
7
+
5
8
  const { replaceHeaders } = require('./middleware-utils');
6
9
  const { FORM_TYPE } = require('../../tools/http');
7
10
  const errors = require('../../errors');
11
+ const {
12
+ findSensitiveValuesFromAuthData,
13
+ } = require('../../tools/secret-scrubber');
8
14
 
9
- const _throwForStatus = (response) => {
15
+ const buildSensitiveValues = (bundle) => {
16
+ const authData = bundle?.authData || {};
17
+ const result = [
18
+ ...findSensitiveValuesFromAuthData(authData),
19
+ ...findSensitiveValues(process.env),
20
+ ];
21
+ return [...new Set(result)];
22
+ };
23
+
24
+ const _throwForStatus = (response, bundle) => {
10
25
  // calling this always throws, regardless of the skipThrowForStatus value
11
26
  // eslint-disable-next-line yoda
12
27
  if (400 <= response.status && response.status < 600) {
28
+ // Create a cleaned version of the response to avoid sensitive data leaks
29
+ try {
30
+ // Find sensitive values from environment variables and bundle authData
31
+ const sensitiveValues = buildSensitiveValues(bundle);
32
+ if (sensitiveValues.length > 0 && response?.request?.url) {
33
+ response.request.url = scrub(response.request.url, sensitiveValues);
34
+ }
35
+ } catch (err) {
36
+ // don't fail the whole request if we can't scrub for some reason
37
+ }
13
38
  throw new errors.ResponseError(response);
14
39
  }
15
40
  };
16
41
 
17
- const prepareRawResponse = (resp, request) => {
42
+ const prepareRawResponse = (resp, request, bundle) => {
18
43
  // TODO: if !2xx should we go ahead and get response.content for them?
19
44
  // retain the response signature for raw control
20
45
  const extendedResp = {
@@ -24,7 +49,7 @@ const prepareRawResponse = (resp, request) => {
24
49
  const outResp = _.extend(resp, extendedResp, replaceHeaders(resp));
25
50
 
26
51
  outResp.throwForStatus = () => {
27
- _throwForStatus(outResp);
52
+ _throwForStatus(outResp, bundle);
28
53
  };
29
54
 
30
55
  Object.defineProperty(outResp, 'content', {
@@ -39,7 +64,7 @@ const prepareRawResponse = (resp, request) => {
39
64
  return outResp;
40
65
  };
41
66
 
42
- const prepareContentResponse = async (resp, request) => {
67
+ const prepareContentResponse = async (resp, request, bundle) => {
43
68
  // TODO: does it make sense to not trim the signature? more equivalence to raw...
44
69
  const content = await resp.text();
45
70
 
@@ -67,14 +92,14 @@ const prepareContentResponse = async (resp, request) => {
67
92
  } catch (_e) {}
68
93
 
69
94
  outResp.throwForStatus = () => {
70
- _throwForStatus(outResp);
95
+ _throwForStatus(outResp, bundle);
71
96
  };
72
97
 
73
98
  return outResp;
74
99
  };
75
100
 
76
101
  // Provide a standardized plain JS responseObj for common consumption, or raw response for streaming.
77
- const prepareResponse = (resp) => {
102
+ const prepareResponse = (resp, z, bundle) => {
78
103
  const request = resp.input;
79
104
  delete resp.input;
80
105
 
@@ -82,7 +107,7 @@ const prepareResponse = (resp) => {
82
107
  ? prepareRawResponse
83
108
  : prepareContentResponse;
84
109
 
85
- return responseFunc(resp, request);
110
+ return responseFunc(resp, request, bundle);
86
111
  };
87
112
 
88
113
  module.exports = prepareResponse;
@@ -15,16 +15,8 @@ const {
15
15
  SAFE_LOG_KEYS,
16
16
  } = require('../constants');
17
17
  const { unheader } = require('./http');
18
- const {
19
- scrub,
20
- findSensitiveValues,
21
- recurseExtract,
22
- } = require('@zapier/secret-scrubber');
23
- // not really a public function, but it came from here originally
24
- const {
25
- isUrlWithSecrets,
26
- isSensitiveKey,
27
- } = require('@zapier/secret-scrubber/lib/convenience');
18
+ const { scrub, findSensitiveValues } = require('@zapier/secret-scrubber');
19
+ const { findSensitiveValuesFromAuthData } = require('./secret-scrubber');
28
20
 
29
21
  // The payload size per request to stream logs. This should be slighly lower
30
22
  // than the limit (16 MB) on the server side.
@@ -32,70 +24,8 @@ const LOG_STREAM_BYTES_LIMIT = 15 * 1024 * 1024;
32
24
 
33
25
  const DEFAULT_LOGGER_TIMEOUT = 200;
34
26
 
35
- // Will be initialized lazily
36
- const SAFE_AUTH_DATA_KEYS = new Set();
37
-
38
27
  const sleep = promisify(setTimeout);
39
28
 
40
- const initSafeAuthDataKeys = () => {
41
- // An authData key in (safePrefixes x safeSuffixes) is considered safe to log
42
- // uncensored
43
- const safePrefixes = [
44
- 'account',
45
- 'bot_user',
46
- 'cloud',
47
- 'cloud_site',
48
- 'company',
49
- 'domain',
50
- 'email',
51
- 'environment',
52
- 'location',
53
- 'org',
54
- 'organization',
55
- 'project',
56
- 'region',
57
- 'scope',
58
- 'scopes',
59
- 'site',
60
- 'subdomain',
61
- 'team',
62
- 'token_type',
63
- 'user',
64
- 'workspace',
65
- ];
66
- const safeSuffixes = ['', '_id', '_name', 'id', 'name'];
67
- for (const prefix of safePrefixes) {
68
- for (const suffix of safeSuffixes) {
69
- SAFE_AUTH_DATA_KEYS.add(prefix + suffix);
70
- }
71
- }
72
- };
73
-
74
- const isUrl = (value) => {
75
- if (!value || typeof value !== 'string') {
76
- return false;
77
- }
78
- const commonProtocols = [
79
- 'https://',
80
- 'http://',
81
- 'ftp://',
82
- 'ftps://',
83
- 'file://',
84
- ];
85
- for (const protocol of commonProtocols) {
86
- if (value.startsWith(protocol)) {
87
- try {
88
- // eslint-disable-next-line no-new
89
- new URL(value);
90
- return true;
91
- } catch (e) {
92
- return false;
93
- }
94
- }
95
- }
96
- return false;
97
- };
98
-
99
29
  const MAX_LENGTH = 3500;
100
30
  const truncateString = (str) => simpleTruncate(str, MAX_LENGTH, ' [...]');
101
31
 
@@ -188,35 +118,12 @@ const attemptFindSecretsInStr = (s, isGettingNewSecret) => {
188
118
  return findSensitiveValues(parsedRespContent);
189
119
  };
190
120
 
191
- const isSafeAuthDataKey = (key) => {
192
- if (SAFE_AUTH_DATA_KEYS.size === 0) {
193
- initSafeAuthDataKeys();
194
- }
195
- return SAFE_AUTH_DATA_KEYS.has(key.toLowerCase());
196
- };
197
-
198
121
  const buildSensitiveValues = (event, data) => {
199
122
  const bundle = event.bundle || {};
200
123
  const authData = bundle.authData || {};
201
- // for the most part, we should censor all the values from authData
202
- // the exception is safe urls, which should be filtered out - we want those to be logged
203
- // but, we _should_ censor-no-matter-what sensitive keys, even if their value is a safe url
204
- // this covers the case where someone's password is a valid url ¯\_(ツ)_/¯
205
- const sensitiveAuthData = recurseExtract(authData, (key, value) => {
206
- if (isSensitiveKey(key)) {
207
- return true;
208
- }
209
- if (isSafeAuthDataKey(key)) {
210
- return false;
211
- }
212
- if (isUrl(value) && !isUrlWithSecrets(value)) {
213
- return false;
214
- }
215
- return true;
216
- });
217
124
 
218
125
  const result = [
219
- ...sensitiveAuthData,
126
+ ...findSensitiveValuesFromAuthData(authData),
220
127
  ...findSensitiveValues(process.env),
221
128
  ...findSensitiveValues(data),
222
129
  ];
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ const { recurseExtract } = require('@zapier/secret-scrubber');
4
+ const {
5
+ isUrlWithSecrets,
6
+ isSensitiveKey,
7
+ } = require('@zapier/secret-scrubber/lib/convenience');
8
+
9
+ // Will be initialized lazily
10
+ const SAFE_AUTH_DATA_KEYS = new Set();
11
+
12
+ const initSafeAuthDataKeys = () => {
13
+ // An authData key in (safePrefixes x safeSuffixes) is considered safe to log
14
+ // uncensored
15
+ const safePrefixes = [
16
+ 'account',
17
+ 'bot_user',
18
+ 'cloud',
19
+ 'cloud_site',
20
+ 'company',
21
+ 'domain',
22
+ 'email',
23
+ 'environment',
24
+ 'location',
25
+ 'org',
26
+ 'organization',
27
+ 'project',
28
+ 'region',
29
+ 'scope',
30
+ 'scopes',
31
+ 'site',
32
+ 'subdomain',
33
+ 'team',
34
+ 'token_type',
35
+ 'user',
36
+ 'workspace',
37
+ ];
38
+ const safeSuffixes = ['', '_id', '_name', 'id', 'name'];
39
+ for (const prefix of safePrefixes) {
40
+ for (const suffix of safeSuffixes) {
41
+ SAFE_AUTH_DATA_KEYS.add(prefix + suffix);
42
+ }
43
+ }
44
+ };
45
+
46
+ const isSafeAuthDataKey = (key) => {
47
+ if (SAFE_AUTH_DATA_KEYS.size === 0) {
48
+ initSafeAuthDataKeys();
49
+ }
50
+ return SAFE_AUTH_DATA_KEYS.has(key.toLowerCase());
51
+ };
52
+
53
+ const isUrl = (value) => {
54
+ if (!value || typeof value !== 'string') {
55
+ return false;
56
+ }
57
+ const commonProtocols = [
58
+ 'https://',
59
+ 'http://',
60
+ 'ftp://',
61
+ 'ftps://',
62
+ 'file://',
63
+ ];
64
+ for (const protocol of commonProtocols) {
65
+ if (value.startsWith(protocol)) {
66
+ try {
67
+ // eslint-disable-next-line no-new
68
+ new URL(value);
69
+ return true;
70
+ } catch (e) {
71
+ return false;
72
+ }
73
+ }
74
+ }
75
+ return false;
76
+ };
77
+
78
+ const findSensitiveValuesFromAuthData = (authData) =>
79
+ recurseExtract(authData, (key, value) => {
80
+ // for the most part, we should censor all the values from authData
81
+ // the exception is safe urls, which should be filtered out - we want those to be logged
82
+ // but, we _should_ censor-no-matter-what sensitive keys, even if their value is a safe url
83
+ // this covers the case where someone's password is a valid url ¯\_(ツ)_/¯
84
+ if (isSensitiveKey(key)) {
85
+ return true;
86
+ }
87
+ if (isSafeAuthDataKey(key)) {
88
+ return false;
89
+ }
90
+ if (isUrl(value) && !isUrlWithSecrets(value)) {
91
+ return false;
92
+ }
93
+ return true;
94
+ });
95
+
96
+ module.exports = {
97
+ findSensitiveValuesFromAuthData,
98
+ };
package/types/custom.d.ts CHANGED
@@ -112,6 +112,14 @@ export interface Bundle<
112
112
  };
113
113
  };
114
114
 
115
+ /**
116
+ * A token, such as a pagination cursor, that means this run should
117
+ * continue pulling results. Currently only used for search
118
+ * pagination. Set by previous invocations by returning `{results:
119
+ * […], paging_token: '…'}`
120
+ */
121
+ paging_token?: string;
122
+
115
123
  /**
116
124
  * Contains metadata about the input fields, optionally provided
117
125
  * by the inputField.meta property. Useful for storing extra data
@@ -142,7 +150,11 @@ export interface Bundle<
142
150
 
143
151
  // Error class types that match the runtime structure from src/errors.js
144
152
  type ErrorConstructor = new (message?: string) => Error;
145
- type AppErrorConstructor = new (message: string, code?: string, status?: number) => Error;
153
+ type AppErrorConstructor = new (
154
+ message: string,
155
+ code?: string,
156
+ status?: number,
157
+ ) => Error;
146
158
  type ThrottledErrorConstructor = new (message: string, delay?: number) => Error;
147
159
  type ResponseErrorConstructor = new (response: HttpResponse) => Error;
148
160
 
@@ -163,7 +175,6 @@ interface ErrorsModule {
163
175
  handleError: (...args: any[]) => never;
164
176
  }
165
177
 
166
-
167
178
  // copied http stuff from external typings
168
179
  export interface HttpRequestOptions {
169
180
  agent?: Agent;
@@ -1,7 +1,20 @@
1
1
  import type { Bundle, ZObject } from './custom';
2
+ import type { InferInputData, InputField } from './inputs';
2
3
 
3
4
  type DefaultInputData = Record<string, unknown>;
4
5
 
6
+ /**
7
+ * Automatically infer the type of the bundle based on if a raw
8
+ * inputData object shape is given, or an array of input fields that
9
+ * should be automatically inferred into an inputData object.
10
+ */
11
+ type AutoBundle<$InputDataOrFields extends DefaultInputData | InputField[]> =
12
+ $InputDataOrFields extends InputField[]
13
+ ? Bundle<InferInputData<$InputDataOrFields>>
14
+ : $InputDataOrFields extends DefaultInputData
15
+ ? Bundle<$InputDataOrFields>
16
+ : never;
17
+
5
18
  /**
6
19
  * Wraps a `perform` function that is used to poll for data from an API.
7
20
  * By default must return an array of objects with an `id` field, but
@@ -10,9 +23,12 @@ type DefaultInputData = Record<string, unknown>;
10
23
  * those keys.
11
24
  */
12
25
  export type PollingTriggerPerform<
13
- $InputData extends DefaultInputData = DefaultInputData,
26
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
14
27
  $Return extends {} = { id: string },
15
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return[] | Promise<$Return[]>;
28
+ > = (
29
+ z: ZObject,
30
+ bundle: AutoBundle<$InputDataOrFields>,
31
+ ) => $Return[] | Promise<$Return[]>;
16
32
 
17
33
  /**
18
34
  * Process the data of a webhook sent to Zapier.
@@ -21,62 +37,83 @@ export type PollingTriggerPerform<
21
37
  * in `bundle.cleanedRequest` (and `bundle.rawRequest`).
22
38
  */
23
39
  export type WebhookTriggerPerform<
24
- $InputData extends DefaultInputData = DefaultInputData,
40
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
25
41
  $Return extends {} = {},
26
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return[] | Promise<$Return[]>;
42
+ > = (
43
+ z: ZObject,
44
+ bundle: AutoBundle<$InputDataOrFields>,
45
+ ) => $Return[] | Promise<$Return[]>;
27
46
 
28
47
  /**
29
48
  * Pull sample data from the webhook trigger. Try to make these resemble
30
49
  * to data of the hooks as closely as possible.
31
50
  */
32
51
  export type WebhookTriggerPerformList<
33
- $InputData extends DefaultInputData = DefaultInputData,
52
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
34
53
  $Return extends {} = {},
35
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return[] | Promise<$Return[]>;
54
+ > = (
55
+ z: ZObject,
56
+ bundle: AutoBundle<$InputDataOrFields>,
57
+ ) => $Return[] | Promise<$Return[]>;
36
58
 
37
59
  /**
38
60
  * Return must be an object of subscription data. It will be passed as
39
61
  * `bundle.subscribeData` in the performUnsubscribe function.
40
62
  */
41
63
  export type WebhookTriggerPerformSubscribe<
42
- $InputData extends DefaultInputData = DefaultInputData,
64
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
43
65
  $Return extends {} = {},
44
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
66
+ > = (
67
+ z: ZObject,
68
+ bundle: AutoBundle<$InputDataOrFields>,
69
+ ) => $Return | Promise<$Return>;
45
70
 
46
71
  /**
47
72
  * Unsubscribe from the webhook.
48
73
  * Data from the subscribe function is provided in `bundle.subscribeData`.
49
74
  */
50
75
  export type WebhookTriggerPerformUnsubscribe<
51
- $InputData extends DefaultInputData = DefaultInputData,
76
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
52
77
  $Return extends {} = {},
53
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
78
+ > = (
79
+ z: ZObject,
80
+ bundle: AutoBundle<$InputDataOrFields>,
81
+ ) => $Return | Promise<$Return>;
54
82
 
55
83
  /**
56
84
  * Pull data from the API service, same as a polling trigger.
57
85
  */
58
86
  export type HookToPollTriggerPerformList<
59
- $InputData extends DefaultInputData = DefaultInputData,
87
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
60
88
  $Return extends {} = {},
61
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return[] | Promise<$Return[]>;
89
+ > = (
90
+ z: ZObject,
91
+ bundle: AutoBundle<$InputDataOrFields>,
92
+ ) => $Return[] | Promise<$Return[]>;
62
93
 
63
94
  /**
64
95
  * Return must be an object of subscription data. It will be passed as
65
96
  * `bundle.subscribeData` in the performUnsubscribe function.
66
97
  */
67
98
  export type HookToPollTriggerPerformSubscribe<
68
- $InputData extends DefaultInputData = DefaultInputData,
99
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
69
100
  $Return extends {} = {},
70
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
101
+ > = (
102
+ z: ZObject,
103
+ bundle: AutoBundle<$InputDataOrFields>,
104
+ ) => $Return | Promise<$Return>;
71
105
 
72
106
  /**
73
107
  * Unsubscribe from the HookToPoll.
74
108
  * Data from the subscribe function is provided in `bundle.subscribeData`.
75
109
  */
76
110
  export type HookToPollTriggerPerformUnsubscribe<
77
- $InputData extends DefaultInputData = DefaultInputData,
111
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
78
112
  $Return extends {} = {},
79
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
113
+ > = (
114
+ z: ZObject,
115
+ bundle: AutoBundle<$InputDataOrFields>,
116
+ ) => $Return | Promise<$Return>;
80
117
 
81
118
  /**
82
119
  * Create an item on a partner API.
@@ -85,9 +122,12 @@ export type HookToPollTriggerPerformUnsubscribe<
85
122
  * to populate more data.
86
123
  */
87
124
  export type CreatePerform<
88
- $InputData extends DefaultInputData = DefaultInputData,
125
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
89
126
  $Return extends {} = {},
90
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
127
+ > = (
128
+ z: ZObject,
129
+ bundle: AutoBundle<$InputDataOrFields>,
130
+ ) => $Return | Promise<$Return>;
91
131
 
92
132
  /**
93
133
  * A `perform` function can setup a partner API to call back to this
@@ -104,18 +144,24 @@ export type CreatePerform<
104
144
  * - bundle.outputData: The output data from the original `perform`.
105
145
  */
106
146
  export type CreatePerformResume<
107
- $InputData extends DefaultInputData = DefaultInputData,
147
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
108
148
  // TODO: Type cleanedRequest & outputData on Bundle interface
109
149
  $Return extends {} = {},
110
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
150
+ > = (
151
+ z: ZObject,
152
+ bundle: AutoBundle<$InputDataOrFields>,
153
+ ) => $Return | Promise<$Return>;
111
154
 
112
155
  /**
113
156
  * Look up an object to populate it after creation?
114
157
  */
115
158
  export type CreatePerformGet<
116
- $InputData extends DefaultInputData = DefaultInputData,
159
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
117
160
  $Return extends {} = {},
118
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
161
+ > = (
162
+ z: ZObject,
163
+ bundle: AutoBundle<$InputDataOrFields>,
164
+ ) => $Return | Promise<$Return>;
119
165
 
120
166
  /**
121
167
  * Helper type for search results that can optionally include pagination.
@@ -125,7 +171,7 @@ export type CreatePerformGet<
125
171
  *
126
172
  * When `canPaginate` is true for the search, the object shape is required.
127
173
  */
128
- type SearchResult<T> = T[] | { results: T[], paging_token: string };
174
+ type SearchResult<T> = T[] | { results: T[]; paging_token: string };
129
175
 
130
176
  /**
131
177
  * Search for objects on a partner API.
@@ -136,9 +182,12 @@ type SearchResult<T> = T[] | { results: T[], paging_token: string };
136
182
  * revisit this?
137
183
  */
138
184
  export type SearchPerform<
139
- $InputData extends DefaultInputData = DefaultInputData,
185
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
140
186
  $Return extends {} = {},
141
- > = (z: ZObject, bundle: Bundle<$InputData>) => SearchResult<$Return> | Promise<SearchResult<$Return>>;
187
+ > = (
188
+ z: ZObject,
189
+ bundle: AutoBundle<$InputDataOrFields>,
190
+ ) => SearchResult<$Return> | Promise<SearchResult<$Return>>;
142
191
 
143
192
  /**
144
193
  * Follow up a search's perform with additional data.
@@ -148,17 +197,23 @@ export type SearchPerform<
148
197
  * -> PROBABLY: Just the result of searchPerform, no inputFields.
149
198
  */
150
199
  export type SearchPerformGet<
151
- $InputData extends DefaultInputData = DefaultInputData,
200
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
152
201
  $Return extends {} = {},
153
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
202
+ > = (
203
+ z: ZObject,
204
+ bundle: AutoBundle<$InputDataOrFields>,
205
+ ) => $Return | Promise<$Return>;
154
206
 
155
207
  /**
156
208
  *
157
209
  */
158
210
  export type SearchPerformResume<
159
- $InputData extends DefaultInputData = DefaultInputData,
211
+ $InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
160
212
  $Return extends {} = {},
161
- > = (z: ZObject, bundle: Bundle<$InputData>) => $Return | Promise<$Return>;
213
+ > = (
214
+ z: ZObject,
215
+ bundle: AutoBundle<$InputDataOrFields>,
216
+ ) => $Return | Promise<$Return>;
162
217
 
163
218
  /**
164
219
  * Produce the URL to send the user to authorise with the OAuth2 provider.
@@ -1,5 +1,8 @@
1
- import { expectAssignable } from 'tsd';
1
+ import { expectAssignable, expectType } from 'tsd';
2
2
  import type { PollingTriggerPerform } from './functions';
3
+ import { Bundle, ZObject } from './custom';
4
+ import { InferInputData } from './inputs';
5
+ import { defineInputFields } from './typeHelpers';
3
6
 
4
7
  const simplePerform = (async (z, bundle) => {
5
8
  return [{ id: '1', name: 'test' }];
@@ -16,3 +19,46 @@ const primaryKeyOverridePerform = (async (z, bundle) => {
16
19
  expectAssignable<PollingTriggerPerform<{}, { itemId: number; name: string }>>(
17
20
  primaryKeyOverridePerform,
18
21
  );
22
+
23
+ //
24
+ // Automatic inputData inference
25
+ //
26
+ // Pass shape directly
27
+ const simplePerformWithInputFields = (async (z, bundle) => {
28
+ return [{ id: '1', name: 'test' }];
29
+ }) satisfies PollingTriggerPerform<{ key: 'string' }>;
30
+ expectType<
31
+ (
32
+ z: ZObject,
33
+ bundle: Bundle<{ key: 'string' }>,
34
+ ) => Promise<{ id: string; name: string }[]>
35
+ >(simplePerformWithInputFields);
36
+
37
+ // Use InputFields and manually infer inputData
38
+ const inputFields = defineInputFields([
39
+ { key: 'key1', type: 'string', required: true },
40
+ { key: 'key2', type: 'number', required: false },
41
+ ]);
42
+ const simplePerformWithInputFieldsAndManualInference = (async (z, bundle) => {
43
+ return [{ id: '1', name: 'test' }];
44
+ }) satisfies PollingTriggerPerform<InferInputData<typeof inputFields>>;
45
+ expectType<
46
+ (
47
+ z: ZObject,
48
+ bundle: Bundle<{ key1: string; key2?: number | undefined }>,
49
+ ) => Promise<{ id: string; name: string }[]>
50
+ >(simplePerformWithInputFieldsAndManualInference);
51
+
52
+ // Use inputFields and automatically infer inputData
53
+ const simplePerformWithInputFieldsAndAutomaticInference = (async (
54
+ z,
55
+ bundle,
56
+ ) => {
57
+ return [{ id: '1', name: 'test' }];
58
+ }) satisfies PollingTriggerPerform<typeof inputFields>;
59
+ expectType<
60
+ (
61
+ z: ZObject,
62
+ bundle: Bundle<{ key1: string; key2?: number | undefined }>,
63
+ ) => Promise<{ id: string; name: string }[]>
64
+ >(simplePerformWithInputFieldsAndAutomaticInference);