@daeda/mcp-pro 0.1.18 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +781 -708
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -456,6 +456,15 @@ var ATTACH_FATAL_PATTERNS = [
456
456
  "Failed to deserialize",
457
457
  "database has been invalidated"
458
458
  ];
459
+ var LOCK_ERROR_PATTERNS = [
460
+ "Could not set lock",
461
+ "lock on file",
462
+ "already locked"
463
+ ];
464
+ var isLockedDatabaseError = (e) => {
465
+ const msg = String(e);
466
+ return LOCK_ERROR_PATTERNS.some((p) => msg.includes(p));
467
+ };
459
468
  var isCorruptedDatabaseError = (e) => {
460
469
  const msg = String(e);
461
470
  return ATTACH_FATAL_PATTERNS.some((p) => msg.includes(p));
@@ -596,6 +605,17 @@ var initializePortalDb = async (portalId, encryptionKey) => {
596
605
  try {
597
606
  await conn.run(attachSql);
598
607
  } catch (e) {
608
+ if (isLockedDatabaseError(e)) {
609
+ try {
610
+ conn.closeSync();
611
+ } catch {
612
+ }
613
+ try {
614
+ instance.closeSync();
615
+ } catch {
616
+ }
617
+ throw new Error(`Database for portal ${portalId} is locked by another client. Only one MCP client can access a portal at a time.`);
618
+ }
599
619
  if (!isCorruptedDatabaseError(e)) throw e;
600
620
  const file = dbPath(portalId);
601
621
  console.error(`[recovery] Corrupted database detected during ATTACH for portal ${portalId}, deleting and retrying`);
@@ -1417,7 +1437,20 @@ var FATAL_ERROR_PATTERNS = [
1417
1437
  "Serialization Error",
1418
1438
  "field id mismatch"
1419
1439
  ];
1440
+ var LOCK_ERROR_PATTERNS2 = [
1441
+ "Could not set lock",
1442
+ "lock on file",
1443
+ "already locked"
1444
+ ];
1445
+ var isLockedDatabaseError2 = (err) => {
1446
+ const message = String(err);
1447
+ const causeMessage = String(err?.cause ?? "");
1448
+ return LOCK_ERROR_PATTERNS2.some(
1449
+ (pattern) => message.includes(pattern) || causeMessage.includes(pattern)
1450
+ );
1451
+ };
1420
1452
  var isFatalDatabaseError = (err) => {
1453
+ if (isLockedDatabaseError2(err)) return false;
1421
1454
  const message = String(err);
1422
1455
  const causeMessage = String(err?.cause ?? "");
1423
1456
  return FATAL_ERROR_PATTERNS.some(
@@ -2140,6 +2173,7 @@ var workflowsPlugin = {
2140
2173
  initStrategy: "eager",
2141
2174
  queryDocs: QUERY_DOCS7,
2142
2175
  transport: "message",
2176
+ timeoutSeconds: 120,
2143
2177
  payloadSchema: workflowsPayloadSchema,
2144
2178
  buildStatusSection: async ({ conn }) => {
2145
2179
  const workflowsReader = await conn.runAndReadAll(
@@ -7164,7 +7198,10 @@ var createWorkflowMeta = {
7164
7198
  }),
7165
7199
  requiredScopes: () => ["automation"],
7166
7200
  getSummary: (op) => `Create Workflow \u2014 ${op.name}`,
7167
- getResourceUrl: () => null,
7201
+ getResourceUrl: (portalId, _op, result) => {
7202
+ const workflowId = result?.affected_ids?.[0];
7203
+ return workflowId ? `${HUBSPOT_BASE}/workflows/${portalId}/platform/flow/${workflowId}/edit` : null;
7204
+ },
7168
7205
  renderDetails: (op) => {
7169
7206
  const rows = [
7170
7207
  { label: "Name", value: op.name },
@@ -11576,6 +11613,133 @@ var deleteSnippetHandler = {
11576
11613
  // ../shared/operations/create-workflow/index.ts
11577
11614
  import { Effect as Effect92, pipe as pipe79 } from "effect";
11578
11615
 
11616
+ // ../shared/pure/workflow-validation/helpers.ts
11617
+ var isRecord = (value) => typeof value === "object" && value !== null;
11618
+ var asBranchArray = (value) => Array.isArray(value) ? value.filter(isRecord) : [];
11619
+ var isMissingRequiredField = (value) => value === void 0 || value === null || value === "";
11620
+ var isIntegerInRange = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
11621
+ var toAssociationDirectionKey = (fromObjectTypeId, toObjectTypeId3) => `${fromObjectTypeId}->${toObjectTypeId3}`;
11622
+ var makeDeduplicatedIssueAdder = () => {
11623
+ const seen = /* @__PURE__ */ new Set();
11624
+ const issues = [];
11625
+ const add = (issue) => {
11626
+ const key = `${issue.code}:${issue.message}`;
11627
+ if (seen.has(key)) return;
11628
+ seen.add(key);
11629
+ issues.push(issue);
11630
+ };
11631
+ return { issues, add };
11632
+ };
11633
+
11634
+ // ../shared/pure/workflow-validation/enrollment.ts
11635
+ var OPERATION_TYPE_FOR_PROPERTY_TYPE2 = {
11636
+ enumeration: "ENUMERATION",
11637
+ string: "MULTISTRING",
11638
+ number: "NUMBER",
11639
+ bool: "BOOL",
11640
+ date: "TIME_POINT",
11641
+ datetime: "TIME_POINT"
11642
+ };
11643
+ var isCompatibleEnrollmentOperationType = (operationType, expectedOperationType, propertyType) => operationType === expectedOperationType || operationType === "ALL_PROPERTY" || operationType === "TIME_RANGED" && (propertyType === "date" || propertyType === "datetime");
11644
+ var collectPropertyFiltersFromBranches = (branches, parentObjectTypeId) => {
11645
+ const refs = [];
11646
+ for (const branch of branches) {
11647
+ const contextObjectTypeId = branch.filterBranchType === "ASSOCIATION" && typeof branch.objectTypeId === "string" ? branch.objectTypeId : parentObjectTypeId;
11648
+ const filters = asBranchArray(branch.filters);
11649
+ for (const filter of filters) {
11650
+ if (filter.filterType !== "PROPERTY" || typeof filter.property !== "string") continue;
11651
+ const operation = isRecord(filter.operation) ? filter.operation : void 0;
11652
+ if (!operation || typeof operation.operationType !== "string") continue;
11653
+ refs.push({
11654
+ property: filter.property,
11655
+ operationType: operation.operationType,
11656
+ objectTypeId: contextObjectTypeId
11657
+ });
11658
+ }
11659
+ const nestedBranches = asBranchArray(branch.filterBranches);
11660
+ if (nestedBranches.length > 0) {
11661
+ refs.push(...collectPropertyFiltersFromBranches(nestedBranches, contextObjectTypeId));
11662
+ }
11663
+ }
11664
+ return refs;
11665
+ };
11666
+ var collectWorkflowEnrollmentPropertyFilters = (enrollmentCriteria, rootObjectTypeId) => {
11667
+ if (!enrollmentCriteria) return [];
11668
+ return [
11669
+ ...collectPropertyFiltersFromBranches(asBranchArray(enrollmentCriteria.eventFilterBranches), rootObjectTypeId),
11670
+ ...collectPropertyFiltersFromBranches(asBranchArray(enrollmentCriteria.listMembershipFilterBranches), rootObjectTypeId),
11671
+ ...collectPropertyFiltersFromBranches(
11672
+ isRecord(enrollmentCriteria.listFilterBranch) ? [enrollmentCriteria.listFilterBranch] : [],
11673
+ rootObjectTypeId
11674
+ ),
11675
+ ...collectPropertyFiltersFromBranches(
11676
+ asBranchArray(enrollmentCriteria.reEnrollmentTriggersFilterBranches),
11677
+ rootObjectTypeId
11678
+ )
11679
+ ];
11680
+ };
11681
+ var validateWorkflowEnrollmentMetadata = (params) => {
11682
+ const {
11683
+ enrollmentCriteria,
11684
+ suppressionListIds,
11685
+ objectTypeId,
11686
+ enabledObjectTypeIds,
11687
+ propertyDefinitionsByObjectType,
11688
+ listIds,
11689
+ operationIndex
11690
+ } = params;
11691
+ const { issues, add } = makeDeduplicatedIssueAdder();
11692
+ const filterRefs = collectWorkflowEnrollmentPropertyFilters(enrollmentCriteria, objectTypeId);
11693
+ const referencedObjectTypeIds = /* @__PURE__ */ new Set([
11694
+ objectTypeId,
11695
+ ...filterRefs.map((filterRef) => filterRef.objectTypeId)
11696
+ ]);
11697
+ for (const referencedObjectTypeId of referencedObjectTypeIds) {
11698
+ if (enabledObjectTypeIds.has(referencedObjectTypeId)) continue;
11699
+ add({
11700
+ severity: "error",
11701
+ operation_index: operationIndex,
11702
+ code: "WORKFLOW_OBJECT_TYPE_NOT_ENABLED",
11703
+ message: `Workflow references object type '${referencedObjectTypeId}', but it is not enabled in HubSpot`
11704
+ });
11705
+ }
11706
+ for (const filterRef of filterRefs) {
11707
+ if (!enabledObjectTypeIds.has(filterRef.objectTypeId)) continue;
11708
+ const propertyDefinitions = propertyDefinitionsByObjectType.get(filterRef.objectTypeId);
11709
+ const propertyDefinition = propertyDefinitions?.get(filterRef.property);
11710
+ if (!propertyDefinition) {
11711
+ add({
11712
+ severity: "error",
11713
+ operation_index: operationIndex,
11714
+ code: "WORKFLOW_ENROLLMENT_PROPERTY_NOT_FOUND",
11715
+ message: `Workflow enrollment filter property '${filterRef.property}' was not found for object type '${filterRef.objectTypeId}'`
11716
+ });
11717
+ continue;
11718
+ }
11719
+ const expectedOperationType = OPERATION_TYPE_FOR_PROPERTY_TYPE2[propertyDefinition.type];
11720
+ if (!expectedOperationType) continue;
11721
+ if (isCompatibleEnrollmentOperationType(filterRef.operationType, expectedOperationType, propertyDefinition.type)) {
11722
+ continue;
11723
+ }
11724
+ add({
11725
+ severity: "error",
11726
+ operation_index: operationIndex,
11727
+ code: "INVALID_ENROLLMENT_FILTER_OPERATION_TYPE",
11728
+ message: `Workflow enrollment filter property '${filterRef.property}' has type '${propertyDefinition.type}' but uses operationType '${filterRef.operationType}'. Use '${expectedOperationType}' instead.${propertyDefinition.type === "enumeration" ? " For enumeration properties use operators like IS_ANY_OF or IS_NONE_OF." : ""}`
11729
+ });
11730
+ }
11731
+ for (const suppressionListId of suppressionListIds ?? []) {
11732
+ if (listIds.has(String(suppressionListId))) continue;
11733
+ add({
11734
+ severity: "error",
11735
+ operation_index: operationIndex,
11736
+ code: "WORKFLOW_SUPPRESSION_LIST_NOT_FOUND",
11737
+ message: `Workflow suppression list '${suppressionListId}' was not found in HubSpot`
11738
+ });
11739
+ }
11740
+ return issues;
11741
+ };
11742
+
11579
11743
  // ../shared/pure/workflow-action-types.ts
11580
11744
  var BUILT_IN_ACTION_TYPES = {
11581
11745
  "0-1": {
@@ -11648,10 +11812,12 @@ var BUILT_IN_ACTION_TYPES = {
11648
11812
  "0-11": {
11649
11813
  actionTypeId: "0-11",
11650
11814
  name: "Rotate record to owner",
11651
- description: "Evenly distribute enrolled records to selected users or teams",
11815
+ description: "Evenly distribute enrolled records to selected users or teams. IMPORTANT: user_ids must contain HubSpot user IDs (owners table user_id column), NOT owner IDs (owners table owner_id column) \u2014 these are different values for the same person. Using owner IDs will cause an internal HubSpot error. CONTACT_FLOW requires all three fields (user_ids, target_property, overwrite_current_owner). PLATFORM_FLOW auto-fills target_property and overwrite_current_owner when only user_ids is provided.",
11652
11816
  connectionType: "SINGLE_CONNECTION",
11653
11817
  fields: [
11654
- { name: "user_ids", type: "array", required: true, description: "Array of user ID strings to rotate between" }
11818
+ { name: "user_ids", type: "array", required: true, description: "Array of HubSpot user ID strings to rotate between (can be empty array). IMPORTANT: query the owners table and use the user_id column, NOT owner_id. owner_id and user_id are different values \u2014 using owner_id will cause HubSpot API errors." },
11819
+ { name: "target_property", type: "string", required: true, description: "Owner property to set, e.g. 'hubspot_owner_id'. Required on CONTACT_FLOW; auto-filled on PLATFORM_FLOW." },
11820
+ { name: "overwrite_current_owner", type: "string", required: true, description: "'true' or 'false' \u2014 whether to overwrite existing owner. Required on CONTACT_FLOW; auto-filled as 'false' on PLATFORM_FLOW." }
11655
11821
  ]
11656
11822
  },
11657
11823
  "0-14": {
@@ -11669,11 +11835,12 @@ var BUILT_IN_ACTION_TYPES = {
11669
11835
  "0-15": {
11670
11836
  actionTypeId: "0-15",
11671
11837
  name: "Go to workflow",
11672
- description: "Enroll record in another workflow of the same type",
11838
+ description: "Enroll record in another workflow of the same type. API-INCOMPATIBLE: HubSpot V4 API returns 500 on all workflow types (contact, deal, ticket) regardless of field configuration (flow_id, valid/invalid IDs, empty fields, all versions). Create without actions and configure in HubSpot UI.",
11673
11839
  connectionType: "SINGLE_CONNECTION",
11674
11840
  fields: [
11675
- { name: "workflow_id", type: "string", required: true, description: "ID of the target workflow" }
11676
- ]
11841
+ { name: "flow_id", type: "string", required: true, description: "ID of the target workflow (only configurable via HubSpot UI)" }
11842
+ ],
11843
+ apiIncompatible: true
11677
11844
  },
11678
11845
  "0-18": {
11679
11846
  actionTypeId: "0-18",
@@ -11692,18 +11859,21 @@ var BUILT_IN_ACTION_TYPES = {
11692
11859
  "0-23": {
11693
11860
  actionTypeId: "0-23",
11694
11861
  name: "Send internal marketing email",
11695
- description: "Send automated emails internally to other HubSpot users",
11862
+ description: "Send automated emails internally to other HubSpot users. API-INCOMPATIBLE: HubSpot V4 API returns 500 on all workflow types regardless of field configuration (any email_content_id, recipient_emails combos, empty fields, all versions). Contrast: 0-4 (automated email) accepts content_id. Create without actions and configure in HubSpot UI.",
11696
11863
  connectionType: "SINGLE_CONNECTION",
11697
11864
  fields: [
11698
- { name: "content_id", type: "string", required: true, description: "ID of the marketing email" }
11699
- ]
11865
+ { name: "email_content_id", type: "string", required: true, description: "ID of the marketing email (only configurable via HubSpot UI)" },
11866
+ { name: "recipient_emails", type: "array", required: true, description: "Array of email address strings for internal recipients" }
11867
+ ],
11868
+ apiIncompatible: true
11700
11869
  },
11701
11870
  "0-29": {
11702
11871
  actionTypeId: "0-29",
11703
11872
  name: "Delay until an event occurs",
11704
- description: "Delay enrolled records until a specified event occurs",
11873
+ description: "Delay enrolled records until a specified event occurs. API-INCOMPATIBLE: HubSpot V4 API returns 500 on all workflow types regardless of field configuration (empty, event filters, delay-style fields, all versions). Create without actions and configure in HubSpot UI.",
11705
11874
  connectionType: "SINGLE_CONNECTION",
11706
- fields: []
11875
+ fields: [],
11876
+ apiIncompatible: true
11707
11877
  },
11708
11878
  "0-30": {
11709
11879
  actionTypeId: "0-30",
@@ -11808,7 +11978,7 @@ var BUILT_IN_ACTION_TYPES = {
11808
11978
  "0-169425243": {
11809
11979
  actionTypeId: "0-169425243",
11810
11980
  name: "Create note",
11811
- description: "Create and associate a note with the enrolled record",
11981
+ description: "Create and associate a note with the enrolled record. Field schema is undocumented \u2014 do NOT pass fields via the API. Add this action without fields and configure it in the HubSpot UI.",
11812
11982
  connectionType: "SINGLE_CONNECTION",
11813
11983
  fields: []
11814
11984
  },
@@ -11822,7 +11992,7 @@ var BUILT_IN_ACTION_TYPES = {
11822
11992
  "0-225935194": {
11823
11993
  actionTypeId: "0-225935194",
11824
11994
  name: "Validate and format phone number",
11825
- description: "Format phone numbers for calling compatibility",
11995
+ description: "Format phone numbers for calling compatibility. Accepts property-to-validate and default-country-code settings, but exact API field names are undocumented \u2014 do NOT pass fields via the API. Add this action without fields and configure it in the HubSpot UI.",
11826
11996
  connectionType: "SINGLE_CONNECTION",
11827
11997
  fields: []
11828
11998
  },
@@ -11946,202 +12116,232 @@ var BUILT_IN_ACTION_TYPES = {
11946
12116
  fields: []
11947
12117
  }
11948
12118
  };
12119
+ var isApiIncompatibleActionType = (actionTypeId) => BUILT_IN_ACTION_TYPES[actionTypeId]?.apiIncompatible === true;
11949
12120
 
11950
- // ../shared/pure/workflow-event-types.ts
11951
- var WORKFLOW_EVENT_TYPES = {
11952
- "4-655002": { eventTypeId: "4-655002", name: "Property value changed", description: "Triggers when a property value is updated on a CRM record", requiresRefinementFilters: true, refinementNote: "Requires event-specific refinement filters (e.g. hs_property_name, hs_property_value) embedded in the event filter branch to specify which property and value to trigger on. Standard PROPERTY filters are NOT sufficient. Prefer LIST_BASED enrollment with re-enrollment triggers for 'property changed to X' scenarios." },
11953
- "4-1463224": { eventTypeId: "4-1463224", name: "CRM Object created", description: "Triggers when a new object is created in the CRM" },
11954
- "4-1553675": { eventTypeId: "4-1553675", name: "Ad interaction", description: "Triggers when an ad is interacted with" },
11955
- "4-1741072": { eventTypeId: "4-1741072", name: "Call ended", description: "Triggers when a call has ended" },
11956
- "4-1733817": { eventTypeId: "4-1733817", name: "Call started", description: "Triggers when a call is initiated" },
11957
- "4-1814177": { eventTypeId: "4-1814177", name: "Playbook log", description: "Triggers when a playbook is logged on a record" },
11958
- "6-2117363": { eventTypeId: "6-2117363", name: "Custom event occurs", description: "Triggers when a custom event occurs with an existing contact record" },
11959
- "4-666288": { eventTypeId: "4-666288", name: "Clicked link in email", description: "Triggers when a contact clicks a link in a marketing email" },
11960
- "4-5470331": { eventTypeId: "4-5470331", name: "Email bounced", description: "Triggers when an email bounces" },
11961
- "4-665536": { eventTypeId: "4-665536", name: "Email delivered", description: "Triggers when an email is delivered to a contact" },
11962
- "4-667638": { eventTypeId: "4-667638", name: "Email sent", description: "Triggers when an email is sent to a contact" },
11963
- "4-666440": { eventTypeId: "4-666440", name: "Opened email", description: "Triggers when a contact opens a marketing email" },
11964
- "4-665538": { eventTypeId: "4-665538", name: "Replied to email", description: "Triggers when a contact replies to a marketing email" },
11965
- "4-666289": { eventTypeId: "4-666289", name: "Updated email subscription status", description: "Triggers when a contact updates their email subscription status" },
11966
- "4-1639799": { eventTypeId: "4-1639799", name: "Form interaction", description: "Triggers when a contact interacts with a form field" },
11967
- "4-1639801": { eventTypeId: "4-1639801", name: "Form submission", description: "Triggers when a form is submitted" },
11968
- "4-1639797": { eventTypeId: "4-1639797", name: "Form view", description: "Triggers when a contact views a form" },
11969
- "4-1720599": { eventTypeId: "4-1720599", name: "Meeting booked", description: "Triggers when a contact books a meeting" },
11970
- "4-1724222": { eventTypeId: "4-1724222", name: "Meeting outcome change", description: "Triggers when a meeting outcome is changed" },
11971
- "4-1522438": { eventTypeId: "4-1522438", name: "Contact finished viewing a document", description: "Triggers when a contact finishes viewing a sales document" },
11972
- "4-1522436": { eventTypeId: "4-1522436", name: "Contact viewed a document", description: "Triggers when a contact views a sales document" },
11973
- "4-1522437": { eventTypeId: "4-1522437", name: "Document shared with contact", description: "Triggers when a sales document is shared with a contact" },
11974
- "4-675773": { eventTypeId: "4-675773", name: "Contact booked a meeting through a sequence", description: "Triggers when a contact books a meeting through a sequence" },
11975
- "4-675777": { eventTypeId: "4-675777", name: "Contact enrolled in a sequence", description: "Triggers when a contact is enrolled in a sequence" },
11976
- "4-675778": { eventTypeId: "4-675778", name: "Contact finished sequence", description: "Triggers when a contact finishes a sequence" },
11977
- "4-675776": { eventTypeId: "4-675776", name: "Contact unenrolled from sequence", description: "Triggers when a contact is unenrolled from a sequence" },
11978
- "4-1497402": { eventTypeId: "4-1497402", name: "Contact unenrolled from sequence via workflow", description: "Triggers when a contact is unenrolled from a sequence via a workflow" },
11979
- "4-675775": { eventTypeId: "4-675775", name: "Contact unenrolled from sequence manually", description: "Triggers when a contact is manually unenrolled from a sequence" },
11980
- "4-1752910": { eventTypeId: "4-1752910", name: "Delivering SMS failed", description: "Triggers when an SMS message was not delivered" },
11981
- "4-1722276": { eventTypeId: "4-1722276", name: "Link in SMS clicked", description: "Triggers when a contact clicks a link in an SMS" },
11982
- "4-1752904": { eventTypeId: "4-1752904", name: "Sending SMS failed", description: "Triggers when an SMS message was not sent" },
11983
- "4-1721168": { eventTypeId: "4-1721168", name: "SMS delivered", description: "Triggers when an SMS message is delivered" },
11984
- "4-1725149": { eventTypeId: "4-1725149", name: "SMS dropped", description: "Triggers when an SMS message is dropped" },
11985
- "4-1719453": { eventTypeId: "4-1719453", name: "SMS sent", description: "Triggers when an SMS message is sent" },
11986
- "4-1555805": { eventTypeId: "4-1555805", name: "CTA click", description: "Triggers when a contact clicks a CTA or Web interactive" },
11987
- "4-1555804": { eventTypeId: "4-1555804", name: "CTA viewed", description: "Triggers when a contact views a CTA or Web interactive" },
11988
- "4-100216": { eventTypeId: "4-100216", name: "CTA click (legacy)", description: "Triggers when a contact clicks a CTA (legacy)" },
11989
- "4-100215": { eventTypeId: "4-100215", name: "CTA view (legacy)", description: "Triggers when a contact views a CTA (legacy)" },
11990
- "4-96000": { eventTypeId: "4-96000", name: "Page visited", description: "Triggers when a contact views a landing page or website page" },
11991
- "4-675783": { eventTypeId: "4-675783", name: "Website or Email Media Play", description: "Triggers when a contact plays embedded media" },
11992
- "4-1753168": { eventTypeId: "4-1753168", name: "Achieved workflow goal", description: "Triggers when a contact meets a workflow goal" },
11993
- "4-1466013": { eventTypeId: "4-1466013", name: "Enrolled in workflow", description: "Triggers when an object is enrolled in a workflow" },
11994
- "4-1466014": { eventTypeId: "4-1466014", name: "Unenrolled from workflow", description: "Triggers when an object is unenrolled from a workflow" }
12121
+ // ../shared/pure/workflow-validation/actions.ts
12122
+ var getNestedNextActionId = (value) => {
12123
+ if (!isRecord(value)) return void 0;
12124
+ if (typeof value.nextActionId === "string") return value.nextActionId;
12125
+ const connection = isRecord(value.connection) ? value.connection : void 0;
12126
+ return typeof connection?.nextActionId === "string" ? connection.nextActionId : void 0;
11995
12127
  };
11996
- var KNOWN_EVENT_TYPE_IDS = new Set(Object.keys(WORKFLOW_EVENT_TYPES));
11997
- var isValidEventTypeIdFormat = (id) => /^[46]-\d+$/.test(id);
11998
- var EVENT_TYPES_REQUIRING_REFINEMENT = new Set(
11999
- Object.values(WORKFLOW_EVENT_TYPES).filter((et) => et.requiresRefinementFilters).map((et) => et.eventTypeId)
12000
- );
12001
-
12002
- // ../shared/pure/workflow-operation-validation.ts
12003
- var OPERATION_TYPE_FOR_PROPERTY_TYPE2 = {
12004
- enumeration: "ENUMERATION",
12005
- string: "MULTISTRING",
12006
- number: "NUMBER",
12007
- bool: "BOOL",
12008
- date: "TIME_POINT",
12009
- datetime: "TIME_POINT"
12128
+ var collectActionNextActionIds = (action) => {
12129
+ const nextActionIds = [];
12130
+ if (action.connection?.nextActionId) {
12131
+ nextActionIds.push(action.connection.nextActionId);
12132
+ }
12133
+ for (const branch of action.staticBranches ?? []) {
12134
+ const nextActionId = getNestedNextActionId(branch);
12135
+ if (nextActionId) nextActionIds.push(nextActionId);
12136
+ }
12137
+ for (const branch of action.listBranches ?? []) {
12138
+ const nextActionId = getNestedNextActionId(branch);
12139
+ if (nextActionId) nextActionIds.push(nextActionId);
12140
+ }
12141
+ const defaultBranchNextActionId = getNestedNextActionId(action.defaultBranch);
12142
+ if (defaultBranchNextActionId) {
12143
+ nextActionIds.push(defaultBranchNextActionId);
12144
+ }
12145
+ return nextActionIds;
12010
12146
  };
12011
- var isRecord = (value) => typeof value === "object" && value !== null;
12012
- var asBranchArray = (value) => Array.isArray(value) ? value.filter(isRecord) : [];
12013
- var isCompatibleEnrollmentOperationType = (operationType, expectedOperationType, propertyType) => operationType === expectedOperationType || operationType === "ALL_PROPERTY" || operationType === "TIME_RANGED" && (propertyType === "date" || propertyType === "datetime");
12014
- var collectPropertyFiltersFromBranches = (branches, parentObjectTypeId) => {
12015
- const refs = [];
12016
- for (const branch of branches) {
12017
- const contextObjectTypeId = branch.filterBranchType === "ASSOCIATION" && typeof branch.objectTypeId === "string" ? branch.objectTypeId : parentObjectTypeId;
12018
- const filters = asBranchArray(branch.filters);
12019
- for (const filter of filters) {
12020
- if (filter.filterType !== "PROPERTY" || typeof filter.property !== "string") continue;
12021
- const operation = isRecord(filter.operation) ? filter.operation : void 0;
12022
- if (!operation || typeof operation.operationType !== "string") continue;
12023
- refs.push({
12024
- property: filter.property,
12025
- operationType: operation.operationType,
12026
- objectTypeId: contextObjectTypeId
12147
+ var validateStaticValueType = (params) => {
12148
+ const { action, actionId, operationIndex } = params;
12149
+ const issues = [];
12150
+ const fields48 = action.fields;
12151
+ if (!fields48) return issues;
12152
+ const actionTypeId = action.actionTypeId ?? "";
12153
+ if (actionTypeId === "0-5") {
12154
+ const value = fields48.value;
12155
+ if (isRecord(value) && "staticValue" in value && value.type !== "STATIC_VALUE") {
12156
+ issues.push({
12157
+ severity: "error",
12158
+ operation_index: operationIndex,
12159
+ code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12160
+ message: `Action '${actionId || "<missing>"}' (0-5 set property) has a value with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12027
12161
  });
12028
12162
  }
12029
- const nestedBranches = asBranchArray(branch.filterBranches);
12030
- if (nestedBranches.length > 0) {
12031
- refs.push(...collectPropertyFiltersFromBranches(nestedBranches, contextObjectTypeId));
12032
- }
12033
12163
  }
12034
- return refs;
12035
- };
12036
- var collectWorkflowEnrollmentPropertyFilters = (enrollmentCriteria, rootObjectTypeId) => {
12037
- if (!enrollmentCriteria) return [];
12038
- return [
12039
- ...collectPropertyFiltersFromBranches(asBranchArray(enrollmentCriteria.eventFilterBranches), rootObjectTypeId),
12040
- ...collectPropertyFiltersFromBranches(asBranchArray(enrollmentCriteria.listMembershipFilterBranches), rootObjectTypeId),
12041
- ...collectPropertyFiltersFromBranches(
12042
- isRecord(enrollmentCriteria.listFilterBranch) ? [enrollmentCriteria.listFilterBranch] : [],
12043
- rootObjectTypeId
12044
- ),
12045
- ...collectPropertyFiltersFromBranches(
12046
- asBranchArray(enrollmentCriteria.reEnrollmentTriggersFilterBranches),
12047
- rootObjectTypeId
12048
- )
12049
- ];
12050
- };
12051
- var validateWorkflowEnrollmentMetadata = ({
12052
- enrollmentCriteria,
12053
- suppressionListIds,
12054
- objectTypeId,
12055
- enabledObjectTypeIds,
12056
- propertyDefinitionsByObjectType,
12057
- listIds,
12058
- operationIndex
12059
- }) => {
12060
- const issues = [];
12061
- const seen = /* @__PURE__ */ new Set();
12062
- const addIssue = (issue) => {
12063
- const key = `${issue.code}:${issue.message}`;
12064
- if (seen.has(key)) return;
12065
- seen.add(key);
12066
- issues.push(issue);
12067
- };
12068
- const filterRefs = collectWorkflowEnrollmentPropertyFilters(enrollmentCriteria, objectTypeId);
12069
- const referencedObjectTypeIds = /* @__PURE__ */ new Set([
12070
- objectTypeId,
12071
- ...filterRefs.map((filterRef) => filterRef.objectTypeId)
12072
- ]);
12073
- for (const referencedObjectTypeId of referencedObjectTypeIds) {
12074
- if (enabledObjectTypeIds.has(referencedObjectTypeId)) continue;
12075
- addIssue({
12076
- severity: "error",
12077
- operation_index: operationIndex,
12078
- code: "WORKFLOW_OBJECT_TYPE_NOT_ENABLED",
12079
- message: `Workflow references object type '${referencedObjectTypeId}', but it is not enabled in HubSpot`
12080
- });
12164
+ if (actionTypeId === "0-14") {
12165
+ const properties = fields48.properties;
12166
+ if (Array.isArray(properties)) {
12167
+ for (const prop of properties) {
12168
+ if (!isRecord(prop)) continue;
12169
+ const value = prop.value;
12170
+ if (isRecord(value) && "staticValue" in value && value.type !== "STATIC_VALUE") {
12171
+ const targetProperty = typeof prop.targetProperty === "string" ? prop.targetProperty : "<unknown>";
12172
+ issues.push({
12173
+ severity: "error",
12174
+ operation_index: operationIndex,
12175
+ code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12176
+ message: `Action '${actionId || "<missing>"}' (0-14 create record) property '${targetProperty}' has a value with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12177
+ });
12178
+ }
12179
+ }
12180
+ }
12081
12181
  }
12082
- for (const filterRef of filterRefs) {
12083
- if (!enabledObjectTypeIds.has(filterRef.objectTypeId)) continue;
12084
- const propertyDefinitions = propertyDefinitionsByObjectType.get(filterRef.objectTypeId);
12085
- const propertyDefinition = propertyDefinitions?.get(filterRef.property);
12086
- if (!propertyDefinition) {
12087
- addIssue({
12182
+ if (actionTypeId === "0-35") {
12183
+ const date = fields48.date;
12184
+ if (isRecord(date) && "staticValue" in date && date.type !== "STATIC_VALUE") {
12185
+ issues.push({
12088
12186
  severity: "error",
12089
12187
  operation_index: operationIndex,
12090
- code: "WORKFLOW_ENROLLMENT_PROPERTY_NOT_FOUND",
12091
- message: `Workflow enrollment filter property '${filterRef.property}' was not found for object type '${filterRef.objectTypeId}'`
12188
+ code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12189
+ message: `Action '${actionId || "<missing>"}' (0-35 delay until date) has a date with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12092
12190
  });
12093
- continue;
12094
- }
12095
- const expectedOperationType = OPERATION_TYPE_FOR_PROPERTY_TYPE2[propertyDefinition.type];
12096
- if (!expectedOperationType) continue;
12097
- if (isCompatibleEnrollmentOperationType(filterRef.operationType, expectedOperationType, propertyDefinition.type)) {
12098
- continue;
12099
12191
  }
12100
- addIssue({
12192
+ }
12193
+ return issues;
12194
+ };
12195
+ var validateDelayUntilDateAction = (params) => {
12196
+ const { action, actionId, operationIndex } = params;
12197
+ if (action.actionTypeId !== "0-35") return [];
12198
+ const fields48 = action.fields;
12199
+ if (!fields48) return [];
12200
+ const issues = [];
12201
+ const timeUnit = fields48.time_unit;
12202
+ if (timeUnit !== void 0 && timeUnit !== "DAYS" && timeUnit !== "HOURS" && timeUnit !== "MINUTES") {
12203
+ issues.push({
12101
12204
  severity: "error",
12102
12205
  operation_index: operationIndex,
12103
- code: "INVALID_ENROLLMENT_FILTER_OPERATION_TYPE",
12104
- message: `Workflow enrollment filter property '${filterRef.property}' has type '${propertyDefinition.type}' but uses operationType '${filterRef.operationType}'. Use '${expectedOperationType}' instead.${propertyDefinition.type === "enumeration" ? " For enumeration properties use operators like IS_ANY_OF or IS_NONE_OF." : ""}`
12206
+ code: "WORKFLOW_ACTION_INVALID_TIME_UNIT",
12207
+ message: `Action '${actionId || "<missing>"}' (0-35 delay until date) has invalid time_unit '${String(timeUnit)}'. Use DAYS, HOURS, or MINUTES.`
12105
12208
  });
12106
12209
  }
12107
- for (const suppressionListId of suppressionListIds ?? []) {
12108
- if (listIds.has(String(suppressionListId))) continue;
12109
- addIssue({
12210
+ const timeOfDay = fields48.time_of_day;
12211
+ if (!isRecord(timeOfDay)) return issues;
12212
+ if (!isIntegerInRange(timeOfDay.hour, 0, 23) || !isIntegerInRange(timeOfDay.minute, 0, 59)) {
12213
+ issues.push({
12110
12214
  severity: "error",
12111
12215
  operation_index: operationIndex,
12112
- code: "WORKFLOW_SUPPRESSION_LIST_NOT_FOUND",
12113
- message: `Workflow suppression list '${suppressionListId}' was not found in HubSpot`
12216
+ code: "WORKFLOW_ACTION_INVALID_TIME_OF_DAY",
12217
+ message: `Action '${actionId || "<missing>"}' (0-35 delay until date) must use time_of_day with hour 0-23 and minute 0-59.`
12114
12218
  });
12115
12219
  }
12116
12220
  return issues;
12117
12221
  };
12118
- var getNestedNextActionId = (value) => {
12119
- if (!isRecord(value)) return void 0;
12120
- if (typeof value.nextActionId === "string") return value.nextActionId;
12121
- const connection = isRecord(value.connection) ? value.connection : void 0;
12122
- return typeof connection?.nextActionId === "string" ? connection.nextActionId : void 0;
12123
- };
12124
- var collectActionNextActionIds = (action) => {
12125
- const nextActionIds = [];
12126
- if (action.connection?.nextActionId) {
12127
- nextActionIds.push(action.connection.nextActionId);
12222
+ var validateRotateToOwnerAction = (params) => {
12223
+ const { action, actionId, operationIndex } = params;
12224
+ if (action.actionTypeId !== "0-11") return [];
12225
+ const fields48 = action.fields;
12226
+ if (!fields48) return [];
12227
+ const issues = [];
12228
+ const overwrite = fields48.overwrite_current_owner;
12229
+ if (overwrite !== void 0 && overwrite !== "true" && overwrite !== "false") {
12230
+ issues.push({
12231
+ severity: "error",
12232
+ operation_index: operationIndex,
12233
+ code: "WORKFLOW_ACTION_INVALID_OVERWRITE_OWNER",
12234
+ message: `Action '${actionId || "<missing>"}' (0-11 rotate to owner) has invalid overwrite_current_owner '${String(overwrite)}'. Use 'true' or 'false'.`
12235
+ });
12128
12236
  }
12129
- for (const branch of action.staticBranches ?? []) {
12130
- const nextActionId = getNestedNextActionId(branch);
12131
- if (nextActionId) nextActionIds.push(nextActionId);
12237
+ const userIds = fields48.user_ids;
12238
+ if (userIds !== void 0 && !Array.isArray(userIds)) {
12239
+ issues.push({
12240
+ severity: "error",
12241
+ operation_index: operationIndex,
12242
+ code: "WORKFLOW_ACTION_INVALID_USER_IDS",
12243
+ message: `Action '${actionId || "<missing>"}' (0-11 rotate to owner) user_ids must be an array of user ID strings.`
12244
+ });
12132
12245
  }
12133
- for (const branch of action.listBranches ?? []) {
12134
- const nextActionId = getNestedNextActionId(branch);
12135
- if (nextActionId) nextActionIds.push(nextActionId);
12246
+ return issues;
12247
+ };
12248
+ var validateWorkflowActions = (params) => {
12249
+ const { actions, operationIndex } = params;
12250
+ const issues = [];
12251
+ const actionIds = /* @__PURE__ */ new Set();
12252
+ for (const action of actions) {
12253
+ const actionId = action.actionId?.trim() ?? "";
12254
+ if (actionId.length === 0) {
12255
+ issues.push({
12256
+ severity: "error",
12257
+ operation_index: operationIndex,
12258
+ code: "WORKFLOW_ACTION_ID_REQUIRED",
12259
+ message: "Workflow actions must include a non-empty actionId"
12260
+ });
12261
+ } else {
12262
+ if (actionIds.has(actionId)) {
12263
+ issues.push({
12264
+ severity: "error",
12265
+ operation_index: operationIndex,
12266
+ code: "DUPLICATE_ACTION_ID",
12267
+ message: `Duplicate actionId '${actionId}' in workflow actions`
12268
+ });
12269
+ }
12270
+ actionIds.add(actionId);
12271
+ }
12272
+ const builtInActionType = action.actionTypeId ? BUILT_IN_ACTION_TYPES[action.actionTypeId] : void 0;
12273
+ if (!builtInActionType) continue;
12274
+ if (action.type && action.type !== builtInActionType.connectionType) {
12275
+ issues.push({
12276
+ severity: "error",
12277
+ operation_index: operationIndex,
12278
+ code: "WORKFLOW_ACTION_TYPE_MISMATCH",
12279
+ message: `Action '${actionId || "<missing>"}' uses type '${action.type}' but actionTypeId '${builtInActionType.actionTypeId}' requires '${builtInActionType.connectionType}'`
12280
+ });
12281
+ }
12282
+ if (isApiIncompatibleActionType(builtInActionType.actionTypeId)) {
12283
+ const providedFields = action.fields ? Object.keys(action.fields) : [];
12284
+ if (providedFields.length > 0) {
12285
+ issues.push({
12286
+ severity: "error",
12287
+ operation_index: operationIndex,
12288
+ code: "WORKFLOW_ACTION_API_INCOMPATIBLE",
12289
+ message: `Action '${actionId || "<missing>"}' of type '${builtInActionType.actionTypeId}' ('${builtInActionType.name}') cannot be configured via the API \u2014 HubSpot returns 500 regardless of field values. Remove the fields and configure this action in the HubSpot UI after creation.`
12290
+ });
12291
+ }
12292
+ continue;
12293
+ }
12294
+ for (const field of builtInActionType.fields) {
12295
+ if (!field.required) continue;
12296
+ if (!isMissingRequiredField(action.fields?.[field.name])) continue;
12297
+ issues.push({
12298
+ severity: "error",
12299
+ operation_index: operationIndex,
12300
+ code: "WORKFLOW_ACTION_MISSING_FIELD",
12301
+ message: `Action '${actionId || "<missing>"}' of type '${builtInActionType.actionTypeId}' is missing required field '${field.name}'`
12302
+ });
12303
+ }
12304
+ const providedFieldNames = action.fields ? Object.keys(action.fields) : [];
12305
+ if (providedFieldNames.length > 0) {
12306
+ const knownFieldNames = new Set(builtInActionType.fields.map((f) => f.name));
12307
+ if (knownFieldNames.size === 0) {
12308
+ issues.push({
12309
+ severity: "error",
12310
+ operation_index: operationIndex,
12311
+ code: "WORKFLOW_ACTION_UNDOCUMENTED_FIELDS",
12312
+ message: `Action '${actionId || "<missing>"}' of type '${builtInActionType.actionTypeId}' ('${builtInActionType.name}') has no documented field schema \u2014 provided fields [${providedFieldNames.join(", ")}] will likely be rejected by HubSpot. Remove the fields or configure this action via the HubSpot UI instead.`
12313
+ });
12314
+ } else {
12315
+ const unknownFields = providedFieldNames.filter((f) => !knownFieldNames.has(f));
12316
+ if (unknownFields.length > 0) {
12317
+ issues.push({
12318
+ severity: "warning",
12319
+ operation_index: operationIndex,
12320
+ code: "WORKFLOW_ACTION_UNKNOWN_FIELDS",
12321
+ message: `Action '${actionId || "<missing>"}' of type '${builtInActionType.actionTypeId}' ('${builtInActionType.name}') has fields not in the documented schema: [${unknownFields.join(", ")}]. These may be rejected by HubSpot. Known fields: [${[...knownFieldNames].join(", ")}]`
12322
+ });
12323
+ }
12324
+ }
12325
+ }
12326
+ issues.push(...validateStaticValueType({ action, actionId, operationIndex }));
12327
+ issues.push(...validateDelayUntilDateAction({ action, actionId, operationIndex }));
12328
+ issues.push(...validateRotateToOwnerAction({ action, actionId, operationIndex }));
12136
12329
  }
12137
- const defaultBranchNextActionId = getNestedNextActionId(action.defaultBranch);
12138
- if (defaultBranchNextActionId) {
12139
- nextActionIds.push(defaultBranchNextActionId);
12330
+ for (const action of actions) {
12331
+ for (const nextActionId of collectActionNextActionIds(action)) {
12332
+ if (actionIds.has(nextActionId)) continue;
12333
+ issues.push({
12334
+ severity: "error",
12335
+ operation_index: operationIndex,
12336
+ code: "INVALID_NEXT_ACTION_REF",
12337
+ message: `Action '${action.actionId ?? "<missing>"}' references nextActionId '${nextActionId}' which does not exist`
12338
+ });
12339
+ }
12140
12340
  }
12141
- return nextActionIds;
12341
+ return issues;
12142
12342
  };
12143
- var isMissingRequiredField = (value) => value === void 0 || value === null || value === "";
12144
- var toAssociationDirectionKey = (fromObjectTypeId, toObjectTypeId3) => `${fromObjectTypeId}->${toObjectTypeId3}`;
12343
+
12344
+ // ../shared/pure/workflow-validation/associated-actions.ts
12145
12345
  var collectWorkflowAssociatedPropertyActionRefs = (actions) => actions.flatMap((action) => {
12146
12346
  const actionTypeId = typeof action.actionTypeId === "string" ? action.actionTypeId : "";
12147
12347
  if (actionTypeId !== "0-5") return [];
@@ -12156,16 +12356,17 @@ var collectWorkflowAssociatedPropertyActionRefs = (actions) => actions.flatMap((
12156
12356
  associationTypeId
12157
12357
  }];
12158
12358
  });
12159
- var validateWorkflowAssociatedPropertyActionMetadata = ({
12160
- actions,
12161
- rootObjectTypeId,
12162
- enabledObjects,
12163
- propertyDefinitionsByObjectType,
12164
- associationTypeIdsByDirection,
12165
- failedPropertyDefinitionObjectTypeIds,
12166
- failedAssociationDirectionKeys,
12167
- operationIndex
12168
- }) => {
12359
+ var validateWorkflowAssociatedPropertyActionMetadata = (params) => {
12360
+ const {
12361
+ actions,
12362
+ rootObjectTypeId,
12363
+ enabledObjects,
12364
+ propertyDefinitionsByObjectType,
12365
+ associationTypeIdsByDirection,
12366
+ failedPropertyDefinitionObjectTypeIds,
12367
+ failedAssociationDirectionKeys,
12368
+ operationIndex
12369
+ } = params;
12169
12370
  const issues = [];
12170
12371
  const rootObject = enabledObjects.find((candidate) => candidate.objectTypeId === rootObjectTypeId);
12171
12372
  if (!rootObject) return issues;
@@ -12249,176 +12450,60 @@ var validateWorkflowAssociatedPropertyActionMetadata = ({
12249
12450
  }
12250
12451
  return issues;
12251
12452
  };
12252
- var validateStaticValueType = (action, actionId, operationIndex) => {
12253
- const issues = [];
12254
- const fields48 = action.fields;
12255
- if (!fields48) return issues;
12256
- const actionTypeId = action.actionTypeId ?? "";
12257
- if (actionTypeId === "0-5") {
12258
- const value = fields48.value;
12259
- if (isRecord(value) && "staticValue" in value && value.type !== "STATIC_VALUE") {
12260
- issues.push({
12261
- severity: "error",
12262
- operation_index: operationIndex,
12263
- code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12264
- message: `Action '${actionId || "<missing>"}' (0-5 set property) has a value with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12265
- });
12266
- }
12267
- }
12268
- if (actionTypeId === "0-14") {
12269
- const properties = fields48.properties;
12270
- if (Array.isArray(properties)) {
12271
- for (const prop of properties) {
12272
- if (!isRecord(prop)) continue;
12273
- const value = prop.value;
12274
- if (isRecord(value) && "staticValue" in value && value.type !== "STATIC_VALUE") {
12275
- const targetProperty = typeof prop.targetProperty === "string" ? prop.targetProperty : "<unknown>";
12276
- issues.push({
12277
- severity: "error",
12278
- operation_index: operationIndex,
12279
- code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12280
- message: `Action '${actionId || "<missing>"}' (0-14 create record) property '${targetProperty}' has a value with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12281
- });
12282
- }
12283
- }
12284
- }
12285
- }
12286
- if (actionTypeId === "0-35") {
12287
- const date = fields48.date;
12288
- if (isRecord(date) && "staticValue" in date && date.type !== "STATIC_VALUE") {
12289
- issues.push({
12290
- severity: "error",
12291
- operation_index: operationIndex,
12292
- code: "WORKFLOW_ACTION_VALUE_MISSING_TYPE",
12293
- message: `Action '${actionId || "<missing>"}' (0-35 delay until date) has a date with staticValue but missing type: "STATIC_VALUE". HubSpot returns HTTP 500 without it.`
12294
- });
12295
- }
12296
- }
12297
- return issues;
12298
- };
12299
- var isIntegerInRange = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
12300
- var validateDelayUntilDateAction = (action, actionId, operationIndex) => {
12301
- if (action.actionTypeId !== "0-35") return [];
12302
- const fields48 = action.fields;
12303
- if (!fields48) return [];
12304
- const issues = [];
12305
- const timeUnit = fields48.time_unit;
12306
- if (timeUnit !== void 0 && timeUnit !== "DAYS" && timeUnit !== "HOURS" && timeUnit !== "MINUTES") {
12307
- issues.push({
12308
- severity: "error",
12309
- operation_index: operationIndex,
12310
- code: "WORKFLOW_ACTION_INVALID_TIME_UNIT",
12311
- message: `Action '${actionId || "<missing>"}' (0-35 delay until date) has invalid time_unit '${String(timeUnit)}'. Use DAYS, HOURS, or MINUTES.`
12312
- });
12313
- }
12314
- const timeOfDay = fields48.time_of_day;
12315
- if (!isRecord(timeOfDay)) return issues;
12316
- if (!isIntegerInRange(timeOfDay.hour, 0, 23) || !isIntegerInRange(timeOfDay.minute, 0, 59)) {
12317
- issues.push({
12318
- severity: "error",
12319
- operation_index: operationIndex,
12320
- code: "WORKFLOW_ACTION_INVALID_TIME_OF_DAY",
12321
- message: `Action '${actionId || "<missing>"}' (0-35 delay until date) must use time_of_day with hour 0-23 and minute 0-59.`
12322
- });
12323
- }
12324
- return issues;
12325
- };
12326
- var validateWorkflowActions = (actions, operationIndex) => {
12327
- const issues = [];
12328
- const actionIds = /* @__PURE__ */ new Set();
12329
- for (const action of actions) {
12330
- const actionId = action.actionId?.trim() ?? "";
12331
- if (actionId.length === 0) {
12332
- issues.push({
12333
- severity: "error",
12334
- operation_index: operationIndex,
12335
- code: "WORKFLOW_ACTION_ID_REQUIRED",
12336
- message: "Workflow actions must include a non-empty actionId"
12337
- });
12338
- } else {
12339
- if (actionIds.has(actionId)) {
12340
- issues.push({
12341
- severity: "error",
12342
- operation_index: operationIndex,
12343
- code: "DUPLICATE_ACTION_ID",
12344
- message: `Duplicate actionId '${actionId}' in workflow actions`
12345
- });
12346
- }
12347
- actionIds.add(actionId);
12348
- }
12349
- const builtInActionType = action.actionTypeId ? BUILT_IN_ACTION_TYPES[action.actionTypeId] : void 0;
12350
- if (!builtInActionType) continue;
12351
- if (action.type && action.type !== builtInActionType.connectionType) {
12352
- issues.push({
12353
- severity: "error",
12354
- operation_index: operationIndex,
12355
- code: "WORKFLOW_ACTION_TYPE_MISMATCH",
12356
- message: `Action '${actionId || "<missing>"}' uses type '${action.type}' but actionTypeId '${builtInActionType.actionTypeId}' requires '${builtInActionType.connectionType}'`
12357
- });
12358
- }
12359
- for (const field of builtInActionType.fields) {
12360
- if (!field.required) continue;
12361
- if (!isMissingRequiredField(action.fields?.[field.name])) continue;
12362
- issues.push({
12363
- severity: "error",
12364
- operation_index: operationIndex,
12365
- code: "WORKFLOW_ACTION_MISSING_FIELD",
12366
- message: `Action '${actionId || "<missing>"}' of type '${builtInActionType.actionTypeId}' is missing required field '${field.name}'`
12367
- });
12368
- }
12369
- issues.push(...validateStaticValueType(action, actionId, operationIndex));
12370
- issues.push(...validateDelayUntilDateAction(action, actionId, operationIndex));
12371
- }
12372
- for (const action of actions) {
12373
- for (const nextActionId of collectActionNextActionIds(action)) {
12374
- if (actionIds.has(nextActionId)) continue;
12375
- issues.push({
12376
- severity: "error",
12377
- operation_index: operationIndex,
12378
- code: "INVALID_NEXT_ACTION_REF",
12379
- message: `Action '${action.actionId ?? "<missing>"}' references nextActionId '${nextActionId}' which does not exist`
12380
- });
12381
- }
12382
- }
12383
- return issues;
12384
- };
12385
- var injectStaticValueType = (value) => {
12386
- if (!isRecord(value)) return value;
12387
- if ("staticValue" in value && value.type !== "STATIC_VALUE") {
12388
- return { ...value, type: "STATIC_VALUE" };
12389
- }
12390
- return value;
12391
- };
12392
- var normalizeWorkflowActionValues = (actions) => actions.map((action) => {
12393
- const fields48 = isRecord(action.fields) ? action.fields : void 0;
12394
- if (!fields48) return { ...action };
12395
- const actionTypeId = typeof action.actionTypeId === "string" ? action.actionTypeId : "";
12396
- if (actionTypeId === "0-5") {
12397
- return {
12398
- ...action,
12399
- fields: { ...fields48, value: injectStaticValueType(fields48.value) }
12400
- };
12401
- }
12402
- if (actionTypeId === "0-14" && Array.isArray(fields48.properties)) {
12403
- return {
12404
- ...action,
12405
- fields: {
12406
- ...fields48,
12407
- properties: fields48.properties.map((prop) => {
12408
- if (!isRecord(prop)) return prop;
12409
- return { ...prop, value: injectStaticValueType(prop.value) };
12410
- })
12411
- }
12412
- };
12413
- }
12414
- if (actionTypeId === "0-35") {
12415
- return {
12416
- ...action,
12417
- fields: { ...fields48, date: injectStaticValueType(fields48.date) }
12418
- };
12419
- }
12420
- return { ...action };
12421
- });
12453
+
12454
+ // ../shared/pure/workflow-event-types.ts
12455
+ var WORKFLOW_EVENT_TYPES = {
12456
+ "4-655002": { eventTypeId: "4-655002", name: "Property value changed", description: "Triggers when a property value is updated on a CRM record", requiresRefinementFilters: true, refinementNote: "Requires event-specific refinement filters (e.g. hs_property_name, hs_property_value) embedded in the event filter branch to specify which property and value to trigger on. Standard PROPERTY filters are NOT sufficient. Prefer LIST_BASED enrollment with re-enrollment triggers for 'property changed to X' scenarios." },
12457
+ "4-1463224": { eventTypeId: "4-1463224", name: "CRM Object created", description: "Triggers when a new object is created in the CRM" },
12458
+ "4-1553675": { eventTypeId: "4-1553675", name: "Ad interaction", description: "Triggers when an ad is interacted with" },
12459
+ "4-1741072": { eventTypeId: "4-1741072", name: "Call ended", description: "Triggers when a call has ended" },
12460
+ "4-1733817": { eventTypeId: "4-1733817", name: "Call started", description: "Triggers when a call is initiated" },
12461
+ "4-1814177": { eventTypeId: "4-1814177", name: "Playbook log", description: "Triggers when a playbook is logged on a record" },
12462
+ "6-2117363": { eventTypeId: "6-2117363", name: "Custom event occurs", description: "Triggers when a custom event occurs with an existing contact record" },
12463
+ "4-666288": { eventTypeId: "4-666288", name: "Clicked link in email", description: "Triggers when a contact clicks a link in a marketing email" },
12464
+ "4-5470331": { eventTypeId: "4-5470331", name: "Email bounced", description: "Triggers when an email bounces" },
12465
+ "4-665536": { eventTypeId: "4-665536", name: "Email delivered", description: "Triggers when an email is delivered to a contact" },
12466
+ "4-667638": { eventTypeId: "4-667638", name: "Email sent", description: "Triggers when an email is sent to a contact" },
12467
+ "4-666440": { eventTypeId: "4-666440", name: "Opened email", description: "Triggers when a contact opens a marketing email" },
12468
+ "4-665538": { eventTypeId: "4-665538", name: "Replied to email", description: "Triggers when a contact replies to a marketing email" },
12469
+ "4-666289": { eventTypeId: "4-666289", name: "Updated email subscription status", description: "Triggers when a contact updates their email subscription status" },
12470
+ "4-1639799": { eventTypeId: "4-1639799", name: "Form interaction", description: "Triggers when a contact interacts with a form field" },
12471
+ "4-1639801": { eventTypeId: "4-1639801", name: "Form submission", description: "Triggers when a form is submitted" },
12472
+ "4-1639797": { eventTypeId: "4-1639797", name: "Form view", description: "Triggers when a contact views a form" },
12473
+ "4-1720599": { eventTypeId: "4-1720599", name: "Meeting booked", description: "Triggers when a contact books a meeting" },
12474
+ "4-1724222": { eventTypeId: "4-1724222", name: "Meeting outcome change", description: "Triggers when a meeting outcome is changed" },
12475
+ "4-1522438": { eventTypeId: "4-1522438", name: "Contact finished viewing a document", description: "Triggers when a contact finishes viewing a sales document" },
12476
+ "4-1522436": { eventTypeId: "4-1522436", name: "Contact viewed a document", description: "Triggers when a contact views a sales document" },
12477
+ "4-1522437": { eventTypeId: "4-1522437", name: "Document shared with contact", description: "Triggers when a sales document is shared with a contact" },
12478
+ "4-675773": { eventTypeId: "4-675773", name: "Contact booked a meeting through a sequence", description: "Triggers when a contact books a meeting through a sequence" },
12479
+ "4-675777": { eventTypeId: "4-675777", name: "Contact enrolled in a sequence", description: "Triggers when a contact is enrolled in a sequence" },
12480
+ "4-675778": { eventTypeId: "4-675778", name: "Contact finished sequence", description: "Triggers when a contact finishes a sequence" },
12481
+ "4-675776": { eventTypeId: "4-675776", name: "Contact unenrolled from sequence", description: "Triggers when a contact is unenrolled from a sequence" },
12482
+ "4-1497402": { eventTypeId: "4-1497402", name: "Contact unenrolled from sequence via workflow", description: "Triggers when a contact is unenrolled from a sequence via a workflow" },
12483
+ "4-675775": { eventTypeId: "4-675775", name: "Contact unenrolled from sequence manually", description: "Triggers when a contact is manually unenrolled from a sequence" },
12484
+ "4-1752910": { eventTypeId: "4-1752910", name: "Delivering SMS failed", description: "Triggers when an SMS message was not delivered" },
12485
+ "4-1722276": { eventTypeId: "4-1722276", name: "Link in SMS clicked", description: "Triggers when a contact clicks a link in an SMS" },
12486
+ "4-1752904": { eventTypeId: "4-1752904", name: "Sending SMS failed", description: "Triggers when an SMS message was not sent" },
12487
+ "4-1721168": { eventTypeId: "4-1721168", name: "SMS delivered", description: "Triggers when an SMS message is delivered" },
12488
+ "4-1725149": { eventTypeId: "4-1725149", name: "SMS dropped", description: "Triggers when an SMS message is dropped" },
12489
+ "4-1719453": { eventTypeId: "4-1719453", name: "SMS sent", description: "Triggers when an SMS message is sent" },
12490
+ "4-1555805": { eventTypeId: "4-1555805", name: "CTA click", description: "Triggers when a contact clicks a CTA or Web interactive" },
12491
+ "4-1555804": { eventTypeId: "4-1555804", name: "CTA viewed", description: "Triggers when a contact views a CTA or Web interactive" },
12492
+ "4-100216": { eventTypeId: "4-100216", name: "CTA click (legacy)", description: "Triggers when a contact clicks a CTA (legacy)" },
12493
+ "4-100215": { eventTypeId: "4-100215", name: "CTA view (legacy)", description: "Triggers when a contact views a CTA (legacy)" },
12494
+ "4-96000": { eventTypeId: "4-96000", name: "Page visited", description: "Triggers when a contact views a landing page or website page" },
12495
+ "4-675783": { eventTypeId: "4-675783", name: "Website or Email Media Play", description: "Triggers when a contact plays embedded media" },
12496
+ "4-1753168": { eventTypeId: "4-1753168", name: "Achieved workflow goal", description: "Triggers when a contact meets a workflow goal" },
12497
+ "4-1466013": { eventTypeId: "4-1466013", name: "Enrolled in workflow", description: "Triggers when an object is enrolled in a workflow" },
12498
+ "4-1466014": { eventTypeId: "4-1466014", name: "Unenrolled from workflow", description: "Triggers when an object is unenrolled from a workflow" }
12499
+ };
12500
+ var KNOWN_EVENT_TYPE_IDS = new Set(Object.keys(WORKFLOW_EVENT_TYPES));
12501
+ var isValidEventTypeIdFormat = (id) => /^[46]-\d+$/.test(id);
12502
+ var EVENT_TYPES_REQUIRING_REFINEMENT = new Set(
12503
+ Object.values(WORKFLOW_EVENT_TYPES).filter((et) => et.requiresRefinementFilters).map((et) => et.eventTypeId)
12504
+ );
12505
+
12506
+ // ../shared/pure/workflow-validation/event-types.ts
12422
12507
  var collectUnifiedEventBranches = (enrollmentCriteria) => {
12423
12508
  if (!enrollmentCriteria) return [];
12424
12509
  const branches = [];
@@ -12436,7 +12521,8 @@ var collectUnifiedEventBranches = (enrollmentCriteria) => {
12436
12521
  walk(asBranchArray(enrollmentCriteria.reEnrollmentTriggersFilterBranches));
12437
12522
  return branches;
12438
12523
  };
12439
- var validateWorkflowEventTypeIds = (enrollmentCriteria, operationIndex) => {
12524
+ var validateWorkflowEventTypeIds = (params) => {
12525
+ const { enrollmentCriteria, operationIndex } = params;
12440
12526
  const issues = [];
12441
12527
  const unifiedBranches = collectUnifiedEventBranches(enrollmentCriteria);
12442
12528
  for (const branch of unifiedBranches) {
@@ -12471,7 +12557,8 @@ var validateWorkflowEventTypeIds = (enrollmentCriteria, operationIndex) => {
12471
12557
  }
12472
12558
  return issues;
12473
12559
  };
12474
- var validateWorkflowRefinementFilters = (enrollmentCriteria, operationIndex) => {
12560
+ var validateWorkflowRefinementFilters = (params) => {
12561
+ const { enrollmentCriteria, operationIndex } = params;
12475
12562
  const issues = [];
12476
12563
  const unifiedBranches = collectUnifiedEventBranches(enrollmentCriteria);
12477
12564
  for (const branch of unifiedBranches) {
@@ -12488,11 +12575,10 @@ var validateWorkflowRefinementFilters = (enrollmentCriteria, operationIndex) =>
12488
12575
  }
12489
12576
  return issues;
12490
12577
  };
12491
- var validateWorkflowStructure = ({
12492
- actions,
12493
- enrollmentCriteria,
12494
- operationIndex
12495
- }) => {
12578
+
12579
+ // ../shared/pure/workflow-validation/structure.ts
12580
+ var validateWorkflowStructure = (params) => {
12581
+ const { actions, enrollmentCriteria, operationIndex } = params;
12496
12582
  const issues = [];
12497
12583
  for (const action of actions ?? []) {
12498
12584
  const result = WorkflowActionSchema.safeParse(action);
@@ -12517,12 +12603,51 @@ var validateWorkflowStructure = ({
12517
12603
  }
12518
12604
  }
12519
12605
  const enrollmentCriteriaRecord = isRecord(enrollmentCriteria) ? enrollmentCriteria : void 0;
12520
- issues.push(...validateWorkflowEventTypeIds(enrollmentCriteriaRecord, operationIndex));
12521
- issues.push(...validateWorkflowRefinementFilters(enrollmentCriteriaRecord, operationIndex));
12606
+ issues.push(...validateWorkflowEventTypeIds({ enrollmentCriteria: enrollmentCriteriaRecord, operationIndex }));
12607
+ issues.push(...validateWorkflowRefinementFilters({ enrollmentCriteria: enrollmentCriteriaRecord, operationIndex }));
12522
12608
  return issues;
12523
12609
  };
12524
12610
 
12525
- // ../shared/operations/workflow-associated-action-validation.ts
12611
+ // ../shared/pure/workflow-validation/normalization.ts
12612
+ var injectStaticValueType = (value) => {
12613
+ if (!isRecord(value)) return value;
12614
+ if ("staticValue" in value && value.type !== "STATIC_VALUE") {
12615
+ return { ...value, type: "STATIC_VALUE" };
12616
+ }
12617
+ return value;
12618
+ };
12619
+ var normalizeWorkflowActionValues = (actions) => actions.map((action) => {
12620
+ const fields48 = isRecord(action.fields) ? action.fields : void 0;
12621
+ if (!fields48) return { ...action };
12622
+ const actionTypeId = typeof action.actionTypeId === "string" ? action.actionTypeId : "";
12623
+ if (actionTypeId === "0-5") {
12624
+ return {
12625
+ ...action,
12626
+ fields: { ...fields48, value: injectStaticValueType(fields48.value) }
12627
+ };
12628
+ }
12629
+ if (actionTypeId === "0-14" && Array.isArray(fields48.properties)) {
12630
+ return {
12631
+ ...action,
12632
+ fields: {
12633
+ ...fields48,
12634
+ properties: fields48.properties.map((prop) => {
12635
+ if (!isRecord(prop)) return prop;
12636
+ return { ...prop, value: injectStaticValueType(prop.value) };
12637
+ })
12638
+ }
12639
+ };
12640
+ }
12641
+ if (actionTypeId === "0-35") {
12642
+ return {
12643
+ ...action,
12644
+ fields: { ...fields48, date: injectStaticValueType(fields48.date) }
12645
+ };
12646
+ }
12647
+ return { ...action };
12648
+ });
12649
+
12650
+ // ../shared/operations/workflow-validation-effectful.ts
12526
12651
  import { Effect as Effect91, pipe as pipe78 } from "effect";
12527
12652
  var toDirectionKey = (fromObjectTypeId, toObjectTypeId3) => `${fromObjectTypeId}->${toObjectTypeId3}`;
12528
12653
  var collectWorkflowPropertyActionRefs = (actions) => actions.flatMap((action) => {
@@ -12540,247 +12665,271 @@ var collectWorkflowPropertyActionRefs = (actions) => actions.flatMap((action) =>
12540
12665
  staticValue: typeof value?.staticValue === "string" ? value.staticValue : void 0
12541
12666
  }];
12542
12667
  });
12668
+ var collectRotateToOwnerActions = (actions) => actions.flatMap((action) => {
12669
+ if (typeof action.actionTypeId !== "string" || action.actionTypeId !== "0-11") return [];
12670
+ const fields48 = typeof action.fields === "object" && action.fields !== null ? action.fields : void 0;
12671
+ if (!fields48 || !Array.isArray(fields48.user_ids)) return [];
12672
+ return [{
12673
+ actionId: typeof action.actionId === "string" ? action.actionId : "<missing>",
12674
+ userIds: fields48.user_ids.filter((id) => typeof id === "string")
12675
+ }];
12676
+ });
12543
12677
  var hasValidEnumerationOption = (propertyDefinition, staticValue) => propertyDefinition.type !== "enumeration" || propertyDefinition.options.some((option) => option.value === staticValue);
12544
- var validateWorkflowAssociatedActionMetadataEffectful = ({
12545
- actions,
12546
- objectTypeId,
12547
- enabledObjects,
12548
- operationIndex,
12549
- hubspot
12550
- }) => {
12551
- const propertyActionRefs = collectWorkflowPropertyActionRefs(actions ?? []);
12552
- const associatedActionRefs = collectWorkflowAssociatedPropertyActionRefs(actions ?? []);
12553
- if (propertyActionRefs.length === 0 || objectTypeId.length === 0) {
12554
- return Effect91.succeed([]);
12555
- }
12556
- const rootObject = enabledObjects.find((candidate) => candidate.objectTypeId === objectTypeId);
12557
- if (!rootObject) {
12558
- return Effect91.succeed([]);
12559
- }
12560
- const relatedObjects = enabledObjects.filter(
12561
- (candidate) => candidate.objectTypeId !== rootObject.objectTypeId
12562
- );
12563
- return pipe78(
12564
- Effect91.Do,
12565
- Effect91.bind(
12566
- "propertyDefinitions",
12567
- () => Effect91.all(
12568
- enabledObjects.map(
12569
- (enabledObject) => pipe78(
12570
- hubspot.getObjectPropertyDefinitions(enabledObject.objectTypeId),
12571
- Effect91.map(
12572
- (definitions) => ({
12573
- objectTypeId: enabledObject.objectTypeId,
12574
- definitions,
12575
- failed: false
12576
- })
12577
- ),
12578
- Effect91.catchAll(
12579
- () => Effect91.succeed({
12580
- objectTypeId: enabledObject.objectTypeId,
12581
- definitions: [],
12582
- failed: true
12583
- })
12584
- )
12585
- )
12586
- ),
12587
- { concurrency: 5 }
12588
- )
12589
- ),
12590
- Effect91.bind(
12591
- "associationLabels",
12592
- () => Effect91.all(
12593
- relatedObjects.flatMap((relatedObject) => [
12594
- pipe78(
12595
- hubspot.getAssociationLabels(rootObject.objectTypeId, relatedObject.objectTypeId),
12596
- Effect91.map((labels) => ({
12597
- directionKey: toDirectionKey(rootObject.objectTypeId, relatedObject.objectTypeId),
12598
- labels,
12599
- failed: false
12600
- })),
12601
- Effect91.catchAll(
12602
- (_error) => Effect91.succeed({
12603
- directionKey: toDirectionKey(rootObject.objectTypeId, relatedObject.objectTypeId),
12604
- labels: [],
12605
- failed: true
12606
- })
12607
- )
12608
- ),
12609
- pipe78(
12610
- hubspot.getAssociationLabels(relatedObject.objectTypeId, rootObject.objectTypeId),
12611
- Effect91.map((labels) => ({
12612
- directionKey: toDirectionKey(relatedObject.objectTypeId, rootObject.objectTypeId),
12613
- labels,
12614
- failed: false
12615
- })),
12616
- Effect91.catchAll(
12617
- (_error) => Effect91.succeed({
12618
- directionKey: toDirectionKey(relatedObject.objectTypeId, rootObject.objectTypeId),
12619
- labels: [],
12620
- failed: true
12621
- })
12622
- )
12623
- )
12624
- ]),
12625
- { concurrency: 5 }
12626
- )
12627
- ),
12628
- Effect91.map(({ propertyDefinitions, associationLabels }) => {
12629
- const propertyDefinitionsByObjectType = new Map(
12630
- propertyDefinitions.filter((result) => !result.failed).map((result) => [
12631
- result.objectTypeId,
12632
- new Map(
12633
- result.definitions.map((definition) => [definition.name, {
12634
- type: definition.type,
12635
- options: definition.options.map((option) => ({ value: option.value }))
12636
- }])
12637
- )
12638
- ])
12639
- );
12640
- const associationTypeIdsByDirection = new Map(
12641
- associationLabels.filter((result) => !result.failed).map((result) => [
12642
- result.directionKey,
12643
- new Set(result.labels.map((label) => label.typeId))
12644
- ])
12645
- );
12646
- const failedPropertyDefinitionObjectTypeIds = new Set(
12647
- propertyDefinitions.filter((result) => result.failed).map((result) => result.objectTypeId)
12648
- );
12649
- const failedAssociationDirectionKeys = new Set(
12650
- associationLabels.filter((result) => result.failed).map((result) => result.directionKey)
12651
- );
12652
- const directPropertyIssues = propertyActionRefs.reduce((issues, action) => {
12653
- if (action.associationTypeId !== void 0) return issues;
12654
- const rootPropertyDefinitions = propertyDefinitionsByObjectType.get(objectTypeId);
12655
- const propertyDefinition = rootPropertyDefinitions?.get(action.propertyName);
12656
- if (!propertyDefinition) {
12657
- if (failedPropertyDefinitionObjectTypeIds.has(objectTypeId)) {
12658
- issues.push({
12659
- severity: "warning",
12660
- operation_index: operationIndex,
12661
- code: "WORKFLOW_ACTION_PROPERTY_VALIDATION_INCOMPLETE",
12662
- message: `Action '${action.actionId}' could not fully validate property '${action.propertyName}' because property metadata lookup failed for the root object.`
12663
- });
12664
- return issues;
12665
- }
12666
- issues.push({
12667
- severity: "error",
12668
- operation_index: operationIndex,
12669
- code: "WORKFLOW_ACTION_PROPERTY_NOT_FOUND",
12670
- message: `Action '${action.actionId}' targets property '${action.propertyName}', but that property was not found on the workflow object type '${objectTypeId}'.`
12671
- });
12672
- return issues;
12673
- }
12674
- if (!action.staticValue || hasValidEnumerationOption(propertyDefinition, action.staticValue)) {
12675
- return issues;
12676
- }
12677
- issues.push({
12678
- severity: "error",
12679
- operation_index: operationIndex,
12680
- code: "WORKFLOW_ACTION_INVALID_PROPERTY_OPTION",
12681
- message: `Action '${action.actionId}' sets property '${action.propertyName}' to '${action.staticValue}', but that is not a valid option for the workflow object type '${objectTypeId}'.`
12682
- });
12683
- return issues;
12684
- }, []);
12685
- const associatedActionIssues = validateWorkflowAssociatedPropertyActionMetadata({
12686
- actions: associatedActionRefs,
12687
- rootObjectTypeId: objectTypeId,
12688
- enabledObjects,
12689
- propertyDefinitionsByObjectType,
12690
- associationTypeIdsByDirection,
12691
- failedPropertyDefinitionObjectTypeIds,
12692
- failedAssociationDirectionKeys,
12693
- operationIndex
12694
- });
12695
- return [...directPropertyIssues, ...associatedActionIssues];
12696
- })
12697
- );
12698
- };
12699
-
12700
- // ../shared/operations/create-workflow/index.ts
12701
- var computeNextAvailableActionId = (actions) => {
12702
- let max = 0;
12703
- for (const action of actions) {
12704
- const id = Number(action.actionId);
12705
- if (!isNaN(id) && id > max) max = id;
12706
- }
12707
- return String(max + 1);
12708
- };
12709
- var EMPTY_WORKFLOW_PROPERTY_DEFINITIONS = [];
12710
- var validateWorkflowMetadataEffectful = (op, index) => {
12678
+ var EMPTY_PROPERTY_DEFINITIONS = [];
12679
+ var validateWorkflowMetadataEffectful = (params) => {
12680
+ const { objectTypeId, enrollmentCriteria, suppressionListIds, actions, operationIndex } = params;
12711
12681
  const enrollmentPropertyFilters = collectWorkflowEnrollmentPropertyFilters(
12712
- op.enrollment_criteria,
12713
- op.object_type_id
12682
+ enrollmentCriteria,
12683
+ objectTypeId
12714
12684
  );
12715
12685
  const referencedObjectTypeIds = Array.from(
12716
- /* @__PURE__ */ new Set([op.object_type_id, ...enrollmentPropertyFilters.map((filterRef) => filterRef.objectTypeId)])
12717
- );
12718
- return pipe79(
12686
+ /* @__PURE__ */ new Set([objectTypeId, ...enrollmentPropertyFilters.map((filterRef) => filterRef.objectTypeId)])
12687
+ ).filter((candidate) => candidate.length > 0);
12688
+ const propertyActionRefs = collectWorkflowPropertyActionRefs(actions ?? []);
12689
+ const associatedActionRefs = collectWorkflowAssociatedPropertyActionRefs(actions ?? []);
12690
+ const rotateActions = collectRotateToOwnerActions(actions ?? []);
12691
+ return pipe78(
12719
12692
  HubSpotService,
12720
- Effect92.flatMap(
12721
- (hs) => pipe79(
12722
- Effect92.all(
12693
+ Effect91.flatMap(
12694
+ (hs) => pipe78(
12695
+ Effect91.all(
12723
12696
  [
12724
12697
  hs.getEnabledObjects,
12725
- op.suppression_list_ids?.length ? hs.getLists : Effect92.succeed([])
12698
+ suppressionListIds?.length ? hs.getLists : Effect91.succeed([]),
12699
+ rotateActions.length > 0 ? hs.getOwners.pipe(
12700
+ Effect91.catchAll(() => Effect91.succeed([]))
12701
+ ) : Effect91.succeed([])
12726
12702
  ],
12727
12703
  { concurrency: "unbounded" }
12728
12704
  ),
12729
- Effect92.flatMap(([enabledObjects, lists]) => {
12705
+ Effect91.flatMap(([enabledObjects, lists, owners]) => {
12730
12706
  const enabledObjectTypeIds = new Set(
12731
- enabledObjects.enabledObjects.map((enabledObject) => enabledObject.objectTypeId)
12707
+ enabledObjects.enabledObjects.map((obj) => obj.objectTypeId)
12732
12708
  );
12733
12709
  const objectTypeIdsWithDefinitions = referencedObjectTypeIds.filter(
12734
- (objectTypeId) => enabledObjectTypeIds.has(objectTypeId)
12710
+ (candidate) => enabledObjectTypeIds.has(candidate)
12735
12711
  );
12736
- const propertyDefinitionsEffect = objectTypeIdsWithDefinitions.length === 0 ? Effect92.succeed(EMPTY_WORKFLOW_PROPERTY_DEFINITIONS) : Effect92.all(
12737
- objectTypeIdsWithDefinitions.map(
12738
- (objectTypeId) => pipe79(
12739
- hs.getObjectPropertyDefinitions(objectTypeId),
12740
- Effect92.map(
12741
- (definitions) => [objectTypeId, definitions]
12712
+ const shouldFetchAllPropertyDefinitions = propertyActionRefs.length > 0 && objectTypeId.length > 0;
12713
+ const propertyDefinitionsEffect = objectTypeIdsWithDefinitions.length === 0 && !shouldFetchAllPropertyDefinitions ? Effect91.succeed(EMPTY_PROPERTY_DEFINITIONS) : Effect91.all(
12714
+ (shouldFetchAllPropertyDefinitions ? enabledObjects.enabledObjects : objectTypeIdsWithDefinitions.map((id) => ({ objectTypeId: id }))).map(
12715
+ (obj) => pipe78(
12716
+ hs.getObjectPropertyDefinitions(obj.objectTypeId),
12717
+ Effect91.map(
12718
+ (definitions) => [obj.objectTypeId, definitions]
12719
+ ),
12720
+ Effect91.catchAll(
12721
+ () => Effect91.succeed(
12722
+ [obj.objectTypeId, []]
12723
+ )
12742
12724
  )
12743
12725
  )
12744
12726
  ),
12745
- { concurrency: "unbounded" }
12727
+ { concurrency: 5 }
12728
+ );
12729
+ const relatedObjects = enabledObjects.enabledObjects.filter(
12730
+ (obj) => obj.objectTypeId !== objectTypeId
12731
+ );
12732
+ const associationLabelsEffect = associatedActionRefs.length === 0 || !objectTypeId ? Effect91.succeed([]) : Effect91.all(
12733
+ relatedObjects.flatMap((relatedObject) => [
12734
+ pipe78(
12735
+ hs.getAssociationLabels(objectTypeId, relatedObject.objectTypeId),
12736
+ Effect91.map((labels) => ({
12737
+ directionKey: toDirectionKey(objectTypeId, relatedObject.objectTypeId),
12738
+ labels,
12739
+ failed: false
12740
+ })),
12741
+ Effect91.catchAll(
12742
+ () => Effect91.succeed({
12743
+ directionKey: toDirectionKey(objectTypeId, relatedObject.objectTypeId),
12744
+ labels: [],
12745
+ failed: true
12746
+ })
12747
+ )
12748
+ ),
12749
+ pipe78(
12750
+ hs.getAssociationLabels(relatedObject.objectTypeId, objectTypeId),
12751
+ Effect91.map((labels) => ({
12752
+ directionKey: toDirectionKey(relatedObject.objectTypeId, objectTypeId),
12753
+ labels,
12754
+ failed: false
12755
+ })),
12756
+ Effect91.catchAll(
12757
+ () => Effect91.succeed({
12758
+ directionKey: toDirectionKey(relatedObject.objectTypeId, objectTypeId),
12759
+ labels: [],
12760
+ failed: true
12761
+ })
12762
+ )
12763
+ )
12764
+ ]),
12765
+ { concurrency: 5 }
12746
12766
  );
12747
- return pipe79(
12748
- propertyDefinitionsEffect,
12749
- Effect92.flatMap((propertyDefinitionResults) => {
12767
+ return pipe78(
12768
+ Effect91.all([propertyDefinitionsEffect, associationLabelsEffect], { concurrency: "unbounded" }),
12769
+ Effect91.map(([propertyDefinitionResults, associationLabelResults]) => {
12770
+ const failedPropertyDefinitionObjectTypeIds = new Set(
12771
+ propertyDefinitionResults.filter(([, defs]) => defs.length === 0).map(([id]) => id)
12772
+ );
12750
12773
  const propertyDefinitionsByObjectType = new Map(
12751
- propertyDefinitionResults.map(([objectTypeId, definitions]) => [
12752
- objectTypeId,
12774
+ propertyDefinitionResults.filter(([, defs]) => defs.length > 0).map(([id, definitions]) => [
12775
+ id,
12776
+ new Map(
12777
+ definitions.map((definition) => [definition.name, {
12778
+ type: definition.type,
12779
+ options: definition.options.map((option) => ({ value: option.value }))
12780
+ }])
12781
+ )
12782
+ ])
12783
+ );
12784
+ const enrollmentPropertyDefinitionsByObjectType = new Map(
12785
+ propertyDefinitionResults.filter(([, defs]) => defs.length > 0).map(([id, definitions]) => [
12786
+ id,
12753
12787
  new Map(
12754
12788
  definitions.map((definition) => [definition.name, { type: definition.type }])
12755
12789
  )
12756
12790
  ])
12757
12791
  );
12792
+ const associationTypeIdsByDirection = new Map(
12793
+ associationLabelResults.filter((result) => !result.failed).map((result) => [
12794
+ result.directionKey,
12795
+ new Set(result.labels.map((label) => label.typeId))
12796
+ ])
12797
+ );
12798
+ const failedAssociationDirectionKeys = new Set(
12799
+ associationLabelResults.filter((result) => result.failed).map((result) => result.directionKey)
12800
+ );
12758
12801
  const enrollmentIssues = validateWorkflowEnrollmentMetadata({
12759
- enrollmentCriteria: op.enrollment_criteria,
12760
- suppressionListIds: op.suppression_list_ids,
12761
- objectTypeId: op.object_type_id,
12802
+ enrollmentCriteria,
12803
+ suppressionListIds,
12804
+ objectTypeId,
12762
12805
  enabledObjectTypeIds,
12763
- propertyDefinitionsByObjectType,
12806
+ propertyDefinitionsByObjectType: enrollmentPropertyDefinitionsByObjectType,
12764
12807
  listIds: new Set(lists.map((list) => list.listId)),
12765
- operationIndex: index
12808
+ operationIndex
12766
12809
  });
12767
- return pipe79(
12768
- validateWorkflowAssociatedActionMetadataEffectful({
12769
- actions: op.actions,
12770
- objectTypeId: op.object_type_id,
12771
- enabledObjects: enabledObjects.enabledObjects,
12772
- operationIndex: index,
12773
- hubspot: hs
12774
- }),
12775
- Effect92.map((associationIssues) => [...enrollmentIssues, ...associationIssues])
12776
- );
12810
+ const directPropertyIssues = validateDirectPropertyActions({
12811
+ propertyActionRefs,
12812
+ objectTypeId,
12813
+ propertyDefinitionsByObjectType,
12814
+ failedPropertyDefinitionObjectTypeIds,
12815
+ operationIndex
12816
+ });
12817
+ const associatedActionIssues = validateWorkflowAssociatedPropertyActionMetadata({
12818
+ actions: associatedActionRefs,
12819
+ rootObjectTypeId: objectTypeId,
12820
+ enabledObjects: enabledObjects.enabledObjects,
12821
+ propertyDefinitionsByObjectType,
12822
+ associationTypeIdsByDirection,
12823
+ failedPropertyDefinitionObjectTypeIds,
12824
+ failedAssociationDirectionKeys,
12825
+ operationIndex
12826
+ });
12827
+ const rotateOwnerIssues = validateRotateToOwnerUserIds({
12828
+ rotateActions,
12829
+ owners,
12830
+ operationIndex
12831
+ });
12832
+ return [...enrollmentIssues, ...directPropertyIssues, ...associatedActionIssues, ...rotateOwnerIssues];
12777
12833
  })
12778
12834
  );
12779
12835
  })
12780
12836
  )
12837
+ ),
12838
+ Effect91.catchAll(
12839
+ () => Effect91.succeed([{
12840
+ severity: "warning",
12841
+ operation_index: operationIndex,
12842
+ code: "VALIDATION_API_UNAVAILABLE",
12843
+ message: "Unable to verify workflow enrollment or associated-action metadata during validation (API error)"
12844
+ }])
12781
12845
  )
12782
12846
  );
12783
12847
  };
12848
+ var validateDirectPropertyActions = (params) => {
12849
+ const {
12850
+ propertyActionRefs,
12851
+ objectTypeId,
12852
+ propertyDefinitionsByObjectType,
12853
+ failedPropertyDefinitionObjectTypeIds,
12854
+ operationIndex
12855
+ } = params;
12856
+ return propertyActionRefs.reduce((issues, action) => {
12857
+ if (action.associationTypeId !== void 0) return issues;
12858
+ const rootPropertyDefinitions = propertyDefinitionsByObjectType.get(objectTypeId);
12859
+ const propertyDefinition = rootPropertyDefinitions?.get(action.propertyName);
12860
+ if (!propertyDefinition) {
12861
+ if (failedPropertyDefinitionObjectTypeIds.has(objectTypeId)) {
12862
+ issues.push({
12863
+ severity: "warning",
12864
+ operation_index: operationIndex,
12865
+ code: "WORKFLOW_ACTION_PROPERTY_VALIDATION_INCOMPLETE",
12866
+ message: `Action '${action.actionId}' could not fully validate property '${action.propertyName}' because property metadata lookup failed for the root object.`
12867
+ });
12868
+ return issues;
12869
+ }
12870
+ issues.push({
12871
+ severity: "error",
12872
+ operation_index: operationIndex,
12873
+ code: "WORKFLOW_ACTION_PROPERTY_NOT_FOUND",
12874
+ message: `Action '${action.actionId}' targets property '${action.propertyName}', but that property was not found on the workflow object type '${objectTypeId}'.`
12875
+ });
12876
+ return issues;
12877
+ }
12878
+ if (!action.staticValue || hasValidEnumerationOption(propertyDefinition, action.staticValue)) {
12879
+ return issues;
12880
+ }
12881
+ issues.push({
12882
+ severity: "error",
12883
+ operation_index: operationIndex,
12884
+ code: "WORKFLOW_ACTION_INVALID_PROPERTY_OPTION",
12885
+ message: `Action '${action.actionId}' sets property '${action.propertyName}' to '${action.staticValue}', but that is not a valid option for the workflow object type '${objectTypeId}'.`
12886
+ });
12887
+ return issues;
12888
+ }, []);
12889
+ };
12890
+ var validateRotateToOwnerUserIds = (params) => {
12891
+ const { rotateActions, owners, operationIndex } = params;
12892
+ if (rotateActions.length === 0 || owners.length === 0) return [];
12893
+ const validUserIds = new Set(
12894
+ owners.filter((o) => o.userId !== null).map((o) => String(o.userId))
12895
+ );
12896
+ const ownerIdToUserId = new Map(
12897
+ owners.filter((o) => o.userId !== null).map((o) => [o.id, String(o.userId)])
12898
+ );
12899
+ const issues = [];
12900
+ for (const action of rotateActions) {
12901
+ for (const id of action.userIds) {
12902
+ if (validUserIds.has(id)) continue;
12903
+ const correctUserId = ownerIdToUserId.get(id);
12904
+ if (correctUserId) {
12905
+ issues.push({
12906
+ severity: "error",
12907
+ operation_index: operationIndex,
12908
+ code: "WORKFLOW_ACTION_OWNER_ID_NOT_USER_ID",
12909
+ message: `Action '${action.actionId}' (0-11 rotate to owner) user_ids contains owner ID '${id}' \u2014 use the corresponding user ID '${correctUserId}' instead. Query the owners table user_id column for correct values.`
12910
+ });
12911
+ } else {
12912
+ issues.push({
12913
+ severity: "warning",
12914
+ operation_index: operationIndex,
12915
+ code: "WORKFLOW_ACTION_UNKNOWN_USER_ID",
12916
+ message: `Action '${action.actionId}' (0-11 rotate to owner) user_ids contains '${id}' which was not found in the owners table. Verify this is a valid HubSpot user ID (owners table user_id column, not owner_id).`
12917
+ });
12918
+ }
12919
+ }
12920
+ }
12921
+ return issues;
12922
+ };
12923
+
12924
+ // ../shared/operations/create-workflow/index.ts
12925
+ var computeNextAvailableActionId = (actions) => {
12926
+ let max = 0;
12927
+ for (const action of actions) {
12928
+ const id = Number(action.actionId);
12929
+ if (!isNaN(id) && id > max) max = id;
12930
+ }
12931
+ return String(max + 1);
12932
+ };
12784
12933
  var createWorkflowHandler = {
12785
12934
  type: "create_workflow",
12786
12935
  validate: (op, index) => {
@@ -12798,7 +12947,7 @@ var createWorkflowHandler = {
12798
12947
  message: "Workflow must have at least one action"
12799
12948
  });
12800
12949
  }
12801
- issues.push(...validateWorkflowActions(op.actions, index));
12950
+ issues.push(...validateWorkflowActions({ actions: op.actions, operationIndex: index }));
12802
12951
  if (op.object_type_id === "0-1" && op.workflow_type !== "CONTACT_FLOW") {
12803
12952
  issues.push({
12804
12953
  severity: "warning",
@@ -12817,28 +12966,14 @@ var createWorkflowHandler = {
12817
12966
  }
12818
12967
  return issues;
12819
12968
  },
12820
- validateEffectful: (op, index) => pipe79(
12821
- validateWorkflowMetadataEffectful(op, index),
12822
- catchAllToWarning(
12823
- "VALIDATION_API_UNAVAILABLE",
12824
- index,
12825
- "Unable to verify workflow enrollment or associated-action metadata during validation (API error)"
12826
- )
12827
- ),
12828
- preCheck: (op, index) => pipe79(
12829
- validateWorkflowMetadataEffectful(op, index),
12830
- Effect92.map(
12831
- (issues) => issues.map((issue) => ({
12832
- ...issue,
12833
- severity: "warning"
12834
- }))
12835
- ),
12836
- catchAllToWarning(
12837
- "PRECHECK_UNAVAILABLE",
12838
- index,
12839
- "Unable to verify workflow enrollment or associated-action metadata before execution (API error)"
12840
- )
12841
- ),
12969
+ validateEffectful: (op, index) => validateWorkflowMetadataEffectful({
12970
+ objectTypeId: op.object_type_id,
12971
+ enrollmentCriteria: op.enrollment_criteria,
12972
+ suppressionListIds: op.suppression_list_ids,
12973
+ actions: op.actions,
12974
+ operationIndex: index
12975
+ }),
12976
+ preCheck: (_op, _index) => Effect92.succeed([]),
12842
12977
  execute: (op, index) => pipe79(
12843
12978
  HubSpotService,
12844
12979
  Effect92.flatMap((hs) => {
@@ -12904,104 +13039,6 @@ var computeNextAvailableActionId2 = (actions) => {
12904
13039
  }
12905
13040
  return String(max + 1);
12906
13041
  };
12907
- var EMPTY_WORKFLOW_PROPERTY_DEFINITIONS2 = [];
12908
- var validateUpdateWorkflowMetadataEffectful = (op, index) => pipe80(
12909
- HubSpotService,
12910
- Effect93.flatMap(
12911
- (hs) => pipe80(
12912
- hs.getWorkflow(op.workflow_id),
12913
- Effect93.flatMap((workflow) => {
12914
- if (!op.enrollment_criteria && !op.suppression_list_ids && !op.actions) {
12915
- return Effect93.succeed([]);
12916
- }
12917
- const objectTypeId = workflow.objectTypeId ?? "";
12918
- const enrollmentPropertyFilters = collectWorkflowEnrollmentPropertyFilters(
12919
- op.enrollment_criteria,
12920
- objectTypeId
12921
- );
12922
- const referencedObjectTypeIds = Array.from(
12923
- /* @__PURE__ */ new Set([objectTypeId, ...enrollmentPropertyFilters.map((filterRef) => filterRef.objectTypeId)])
12924
- ).filter((candidate) => candidate.length > 0);
12925
- return pipe80(
12926
- Effect93.all(
12927
- [
12928
- hs.getEnabledObjects,
12929
- op.suppression_list_ids?.length ? hs.getLists : Effect93.succeed([])
12930
- ],
12931
- { concurrency: "unbounded" }
12932
- ),
12933
- Effect93.flatMap(([enabledObjects, lists]) => {
12934
- const enabledObjectTypeIds = new Set(
12935
- enabledObjects.enabledObjects.map((enabledObject) => enabledObject.objectTypeId)
12936
- );
12937
- const objectTypeIdsWithDefinitions = referencedObjectTypeIds.filter(
12938
- (candidate) => enabledObjectTypeIds.has(candidate)
12939
- );
12940
- const propertyDefinitionsEffect = objectTypeIdsWithDefinitions.length === 0 ? Effect93.succeed(EMPTY_WORKFLOW_PROPERTY_DEFINITIONS2) : Effect93.all(
12941
- objectTypeIdsWithDefinitions.map(
12942
- (candidate) => pipe80(
12943
- hs.getObjectPropertyDefinitions(candidate),
12944
- Effect93.map(
12945
- (definitions) => [candidate, definitions]
12946
- )
12947
- )
12948
- ),
12949
- { concurrency: "unbounded" }
12950
- );
12951
- return pipe80(
12952
- propertyDefinitionsEffect,
12953
- Effect93.flatMap((propertyDefinitionResults) => {
12954
- const propertyDefinitionsByObjectType = new Map(
12955
- propertyDefinitionResults.map(([candidate, definitions]) => [
12956
- candidate,
12957
- new Map(
12958
- definitions.map((definition) => [definition.name, { type: definition.type }])
12959
- )
12960
- ])
12961
- );
12962
- const enrollmentIssues = validateWorkflowEnrollmentMetadata({
12963
- enrollmentCriteria: op.enrollment_criteria,
12964
- suppressionListIds: op.suppression_list_ids,
12965
- objectTypeId,
12966
- enabledObjectTypeIds,
12967
- propertyDefinitionsByObjectType,
12968
- listIds: new Set(lists.map((list) => list.listId)),
12969
- operationIndex: index
12970
- });
12971
- return pipe80(
12972
- validateWorkflowAssociatedActionMetadataEffectful({
12973
- actions: op.actions,
12974
- objectTypeId,
12975
- enabledObjects: enabledObjects.enabledObjects,
12976
- operationIndex: index,
12977
- hubspot: hs
12978
- }),
12979
- Effect93.map((associationIssues) => [...enrollmentIssues, ...associationIssues])
12980
- );
12981
- })
12982
- );
12983
- })
12984
- );
12985
- }),
12986
- Effect93.catchAll((error) => {
12987
- if (error instanceof HubSpotError && error.code === 404) {
12988
- return Effect93.succeed([{
12989
- severity: "error",
12990
- operation_index: index,
12991
- code: "WORKFLOW_NOT_FOUND",
12992
- message: `Workflow '${op.workflow_id}' does not exist`
12993
- }]);
12994
- }
12995
- return Effect93.succeed([{
12996
- severity: "warning",
12997
- operation_index: index,
12998
- code: "VALIDATION_API_UNAVAILABLE",
12999
- message: `Unable to verify workflow '${op.workflow_id}' enrollment or associated-action metadata during validation (API error)`
13000
- }]);
13001
- })
13002
- )
13003
- )
13004
- );
13005
13042
  var updateWorkflowHandler = {
13006
13043
  type: "update_workflow",
13007
13044
  validate: (op, index) => {
@@ -13020,18 +13057,54 @@ var updateWorkflowHandler = {
13020
13057
  message: "Workflow must have at least one action"
13021
13058
  });
13022
13059
  }
13023
- issues.push(...validateWorkflowActions(op.actions, index));
13060
+ issues.push(...validateWorkflowActions({ actions: op.actions, operationIndex: index }));
13024
13061
  }
13025
13062
  return issues;
13026
13063
  },
13027
- validateEffectful: (op, index) => validateUpdateWorkflowMetadataEffectful(op, index),
13064
+ validateEffectful: (op, index) => pipe80(
13065
+ HubSpotService,
13066
+ Effect93.flatMap(
13067
+ (hs) => pipe80(
13068
+ hs.getWorkflow(op.workflow_id),
13069
+ Effect93.flatMap((workflow) => {
13070
+ if (!op.enrollment_criteria && !op.suppression_list_ids && !op.actions) {
13071
+ return Effect93.succeed([]);
13072
+ }
13073
+ return validateWorkflowMetadataEffectful({
13074
+ objectTypeId: workflow.objectTypeId ?? "",
13075
+ enrollmentCriteria: op.enrollment_criteria,
13076
+ suppressionListIds: op.suppression_list_ids,
13077
+ actions: op.actions,
13078
+ operationIndex: index
13079
+ });
13080
+ }),
13081
+ Effect93.catchAll((error) => {
13082
+ if (error instanceof HubSpotError && error.code === 404) {
13083
+ return Effect93.succeed([{
13084
+ severity: "error",
13085
+ operation_index: index,
13086
+ code: "WORKFLOW_NOT_FOUND",
13087
+ message: `Workflow '${op.workflow_id}' does not exist`
13088
+ }]);
13089
+ }
13090
+ return Effect93.succeed([{
13091
+ severity: "warning",
13092
+ operation_index: index,
13093
+ code: "VALIDATION_API_UNAVAILABLE",
13094
+ message: `Unable to verify workflow '${op.workflow_id}' enrollment or associated-action metadata during validation (API error)`
13095
+ }]);
13096
+ })
13097
+ )
13098
+ )
13099
+ ),
13028
13100
  preCheck: (op, index) => pipe80(
13029
- validateUpdateWorkflowMetadataEffectful(op, index),
13030
- Effect93.map(
13031
- (issues) => issues.map((issue) => ({
13032
- ...issue,
13033
- severity: "warning"
13034
- }))
13101
+ HubSpotService,
13102
+ Effect93.flatMap((hs) => hs.getWorkflow(op.workflow_id)),
13103
+ Effect93.map(() => []),
13104
+ catchAllToWarning(
13105
+ "PRECHECK_UNAVAILABLE",
13106
+ index,
13107
+ "Unable to verify target workflow exists before execution (API error)"
13035
13108
  )
13036
13109
  ),
13037
13110
  execute: (op, index) => pipe80(