@prismatic-io/spectral 10.19.0 → 10.20.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/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { convertComponent } from "./serverTypes/convertComponent";
7
7
  import { convertIntegration } from "./serverTypes/convertIntegration";
8
- import type { ActionDefinition, ActionPerformReturn, ComponentDefinition, ComponentManifest, ConfigPage, ConfigVarResultCollection, ConnectionConfigVar, CustomerActivatedConnectionConfigVar, DataSourceConfigVar, DataSourceDefinition, DataSourceType, DefaultConnectionDefinition, DynamicObjectInputField, Flow, InputFieldDefinition, Inputs, IntegrationDefinition, OAuth2ConnectionDefinition, OnPremConnectionDefinition, OrganizationActivatedConnectionConfigVar, StandardConfigVar, StructuredObjectInputField, TriggerDefinition, TriggerPayload, TriggerResult } from "./types";
8
+ import type { ActionDefinition, ActionPerformReturn, BatchTrigger, ComponentDefinition, ComponentManifest, ConfigPage, ConfigVarResultCollection, ConnectionConfigVar, CustomerActivatedConnectionConfigVar, DataSourceConfigVar, DataSourceDefinition, DataSourceType, DefaultConnectionDefinition, DynamicObjectInputField, Flow, InputFieldDefinition, Inputs, IntegrationDefinition, OAuth2ConnectionDefinition, OnPremConnectionDefinition, OrganizationActivatedConnectionConfigVar, StandardConfigVar, StructuredObjectInputField, TriggerDefinition, TriggerPayload, TriggerResult } from "./types";
9
9
  import type { PollingTriggerDefinition } from "./types/PollingTriggerDefinition";
10
10
  /**
11
11
  * This function creates a code-native integration object that can be
@@ -73,7 +73,48 @@ export declare const integration: <TInputs extends Inputs, TActionInputs extends
73
73
  * },
74
74
  * });
75
75
  */
76
- export declare const flow: <TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload, T extends Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload> = Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload>>(definition: T) => T;
76
+ export declare const flow: <TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>, T extends Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TPaginationState> = Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TPaginationState>>(definition: T & {
77
+ trigger?: BatchTrigger<TItem, TPaginationState>;
78
+ }) => T;
79
+ /**
80
+ * Builds a flow's batched `trigger` — the ergonomic way to define a batching flow. Instead of
81
+ * writing `onTrigger`/`onDeployTrigger` (returning a full payload) plus `triggerResolver`/
82
+ * `onDeployResolver` (to extract and paginate), each trigger fire returns `{ items,
83
+ * paginationState? }`: the records to dispatch and, when paginating, the cursor for the next
84
+ * page. spectral wraps the items into the wire payload and synthesizes the `resolveItems` and
85
+ * `getNextPaginationState` that read them back — there is no separate pagination callback.
86
+ *
87
+ * Supply the item and pagination-state types explicitly —
88
+ * `batchFlowTrigger<Order, { cursor: number }>({ ... })`. They flow through the whole flow:
89
+ * the trigger fires return `Order[]`, `payload.paginationState` reads back as `{ cursor: number }`,
90
+ * and the flow's `onExecution` sees `params.onTrigger.results.body.data` typed as `Order | Order[]`.
91
+ *
92
+ * @typeParam TItem - the item type each batched execution receives.
93
+ * @typeParam TPaginationState - the pagination state round-tripped via `payload.paginationState`.
94
+ * @see {@link https://prismatic.io/docs/integrations/code-native/flows/ | Code-Native Flows}
95
+ * @example
96
+ * import { flow, batchFlowTrigger } from "@prismatic-io/spectral";
97
+ *
98
+ * flow({
99
+ * name: "Sync Orders",
100
+ * stableKey: "sync-orders",
101
+ * batchConfig: { batchSize: 50 },
102
+ * trigger: batchFlowTrigger<Order, { cursor: number }>({
103
+ * onTrigger: async (context, payload) => {
104
+ * const page = await fetchOrders(payload.paginationState?.cursor);
105
+ * return {
106
+ * items: page.orders,
107
+ * paginationState: page.nextCursor ? { cursor: page.nextCursor } : null,
108
+ * };
109
+ * },
110
+ * }),
111
+ * onExecution: async (context, params) => {
112
+ * const orders = params.onTrigger.results.body.data; // Order | Order[]
113
+ * return { data: orders };
114
+ * },
115
+ * });
116
+ */
117
+ export declare const batchFlowTrigger: <TItem, TPaginationState extends Record<string, unknown> = Record<string, unknown>>(trigger: BatchTrigger<TItem, TPaginationState>) => BatchTrigger<TItem, TPaginationState>;
77
118
  /**
78
119
  * This function creates a config wizard page object for use in code-native
79
120
  * integrations.
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.util = exports.testing = exports.componentManifests = exports.oauth2Connection = exports.onPremConnection = exports.connection = exports.dynamicObjectInput = exports.structuredObjectInput = exports.input = exports.dataSource = exports.pollingTrigger = exports.trigger = exports.action = exports.component = exports.componentManifest = exports.organizationActivatedConnection = exports.customerActivatedConnection = exports.connectionConfigVar = exports.dataSourceConfigVar = exports.configVar = exports.configPage = exports.flow = exports.integration = void 0;
25
+ exports.util = exports.testing = exports.componentManifests = exports.oauth2Connection = exports.onPremConnection = exports.connection = exports.dynamicObjectInput = exports.structuredObjectInput = exports.input = exports.dataSource = exports.pollingTrigger = exports.trigger = exports.action = exports.component = exports.componentManifest = exports.organizationActivatedConnection = exports.customerActivatedConnection = exports.connectionConfigVar = exports.dataSourceConfigVar = exports.configVar = exports.configPage = exports.batchFlowTrigger = exports.flow = exports.integration = void 0;
26
26
  const serverTypes_1 = require("./serverTypes");
27
27
  const convertComponent_1 = require("./serverTypes/convertComponent");
28
28
  const convertIntegration_1 = require("./serverTypes/convertIntegration");
@@ -102,8 +102,53 @@ exports.integration = integration;
102
102
  * },
103
103
  * });
104
104
  */
105
- const flow = (definition) => definition;
105
+ const flow = (
106
+ // The intersection adds an explicit inference site for `TItem`/`TPaginationState` that the
107
+ // `T extends Flow<...>` capture alone does not provide, while `T` still preserves the precise
108
+ // literal type for the return value. They are inferred from a batched `trigger`'s
109
+ // `items`/pagination-state types (see `batchFlowTrigger`).
110
+ definition) => definition;
106
111
  exports.flow = flow;
112
+ /**
113
+ * Builds a flow's batched `trigger` — the ergonomic way to define a batching flow. Instead of
114
+ * writing `onTrigger`/`onDeployTrigger` (returning a full payload) plus `triggerResolver`/
115
+ * `onDeployResolver` (to extract and paginate), each trigger fire returns `{ items,
116
+ * paginationState? }`: the records to dispatch and, when paginating, the cursor for the next
117
+ * page. spectral wraps the items into the wire payload and synthesizes the `resolveItems` and
118
+ * `getNextPaginationState` that read them back — there is no separate pagination callback.
119
+ *
120
+ * Supply the item and pagination-state types explicitly —
121
+ * `batchFlowTrigger<Order, { cursor: number }>({ ... })`. They flow through the whole flow:
122
+ * the trigger fires return `Order[]`, `payload.paginationState` reads back as `{ cursor: number }`,
123
+ * and the flow's `onExecution` sees `params.onTrigger.results.body.data` typed as `Order | Order[]`.
124
+ *
125
+ * @typeParam TItem - the item type each batched execution receives.
126
+ * @typeParam TPaginationState - the pagination state round-tripped via `payload.paginationState`.
127
+ * @see {@link https://prismatic.io/docs/integrations/code-native/flows/ | Code-Native Flows}
128
+ * @example
129
+ * import { flow, batchFlowTrigger } from "@prismatic-io/spectral";
130
+ *
131
+ * flow({
132
+ * name: "Sync Orders",
133
+ * stableKey: "sync-orders",
134
+ * batchConfig: { batchSize: 50 },
135
+ * trigger: batchFlowTrigger<Order, { cursor: number }>({
136
+ * onTrigger: async (context, payload) => {
137
+ * const page = await fetchOrders(payload.paginationState?.cursor);
138
+ * return {
139
+ * items: page.orders,
140
+ * paginationState: page.nextCursor ? { cursor: page.nextCursor } : null,
141
+ * };
142
+ * },
143
+ * }),
144
+ * onExecution: async (context, params) => {
145
+ * const orders = params.onTrigger.results.body.data; // Order | Order[]
146
+ * return { data: orders };
147
+ * },
148
+ * });
149
+ */
150
+ const batchFlowTrigger = (trigger) => trigger;
151
+ exports.batchFlowTrigger = batchFlowTrigger;
107
152
  /**
108
153
  * This function creates a config wizard page object for use in code-native
109
154
  * integrations.
@@ -7,6 +7,18 @@ import { type CleanFn } from "./perform";
7
7
  * declare a top-level clean on these containers — the conversion always
8
8
  * supplies one so nested clean functions are applied at runtime. */
9
9
  export declare const cleanerFor: (input: InputFieldDefinition) => CleanFn | undefined;
