snow-flow 10.0.41 → 10.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -54,17 +54,69 @@ function generateUUID(): string {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Get the current max global order from the flow's
|
|
57
|
+
* Get the current max global order from the flow's live state.
|
|
58
58
|
*
|
|
59
59
|
* IMPORTANT: Flow Designer elements (actions, flow logic, subflows) are NOT stored as
|
|
60
60
|
* individual records in sys_hub_action_instance / sys_hub_flow_logic / sys_hub_sub_flow_instance.
|
|
61
|
-
* They only exist inside the
|
|
61
|
+
* They only exist inside the version payload managed by the GraphQL API.
|
|
62
62
|
* Table API queries on these tables will always return 0 results.
|
|
63
63
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* Strategy (most reliable first):
|
|
65
|
+
* 1. processflow API — always returns real-time state, even right after mutations
|
|
66
|
+
* 2. sys_hub_flow_version.payload — fallback, may be stale after rapid mutations
|
|
67
|
+
* 3. Return 0 (caller should use explicit order)
|
|
66
68
|
*/
|
|
67
69
|
async function getMaxOrderFromVersion(client: any, flowId: string): Promise<number> {
|
|
70
|
+
// Helper: recursively extract all "order" values from a nested structure
|
|
71
|
+
const findMaxOrder = (obj: any): number => {
|
|
72
|
+
if (!obj || typeof obj !== 'object') return 0;
|
|
73
|
+
let max = 0;
|
|
74
|
+
if (obj.order !== undefined) {
|
|
75
|
+
const o = parseInt(String(obj.order), 10);
|
|
76
|
+
if (!isNaN(o) && o > max) max = o;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(obj)) {
|
|
79
|
+
for (let i = 0; i < obj.length; i++) {
|
|
80
|
+
const v = findMaxOrder(obj[i]);
|
|
81
|
+
if (v > max) max = v;
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
const vals = Object.values(obj);
|
|
85
|
+
for (let i = 0; i < vals.length; i++) {
|
|
86
|
+
const v = findMaxOrder(vals[i]);
|
|
87
|
+
if (v > max) max = v;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return max;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Strategy 1: processflow API (real-time, same as Flow Designer UI)
|
|
94
|
+
try {
|
|
95
|
+
const pfResp = await client.get('/api/now/processflow/flow/' + flowId);
|
|
96
|
+
const pfRaw = pfResp.data;
|
|
97
|
+
if (typeof pfRaw === 'string') {
|
|
98
|
+
// XML — extract all order="N" and <order>N</order> values
|
|
99
|
+
let max = 0;
|
|
100
|
+
const orderAttrRx = /\border="(\d+)"/g;
|
|
101
|
+
const orderElemRx = /<order>(\d+)<\/order>/g;
|
|
102
|
+
let m;
|
|
103
|
+
while ((m = orderAttrRx.exec(pfRaw)) !== null) {
|
|
104
|
+
const v = parseInt(m[1], 10);
|
|
105
|
+
if (v > max) max = v;
|
|
106
|
+
}
|
|
107
|
+
while ((m = orderElemRx.exec(pfRaw)) !== null) {
|
|
108
|
+
const v = parseInt(m[1], 10);
|
|
109
|
+
if (v > max) max = v;
|
|
110
|
+
}
|
|
111
|
+
if (max > 0) return max;
|
|
112
|
+
} else if (pfRaw && typeof pfRaw === 'object') {
|
|
113
|
+
const data = pfRaw.result?.data || pfRaw.result || pfRaw.data || pfRaw;
|
|
114
|
+
const max = findMaxOrder(data);
|
|
115
|
+
if (max > 0) return max;
|
|
116
|
+
}
|
|
117
|
+
} catch (_) {}
|
|
118
|
+
|
|
119
|
+
// Strategy 2: sys_hub_flow_version.payload (may be stale after rapid mutations)
|
|
68
120
|
try {
|
|
69
121
|
const resp = await client.get('/api/now/table/sys_hub_flow_version', {
|
|
70
122
|
params: {
|
|
@@ -74,25 +126,14 @@ async function getMaxOrderFromVersion(client: any, flowId: string): Promise<numb
|
|
|
74
126
|
}
|
|
75
127
|
});
|
|
76
128
|
const payload = resp.data.result?.[0]?.payload;
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const o = parseInt(String(obj.order), 10);
|
|
86
|
-
if (!isNaN(o) && o > maxOrder) maxOrder = o;
|
|
87
|
-
}
|
|
88
|
-
if (Array.isArray(obj)) obj.forEach(findOrders);
|
|
89
|
-
else Object.values(obj).forEach(findOrders);
|
|
90
|
-
};
|
|
91
|
-
findOrders(parsed);
|
|
92
|
-
return maxOrder;
|
|
93
|
-
} catch (_) {
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
129
|
+
if (payload) {
|
|
130
|
+
const parsed = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
|
131
|
+
const max = findMaxOrder(parsed);
|
|
132
|
+
if (max > 0) return max;
|
|
133
|
+
}
|
|
134
|
+
} catch (_) {}
|
|
135
|
+
|
|
136
|
+
return 0;
|
|
96
137
|
}
|
|
97
138
|
|
|
98
139
|
async function executeFlowPatchMutation(
|
|
@@ -1446,6 +1487,8 @@ async function addActionViaGraphQL(
|
|
|
1446
1487
|
// ── Rewrite shorthand pills in generic action inputs (e.g. Log "message") ────
|
|
1447
1488
|
// Non-record inputs (anything other than record/table_name/values) that contain
|
|
1448
1489
|
// {{trigger.current.X}} need rewriting to {{Created or Updated_1.current.X}} + labelCache.
|
|
1490
|
+
// Also handle inputs that already have full pill references (e.g. {{Created or Updated_1.current.caller_id}})
|
|
1491
|
+
// — these still need labelCache entries or they render as empty grey pills.
|
|
1449
1492
|
var PILL_SHORTHANDS_ACTION = ['trigger.current', 'current', 'trigger_record', 'trigger.record'];
|
|
1450
1493
|
var RECORD_INPUTS = ['record', 'table_name', 'values'];
|
|
1451
1494
|
var genericPillInputs: { name: string; fields: string[]; isRecordLevel: boolean }[] = [];
|
|
@@ -1460,28 +1503,35 @@ async function addActionViaGraphQL(
|
|
|
1460
1503
|
var gpHasShorthand = PILL_SHORTHANDS_ACTION.some(function (sh) {
|
|
1461
1504
|
return gpVal.includes('{{' + sh + '.') || gpVal.includes('{{' + sh + '}}');
|
|
1462
1505
|
});
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
//
|
|
1466
|
-
if (
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1506
|
+
|
|
1507
|
+
// If it has shorthand pills, we need trigger info to rewrite them.
|
|
1508
|
+
// If it has ANY pill references (even non-shorthand), we still need trigger info for labelCache.
|
|
1509
|
+
if (gpHasShorthand || gpVal.includes('{{')) {
|
|
1510
|
+
// Get trigger info if not already fetched
|
|
1511
|
+
if (!actionTriggerInfo) {
|
|
1512
|
+
actionTriggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
1513
|
+
steps.action_trigger_info = {
|
|
1514
|
+
dataPillBase: actionTriggerInfo.dataPillBase, triggerName: actionTriggerInfo.triggerName,
|
|
1515
|
+
table: actionTriggerInfo.table, tableLabel: actionTriggerInfo.tableLabel
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
if (!actionTriggerInfo.dataPillBase) continue;
|
|
1472
1519
|
}
|
|
1473
|
-
if (!actionTriggerInfo.dataPillBase) continue;
|
|
1474
1520
|
|
|
1475
1521
|
var gpPillBase = actionTriggerInfo.dataPillBase;
|
|
1476
1522
|
var gpOrigVal = gpVal;
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1523
|
+
|
|
1524
|
+
// Rewrite shorthand pills to full dataPillBase
|
|
1525
|
+
if (gpHasShorthand) {
|
|
1526
|
+
for (var gsi = 0; gsi < PILL_SHORTHANDS_ACTION.length; gsi++) {
|
|
1527
|
+
var gsh = PILL_SHORTHANDS_ACTION[gsi];
|
|
1528
|
+
gpVal = gpVal.split('{{' + gsh + '.').join('{{' + gpPillBase + '.');
|
|
1529
|
+
gpVal = gpVal.split('{{' + gsh + '}}').join('{{' + gpPillBase + '}}');
|
|
1530
|
+
}
|
|
1531
|
+
gpInput.value.value = gpVal;
|
|
1481
1532
|
}
|
|
1482
|
-
gpInput.value.value = gpVal;
|
|
1483
1533
|
|
|
1484
|
-
// Extract field names from pills for labelCache
|
|
1534
|
+
// Extract field names from ALL pills in the value for labelCache
|
|
1485
1535
|
var gpPillFields: string[] = [];
|
|
1486
1536
|
var gpIsRecordLevel = false;
|
|
1487
1537
|
var gpPillRx = /\{\{([^}]+)\}\}/g;
|
|
@@ -1495,8 +1545,14 @@ async function addActionViaGraphQL(
|
|
|
1495
1545
|
}
|
|
1496
1546
|
}
|
|
1497
1547
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1548
|
+
if (gpPillFields.length > 0 || gpIsRecordLevel) {
|
|
1549
|
+
genericPillInputs.push({ name: gpInput.name, fields: gpPillFields, isRecordLevel: gpIsRecordLevel });
|
|
1550
|
+
if (gpOrigVal !== gpVal) {
|
|
1551
|
+
steps['pill_rewrite_' + gpInput.name] = { original: gpOrigVal, rewritten: gpVal };
|
|
1552
|
+
} else {
|
|
1553
|
+
steps['pill_labelcache_' + gpInput.name] = { fields: gpPillFields, isRecordLevel: gpIsRecordLevel };
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1500
1556
|
}
|
|
1501
1557
|
|
|
1502
1558
|
// For record actions: clear data pill values from INSERT — they'll be set via separate UPDATE
|
|
@@ -2614,11 +2670,12 @@ async function addFlowLogicViaGraphQL(
|
|
|
2614
2670
|
[/(\}\})\s+ends\s+with\s+/gi, '$1ENDSWITH'],
|
|
2615
2671
|
[/(\}\})\s+greater\s+than\s+/gi, '$1>'],
|
|
2616
2672
|
[/(\}\})\s+less\s+than\s+/gi, '$1<'],
|
|
2617
|
-
// Symbol operators: == / === / != / !== / >= / <= / > / < with optional spaces
|
|
2618
|
-
|
|
2619
|
-
[/(\}\})\s*!==?\s*/g, '$1!='],
|
|
2673
|
+
// Symbol operators: = / == / === / != / !== / >= / <= / > / < with optional spaces
|
|
2674
|
+
// IMPORTANT: >= and <= must come BEFORE single > and < to avoid partial matches
|
|
2620
2675
|
[/(\}\})\s*>=\s*/g, '$1>='],
|
|
2621
2676
|
[/(\}\})\s*<=\s*/g, '$1<='],
|
|
2677
|
+
[/(\}\})\s*!={1,2}\s*/g, '$1!='],
|
|
2678
|
+
[/(\}\})\s*={1,3}\s*/g, '$1='],
|
|
2622
2679
|
[/(\}\})\s*>\s*/g, '$1>'],
|
|
2623
2680
|
[/(\}\})\s*<\s*/g, '$1<'],
|
|
2624
2681
|
];
|
|
@@ -2637,8 +2694,11 @@ async function addFlowLogicViaGraphQL(
|
|
|
2637
2694
|
var hasShorthandPills = rawCondition.includes('{{') && PILL_SHORTHANDS.some(function (sh) {
|
|
2638
2695
|
return rawCondition.includes('{{' + sh + '.') || rawCondition.includes('{{' + sh + '}}');
|
|
2639
2696
|
});
|
|
2697
|
+
// Also detect conditions that already contain full data pill references (non-shorthand)
|
|
2698
|
+
// e.g. {{Created or Updated_1.current.priority}}=1 — these still need two-step + labelCache
|
|
2699
|
+
var hasFullPillRefs = rawCondition.includes('{{') && !hasShorthandPills;
|
|
2640
2700
|
|
|
2641
|
-
if (rawCondition && rawCondition !== '^EQ' && (isStandardEncodedQuery(rawCondition) || hasShorthandPills)) {
|
|
2701
|
+
if (rawCondition && rawCondition !== '^EQ' && (isStandardEncodedQuery(rawCondition) || hasShorthandPills || hasFullPillRefs)) {
|
|
2642
2702
|
conditionTriggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
2643
2703
|
steps.trigger_info = {
|
|
2644
2704
|
dataPillBase: conditionTriggerInfo.dataPillBase, triggerName: conditionTriggerInfo.triggerName,
|
|
@@ -2674,6 +2734,7 @@ async function addFlowLogicViaGraphQL(
|
|
|
2674
2734
|
// ── Rewrite shorthand pills in non-condition inputs (e.g. FOR_EACH "items") ────
|
|
2675
2735
|
// These inputs may contain {{trigger.current}} or {{current.field}} that need rewriting
|
|
2676
2736
|
// to the full dataPillBase (e.g. {{Created or Updated_1.current}}) + labelCache for rendering.
|
|
2737
|
+
// Also handle inputs that already have full pill references — they still need labelCache.
|
|
2677
2738
|
var nonConditionPillInputs: { name: string; fields: string[]; isRecordLevel: boolean }[] = [];
|
|
2678
2739
|
for (var nci = 0; nci < inputResult.inputs.length; nci++) {
|
|
2679
2740
|
var ncInput = inputResult.inputs[nci];
|
|
@@ -2684,9 +2745,8 @@ async function addFlowLogicViaGraphQL(
|
|
|
2684
2745
|
var ncHasShorthand = PILL_SHORTHANDS.some(function (sh) {
|
|
2685
2746
|
return ncVal.includes('{{' + sh + '.') || ncVal.includes('{{' + sh + '}}');
|
|
2686
2747
|
});
|
|
2687
|
-
if (!ncHasShorthand) continue;
|
|
2688
2748
|
|
|
2689
|
-
// Get trigger info
|
|
2749
|
+
// Get trigger info for shorthand rewriting OR for labelCache building (even non-shorthand pills)
|
|
2690
2750
|
if (!conditionTriggerInfo) {
|
|
2691
2751
|
conditionTriggerInfo = await getFlowTriggerInfo(client, flowId);
|
|
2692
2752
|
steps.trigger_info = {
|
|
@@ -2699,14 +2759,18 @@ async function addFlowLogicViaGraphQL(
|
|
|
2699
2759
|
|
|
2700
2760
|
var ncPillBase = conditionTriggerInfo.dataPillBase;
|
|
2701
2761
|
var ncOrigVal = ncVal;
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2762
|
+
|
|
2763
|
+
// Rewrite shorthand pills to full dataPillBase
|
|
2764
|
+
if (ncHasShorthand) {
|
|
2765
|
+
for (var si2 = 0; si2 < PILL_SHORTHANDS.length; si2++) {
|
|
2766
|
+
var sh2 = PILL_SHORTHANDS[si2];
|
|
2767
|
+
ncVal = ncVal.split('{{' + sh2 + '.').join('{{' + ncPillBase + '.');
|
|
2768
|
+
ncVal = ncVal.split('{{' + sh2 + '}}').join('{{' + ncPillBase + '}}');
|
|
2769
|
+
}
|
|
2770
|
+
ncInput.value.value = ncVal;
|
|
2706
2771
|
}
|
|
2707
|
-
ncInput.value.value = ncVal;
|
|
2708
2772
|
|
|
2709
|
-
// Extract field names from pills for labelCache
|
|
2773
|
+
// Extract field names from ALL pills in the value for labelCache
|
|
2710
2774
|
var ncPillFields: string[] = [];
|
|
2711
2775
|
var ncIsRecordLevel = false;
|
|
2712
2776
|
var ncPillRx = /\{\{([^}]+)\}\}/g;
|
|
@@ -2721,8 +2785,14 @@ async function addFlowLogicViaGraphQL(
|
|
|
2721
2785
|
}
|
|
2722
2786
|
}
|
|
2723
2787
|
|
|
2724
|
-
|
|
2725
|
-
|
|
2788
|
+
if (ncPillFields.length > 0 || ncIsRecordLevel) {
|
|
2789
|
+
nonConditionPillInputs.push({ name: ncInput.name, fields: ncPillFields, isRecordLevel: ncIsRecordLevel });
|
|
2790
|
+
if (ncOrigVal !== ncVal) {
|
|
2791
|
+
steps['pill_rewrite_' + ncInput.name] = { original: ncOrigVal, rewritten: ncVal };
|
|
2792
|
+
} else {
|
|
2793
|
+
steps['pill_labelcache_' + ncInput.name] = { fields: ncPillFields, isRecordLevel: ncIsRecordLevel };
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2726
2796
|
}
|
|
2727
2797
|
|
|
2728
2798
|
// Calculate insertion order
|