@prismatic-io/spectral 10.18.7 → 10.18.9-preview.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.
@@ -3,13 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.INPUT_TYPE_MAP = exports.getInputs = void 0;
4
4
  const escapeSpecialCharacters_1 = require("../utils/escapeSpecialCharacters");
5
5
  const docBlock_1 = require("./docBlock");
6
- const getDefaultValue = (value, isCollection) => {
7
- if (value === undefined || value === "") {
8
- return isCollection ? [] : value;
9
- }
10
- const stringValue = typeof value === "string" ? value : JSON.stringify(value);
11
- return (0, escapeSpecialCharacters_1.escapeSpecialCharacters)(stringValue);
12
- };
13
6
  const getInputs = ({ inputs, docBlock = docBlock_1.DOC_BLOCK_DEFAULT }) => {
14
7
  return inputs.reduce((acc, input) => {
15
8
  if ((typeof input.shown === "boolean" && input.shown === false) ||
@@ -68,8 +61,23 @@ exports.INPUT_TYPE_MAP = {
68
61
  timestamp: "string",
69
62
  flow: "string",
70
63
  template: "string",
64
+ structuredObject: {
65
+ module: "@prismatic-io/spectral/dist/types",
66
+ type: "StructuredObject",
67
+ },
68
+ dynamicObject: {
69
+ module: "@prismatic-io/spectral/dist/types",
70
+ type: "DynamicObject",
71
+ },
71
72
  };
72
73
  const getInputValueType = (input) => {
74
+ var _a, _b;
75
+ if (input.type === "structuredObject") {
76
+ return structuredObjectTypeString((_a = input.inputs) !== null && _a !== void 0 ? _a : []);
77
+ }
78
+ if (input.type === "dynamicObject") {
79
+ return dynamicObjectTypeString((_b = input.inputs) !== null && _b !== void 0 ? _b : []);
80
+ }
73
81
  const inputType = exports.INPUT_TYPE_MAP[input.type];
74
82
  const valueType = input.model
75
83
  ? input.model
@@ -94,3 +102,70 @@ const getInputValueType = (input) => {
94
102
  }
95
103
  return valueType;
96
104
  };
105
+ const getDefaultValue = (value, isCollection) => {
106
+ if (value === undefined || value === "") {
107
+ return isCollection ? [] : value;
108
+ }
109
+ const stringValue = typeof value === "string" ? value : JSON.stringify(value);
110
+ return (0, escapeSpecialCharacters_1.escapeSpecialCharacters)(stringValue);
111
+ };
112
+ const isValidIdentifier = (key) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
113
+ const wrapCollection = (valueType, collection) => {
114
+ if (collection === "valuelist") {
115
+ return `Array<${valueType}>`;
116
+ }
117
+ if (collection === "keyvaluelist") {
118
+ return `Record<string, ${valueType}> | Array<{key: string, value: ${valueType}}>`;
119
+ }
120
+ return valueType;
121
+ };
122
+ const getLeafBaseType = (child) => {
123
+ var _a;
124
+ if ((_a = child.model) === null || _a === void 0 ? void 0 : _a.length) {
125
+ return child.model
126
+ .map(({ value }) => `\`${value.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}\``)
127
+ .join(" | ");
128
+ }
129
+ const mapped = exports.INPUT_TYPE_MAP[child.type];
130
+ if (!mapped) {
131
+ return "unknown";
132
+ }
133
+ if (typeof mapped === "string") {
134
+ return mapped;
135
+ }
136
+ return `import("${mapped.module}").${mapped.type}`;
137
+ };
138
+ const getLeafTypeString = (child) => {
139
+ var _a;
140
+ if (child.type === "structuredObject") {
141
+ return structuredObjectTypeString((_a = child.inputs) !== null && _a !== void 0 ? _a : []);
142
+ }
143
+ return wrapCollection(getLeafBaseType(child), child.collection);
144
+ };
145
+ const SPECTRAL_TYPES_MODULE = "@prismatic-io/spectral/dist/types";
146
+ const structuredObjectTypeString = (inputs) => {
147
+ if (!inputs.length) {
148
+ return `import("${SPECTRAL_TYPES_MODULE}").StructuredObject`;
149
+ }
150
+ const fields = inputs
151
+ .map((child) => {
152
+ const key = isValidIdentifier(child.key) ? child.key : JSON.stringify(child.key);
153
+ return `${key}: ${getLeafTypeString(child)}`;
154
+ })
155
+ .join("; ");
156
+ return `{ ${fields} }`;
157
+ };
158
+ const dynamicObjectTypeString = (configurations) => {
159
+ if (!configurations.length) {
160
+ return `import("${SPECTRAL_TYPES_MODULE}").DynamicObject`;
161
+ }
162
+ return configurations
163
+ .map((config) => {
164
+ var _a;
165
+ const valuesType = ((_a = config.inputs) === null || _a === void 0 ? void 0 : _a.length)
166
+ ? structuredObjectTypeString(config.inputs)
167
+ : `import("${SPECTRAL_TYPES_MODULE}").StructuredObject`;
168
+ return `{ configuration: ${JSON.stringify(config.key)}; values: ${valuesType} }`;
169
+ })
170
+ .join(" | ");
171
+ };
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, Flow, InputFieldDefinition, Inputs, IntegrationDefinition, OAuth2ConnectionDefinition, OnPremConnectionDefinition, OrganizationActivatedConnectionConfigVar, StandardConfigVar, 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, TDiscoveryState extends Record<string, unknown> = Record<string, unknown>, T extends Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TDiscoveryState> = Flow<TInputs, TActionInputs, TPayload, TAllowsBranching, TResult, TTriggerPayload, TItem, TDiscoveryState>>(definition: T & {
77
+ trigger?: BatchTrigger<TItem, TDiscoveryState>;
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), the trigger fires return just their `items` and
83
+ * the pagination callbacks live alongside them. spectral wraps the items into the wire payload
84
+ * and synthesizes the `resolveItems` that reads them back.
85
+ *
86
+ * Supply the item and pagination-state types explicitly —
87
+ * `batchFlowTrigger<Order, { cursor: number }>({ ... })`. They flow through the whole flow:
88
+ * the trigger fires return `Order[]`, `payload.discoveryState` reads back as `{ cursor: number }`,
89
+ * and the flow's `onExecution` sees `params.onTrigger.results.body.data` typed as `Order | Order[]`.
90
+ *
91
+ * @typeParam TItem - the item type each batched execution receives.
92
+ * @typeParam TPaginationState - the pagination state round-tripped via `payload.discoveryState`.
93
+ * @see {@link https://prismatic.io/docs/integrations/code-native/flows/ | Code-Native Flows}
94
+ * @example
95
+ * import { flow, batchFlowTrigger } from "@prismatic-io/spectral";
96
+ *
97
+ * flow({
98
+ * name: "Sync Orders",
99
+ * stableKey: "sync-orders",
100
+ * batchConfig: { batchSize: 50 },
101
+ * trigger: batchFlowTrigger<Order, { cursor: number }>({
102
+ * onTrigger: async (context, payload) => {
103
+ * const page = await fetchOrders(payload.discoveryState?.cursor);
104
+ * return { items: page.orders };
105
+ * },
106
+ * getNextOnTriggerPaginationState: (context, result) => {
107
+ * const next = result.payload.discoveryState?.cursor;
108
+ * return next === undefined ? null : { cursor: next };
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.
@@ -512,6 +553,69 @@ export declare const dataSource: <TInputs extends Inputs, TConfigVars extends Co
512
553
  * });
513
554
  */
514
555
  export declare const input: <T extends InputFieldDefinition>(definition: T) => T;
556
+ /**
557
+ * Groups related primitive inputs under a single named container. Children
558
+ * may not themselves be structuredObject inputs (the type signature enforces
559
+ * this at compile time).
560
+ *
561
+ * @example
562
+ * import { input, structuredObjectInput } from "@prismatic-io/spectral";
563
+ *
564
+ * const name = structuredObjectInput({
565
+ * label: "Name",
566
+ * inputs: {
567
+ * first: input({ type: "string", label: "First Name", required: true }),
568
+ * last: input({ type: "string", label: "Last Name", required: true }),
569
+ * },
570
+ * });
571
+ */
572
+ export declare const structuredObjectInput: <T extends Omit<StructuredObjectInputField, "type"> & {
573
+ type?: never;
574
+ }>(definition: T) => T & {
575
+ type: "structuredObject";
576
+ };
577
+ /**
578
+ * Presents a discriminated set of input configurations; the integration builder
579
+ * picks a configuration and its inputs become available. Configurations may
580
+ * contain leaf inputs and structuredObject inputs but not nested dynamicObjects
581
+ * (the type signature enforces this at compile time).
582
+ *
583
+ * @example
584
+ * import { input, structuredObjectInput, dynamicObjectInput } from "@prismatic-io/spectral";
585
+ *
586
+ * const recordData = dynamicObjectInput({
587
+ * label: "Record Data",
588
+ * required: true,
589
+ * configurations: {
590
+ * contact: {
591
+ * label: "Contact",
592
+ * comments: "Create a new contact",
593
+ * inputs: {
594
+ * name: structuredObjectInput({
595
+ * label: "Name",
596
+ * inputs: {
597
+ * first: input({ type: "string", label: "First Name", required: true }),
598
+ * last: input({ type: "string", label: "Last Name", required: true }),
599
+ * },
600
+ * }),
601
+ * email: input({ type: "string", label: "Email", required: true }),
602
+ * },
603
+ * },
604
+ * account: {
605
+ * label: "Account",
606
+ * comments: "Create a new account",
607
+ * inputs: {
608
+ * companyName: input({ type: "string", label: "Company Name", required: true }),
609
+ * },
610
+ * },
611
+ * },
612
+ * });
613
+ */
614
+ export declare const dynamicObjectInput: <T extends Omit<DynamicObjectInputField, "type"> & {
615
+ type?: never;
616
+ }>(definition: T) => T & {
617
+ type: "dynamicObject";
618
+ };
515
619
  /**
516
620
  * This function creates a connection that can be used by a code-native integration
517
621
  * or custom component. Connections define the fields (API keys, tokens, etc.) needed
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.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`/`TDiscoveryState` 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), the trigger fires return just their `items` and
116
+ * the pagination callbacks live alongside them. spectral wraps the items into the wire payload
117
+ * and synthesizes the `resolveItems` that reads them back.
118
+ *
119
+ * Supply the item and pagination-state types explicitly —
120
+ * `batchFlowTrigger<Order, { cursor: number }>({ ... })`. They flow through the whole flow:
121
+ * the trigger fires return `Order[]`, `payload.discoveryState` reads back as `{ cursor: number }`,
122
+ * and the flow's `onExecution` sees `params.onTrigger.results.body.data` typed as `Order | Order[]`.
123
+ *
124
+ * @typeParam TItem - the item type each batched execution receives.
125
+ * @typeParam TPaginationState - the pagination state round-tripped via `payload.discoveryState`.
126
+ * @see {@link https://prismatic.io/docs/integrations/code-native/flows/ | Code-Native Flows}
127
+ * @example
128
+ * import { flow, batchFlowTrigger } from "@prismatic-io/spectral";
129
+ *
130
+ * flow({
131
+ * name: "Sync Orders",
132
+ * stableKey: "sync-orders",
133
+ * batchConfig: { batchSize: 50 },
134
+ * trigger: batchFlowTrigger<Order, { cursor: number }>({
135
+ * onTrigger: async (context, payload) => {
136
+ * const page = await fetchOrders(payload.discoveryState?.cursor);
137
+ * return { items: page.orders };
138
+ * },
139
+ * getNextOnTriggerPaginationState: (context, result) => {
140
+ * const next = result.payload.discoveryState?.cursor;
141
+ * return next === undefined ? null : { cursor: next };
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.
@@ -557,6 +602,63 @@ exports.dataSource = dataSource;
557
602
  */
558
603
  const input = (definition) => definition;
559
604
  exports.input = input;
605
+ /**
606
+ * Groups related primitive inputs under a single named container. Children
607
+ * may not themselves be structuredObject inputs (the type signature enforces
608
+ * this at compile time).
609
+ *
610
+ * @example
611
+ * import { input, structuredObjectInput } from "@prismatic-io/spectral";
612
+ *
613
+ * const name = structuredObjectInput({
614
+ * label: "Name",
615
+ * inputs: {
616
+ * first: input({ type: "string", label: "First Name", required: true }),
617
+ * last: input({ type: "string", label: "Last Name", required: true }),
618
+ * },
619
+ * });
620
+ */
621
+ const structuredObjectInput = (definition) => (Object.assign(Object.assign({}, definition), { type: "structuredObject" }));
622
+ exports.structuredObjectInput = structuredObjectInput;
623
+ /**
624
+ * Presents a discriminated set of input configurations; the integration builder
625
+ * picks a configuration and its inputs become available. Configurations may
626
+ * contain leaf inputs and structuredObject inputs but not nested dynamicObjects
627
+ * (the type signature enforces this at compile time).
628
+ *
629
+ * @example
630
+ * import { input, structuredObjectInput, dynamicObjectInput } from "@prismatic-io/spectral";
631
+ *
632
+ * const recordData = dynamicObjectInput({
633
+ * label: "Record Data",
634
+ * required: true,
635
+ * configurations: {
636
+ * contact: {
637
+ * label: "Contact",
638
+ * comments: "Create a new contact",
639
+ * inputs: {
640
+ * name: structuredObjectInput({
641
+ * label: "Name",
642
+ * inputs: {
643
+ * first: input({ type: "string", label: "First Name", required: true }),
644
+ * last: input({ type: "string", label: "Last Name", required: true }),
645
+ * },
646
+ * }),
647
+ * email: input({ type: "string", label: "Email", required: true }),
648
+ * },
649
+ * },
650
+ * account: {
651
+ * label: "Account",
652
+ * comments: "Create a new account",
653
+ * inputs: {
654
+ * companyName: input({ type: "string", label: "Company Name", required: true }),
655
+ * },
656
+ * },
657
+ * },
658
+ * });
659
+ */
660
+ const dynamicObjectInput = (definition) => (Object.assign(Object.assign({}, definition), { type: "dynamicObject" }));
661
+ exports.dynamicObjectInput = dynamicObjectInput;
560
662
  /**
561
663
  * This function creates a connection that can be used by a code-native integration
562
664
  * or custom component. Connections define the fields (API keys, tokens, etc.) needed
@@ -1,7 +1,25 @@
1
1
  import { type ComponentDefinition, type ComponentHooks, type ConfigVarResultCollection, type ConnectionDefinition, type ConnectionInput, type ConnectionTemplateInputField, type InputFieldDefinition, type Inputs, type OnPremConnectionInput, type TriggerDefinition, type TriggerPayload, type TriggerResult } from "../types";
2
2
  import { type PollingTriggerDefinition } from "../types/PollingTriggerDefinition";
3
3
  import type { Component as ServerComponent, Connection as ServerConnection, Input as ServerInput, Trigger as ServerTrigger } from ".";
4
- export declare const convertInput: (key: string, { default: defaultValue, type, label, collection, ...rest }: InputFieldDefinition | OnPremConnectionInput | ConnectionInput) => ServerInput;
4
+ import { type CleanFn } from "./perform";
5
+ /** Auto-generated cleaner for structuredObject/dynamicObject containers.
6
+ * Recursively delegates to each child's clean function. Developers do not
7
+ * declare a top-level clean on these containers — the conversion always
8
+ * supplies one so nested clean functions are applied at runtime. */
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;
22
+ export declare const convertInput: (key: string, definition: InputFieldDefinition | OnPremConnectionInput | ConnectionInput) => ServerInput;
5
23
  export declare const _isValidTemplateValue: (template: string, inputs: {
6
24
  [key: string]: ConnectionInput | ConnectionTemplateInputField;
7
25
  }) => {
@@ -14,21 +14,155 @@ 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 = 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");
21
21
  const perform_1 = require("./perform");
22
- const convertInput = (key, _a) => {
23
- var { default: defaultValue, type, label, collection } = _a, rest = __rest(_a, ["default", "type", "label", "collection"]);
22
+ const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
23
+ /** Auto-generated cleaner for structuredObject/dynamicObject containers.
24
+ * Recursively delegates to each child's clean function. Developers do not
25
+ * declare a top-level clean on these containers — the conversion always
26
+ * supplies one so nested clean functions are applied at runtime. */
27
+ const cleanerFor = (input) => {
28
+ if (input.type === "structuredObject") {
29
+ const childCleaners = Object.entries(input.inputs).reduce((acc, [childKey, childDef]) => (Object.assign(Object.assign({}, acc), { [childKey]: (0, exports.cleanerFor)(childDef) })), {});
30
+ return (value) => {
31
+ if (!isPlainObject(value)) {
32
+ return value;
33
+ }
34
+ return (0, perform_1.cleanParams)(value, childCleaners);
35
+ };
36
+ }
37
+ if (input.type === "dynamicObject") {
38
+ const configCleaners = {};
39
+ for (const [configKey, configDef] of Object.entries(input.configurations)) {
40
+ configCleaners[configKey] = Object.entries(configDef.inputs).reduce((acc, [childKey, childDef]) => (Object.assign(Object.assign({}, acc), { [childKey]: (0, exports.cleanerFor)(childDef) })), {});
41
+ }
42
+ return (value) => {
43
+ if (!isPlainObject(value)) {
44
+ return value;
45
+ }
46
+ const { configuration, values } = value;
47
+ if (typeof configuration !== "string") {
48
+ return value;
49
+ }
50
+ const cleaners = configCleaners[configuration];
51
+ if (!cleaners) {
52
+ return { configuration, values };
53
+ }
54
+ return {
55
+ configuration,
56
+ values: isPlainObject(values) ? (0, perform_1.cleanParams)(values, cleaners) : values,
57
+ };
58
+ };
59
+ }
60
+ return "clean" in input ? input.clean : undefined;
61
+ };
62
+ exports.cleanerFor = cleanerFor;
63
+ /**
64
+ * Throws if `batchSize` isn't a positive integer; otherwise returns it. Shared by the
65
+ * component-trigger (`TriggerDefinition.batch.batchSize`) and CNI flow (`flow.batch.batchSize`)
66
+ * validation paths.
67
+ */
68
+ const validateBatchSize = (ownerLabel, fieldName, batchSize) => {
69
+ if (typeof batchSize !== "number" || !Number.isInteger(batchSize) || batchSize < 1) {
70
+ throw new Error(`${ownerLabel} has an invalid ${fieldName} batchSize of ${String(batchSize)}. batchSize must be an integer >= 1.`);
71
+ }
72
+ return batchSize;
73
+ };
74
+ exports.validateBatchSize = validateBatchSize;
75
+ /**
76
+ * Throws if `concurrentBatchLimit` is set but isn't a positive integer; returns it
77
+ * unchanged (including `undefined`, which the platform treats as unlimited). Shared by the
78
+ * component-trigger and CNI flow paths, both sourcing it from the single `batchConfig`.
79
+ */
80
+ const validateConcurrentBatchLimit = (ownerLabel, fieldName, concurrentBatchLimit) => {
81
+ if (concurrentBatchLimit === undefined) {
82
+ return undefined;
83
+ }
84
+ if (typeof concurrentBatchLimit !== "number" ||
85
+ !Number.isInteger(concurrentBatchLimit) ||
86
+ concurrentBatchLimit < 1) {
87
+ throw new Error(`${ownerLabel} has an invalid ${fieldName} concurrentBatchLimit of ${String(concurrentBatchLimit)}. concurrentBatchLimit must be an integer >= 1.`);
88
+ }
89
+ return concurrentBatchLimit;
90
+ };
91
+ exports.validateConcurrentBatchLimit = validateConcurrentBatchLimit;
92
+ /**
93
+ * Emits the trigger's single default batch size to the one wire field the platform reads
94
+ * (`triggerResolverDefaultBatchSize`), shared by both the trigger and on-deploy resolution.
95
+ * Emitted when the trigger declares a resolver — `triggerResolverSupport` `"valid"`/`"required"`
96
+ * for the normal path, or an `onDeployResolver` for the on-deploy path. Defaults to 1 when no
97
+ * `batchConfig` was declared.
98
+ */
99
+ const buildBatchDefaultField = (triggerLabel, triggerResolverSupport, hasOnDeployResolver, batchConfig) => {
100
+ if (triggerResolverSupport === "invalid" && !hasOnDeployResolver) {
101
+ return {};
102
+ }
103
+ const concurrentBatchLimit = batchConfig
104
+ ? (0, exports.validateConcurrentBatchLimit)(`Trigger "${triggerLabel}"`, "batchConfig", batchConfig.concurrentBatchLimit)
105
+ : undefined;
106
+ return Object.assign({ triggerResolverDefaultBatchSize: batchConfig
107
+ ? (0, exports.validateBatchSize)(`Trigger "${triggerLabel}"`, "batchConfig", batchConfig.batchSize)
108
+ : 1 }, (concurrentBatchLimit !== undefined
109
+ ? { triggerResolverDefaultConcurrentBatchLimit: concurrentBatchLimit }
110
+ : {}));
111
+ };
112
+ const buildTriggerResolverFields = (resolver) => {
113
+ if (!resolver) {
114
+ return {};
115
+ }
116
+ return Object.assign(Object.assign({}, (resolver.resolveItems
117
+ ? {
118
+ resolveTriggerItems: resolver.resolveItems,
119
+ hasResolveTriggerItems: true,
120
+ }
121
+ : {})), (resolver.getNextDiscoveryState
122
+ ? {
123
+ getNextDiscoveryState: resolver.getNextDiscoveryState,
124
+ hasGetNextDiscoveryState: true,
125
+ }
126
+ : {}));
127
+ };
128
+ const buildOnDeployResolverFields = (resolver) => {
129
+ if (!resolver) {
130
+ return {};
131
+ }
132
+ return Object.assign(Object.assign({}, (resolver.resolveItems
133
+ ? {
134
+ resolveOnDeployItems: resolver.resolveItems,
135
+ hasResolveOnDeployItems: true,
136
+ }
137
+ : {})), (resolver.getNextDiscoveryState
138
+ ? {
139
+ getOnDeployNextDiscoveryState: resolver.getNextDiscoveryState,
140
+ hasGetOnDeployNextDiscoveryState: true,
141
+ }
142
+ : {}));
143
+ };
144
+ const convertInput = (key, definition) => {
145
+ // Cast: the field union is wider than any single member; runtime guards below handle it.
146
+ const _a = definition, { default: defaultValue, type, label, collection, inputs: childInputs, configurations } = _a, rest = __rest(_a, ["default", "type", "label", "collection", "inputs", "configurations"]);
24
147
  const keyLabel = collection === "keyvaluelist" && typeof label === "object" ? label.key : undefined;
148
+ const nestedInputs = type === "structuredObject" && childInputs
149
+ ? Object.entries(childInputs).map(([childKey, childDef]) => (0, exports.convertInput)(childKey, childDef))
150
+ : type === "dynamicObject" && configurations
151
+ ? Object.entries(configurations).map(([configKey, configDef]) => ({
152
+ key: configKey,
153
+ type: "structuredObject",
154
+ label: typeof configDef.label === "string" ? configDef.label : configDef.label.value,
155
+ comments: configDef.comments,
156
+ inputs: Object.entries(configDef.inputs).map(([childKey, childDef]) => (0, exports.convertInput)(childKey, childDef)),
157
+ }))
158
+ : undefined;
25
159
  return Object.assign(Object.assign({}, (0, omit_1.default)(rest, [
26
160
  "onPremControlled",
27
161
  "permissionAndVisibilityType",
28
162
  "visibleToOrgDeployer",
29
163
  "writeOnly",
30
164
  ])), { key,
31
- type, default: defaultValue !== null && defaultValue !== void 0 ? defaultValue : types_1.InputFieldDefaultMap[type], collection, label: typeof label === "string" ? label : label.value, keyLabel, onPremiseControlled: ("onPremControlled" in rest && rest.onPremControlled) || undefined });
165
+ type, default: defaultValue !== null && defaultValue !== void 0 ? defaultValue : types_1.InputFieldDefaultMap[type], collection, label: typeof label === "string" ? label : label.value, keyLabel, onPremiseControlled: rest.onPremControlled === true ? true : undefined, inputs: nestedInputs });
32
166
  };
33
167
  exports.convertInput = convertInput;
34
168
  const TEMPLATE_VALUE_REGEX = /{{#(\w+)}}/g;
@@ -73,28 +207,53 @@ exports.convertTemplateInput = convertTemplateInput;
73
207
  const convertAction = (actionKey, _a, hooks) => {
74
208
  var { inputs = {}, perform } = _a, action = __rest(_a, ["inputs", "perform"]);
75
209
  const convertedInputs = Object.entries(inputs).map(([key, value]) => (0, exports.convertInput)(key, value));
76
- const inputCleaners = Object.entries(inputs).reduce((result, [key, { clean }]) => (Object.assign(Object.assign({}, result), { [key]: clean })), {});
210
+ const inputCleaners = Object.entries(inputs).reduce((result, [key, value]) => (Object.assign(Object.assign({}, result), { [key]: (0, exports.cleanerFor)(value) })), {});
77
211
  return Object.assign(Object.assign({}, action), { key: actionKey, inputs: convertedInputs, perform: (0, perform_1.createPerform)(perform, {
78
212
  inputCleaners,
79
213
  errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
80
214
  }) });
81
215
  };
82
- const convertTrigger = (triggerKey, trigger, hooks) => {
216
+ const convertTrigger = (triggerKey,
217
+ // `any` is load-bearing: the user-facing TriggerDefinition / PollingTriggerDefinition
218
+ // type their event-function fields (onInstanceDeploy, webhookLifecycleHandlers, etc.) over
219
+ // TInputs/TConfigVars/TPayload, while the wire-format ServerTrigger drops those generics.
220
+ // The `...trigger` spread in the result construction below would surface variance errors
221
+ // without these `any`s. The user-typed handlers are immediately replaced with
222
+ // createPerform-wrapped versions, so the loose input typing is safe in practice.
223
+ trigger, hooks) => {
83
224
  var _a;
84
225
  const { onInstanceDeploy, onInstanceDelete } = trigger;
85
226
  const webhookLifecycleHandlers = "webhookLifecycleHandlers" in trigger ? trigger.webhookLifecycleHandlers : undefined;
86
227
  const inputs = (_a = trigger.inputs) !== null && _a !== void 0 ? _a : {};
87
- const isPollingTrigger = (0, PollingTriggerDefinition_1.isPollingTriggerDefinition)(trigger);
88
228
  const triggerInputKeys = Object.keys(inputs);
89
229
  const convertedTriggerInputs = Object.entries(inputs).map(([key, value]) => {
90
230
  return (0, exports.convertInput)(key, value);
91
231
  });
92
- const triggerInputCleaners = Object.entries(inputs).reduce((result, [key, { clean }]) => (Object.assign(Object.assign({}, result), { [key]: clean })), {});
232
+ const triggerInputCleaners = Object.entries(inputs).reduce((result, [key, value]) => (Object.assign(Object.assign({}, result), { [key]: (0, exports.cleanerFor)(value) })), {});
93
233
  let scheduleSupport = "scheduleSupport" in trigger ? trigger.scheduleSupport : "invalid";
234
+ const batchConfig = "batchConfig" in trigger ? trigger.batchConfig : undefined;
235
+ const triggerResolver = "triggerResolver" in trigger ? trigger.triggerResolver : undefined;
236
+ const triggerResolverSupport = "triggerResolverSupport" in trigger && trigger.triggerResolverSupport !== undefined
237
+ ? trigger.triggerResolverSupport
238
+ : triggerResolver
239
+ ? "valid"
240
+ : "invalid";
241
+ if (triggerResolverSupport === "required" && !triggerResolver) {
242
+ throw new Error(`Trigger "${trigger.display.label}" declares triggerResolverSupport "required" but is missing triggerResolver.`);
243
+ }
244
+ if (triggerResolverSupport === "invalid" && triggerResolver) {
245
+ throw new Error(`Trigger "${trigger.display.label}" declares triggerResolver but triggerResolverSupport is "invalid".`);
246
+ }
247
+ const onDeployPerform = "onDeployPerform" in trigger ? trigger.onDeployPerform : undefined;
248
+ const onDeployResolver = "onDeployResolver" in trigger ? trigger.onDeployResolver : undefined;
249
+ // On-deploy is presence-driven (no support flag): a trigger that defines an
250
+ // `onDeployResolver` must also define the `onDeployPerform` fire it batches.
251
+ if ((onDeployResolver === null || onDeployResolver === void 0 ? void 0 : onDeployResolver.resolveItems) && !onDeployPerform) {
252
+ throw new Error(`Trigger "${trigger.display.label}" declares onDeployResolver.resolveItems but is missing onDeployPerform.`);
253
+ }
94
254
  let convertedActionInputs = [];
95
255
  let performToUse;
96
- if (isPollingTrigger) {
97
- // Pull inputs up from the action and make them available on the trigger
256
+ if ((0, PollingTriggerDefinition_1.isPollingTriggerDefinition)(trigger)) {
98
257
  const { pollAction: action } = trigger;
99
258
  let actionInputCleaners = {};
100
259
  scheduleSupport = "required";
@@ -106,7 +265,7 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
106
265
  accum.push((0, exports.convertInput)(key, value));
107
266
  return accum;
108
267
  }, []);
109
- actionInputCleaners = Object.entries(action.inputs).reduce((result, [key, { clean }]) => (Object.assign(Object.assign({}, result), { [key]: clean })), {});
268
+ actionInputCleaners = Object.entries(action.inputs).reduce((result, [key, value]) => (Object.assign(Object.assign({}, result), { [key]: (0, exports.cleanerFor)(value) })), {});
110
269
  }
111
270
  const combinedCleaners = Object.assign({}, actionInputCleaners, triggerInputCleaners);
112
271
  performToUse = (0, perform_1.createPollingPerform)(trigger, {
@@ -120,11 +279,11 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
120
279
  errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
121
280
  });
122
281
  }
123
- const result = Object.assign(Object.assign(Object.assign({}, trigger), { key: triggerKey, inputs: convertedTriggerInputs.concat(convertedActionInputs), perform: performToUse, scheduleSupport, synchronousResponseSupport: "synchronousResponseSupport" in trigger
282
+ 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
124
283
  ? trigger.synchronousResponseSupport
125
284
  : scheduleSupport === "invalid"
126
285
  ? "valid"
127
- : "invalid" }), (isPollingTrigger ? { isPollingTrigger: true } : {}));
286
+ : "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 } : {}));
128
287
  if (onInstanceDeploy) {
129
288
  result.onInstanceDeploy = (0, perform_1.createPerform)(onInstanceDeploy, {
130
289
  inputCleaners: triggerInputCleaners,
@@ -132,6 +291,13 @@ const convertTrigger = (triggerKey, trigger, hooks) => {
132
291
  });
133
292
  result.hasOnInstanceDeploy = true;
134
293
  }
294
+ if (onDeployPerform) {
295
+ result.onDeployPerform = (0, perform_1.createPerform)(onDeployPerform, {
296
+ inputCleaners: triggerInputCleaners,
297
+ errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,
298
+ });
299
+ result.hasOnDeployPerform = true;
300
+ }
135
301
  if (onInstanceDelete) {
136
302
  result.onInstanceDelete = (0, perform_1.createPerform)(onInstanceDelete, {
137
303
  inputCleaners: triggerInputCleaners,
@@ -158,7 +324,7 @@ exports.convertTrigger = convertTrigger;
158
324
  const convertDataSource = (dataSourceKey, _a, hooks) => {
159
325
  var { inputs = {}, perform } = _a, dataSource = __rest(_a, ["inputs", "perform"]);
160
326
  const convertedInputs = Object.entries(inputs).map(([key, value]) => (0, exports.convertInput)(key, value));
161
- const inputCleaners = Object.entries(inputs).reduce((result, [key, { clean }]) => (Object.assign(Object.assign({}, result), { [key]: clean })), {});
327
+ const inputCleaners = Object.entries(inputs).reduce((result, [key, value]) => (Object.assign(Object.assign({}, result), { [key]: (0, exports.cleanerFor)(value) })), {});
162
328
  return Object.assign(Object.assign({}, dataSource), { key: dataSourceKey, inputs: convertedInputs, perform: (0, perform_1.createPerform)(perform, {
163
329
  inputCleaners,
164
330
  errorHandler: hooks === null || hooks === void 0 ? void 0 : hooks.error,