10
+ /**
11
+ * Throws if `batchSize` isn't a positive integer; otherwise returns it. Shared by the
12
+ * component-trigger (`TriggerDefinition.batch.batchSize`) and CNI flow (`flow.batch.batchSize`)
13
+ * validation paths.
14
+ */
15
+ export declare const validateBatchSize: (ownerLabel: string, fieldName: string, batchSize: unknown) => number;
16
+ /**
17
+ * Throws if `concurrentBatchLimit` is set but isn't a positive integer; returns it
18
+ * unchanged (including `undefined`, which the platform treats as unlimited). Shared by the
19
+ * component-trigger and CNI flow paths, both sourcing it from the single `batchConfig`.
20
+ */
21
+ export declare const validateConcurrentBatchLimit: (ownerLabel: string, fieldName: string, concurrentBatchLimit: unknown) => number | undefined;
10
22
  export declare const convertInput: (key: string, definition: InputFieldDefinition | OnPremConnectionInput | ConnectionInput) => ServerInput;
11
23
  export declare const _isValidTemplateValue: (template: string, inputs: {
12
24
  [key: string]: ConnectionInput | ConnectionTemplateInputField;
@@ -14,7 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.convertComponent = exports.convertConnection = exports.convertTrigger = exports.convertTemplateInput = exports._isValidTemplateValue = exports.convertInput = exports.cleanerFor = void 0;
17
+ exports.convertComponent = exports.convertConnection = exports.convertTrigger = exports.convertTemplateInput = exports._isValidTemplateValue = exports.convertInput = exports.validateConcurrentBatchLimit = exports.validateBatchSize = exports.cleanerFor = void 0;
18
18
  const omit_1 = __importDefault(require("lodash/omit"));
19
19
  const types_1 = require("../types");
20
20
  const PollingTriggerDefinition_1 = require("../types/PollingTriggerDefinition");
@@ -67,6 +67,89 @@ const cleanerFor = (input) => {
67
67
  return "clean" in input ? input.clean : undefined;
68
68
  };
69
69
  exports.cleanerFor = cleanerFor;
70
+ /**
71
+ * Throws if `batchSize` isn't a positive integer; otherwise returns it. Shared by the
72
+ * component-trigger (`TriggerDefinition.batch.batchSize`) and CNI flow (`flow.batch.batchSize`)
73
+ * validation paths.
74
+ */
75
+ const validateBatchSize = (ownerLabel, fieldName, batchSize) => {
76
+ if (typeof batchSize !== "number" || !Number.isInteger(batchSize) || batchSize < 1) {
77
+ throw new Error(`${ownerLabel} has an invalid ${fieldName} batchSize of ${String(batchSize)}. batchSize must be an integer >= 1.`);
78
+ }
79
+ return batchSize;
80
+ };
81
+ exports.validateBatchSize = validateBatchSize;
82
+ /**
83
+ * Throws if `concurrentBatchLimit` is set but isn't a positive integer; returns it
84
+ * unchanged (including `undefined`, which the platform treats as unlimited). Shared by the
85
+ * component-trigger and CNI flow paths, both sourcing it from the single `batchConfig`.
86
+ */
87
+ const validateConcurrentBatchLimit = (ownerLabel, fieldName, concurrentBatchLimit) => {
88
+ if (concurrentBatchLimit === undefined) {
89
+ return undefined;
90
+ }
91
+ if (typeof concurrentBatchLimit !== "number" ||
92
+ !Number.isInteger(concurrentBatchLimit) ||
93
+ concurrentBatchLimit < 1) {
94
+ throw new Error(`${ownerLabel} has an invalid ${fieldName} concurrentBatchLimit of ${String(concurrentBatchLimit)}. concurrentBatchLimit must be an integer >= 1.`);
95
+ }
96
+ return concurrentBatchLimit;
97
+ };
98
+ exports.validateConcurrentBatchLimit = validateConcurrentBatchLimit;
99
+ /**
100
+ * Emits the trigger's single default batch size to the one wire field the platform reads
101
+ * (`triggerResolverDefaultBatchSize`), shared by both the trigger and on-deploy resolution.
102
+ * Emitted when the trigger declares a resolver — `triggerResolverSupport` `"valid"`/`"required"`
103
+ * for the normal path, or an `onDeployResolver` for the on-deploy path. Defaults to 1 when no
104
+ * `batchConfig` was declared.
105
+ */
106
+ const buildBatchDefaultField = (triggerLabel, triggerResolverSupport, hasOnDeployResolver, batchConfig) => {
107
+ if (triggerResolverSupport === "invalid" && !hasOnDeployResolver) {
108
+ return {};
109
+ }
110
+ const concurrentBatchLimit = batchConfig
111
+ ? (0, exports.validateConcurrentBatchLimit)(`Trigger "${triggerLabel}"`, "batchConfig", batchConfig.concurrentBatchLimit)
112
+ : undefined;
113
+ return Object.assign({ triggerResolverDefaultBatchSize: batchConfig
114
+ ? (0, exports.validateBatchSize)(`Trigger "${triggerLabel}"`, "batchConfig", batchConfig.batchSize)
115
+ : 1 }, (concurrentBatchLimit !== undefined
116
+ ? { triggerResolverDefaultConcurrentBatchLimit: concurrentBatchLimit }
117
+ : {}));
118
+ };
119
+ const buildTriggerResolverFields = (resolver) => {
120
+ if (!resolver) {
121
+ return {};
122
+ }
123
+ const { resolveItems, getNextPaginationState } = resolver;
124
+ return Object.assign(Object.assign({}, (resolveItems
125
+ ? {
126
+ resolveTriggerItems: resolveItems,
127
+ hasResolveTriggerItems: true,
128
+ }
129
+ : {})), (getNextPaginationState
130
+ ? {
131
+ getNextPaginationState,
132
+ hasGetNextDiscoveryState: true,
133
+ }
134
+ : {}));
135
+ };
136
+ const buildOnDeployResolverFields = (resolver) => {
137
+ if (!resolver) {
138
+ return {};
139
+ }
140
+ const { resolveItems, getNextPaginationState } = resolver;
141
+ return Object.assign(Object.assign({}, (resolveItems
142
+ ? {
143
+ resolveOnDeployItems: resolveItems,
144
+ hasResolveOnDeployItems: true,
145
+ }
146
+ : {})), (getNextPaginationState
147
+ ? {
148
+ getOnDeployNextPaginationState: getNextPaginationState,
149
+ hasGetOnDeployNextDiscoveryState: true,
150
+ }
151
+ : {}));
152
+ };
70
153
  const convertInput = (key, definition) => {
71
154
  // Cast: the field union is wider than any single member; runtime guards below handle it.
72
155
  const _a = definition, { default: defaultValue, type, label, collection, inputs: childInputs, configurations } = _a, rest = __rest(_a, ["default", "type", "label", "collection", "inputs", "configurations"]);
@@ -139,7 +222,14 @@ const convertAction = (actionKey, _a, hooks) => {
139
222
  errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
140
223
  }) });
141
224
  };
142
- const convertTrigger = (triggerKey, trigger, hooks) => {
225
+ const convertTrigger = (triggerKey,
226
+ // `any` is load-bearing: the user-facing TriggerDefinition / PollingTriggerDefinition
227
+ // type their event-function fields (onInstanceDeploy, webhookLifecycleHandlers, etc.) over
228
+ // TInputs/TConfigVars/TPayload, while the wire-format ServerTrigger drops those generics.
229
+ // The `...trigger` spread in the result construction below would surface variance errors
230
+ // without these `any`s. The user-typed handlers are immediately replaced with
231
+ // createPerform-wrapped versions, so the loose input typing is safe in practice.
232
+ trigger, hooks) => {
143
233
  var _a;
144
234
  const { onInstanceDeploy, onInstanceDelete } = trigger;
145
235
  const webhookLifecycleHandlers = "webhookLifecycleHandlers" in trigger ? trigger.webhookLifecycleHandlers : undefined;
@@ -150,6 +240,26 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
150
240
  });
151
241
  const triggerInputCleaners = Object.entries(inputs).reduce((result, [key, value]) => (Object.assign(Object.assign({}, result), { [key]: (0, exports.cleanerFor)(value) })), {});
152
242
  let scheduleSupport = "scheduleSupport" in trigger ? trigger.scheduleSupport : "invalid";
243
+ const batchConfig = "batchConfig" in trigger ? trigger.batchConfig : undefined;
244
+ const triggerResolver = "triggerResolver" in trigger ? trigger.triggerResolver : undefined;
245
+ const triggerResolverSupport = "triggerResolverSupport" in trigger && trigger.triggerResolverSupport !== undefined
246
+ ? trigger.triggerResolverSupport
247
+ : triggerResolver
248
+ ? "valid"
249
+ : "invalid";
250
+ if (triggerResolverSupport === "required" && !triggerResolver) {
251
+ throw new Error(`Trigger "${trigger.display.label}" declares triggerResolverSupport "required" but is missing triggerResolver.`);
252
+ }
253
+ if (triggerResolverSupport === "invalid" && triggerResolver) {
254
+ throw new Error(`Trigger "${trigger.display.label}" declares triggerResolver but triggerResolverSupport is "invalid".`);
255
+ }
256
+ const onDeployPerform = "onDeployPerform" in trigger ? trigger.onDeployPerform : undefined;
257
+ const onDeployResolver = "onDeployResolver" in trigger ? trigger.onDeployResolver : undefined;
258
+ // On-deploy is presence-driven (no support flag): a trigger that defines an
259
+ // `onDeployResolver` must also define the `onDeployPerform` fire it batches.
260
+ if ((onDeployResolver === null || onDeployResolver === void 0 ? void 0 : onDeployResolver.resolveItems) && !onDeployPerform) {
261
+ throw new Error(`Trigger "${trigger.display.label}" declares onDeployResolver.resolveItems but is missing onDeployPerform.`);
262
+ }
153
263
  let convertedActionInputs = [];
154
264
  let performToUse;
155
265
  if ((0, PollingTriggerDefinition_1.isPollingTriggerDefinition)(trigger)) {
@@ -178,11 +288,11 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
178
288
  errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
179
289
  });
180
290
  }
181
- const result = Object.assign(Object.assign(Object.assign({}, trigger), { key: triggerKey, inputs: convertedTriggerInputs.concat(convertedActionInputs), perform: performToUse, scheduleSupport, synchronousResponseSupport: "synchronousResponseSupport" in trigger
291
+ const result = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (0, omit_1.default)(trigger, ["batchConfig", "triggerResolver", "onDeployResolver"])), { key: triggerKey, inputs: convertedTriggerInputs.concat(convertedActionInputs), perform: performToUse, scheduleSupport, synchronousResponseSupport: "synchronousResponseSupport" in trigger
182
292
  ? trigger.synchronousResponseSupport
183
293
  : scheduleSupport === "invalid"
184
294
  ? "valid"
185
- : "invalid" }), ((0, PollingTriggerDefinition_1.isPollingTriggerDefinition)(trigger) ? { isPollingTrigger: true } : {}));
295
+ : "invalid", triggerResolverSupport }), buildBatchDefaultField(trigger.display.label, triggerResolverSupport, !!(onDeployResolver === null || onDeployResolver === void 0 ? void 0 : onDeployResolver.resolveItems), batchConfig)), buildTriggerResolverFields(triggerResolver)), buildOnDeployResolverFields(onDeployResolver)), ((0, PollingTriggerDefinition_1.isPollingTriggerDefinition)(trigger) ? { isPollingTrigger: true } : {}));
186
296
  if (onInstanceDeploy) {
187
297
  result.onInstanceDeploy = (0, perform_1.createPerform)(onInstanceDeploy, {
188
298
  inputCleaners: triggerInputCleaners,
@@ -190,6 +300,13 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
190
300
  });
191
301
  result.hasOnInstanceDeploy = true;
192
302
  }
303
+ if (onDeployPerform) {
304
+ result.onDeployPerform = (0, perform_1.createPerform)(onDeployPerform, {
305
+ inputCleaners: triggerInputCleaners,
306
+ errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
307
+ });
308
+ result.hasOnDeployPerform = true;
309
+ }
193
310
  if (onInstanceDelete) {
194
311
  result.onInstanceDelete = (0, perform_1.createPerform)(onInstanceDelete, {
195
312
  inputCleaners: triggerInputCleaners,
@@ -8,7 +8,7 @@ export declare const convertConfigPages: (pages: ConfigPages | undefined, userLe
8
8
  /** Converts typed QueueConfig to legacy format with usesFifoQueue and concurrencyLimit. */
9
9
  export declare const convertQueueConfig: (queueConfig: QueueConfig) => StandardQueueConfig;
10
10
  /** Converts a Flow into the structure necessary for YAML generation. */
11
- export declare const convertFlow: <TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerPerformResult<TAllowsBranching, TPayload> = TriggerPerformResult<TAllowsBranching, TPayload>>(flow: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult>, componentRegistry: ComponentRegistry, referenceKey: string) => Record<string, unknown>;
11
+ export declare const convertFlow: <TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerPerformResult<TAllowsBranching, TPayload> = TriggerPerformResult<TAllowsBranching, TPayload>>(rawFlow: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult>, componentRegistry: ComponentRegistry, referenceKey: string) => Record<string, unknown>;
12
12
  /** Converts an input value to the expected server type by its collection type. */
13
13
  export declare const convertInputValue: (value: unknown, collectionType: CollectionType | undefined) => unknown;
14
14
  /** Converts a Config Var into the structure necessary for YAML generation. */
@@ -8,6 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
11
22
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
23
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
24
  };
@@ -29,6 +40,59 @@ const integration_1 = require("./integration");
29
40
  const perform_1 = require("./perform");
30
41
  exports.CONCURRENCY_LIMIT_MAX = 15;
31
42
  exports.CONCURRENCY_LIMIT_MIN = 2;
43
+ /**
44
+ * Default `resolveItems`: a batched `trigger`'s fires return their records under
45
+ * `payload.body.data` (the wrapper {@link normalizeBatchedFlow} builds writes them there),
46
+ * so extraction is just reading that array back. Authors never write this themselves.
47
+ */
48
+ const defaultResolveItems = (_context, result) => result.payload.body.data;
49
+ /**
50
+ * Default `getNextPaginationState`: a batched fire returns the next page's cursor as
51
+ * `paginationState`, which the wrapper {@link normalizeBatchedFlow} builds stamps onto
52
+ * `payload.paginationState`. Reading it back (defaulting to `null`) is the whole loop: a
53
+ * non-null value re-invokes the fire, `null` ends it. Authors never write this.
54
+ */
55
+ const defaultGetNextPaginationState = (_context, result) => { var _a; return (_a = result.payload.paginationState) !== null && _a !== void 0 ? _a : null; };
56
+ /**
57
+ * Expands a flow's batched `trigger` (built with `batchFlowTrigger`) into the flat
58
+ * `onTrigger`/`onDeployTrigger`/`triggerResolver`/`onDeployResolver` shape the rest of the
59
+ * conversion pipeline already understands. The trigger fires return `{ items, paginationState? }`;
60
+ * here we wrap each into a `TriggerPerformFunction` that emits `{ payload: { …payload, body: {
61
+ * data: items }, paginationState } }`, then synthesize the default `resolveItems` (reads the
62
+ * items back) and `getNextPaginationState` (reads the cursor back). Flows without a `trigger`
63
+ * pass through unchanged.
64
+ *
65
+ * Returns the same `Flow` type it received; the synthesized `triggerResolver`/`onDeployResolver`
66
+ * are wire-only fields (not on the author-facing `Flow`), read downstream via `"x" in flow` checks.
67
+ */
68
+ const normalizeBatchedFlow = (flow) => {
69
+ const trigger = "trigger" in flow ? flow.trigger : undefined;
70
+ if (!trigger) {
71
+ return flow;
72
+ }
73
+ const { onTrigger, onDeploy } = trigger;
74
+ // Wrap a batched fire (returns `{ items, paginationState?, response? }`) into a
75
+ // TriggerPerformFunction that emits the wire payload shape: items at `body.data` and the
76
+ // next-page cursor at `paginationState` (defaulting `null` to terminate the loop). The
77
+ // incoming payload's `paginationState` was already consumed by the fire, so overwriting it
78
+ // with the returned cursor is safe — `getNextPaginationState` reads it straight back.
79
+ const wrapFire = (fire) => (context, payload) => __awaiter(void 0, void 0, void 0, function* () {
80
+ const { items, paginationState, response } = yield fire(context, payload);
81
+ return Object.assign({ payload: Object.assign(Object.assign({}, payload), { body: { data: items, contentType: "application/json" }, paginationState: paginationState !== null && paginationState !== void 0 ? paginationState : null }) }, (response ? { response } : {}));
82
+ });
83
+ const _a = flow, { trigger: _omitTrigger } = _a, rest = __rest(_a, ["trigger"]);
84
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, rest), { onTrigger: wrapFire(onTrigger) }), (onDeploy ? { onDeployTrigger: wrapFire(onDeploy) } : {})), { triggerResolver: {
85
+ resolveItems: defaultResolveItems,
86
+ getNextPaginationState: defaultGetNextPaginationState,
87
+ } }), (onDeploy
88
+ ? {
89
+ onDeployResolver: {
90
+ resolveItems: defaultResolveItems,
91
+ getNextPaginationState: defaultGetNextPaginationState,
92
+ },
93
+ }
94
+ : {}));
95
+ };
32
96
  const convertIntegration = (definition) => {
33
97
  var _a, _b, _c;
34
98
  // Generate a unique reference key that will be used to reference the
@@ -94,7 +158,11 @@ const convertConfigPages = (pages, userLevelConfigured) => {
94
158
  }) })));
95
159
  };
96
160
  exports.convertConfigPages = convertConfigPages;
97
- const codeNativeIntegrationYaml = ({ name, description, category, documentation, version, labels, endpointType, triggerPreprocessFlowConfig, flows, configPages, userLevelConfigPages, scopedConfigVars, instanceProfile, componentRegistry = {}, }, referenceKey, configVars, metadata) => {
161
+ const codeNativeIntegrationYaml = ({ name, description, category, documentation, version, labels, endpointType, triggerPreprocessFlowConfig, flows: rawFlows, configPages, userLevelConfigPages, scopedConfigVars, instanceProfile, componentRegistry = {}, }, referenceKey, configVars, metadata) => {
162
+ // Expand any batched `trigger` (built with `batchFlowTrigger`) into the flat
163
+ // onTrigger/onDeployTrigger/triggerResolver/onDeployResolver shape the rest of this
164
+ // pipeline (convertFlow + the trigger reducer) already handles.
165
+ const flows = rawFlows.map((flow) => normalizeBatchedFlow(flow));
98
166
  // Find the preprocess flow config on the flow, if one exists.
99
167
  const preprocessFlows = flows.filter((flow) => flow.preprocessFlowConfig);
100
168
  // Do some validation of preprocess flow configs.
@@ -310,8 +378,11 @@ const convertFlowSchemas = (flowKey, schemas) => {
310
378
  }, {});
311
379
  };
312
380
  /** Converts a Flow into the structure necessary for YAML generation. */
313
- const convertFlow = (flow, componentRegistry, referenceKey) => {
381
+ const convertFlow = (rawFlow, componentRegistry, referenceKey) => {
314
382
  var _a;
383
+ // Expand a batched `trigger` into the flat shape this function serializes. Idempotent: a flow
384
+ // that already lacks `trigger` (including one pre-normalized by `convertIntegration`) is returned as-is.
385
+ const flow = normalizeBatchedFlow(rawFlow);
315
386
  const result = Object.assign({}, flow);
316
387
  result.onTrigger = undefined;
317
388
  result.trigger = undefined;
@@ -319,6 +390,7 @@ const convertFlow = (flow, componentRegistry, referenceKey) => {
319
390
  result.onInstanceDelete = undefined;
320
391
  result.webhookLifecycleHandlers = undefined;
321
392
  result.onExecution = undefined;
393
+ result.onDeployTrigger = undefined;
322
394
  result.preprocessFlowConfig = undefined;
323
395
  result.errorConfig = undefined;
324
396
  result.testApiKeys = undefined;
@@ -412,6 +484,30 @@ const convertFlow = (flow, componentRegistry, referenceKey) => {
412
484
  }
413
485
  : {}));
414
486
  }
487
+ const triggerResolver = "triggerResolver" in flow ? flow.triggerResolver : undefined;
488
+ const onDeployResolver = "onDeployResolver" in flow ? flow.onDeployResolver : undefined;
489
+ const batchConfig = "batchConfig" in flow ? flow.batchConfig : undefined;
490
+ // Resolver behaviors (resolveItems/getNextPaginationState) are serialized onto the
491
+ // synthesized trigger below. On the flow wire we emit only `triggerResolver`, the single
492
+ // config the platform reads (`trigger_resolver_batch_size` / `trigger_resolver_enabled`)
493
+ // and shares between the normal and on-deploy fires. `batchConfig`/`onDeployResolver` are
494
+ // author-side only — clear them out of the `{ ...flow }` spread.
495
+ result.triggerResolver = undefined;
496
+ result.onDeployResolver = undefined;
497
+ result.batchConfig = undefined;
498
+ if (triggerResolver || onDeployResolver) {
499
+ if (!batchConfig) {
500
+ throw new Error(`${flow.name} defines a triggerResolver/onDeployResolver but no batchConfig. Add \`batchConfig: { batchSize }\` to the flow.`);
501
+ }
502
+ if (onDeployResolver &&
503
+ (!("onDeployTrigger" in flow) || typeof flow.onDeployTrigger !== "function")) {
504
+ throw new Error(`${flow.name} declares onDeployResolver without onDeployTrigger. Set onDeployTrigger to handle the initial-deploy fire that the resolver fans out.`);
505
+ }
506
+ // `enabled: true` is required: for a "valid"-support trigger (which CNI synthesized
507
+ // triggers are) the platform only batches when the flow's resolver is enabled.
508
+ const concurrentBatchLimit = (0, convertComponent_1.validateConcurrentBatchLimit)(flow.name, "batchConfig", batchConfig.concurrentBatchLimit);
509
+ result.triggerResolver = Object.assign({ batchSize: (0, convertComponent_1.validateBatchSize)(flow.name, "batchConfig", batchConfig.batchSize), enabled: true }, (concurrentBatchLimit !== undefined ? { concurrentBatchLimit } : {}));
510
+ }
415
511
  const actionStep = {
416
512
  action: {
417
513
  key: flowFunctionKey(flow.name, "onExecution"),
@@ -695,7 +791,11 @@ function generateTriggerPerformFn(params) {
695
791
  case "standard":
696
792
  return (0, perform_1.createCNIPerform)({ componentRegistry, onTrigger });
697
793
  case "component-ref":
698
- return (0, perform_1.createCNIComponentRefPerform)({ componentRegistry, componentRef, onTrigger });
794
+ return (0, perform_1.createCNIComponentRefPerform)({
795
+ componentRegistry,
796
+ componentRef,
797
+ onTrigger,
798
+ });
699
799
  default:
700
800
  throw new Error(`Invalid trigger configuration detected: ${JSON.stringify(params, null, 2)}`);
701
801
  }
@@ -751,7 +851,9 @@ const convertOnExecution = (onExecution, componentRegistry) => (context, params)
751
851
  });
752
852
  /** Creates the structure necessary to import a Component as part of a
753
853
  * Code Native integration. */
754
- const codeNativeIntegrationComponent = ({ name, iconPath, description, flows = [], componentRegistry = {}, }, referenceKey, configVars) => {
854
+ const codeNativeIntegrationComponent = ({ name, iconPath, description, flows: rawFlows = [], componentRegistry = {}, }, referenceKey, configVars) => {
855
+ // Expand any batched `trigger` so the action/trigger reducers below see the flat shape.
856
+ const flows = rawFlows.map((flow) => normalizeBatchedFlow(flow));
755
857
  const convertedActions = flows.reduce((result, { name, onExecution }) => {
756
858
  const key = flowFunctionKey(name, "onExecution");
757
859
  return Object.assign(Object.assign({}, result), { [key]: {
@@ -764,7 +866,12 @@ const codeNativeIntegrationComponent = ({ name, iconPath, description, flows = [
764
866
  inputs: [],
765
867
  } });
766
868
  }, {});
767
- const convertedTriggers = flows.reduce((result, { name, onTrigger, onInstanceDeploy, onInstanceDelete, webhookLifecycleHandlers, schedule, triggerType, }) => {
869
+ const convertedTriggers = flows.reduce((result, flow) => {
870
+ var _a;
871
+ const { name, onTrigger, onInstanceDeploy, onInstanceDelete, webhookLifecycleHandlers, schedule, triggerType, onDeployTrigger, } = flow;
872
+ // `batchConfig`/`triggerResolver`/`onDeployResolver` are wire-only fields synthesized by
873
+ // `normalizeBatchedFlow`; they aren't on the author-facing `Flow` type, so read via cast.
874
+ const { batchConfig, triggerResolver, onDeployResolver } = flow;
768
875
  if (!flowUsesWrapperTrigger({
769
876
  onTrigger,
770
877
  onInstanceDelete,
@@ -799,26 +906,45 @@ const codeNativeIntegrationComponent = ({ name, iconPath, description, flows = [
799
906
  const deployFn = generateTriggerEventWrapperFn(ref, onTrigger, "onInstanceDeploy", componentRegistry, onInstanceDeploy);
800
907
  const webhookCreateFn = generateTriggerEventWrapperFn(ref, onTrigger, "webhookCreate", componentRegistry, webhookLifecycleHandlers === null || webhookLifecycleHandlers === void 0 ? void 0 : webhookLifecycleHandlers.create);
801
908
  const webhookDeleteFn = generateTriggerEventWrapperFn(ref, onTrigger, "webhookDelete", componentRegistry, webhookLifecycleHandlers === null || webhookLifecycleHandlers === void 0 ? void 0 : webhookLifecycleHandlers.delete);
802
- return Object.assign(Object.assign({}, result), { [key]: {
803
- key,
804
- display: {
909
+ return Object.assign(Object.assign({}, result), { [key]: Object.assign(Object.assign(Object.assign(Object.assign({ key, display: {
805
910
  label: `${name} - onTrigger`,
806
911
  description: "The function that will be executed by the flow to return an HTTP response.",
807
- },
808
- perform: performFn,
809
- onInstanceDeploy: deployFn,
810
- hasOnInstanceDeploy: !!deployFn,
811
- onInstanceDelete: deleteFn,
812
- hasOnInstanceDelete: !!deleteFn,
813
- webhookCreate: webhookCreateFn,
814
- hasWebhookCreateFunction: !!webhookCreateFn,
815
- webhookDelete: webhookDeleteFn,
816
- hasWebhookDeleteFunction: !!webhookDeleteFn,
817
- inputs: wrapperTriggerInputsFromReference(onTrigger, componentRegistry),
818
- scheduleSupport: triggerType === "polling" ? "required" : "valid",
819
- synchronousResponseSupport: "valid",
820
- isPollingTrigger: triggerType === "polling",
821
- } });
912
+ }, perform: performFn, onInstanceDeploy: deployFn, hasOnInstanceDeploy: !!deployFn, onInstanceDelete: deleteFn, hasOnInstanceDelete: !!deleteFn, webhookCreate: webhookCreateFn, hasWebhookCreateFunction: !!webhookCreateFn, webhookDelete: webhookDeleteFn, hasWebhookDeleteFunction: !!webhookDeleteFn, inputs: wrapperTriggerInputsFromReference(onTrigger, componentRegistry), scheduleSupport: triggerType === "polling" ? "required" : "valid", synchronousResponseSupport: "valid", isPollingTrigger: triggerType === "polling", triggerResolverSupport: triggerResolver ? "valid" : "invalid" }, (triggerResolver || onDeployResolver
913
+ ? Object.assign({ triggerResolverDefaultBatchSize: (_a = batchConfig === null || batchConfig === void 0 ? void 0 : batchConfig.batchSize) !== null && _a !== void 0 ? _a : 1 }, ((batchConfig === null || batchConfig === void 0 ? void 0 : batchConfig.concurrentBatchLimit) !== undefined
914
+ ? {
915
+ triggerResolverDefaultConcurrentBatchLimit: batchConfig.concurrentBatchLimit,
916
+ }
917
+ : {})) : {})), (triggerResolver
918
+ ? Object.assign(Object.assign({}, (triggerResolver.resolveItems
919
+ ? {
920
+ resolveTriggerItems: triggerResolver.resolveItems,
921
+ hasResolveTriggerItems: true,
922
+ }
923
+ : {})), (triggerResolver.getNextPaginationState
924
+ ? {
925
+ getNextPaginationState: triggerResolver.getNextPaginationState,
926
+ hasGetNextDiscoveryState: true,
927
+ }
928
+ : {})) : {})), (onDeployTrigger
929
+ ? {
930
+ onDeployPerform: (0, perform_1.createCNIPerform)({
931
+ componentRegistry,
932
+ onTrigger: onDeployTrigger,
933
+ }),
934
+ hasOnDeployPerform: true,
935
+ }
936
+ : {})), (onDeployResolver
937
+ ? Object.assign(Object.assign({}, (onDeployResolver.resolveItems
938
+ ? {
939
+ resolveOnDeployItems: onDeployResolver.resolveItems,
940
+ hasResolveOnDeployItems: true,
941
+ }
942
+ : {})), (onDeployResolver.getNextPaginationState
943
+ ? {
944
+ getOnDeployNextPaginationState: onDeployResolver.getNextPaginationState,
945
+ hasGetOnDeployNextDiscoveryState: true,
946
+ }
947
+ : {})) : {})) });
822
948
  }, {});
823
949
  const convertedDataSources = Object.entries(configVars).reduce((result, [key, configVar]) => {
824
950
  if (!(0, types_1.isDataSourceDefinitionConfigVar)(configVar)) {
@@ -112,8 +112,8 @@ interface HttpResponse {
112
112
  headers?: Record<string, string>;
113
113
  body?: string;
114
114
  }
115
- interface TriggerBaseResult {
116
- payload: TriggerPayload;
115
+ interface TriggerBaseResult<TPayload extends TriggerPayload = TriggerPayload> {
116
+ payload: TPayload;
117
117
  response?: HttpResponse;
118
118
  instanceState?: Record<string, unknown>;
119
119
  crossFlowState?: Record<string, unknown>;
@@ -122,12 +122,23 @@ interface TriggerBaseResult {
122
122
  failed?: boolean;
123
123
  error?: Record<string, unknown>;
124
124
  }
125
- interface TriggerBranchingResult extends TriggerBaseResult {
125
+ interface TriggerBranchingResult<TPayload extends TriggerPayload = TriggerPayload> extends TriggerBaseResult<TPayload> {
126
126
  branch: string;
127
127
  }
128
- export type TriggerResult = TriggerBranchingResult | TriggerBaseResult | undefined;
128
+ export type TriggerResult<TPayload extends TriggerPayload = TriggerPayload> = TriggerBranchingResult<TPayload> | TriggerBaseResult<TPayload> | undefined;
129
129
  export type TriggerEventFunctionResult = TriggerEventFunctionReturn | void;
130
130
  export type TriggerEventFunction = (context: ActionContext, params: Record<string, unknown>) => Promise<TriggerEventFunctionResult>;
131
+ /**
132
+ * Wire format the platform expects for a trigger. Note: function references
133
+ * (perform, resolveTriggerItems, getNextPaginationState, ...) don't survive JSON
134
+ * serialization, so each callback has a paired `hasXxx: boolean` flag the
135
+ * server reads to detect presence. Keep the flag and its callback in sync.
136
+ *
137
+ * The on-deploy fire is named asymmetrically by intent: CNI flow authors set
138
+ * `onDeployTrigger` (sibling to `onTrigger`), component-trigger authors set
139
+ * `onDeployPerform` (sibling to `perform`), and both flatten to
140
+ * `onDeployPerform` here on the wire.
141
+ */
131
142
  export interface Trigger<TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerPerformResult<TAllowsBranching, TPayload> = TriggerPerformResult<TAllowsBranching, TPayload>> {
132
143
  key: string;
133
144
  display: DisplayDefinition & {
@@ -155,6 +166,23 @@ export interface Trigger<TInputs extends Inputs, TActionInputs extends Inputs, T
155
166
  hasWebhookDeleteFunction?: boolean;
156
167
  scheduleSupport: TriggerOptionChoice;
157
168
  synchronousResponseSupport: TriggerOptionChoice;
169
+ triggerResolverSupport?: TriggerOptionChoice;
170
+ /**
171
+ * The single default batch size shared by the trigger and on-deploy resolvers. The
172
+ * platform reads only this field for both paths; there is no separate on-deploy default.
173
+ */
174
+ triggerResolverDefaultBatchSize?: number;
175
+ triggerResolverDefaultConcurrentBatchLimit?: number;
176
+ resolveTriggerItems?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<TPayload>) => unknown[];
177
+ hasResolveTriggerItems?: boolean;
178
+ getNextPaginationState?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<TPayload>) => Record<string, unknown> | null;
179
+ hasGetNextDiscoveryState?: boolean;
180
+ onDeployPerform?: TriggerPerformFunction<TInputs, TConfigVars, TAllowsBranching, TResult> | PollingTriggerPerformFunction<TInputs, TActionInputs, TConfigVars, TPayload, TAllowsBranching, TResult> | CNIPollingPerformFunction<TInputs, TConfigVars, TPayload, TAllowsBranching> | ComponentRefTriggerPerformFunction<TInputs, TConfigVars>;
181
+ hasOnDeployPerform?: boolean;
182
+ resolveOnDeployItems?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<TPayload>) => unknown[];
183
+ hasResolveOnDeployItems?: boolean;
184
+ getOnDeployNextPaginationState?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<TPayload>) => Record<string, unknown> | null;
185
+ hasGetOnDeployNextDiscoveryState?: boolean;
158
186
  examplePayload?: unknown;
159
187
  isCommonTrigger?: boolean;
160
188
  isPollingTrigger?: boolean;
package/dist/testing.d.ts CHANGED
@@ -217,7 +217,7 @@ type ToTestValues<TConfigVars extends ConfigVarResultCollection> = {
217
217
  * expect(result.data).toBeDefined();
218
218
  * });
219
219
  */
220
- export declare const invokeFlow: <TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TConfigVarValues extends TestConfigVarValues = ToTestValues<TConfigVars>, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>>(flow: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult>, { configVars, context, payload, }?: {
220
+ export declare const invokeFlow: <TInputs extends Inputs, TActionInputs extends Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TConfigVarValues extends TestConfigVarValues = ToTestValues<TConfigVars>, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends InvokeTriggerResult<TAllowsBranching, TPayload> = InvokeTriggerResult<TAllowsBranching, TPayload>>(flow: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, any, any, any>, { configVars, context, payload, }?: {
221
221
  configVars?: TConfigVarValues;
222
222
  context?: Partial<ActionContext<TConfigVars>>;
223
223
  payload?: Partial<TriggerPayload>;
package/dist/testing.js CHANGED
@@ -408,7 +408,11 @@ const createConfigVars = (values) => {
408
408
  * expect(result.data).toBeDefined();
409
409
  * });
410
410
  */
411
- const invokeFlow = (flow_1, ...args_1) => __awaiter(void 0, [flow_1, ...args_1], void 0, function* (flow, { configVars, context, payload, } = {}) {
411
+ const invokeFlow = (flow_1, ...args_1) => __awaiter(void 0, [flow_1, ...args_1], void 0, function* (
412
+ // `any` for TItem/TPaginationState/TTriggerPayload: the tester accepts a flow with any
413
+ // resolver item and cursor typing; it drives the flow dynamically and does not rely on
414
+ // the precise `onExecution` param type.
415
+ flow, { configVars, context, payload, } = {}) {
412
416
  const realizedConfigVars = createConfigVars(configVars);
413
417
  const realizedContext = createActionContext(Object.assign(Object.assign({}, context), { configVars: realizedConfigVars }));
414
418
  const realizedPayload = Object.assign(Object.assign({}, (0, exports.defaultTriggerPayload)()), payload);
@@ -4,9 +4,11 @@ import type { ComponentRegistry, ConfigVarExpression, TriggerReference, ValueExp
4
4
  import type { ConfigPages, UserLevelConfigPages } from "./ConfigPages";
5
5
  import type { ConfigVars } from "./ConfigVars";
6
6
  import type { FlowDefinitionFlowSchema } from "./FlowSchemas";
7
+ import type { HttpResponse } from "./HttpResponse";
7
8
  import type { Inputs } from "./Inputs";
8
9
  import type { PollingTriggerPerformFunction } from "./PollingTriggerDefinition";
9
10
  import type { ScopedConfigVarMap } from "./ScopedConfigVars";
11
+ import type { BatchConfig } from "./TriggerDefinition";
10
12
  import type { TriggerEventFunction } from "./TriggerEventFunction";
11
13
  import type { TriggerPayload } from "./TriggerPayload";
12
14
  import type { TriggerPerformFunction } from "./TriggerPerformFunction";
@@ -46,8 +48,13 @@ export type IntegrationDefinition<TInputs extends Inputs = Inputs, TActionInputs
46
48
  /**
47
49
  * Flows for this integration. See
48
50
  * https://prismatic.io/docs/integrations/code-native/flows/
51
+ *
52
+ * The trailing `any`s for `TItem`/`TPaginationState`/`TTriggerPayload` let flows with
53
+ * different resolver item and cursor types live in one array. `integration` only holds
54
+ * and serializes these flows (it never invokes `onExecution`), so the precise — and
55
+ * per-flow distinct — `onExecution` param type does not need to be preserved here.
49
56
  */
50
- flows: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult>[];
57
+ flows: Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, any, any, any>[];
51
58
  /**
52
59
  * Config wizard pages for this integration. See
53
60
  * https://prismatic.io/docs/integrations/code-native/config-wizard/
@@ -70,12 +77,64 @@ export type IntegrationDefinition<TInputs extends Inputs = Inputs, TActionInputs
70
77
  */
71
78
  componentRegistry?: ComponentRegistry;
72
79
  };
73
- export type FlowOnExecution<TTriggerPayload extends TriggerPayload> = ActionPerformFunction<{
80
+ /**
81
+ * The trigger payload as `onExecution` sees it. When a `triggerResolver` is in
82
+ * play, the platform replaces `body.data` with this execution's batch slice —
83
+ * a single `TItem` when `batchSize` is 1, otherwise a `TItem[]`. When no item
84
+ * type is known (`TItem` defaults to `unknown`), the payload is left untouched.
85
+ */
86
+ export type BatchedTriggerPayload<TPayload extends TriggerPayload, TItem> = unknown extends TItem ? TPayload : Omit<TPayload, "body"> & {
87
+ body: {
88
+ data: TItem | TItem[];
89
+ contentType?: string;
90
+ };
91
+ };
92
+ /**
93
+ * The page of records a batched trigger fire (`onTrigger`/`onDeploy`) returns. The author
94
+ * returns the items plus, when paginating, the cursor for the next page. spectral wraps the
95
+ * items into the wire trigger payload (`body.data`), carries the `paginationState` to the next
96
+ * round, and synthesizes the resolver that reads both back. There is no separate pagination
97
+ * callback to define.
98
+ */
99
+ export interface BatchedTriggerReturn<TItem, TPaginationState extends Record<string, unknown> = Record<string, unknown>> {
100
+ /** The records produced by this trigger fire. Chunked into batches of the flow's `batchConfig.batchSize`. */
101
+ items: TItem[];
102
+ /**
103
+ * Cursor for the next page. Return it to paginate — the fire is re-invoked with this object on
104
+ * `payload.paginationState`. Omit or return `null` to stop. Compute it here from the fetch
105
+ * response, where the next-page token is still in scope.
106
+ */
107
+ paginationState?: TPaginationState | null;
108
+ /** Optional HTTP response to the request that invoked the integration. */
109
+ response?: HttpResponse;
110
+ }
111
+ /**
112
+ * A batched trigger built by {@link batchFlowTrigger}. Bundles the normal and on-deploy trigger
113
+ * fires, each returning `{ items, paginationState? }`. The two type parameters — supplied
114
+ * explicitly to `batchFlowTrigger<TItem, TPaginationState>(...)` — flow through to the rest of
115
+ * the flow: `TItem` types `onExecution`'s `params.onTrigger.results.body.data`, and
116
+ * `TPaginationState` types both `payload.paginationState` (read on the way in) and the
117
+ * `paginationState` each fire returns (the next page's cursor).
118
+ */
119
+ export interface BatchTrigger<TItem, TPaginationState extends Record<string, unknown> = Record<string, unknown>> {
120
+ /**
121
+ * The trigger function for this flow. Fetches a page of records and returns them as `items`,
122
+ * plus the next page's `paginationState` to paginate (omit/`null` to stop). Read the cursor for
123
+ * the current page from `payload.paginationState`.
124
+ */
125
+ onTrigger: (context: ActionContext<ConfigVars>, payload: TriggerPayload<TPaginationState>) => Promise<BatchedTriggerReturn<TItem, TPaginationState>>;
126
+ /**
127
+ * The on-deploy trigger fire, run once on initial instance deploy. Same shape as `onTrigger`;
128
+ * return `paginationState` to paginate the backfill.
129
+ */
130
+ onDeploy?: (context: ActionContext<ConfigVars>, payload: TriggerPayload<TPaginationState>) => Promise<BatchedTriggerReturn<TItem, TPaginationState>>;
131
+ }
132
+ export type FlowOnExecution<TTriggerPayload extends TriggerPayload, TItem = unknown> = ActionPerformFunction<{
74
133
  onTrigger: {
75
134
  type: "data";
76
135
  label: string;
77
136
  clean: (value: unknown) => {
78
- results: TTriggerPayload;
137
+ results: BatchedTriggerPayload<TTriggerPayload, TItem>;
79
138
  };
80
139
  };
81
140
  }, ConfigVars, {
@@ -85,8 +144,56 @@ export type FlowExecutionContext = ActionContext<ConfigVars, {
85
144
  [Key in keyof ComponentRegistry]: ComponentRegistry[Key]["actions"];
86
145
  }>;
87
146
  export type FlowExecutionContextActions = FlowExecutionContext["components"];
147
+ /**
148
+ * The batch-discriminated fields of a flow. A flow either batches or it does not, and
149
+ * the presence of `batchConfig` is the discriminant TypeScript uses to choose a member:
150
+ *
151
+ * - {@link BatchFields}: `batchConfig` is present, which requires a batched `trigger`
152
+ * (built with {@link batchFlowTrigger}). The flat `onTrigger`/`onDeployTrigger` are
153
+ * forbidden — the trigger fires live inside the `trigger` object.
154
+ * - {@link NonBatchFields}: `batchConfig`/`trigger` are absent; the flow uses the plain
155
+ * `onTrigger`/`onDeployTrigger` on its variant.
156
+ *
157
+ * Discrimination is structural — it depends only on whether the object literal you write
158
+ * has a `batchConfig` key — so it works without TypeScript having to infer any type
159
+ * parameter from the value. The batched-vs-not shape of `onExecution`'s payload is handled
160
+ * separately, on `FlowBase`, via `TItem`: when a `trigger` is present `TItem` is inferred
161
+ * (so `onExecution` sees `TItem | TItem[]`); otherwise it stays `unknown` and the payload
162
+ * is left untouched (see {@link BatchedTriggerPayload}). Keeping `onExecution` out of this
163
+ * union is deliberate — a function property living in a union member loses contextual
164
+ * parameter typing, which would make `onExecution`'s `context`/`params` implicitly `any`.
165
+ */
166
+ interface BatchFields<TItem, TPaginationState extends Record<string, unknown>> {
167
+ /**
168
+ * Batch-dispatch config for this flow. Items returned by the `trigger`'s fires are chunked
169
+ * into batches of `batchSize`. For a CNI flow this value is authoritative (what's written
170
+ * here is what's used). Its presence requires a batched `trigger`.
171
+ */
172
+ batchConfig: BatchConfig;
173
+ /**
174
+ * The batched trigger for this flow, built with {@link batchFlowTrigger}. Its fires
175
+ * (`onTrigger`/`onDeploy`) return just `items`; spectral synthesizes the resolver that
176
+ * extracts them and maps the pagination callbacks. Required when `batchConfig` is set.
177
+ */
178
+ trigger: BatchTrigger<TItem, TPaginationState>;
179
+ /** A batched flow's trigger fires live inside `trigger`; the flat `onTrigger` is forbidden. */
180
+ onTrigger?: never;
181
+ /** A batched flow's on-deploy fire lives inside `trigger`; the flat `onDeployTrigger` is forbidden. */
182
+ onDeployTrigger?: never;
183
+ }
184
+ interface NonBatchFields {
185
+ /** A non-batching flow may not declare batch config. */
186
+ batchConfig?: never;
187
+ /** A non-batching flow may not declare a batched trigger. */
188
+ trigger?: never;
189
+ }
190
+ /**
191
+ * The discriminated union coupling `batchConfig` and the batched `trigger`. Intersected
192
+ * into each flow variant at {@link Flow}.
193
+ */
194
+ type BatchDiscriminant<TItem, TPaginationState extends Record<string, unknown>> = BatchFields<TItem, TPaginationState> | NonBatchFields;
88
195
  /** Base properties shared by all flow types. */
89
- interface FlowBase<TTriggerPayload extends TriggerPayload = TriggerPayload> {
196
+ interface FlowBase<TTriggerPayload extends TriggerPayload = TriggerPayload, TItem = unknown> {
90
197
  /** The unique name for this flow. */
91
198
  name: string;
92
199
  /** A unique, unchanging value that is used to maintain identity for the flow even if the name changes. */
@@ -143,23 +250,33 @@ interface FlowBase<TTriggerPayload extends TriggerPayload = TriggerPayload> {
143
250
  /** Function to execute for webhook teardown. */
144
251
  delete: TriggerEventFunction<Inputs, ConfigVars>;
145
252
  };
146
- /** Specifies the main function for this flow which is run when this flow is invoked. */
147
- onExecution: FlowOnExecution<TTriggerPayload>;
253
+ /**
254
+ * Specifies the main function for this flow which is run when this flow is invoked.
255
+ * When the flow batches (has a `triggerResolver`/`onDeployResolver`, so `TItem` is
256
+ * inferred), `params.onTrigger.results.body.data` is typed as the resolved item type
257
+ * (`TItem | TItem[]`); otherwise the trigger payload is left untouched.
258
+ */
259
+ onExecution: FlowOnExecution<TTriggerPayload, TItem>;
148
260
  }
149
261
  export type StandardTriggerType = "standard";
150
262
  /** A standard flow with a webhook or scheduled trigger (non-polling). */
151
- interface StandardFlow<TInputs extends Inputs = Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = false, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload> extends FlowBase<TTriggerPayload> {
263
+ interface StandardFlow<TInputs extends Inputs = Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = false, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> extends FlowBase<TTriggerPayload, TItem> {
152
264
  triggerType?: StandardTriggerType;
153
265
  /** Schedule configuration that defines the frequency with which this flow will be automatically executed. */
154
266
  schedule?: (ValueExpression<string> | ConfigVarExpression) & {
155
267
  timezone?: string;
156
268
  };
157
269
  /** Specifies the trigger function for this flow, which returns a payload and optional HTTP response. */
158
- onTrigger?: TriggerReference | TriggerPerformFunction<TInputs, ConfigVars, TAllowsBranching, TResult>;
270
+ onTrigger?: TriggerReference | TriggerPerformFunction<TInputs, ConfigVars, TAllowsBranching, TResult, TPaginationState>;
271
+ /**
272
+ * Function to execute on initial instance deploy, in addition to (and independent of) `onTrigger`.
273
+ * Typically used to backfill baseline records for systems whose webhooks only emit future events.
274
+ */
275
+ onDeployTrigger?: TriggerPerformFunction<TInputs, ConfigVars, TAllowsBranching, TResult, TPaginationState>;
159
276
  }
160
277
  export type PollingTriggerType = "polling";
161
278
  /** A polling flow that runs on a schedule and has access to polling context (getState/setState). */
162
- interface PollingFlow<TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload> extends FlowBase<TTriggerPayload> {
279
+ interface PollingFlow<TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> extends FlowBase<TTriggerPayload, TItem> {
163
280
  /**
164
281
  * Type of trigger for this flow. A "polling" trigger runs on a schedule
165
282
  * and can use context.polling.* functions. Requires schedule to be set.
@@ -173,9 +290,14 @@ interface PollingFlow<TInputs extends Inputs, TActionInputs extends Inputs, TPay
173
290
  * Specifies the trigger function for this flow.
174
291
  */
175
292
  onTrigger: PollingTriggerPerformFunction<TInputs, TActionInputs, ConfigVars, TPayload, TAllowsBranching, TResult>;
293
+ /**
294
+ * Function to execute on initial instance deploy, in addition to (and independent of) `onTrigger`.
295
+ * Typically used to backfill baseline records for systems whose webhooks only emit future events.
296
+ */
297
+ onDeployTrigger?: TriggerPerformFunction<TInputs, ConfigVars, TAllowsBranching, TResult, TPaginationState>;
176
298
  }
177
299
  /** Defines attributes of a flow of a code-native integration. */
178
- export type Flow<TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload> = StandardFlow<TInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload> | PollingFlow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload>;
300
+ export type Flow<TInputs extends Inputs, TActionInputs extends Inputs, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TTriggerPayload extends TriggerPayload = TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> = (StandardFlow<TInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TPaginationState> & BatchDiscriminant<TItem, TPaginationState>) | (PollingFlow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TPaginationState> & BatchDiscriminant<TItem, TPaginationState>);
179
301
  export type FlowTriggerType = PollingTriggerType | StandardTriggerType;
180
302
  /** Defines attributes of a Preprocess flow Configuration used by a flow of an integration. */
181
303
  export type PreprocessFlowConfig = {
@@ -4,6 +4,7 @@ import type { ActionContext } from "./ActionPerformFunction";
4
4
  import type { ActionPerformReturn } from "./ActionPerformReturn";
5
5
  import type { ActionDisplayDefinition } from "./DisplayDefinition";
6
6
  import type { ConfigVarResultCollection, Inputs } from "./Inputs";
7
+ import type { BatchConfig, OnDeployDecl, TriggerResolverDecl } from "./TriggerDefinition";
7
8
  import type { TriggerEventFunction } from "./TriggerEventFunction";
8
9
  import type { TriggerPayload } from "./TriggerPayload";
9
10
  import type { TriggerResult } from "./TriggerResult";
@@ -18,11 +19,21 @@ export type PollingTriggerPerformFunction<TInputs extends Inputs, TActionInputs
18
19
  /**
19
20
  * PollingTriggerDefinition is the type of the object that is passed in to `pollingTrigger` function to
20
21
  * define a component trigger.
22
+ *
23
+ * Composed from `PollingTriggerDefinitionBase` plus `TriggerResolverDecl` (resolver support)
24
+ * and `OnDeployDecl` (the on-deploy perform/resolver relationship), enforced at the type level.
21
25
  */
22
- export interface PollingTriggerDefinition<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TActionInputs extends Inputs = Inputs, TAction extends ActionDefinition<TActionInputs> = ActionDefinition<TActionInputs>, TCombinedInputs extends TInputs & TActionInputs = TInputs & TActionInputs> {
26
+ export type PollingTriggerDefinition<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TActionInputs extends Inputs = Inputs, TAction extends ActionDefinition<TActionInputs> = ActionDefinition<TActionInputs>, TCombinedInputs extends TInputs & TActionInputs = TInputs & TActionInputs> = PollingTriggerDefinitionBase<TInputs, TConfigVars, TPayload, TAllowsBranching, TResult, TActionInputs, TAction, TCombinedInputs> & TriggerResolverDecl<TConfigVars, TPayload> & OnDeployDecl<TInputs, TConfigVars, TPayload, TAllowsBranching, TResult>;
27
+ interface PollingTriggerDefinitionBase<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TPayload> = TriggerResult<TAllowsBranching, TPayload>, TActionInputs extends Inputs = Inputs, TAction extends ActionDefinition<TActionInputs> = ActionDefinition<TActionInputs>, TCombinedInputs extends TInputs & TActionInputs = TInputs & TActionInputs> {
23
28
  triggerType?: "polling";
24
29
  /** Defines how the Action is displayed in the Prismatic interface. */
25
30
  display: ActionDisplayDefinition;
31
+ /**
32
+ * Default batch-dispatch config shared by `triggerResolver` and `onDeployResolver`.
33
+ * Required when this trigger declares either (a `triggerResolver` with
34
+ * `triggerResolverSupport` `"valid"`/`"required"`, or an `onDeployResolver`).
35
+ */
36
+ batchConfig?: BatchConfig;
26
37
  /** Defines your trigger's polling behavior. */
27
38
  pollAction?: TAction;
28
39
  /** Function to perform when this Trigger is invoked. A default perform will be provided for most polling triggers but defining this allows for custom behavior. */
@@ -39,3 +50,4 @@ export interface PollingTriggerDefinition<TInputs extends Inputs = Inputs, TConf
39
50
  examplePayload?: Awaited<ReturnType<this["perform"]>>;
40
51
  }
41
52
  export declare const isPollingTriggerDefinition: (ref: unknown) => ref is PollingTriggerDefinition;
53
+ export {};
@@ -1,22 +1,119 @@
1
+ import type { ActionContext } from "./ActionPerformFunction";
1
2
  import type { ActionDisplayDefinition } from "./DisplayDefinition";
2
3
  import type { ConfigVarResultCollection, Inputs } from "./Inputs";
3
4
  import type { TriggerEventFunction } from "./TriggerEventFunction";
4
5
  import type { TriggerPayload } from "./TriggerPayload";
5
6
  import type { TriggerPerformFunction } from "./TriggerPerformFunction";
6
- import type { TriggerResult } from "./TriggerResult";
7
+ import type { TriggerBaseResult, TriggerResult } from "./TriggerResult";
8
+ /**
9
+ * Encodes the relationship between `triggerResolverSupport` and `triggerResolver`:
10
+ * - absent or `"invalid"`: no resolver allowed
11
+ * - `"valid"`: resolver optional
12
+ * - `"required"`: resolver required
13
+ *
14
+ * `triggerResolver` is the resolver *behavior* only; batch sizing comes from the
15
+ * trigger's shared `batchConfig` (see `TriggerDefinition`).
16
+ */
17
+ export type TriggerResolverDecl<TConfigVars extends ConfigVarResultCollection, TPayload extends TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> = {
18
+ triggerResolverSupport?: "invalid" | undefined;
19
+ triggerResolver?: undefined;
20
+ } | {
21
+ triggerResolverSupport: "valid";
22
+ triggerResolver?: TriggerResolverBehavior<TConfigVars, TPayload, TItem, TPaginationState>;
23
+ } | {
24
+ triggerResolverSupport: "required";
25
+ triggerResolver: TriggerResolverBehavior<TConfigVars, TPayload, TItem, TPaginationState>;
26
+ };
27
+ /**
28
+ * Encodes the relationship between `onDeployPerform` and `onDeployResolver`. On-deploy is
29
+ * presence-driven — there is no separate support flag: a trigger fires on deploy if it
30
+ * defines `onDeployPerform`, and batches that fire if it also defines an `onDeployResolver`.
31
+ * An `onDeployResolver` therefore requires an `onDeployPerform`.
32
+ *
33
+ * `onDeployPerform` is the component-trigger sibling to `perform`. A CNI flow names the
34
+ * same on-deploy fire `onDeployTrigger` (sibling to its `onTrigger`); both flatten to
35
+ * `onDeployPerform` on the wire.
36
+ */
37
+ export type OnDeployDecl<TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TPayload extends TriggerPayload, TAllowsBranching extends boolean, TResult extends TriggerResult<TAllowsBranching, TPayload>, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> = {
38
+ onDeployPerform?: undefined;
39
+ onDeployResolver?: undefined;
40
+ } | {
41
+ onDeployPerform: TriggerPerformFunction<TInputs, TConfigVars, TAllowsBranching, TResult>;
42
+ onDeployResolver?: TriggerResolverBehavior<TConfigVars, TPayload, TItem, TPaginationState>;
43
+ };
7
44
  declare const optionChoices: readonly ["invalid", "valid", "required"];
8
45
  export type TriggerOptionChoice = (typeof optionChoices)[number];
9
46
  export declare const TriggerOptionChoices: TriggerOptionChoice[];
47
+ /**
48
+ * The batching/pagination behavior shared by every resolver surface — component
49
+ * triggers (`TriggerResolver`), CNI flows (`TriggerResolverConfig`), and the
50
+ * on-deploy variants. Defining it once keeps the contract from drifting across
51
+ * those surfaces.
52
+ *
53
+ * The two type variables ARE the data that flows through the batch chain:
54
+ *
55
+ * perform ──▶ resolveItems ──▶ [batch of TItem] ──▶ onExecution
56
+ * │
57
+ * └─ getNextPaginationState ──▶ payload.paginationState ──▶ next perform
58
+ *
59
+ * @typeParam TItem - element produced by `resolveItems`. With `batchSize: 1`
60
+ * each execution receives one `TItem` as its trigger data; with `batchSize > 1`
61
+ * it receives a `TItem[]` slice.
62
+ * @typeParam TPaginationState - the pagination cursor. Whatever
63
+ * `getNextPaginationState` returns is what the next round reads back on
64
+ * `payload.paginationState` (see {@link TriggerPayload}). The `result.payload`
65
+ * passed to both callbacks has its `paginationState` narrowed to this type, so
66
+ * the cursor round-trip is checked end to end.
67
+ */
68
+ export interface TriggerResolverBehavior<TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TPayload extends TriggerPayload = TriggerPayload, TItem = unknown, TPaginationState extends Record<string, unknown> = Record<string, unknown>> {
69
+ /** Extracts the items to dispatch from one trigger result. Receives the same context as the trigger's perform function. With `batchSize: 1` each item is delivered to its own execution; with `batchSize > 1` items are grouped into `TItem[]` slices. */
70
+ resolveItems?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<WithPaginationState<TPayload, TPaginationState>>) => TItem[];
71
+ /** Returns the cursor for the next page, or `null` to stop. A non-null return re-invokes the trigger with this object stamped onto `payload.paginationState`. */
72
+ getNextPaginationState?: (context: ActionContext<TConfigVars>, result: TriggerBaseResult<WithPaginationState<TPayload, TPaginationState>>) => TPaginationState | null;
73
+ }
74
+ /**
75
+ * A trigger payload with its `paginationState` field narrowed to `TPaginationState`,
76
+ * leaving every other field of `TPayload` intact. Lets a resolver type the cursor
77
+ * it reads back independently of how the base payload was parameterized.
78
+ */
79
+ export type WithPaginationState<TPayload extends TriggerPayload, TPaginationState extends Record<string, unknown>> = Omit<TPayload, "paginationState"> & {
80
+ paginationState?: TPaginationState;
81
+ };
82
+ /**
83
+ * The single batch-dispatch config shared by a trigger's `triggerResolver` and
84
+ * `onDeployResolver` — they always batch the same way. One place for batch settings.
85
+ *
86
+ * On a CNI flow (`flow.batchConfig`) this value is authoritative. On a component trigger
87
+ * (`TriggerDefinition.batchConfig`) it's the default the platform seeds, which a low-code
88
+ * user may override per instance.
89
+ */
90
+ export interface BatchConfig {
91
+ /** Number of items per batch. Must be an integer >= 1. `1` dispatches each item individually; `>1` groups items into batches. */
92
+ batchSize: number;
93
+ /** Max batches of a single execution dispatched concurrently. Must be an integer >= 1 when set. Omit for unlimited. */
94
+ concurrentBatchLimit?: number;
95
+ }
10
96
  /**
11
97
  * TriggerDefinition is the type of the object that is passed in to `trigger` function to
12
98
  * define a component trigger. See
13
99
  * https://prismatic.io/docs/custom-connectors/triggers/
100
+ *
101
+ * Composed from `TriggerDefinitionBase` (static fields) plus `TriggerResolverDecl` (the
102
+ * resolver support relationship) and `OnDeployDecl` (the on-deploy perform/resolver
103
+ * relationship), which enforce those constraints at the type level.
14
104
  */
15
- export interface TriggerDefinition<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TriggerPayload> = TriggerResult<TAllowsBranching, TriggerPayload>> {
105
+ export type TriggerDefinition<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TriggerPayload> = TriggerResult<TAllowsBranching, TriggerPayload>> = TriggerDefinitionBase<TInputs, TConfigVars, TAllowsBranching, TResult> & TriggerResolverDecl<TConfigVars, TriggerPayload> & OnDeployDecl<TInputs, TConfigVars, TriggerPayload, TAllowsBranching, TResult>;
106
+ interface TriggerDefinitionBase<TInputs extends Inputs = Inputs, TConfigVars extends ConfigVarResultCollection = ConfigVarResultCollection, TAllowsBranching extends boolean = boolean, TResult extends TriggerResult<TAllowsBranching, TriggerPayload> = TriggerResult<TAllowsBranching, TriggerPayload>> {
16
107
  /** Defines how the trigger is displayed in the Prismatic UI. */
17
108
  display: ActionDisplayDefinition;
18
109
  /** Function to perform when this trigger is invoked. */
19
110
  perform: TriggerPerformFunction<TInputs, TConfigVars, TAllowsBranching, TResult>;
111
+ /**
112
+ * Default batch-dispatch config shared by `triggerResolver` and `onDeployResolver`.
113
+ * Required when this trigger declares either (a `triggerResolver` with
114
+ * `triggerResolverSupport` `"valid"`/`"required"`, or an `onDeployResolver`).
115
+ */
116
+ batchConfig?: BatchConfig;
20
117
  /**
21
118
  * Function to execute when an instance of an integration with a flow that uses this trigger is deployed. See
22
119
  * https://prismatic.io/docs/custom-connectors/triggers/#instance-deploy-and-delete-events-for-triggers
@@ -3,8 +3,13 @@ import type { FlowAttributes } from "./FlowAttributes";
3
3
  import type { InstanceAttributes } from "./InstanceAttributes";
4
4
  import type { IntegrationAttributes } from "./IntegrationAttributes";
5
5
  import type { UserAttributes } from "./UserAttributes";
6
- /** Represents a Trigger Payload, which is data passed into a Trigger to invoke an Integration execution. */
7
- export interface TriggerPayload {
6
+ /** Represents a Trigger Payload, which is data passed into a Trigger to invoke an Integration execution.
7
+ *
8
+ * The optional `TPaginationState` parameter types the `paginationState` field, so authors who
9
+ * declare a pagination-state shape on their `getNextPaginationState` resolver can read back
10
+ * the same shape on the next round's payload. Defaults to `Record<string, unknown>`.
11
+ */
12
+ export interface TriggerPayload<TPaginationState extends Record<string, unknown> = Record<string, unknown>> {
8
13
  /** The headers sent in the webhook request. */
9
14
  headers: {
10
15
  [key: string]: string;
@@ -50,4 +55,8 @@ export interface TriggerPayload {
50
55
  startedAt: string;
51
56
  /** Determines whether the execution will run in debug mode. */
52
57
  globalDebug: boolean;
58
+ /** Managed by the execution when this trigger invocation is a paginated re-run.
59
+ * Contains the object returned by `getNextPaginationState` from the previous round.
60
+ * Absent on the initial invocation. */
61
+ paginationState?: TPaginationState;
53
62
  }
@@ -4,4 +4,4 @@ import type { ConfigVarResultCollection, Inputs } from "./Inputs";
4
4
  import type { TriggerPayload } from "./TriggerPayload";
5
5
  import type { TriggerResult } from "./TriggerResult";
6
6
  /** Definition of the function to perform when a Trigger is invoked. */
7
- export type TriggerPerformFunction<TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean | undefined, TResult extends TriggerResult<TAllowsBranching, TriggerPayload>> = (context: ActionContext<TConfigVars>, payload: TriggerPayload, params: ActionInputParameters<TInputs>) => Promise<TResult>;
7
+ export type TriggerPerformFunction<TInputs extends Inputs, TConfigVars extends ConfigVarResultCollection, TAllowsBranching extends boolean | undefined, TResult extends TriggerResult<TAllowsBranching, TriggerPayload>, TPaginationState extends Record<string, unknown> = Record<string, unknown>> = (context: ActionContext<TConfigVars>, payload: TriggerPayload<TPaginationState>, params: ActionInputParameters<TInputs>) => Promise<TResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismatic-io/spectral",
3
- "version": "10.19.0",
3
+ "version": "10.20.0",
4
4
  "description": "Utility library for building Prismatic connectors and code-native integrations",
5
5
  "keywords": [
6
6
  "prismatic"