@teleporthq/teleport-plugin-next-workflows 0.43.32 → 0.43.33

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.
@@ -176,9 +176,26 @@ async function safeQuery(client, sql, params, mode) {
176
176
  }
177
177
  }
178
178
 
179
- function buildWhereClause(filters, queryParams, startIndex) {
179
+ function isSkippableFilterValue(value) {
180
+ if (value === undefined || value === null) return true;
181
+ if (typeof value === 'string' && value.trim() === '') return true;
182
+ if (Array.isArray(value) && value.length === 0) return true;
183
+ return false;
184
+ }
185
+
186
+ function isAllSentinelFilterValue(value, filter) {
187
+ if (typeof value !== 'string') return false;
188
+ var normalized = value.trim().toLowerCase();
189
+ if (filter && typeof filter.treatAsAll === 'string' && filter.treatAsAll.trim().toLowerCase() === normalized) {
190
+ return true;
191
+ }
192
+ return normalized === 'all' || normalized === 'any' || normalized === 'everything' || normalized === '__all__';
193
+ }
194
+
195
+ function buildWhereClause(filters, queryParams, startIndex, options) {
180
196
  var conditions = [];
181
197
  var paramIndex = startIndex;
198
+ var skipOptionalEmpty = !!(options && options.skipOptionalEmpty);
182
199
 
183
200
  if (!filters || !Array.isArray(filters) || filters.length === 0) {
184
201
  return { clause: '', paramIndex: paramIndex };
@@ -194,6 +211,7 @@ function buildWhereClause(filters, queryParams, startIndex) {
194
211
  var operand = f.operand || f.operator || '=';
195
212
 
196
213
  if (value === undefined) continue;
214
+ if (skipOptionalEmpty && (isSkippableFilterValue(value) || isAllSentinelFilterValue(value, f))) continue;
197
215
 
198
216
  if (Array.isArray(value)) {
199
217
  if (value.length === 0) continue;
@@ -211,6 +229,37 @@ function buildWhereClause(filters, queryParams, startIndex) {
211
229
  conditions.push(field + ' IS NOT NULL');
212
230
  }
213
231
  } else {
232
+ var normalizedOperand = String(operand || '=').toLowerCase();
233
+ if (normalizedOperand === 'contains' || normalizedOperand === 'not_contains' || normalizedOperand === 'not-contains') {
234
+ if (isSkippableFilterValue(value)) {
235
+ conditions.push('FALSE');
236
+ continue;
237
+ }
238
+ conditions.push(field + (normalizedOperand === 'contains' ? ' ILIKE ' : ' NOT ILIKE ') + '$' + paramIndex);
239
+ queryParams.push('%' + String(value) + '%');
240
+ paramIndex++;
241
+ continue;
242
+ }
243
+ if (normalizedOperand === 'startswith' || normalizedOperand === 'starts_with' || normalizedOperand === 'starts-with') {
244
+ if (isSkippableFilterValue(value)) {
245
+ conditions.push('FALSE');
246
+ continue;
247
+ }
248
+ conditions.push(field + ' ILIKE $' + paramIndex);
249
+ queryParams.push(String(value) + '%');
250
+ paramIndex++;
251
+ continue;
252
+ }
253
+ if (normalizedOperand === 'endswith' || normalizedOperand === 'ends_with' || normalizedOperand === 'ends-with') {
254
+ if (isSkippableFilterValue(value)) {
255
+ conditions.push('FALSE');
256
+ continue;
257
+ }
258
+ conditions.push(field + ' ILIKE $' + paramIndex);
259
+ queryParams.push('%' + String(value));
260
+ paramIndex++;
261
+ continue;
262
+ }
214
263
  var validOps = ['=', '!=', '>', '<', '>=', '<=', 'LIKE', 'ILIKE'];
215
264
  var sqlOp = validOps.indexOf(operand) !== -1 ? operand : '=';
216
265
  conditions.push(field + ' ' + sqlOp + ' $' + paramIndex);
@@ -253,7 +302,7 @@ async function handleSelect(client, body) {
253
302
  var cols = selectedColumns.length > 0 ? selectedColumns.join(', ') : '*';
254
303
  var sql = 'SELECT ' + cols + ' FROM ' + tableName;
255
304
 
256
- var where = buildWhereClause(filters, queryParams, 1);
305
+ var where = buildWhereClause(filters, queryParams, 1, { skipOptionalEmpty: true });
257
306
  sql += where.clause;
258
307
 
259
308
  if (sorts.length > 0) {
@@ -289,7 +338,7 @@ async function handleSelect(client, body) {
289
338
 
290
339
  var countSql = 'SELECT COUNT(*) FROM ' + tableName;
291
340
  var countParams = [];
292
- var countWhere = buildWhereClause(filters, countParams, 1);
341
+ var countWhere = buildWhereClause(filters, countParams, 1, { skipOptionalEmpty: true });
293
342
  countSql += countWhere.clause;
294
343
  var countResult = await safeQuery(client, countSql, countParams, 'count');
295
344
  var count = parseInt(countResult.rows[0].count, 10);
@@ -306,7 +355,7 @@ async function handleCount(client, body) {
306
355
 
307
356
  var queryParams = [];
308
357
  var sql = 'SELECT COUNT(*) FROM ' + tableName;
309
- var where = buildWhereClause(filters, queryParams, 1);
358
+ var where = buildWhereClause(filters, queryParams, 1, { skipOptionalEmpty: true });
310
359
  sql += where.clause;
311
360
 
312
361
  var result = await safeQuery(client, sql, queryParams, 'count');
@@ -11,6 +11,42 @@ async function data_select(config: any, context: any) {
11
11
  const rawQueryUserPart = config.rawQueryUserPart
12
12
  const baseUrl = (context && context.__baseUrl) || ''
13
13
 
14
+ function isEmptyFilterValue(value: any) {
15
+ if (value === undefined || value === null) return true
16
+ if (typeof value === 'string' && value.trim() === '') return true
17
+ if (Array.isArray(value) && value.length === 0) return true
18
+ return false
19
+ }
20
+
21
+ function isAllSentinel(value: any, filter: any) {
22
+ if (typeof value !== 'string') return false
23
+ const normalized = value.trim().toLowerCase()
24
+ const configured = filter && filter.treatAsAll
25
+ if (typeof configured === 'string' && configured.trim().toLowerCase() === normalized) {
26
+ return true
27
+ }
28
+ return (
29
+ normalized === 'all' ||
30
+ normalized === 'any' ||
31
+ normalized === 'everything' ||
32
+ normalized === '__all__'
33
+ )
34
+ }
35
+
36
+ function shouldSkipOptionalFilter(filter: any) {
37
+ if (!filter) return true
38
+ const value = filter.destination !== undefined ? filter.destination : filter.value
39
+ if (isEmptyFilterValue(value)) return true
40
+ if (isAllSentinel(value, filter)) return true
41
+ if (Array.isArray(filter.skipIfValue) && filter.skipIfValue.indexOf(value) !== -1) return true
42
+ if (filter.skipIfEmpty === true && isEmptyFilterValue(value)) return true
43
+ return false
44
+ }
45
+
46
+ const effectiveFilters = filters.filter(function (filter: any) {
47
+ return !shouldSkipOptionalFilter(filter)
48
+ })
49
+
14
50
  // A filter whose value is the unresolved route-param sentinel (see
15
51
  // resolveTemplateTokenString in runtime-utils) — e.g. a select scoped to
16
52
  // {{Current Page Entity.id}} that runs on a page with no such route param
@@ -19,8 +55,8 @@ async function data_select(config: any, context: any) {
19
55
  // WHOLE workflow (a single unresolvable filter must never kill the note-create
20
56
  // submit). Never fall through to an unscoped query. Observable via the warn +
21
57
  // the __skippedUnavailableFilter marker.
22
- for (let __fi = 0; __fi < filters.length; __fi++) {
23
- const __f: any = filters[__fi]
58
+ for (let __fi = 0; __fi < effectiveFilters.length; __fi++) {
59
+ const __f: any = effectiveFilters[__fi]
24
60
  if (
25
61
  __f &&
26
62
  (__f.value === '__TQ_UNRESOLVED_ROUTE_PARAM__' ||
@@ -39,7 +75,7 @@ async function data_select(config: any, context: any) {
39
75
  }
40
76
 
41
77
  try {
42
- const payload: any = { tableName, filters, sorts, selectedColumns }
78
+ const payload: any = { tableName, filters: effectiveFilters, sorts, selectedColumns }
43
79
  if (limit !== undefined && limit !== null) {
44
80
  payload.limit = limit
45
81
  }
@@ -97,11 +97,47 @@ async function general_extract_form_data(config: any, context: Record<string, un
97
97
  }
98
98
 
99
99
  const fieldKeys = Object.keys(fields)
100
- const result: Record<string, any> = { fields }
100
+ const result: Record<string, any> = { fields, formData: fields }
101
101
  for (let fi = 0; fi < fieldKeys.length; fi++) {
102
102
  result[fieldKeys[fi]] = fields[fieldKeys[fi]]
103
103
  }
104
104
 
105
+ // Workflows generated/imported from HTML often refer to a form control by
106
+ // its DOM id (`class-session-select`) while FormData exposes submitted
107
+ // values by name (`class_session_id`). Preserve the canonical name keys
108
+ // above, and add non-overwriting aliases for id/data-form-field-id so both
109
+ // conventions resolve to the same submitted value.
110
+ try {
111
+ const addAlias = (alias: string | null | undefined, name: string | undefined) => {
112
+ if (!alias || !name) return
113
+ if (fields[name] === undefined) return
114
+ if (result[alias] === undefined) {
115
+ result[alias] = fields[name]
116
+ }
117
+ }
118
+ const controls = (formEl as HTMLFormElement).querySelectorAll(
119
+ 'input[name], select[name], textarea[name], button[name]'
120
+ )
121
+ for (let ci = 0; ci < controls.length; ci++) {
122
+ const control = controls[ci] as HTMLInputElement
123
+ addAlias(control.id, control.name)
124
+ addAlias(control.getAttribute('data-form-field-id'), control.name)
125
+ }
126
+ const outsideControls = document.querySelectorAll(
127
+ 'input[name], select[name], textarea[name], button[name]'
128
+ )
129
+ for (let oi = 0; oi < outsideControls.length; oi++) {
130
+ const control = outsideControls[oi] as HTMLInputElement
131
+ if (formEl.contains(control)) continue
132
+ const ownerForm = control.closest('form')
133
+ if (ownerForm && ownerForm !== formEl) continue
134
+ addAlias(control.id, control.name)
135
+ addAlias(control.getAttribute('data-form-field-id'), control.name)
136
+ }
137
+ } catch (_aliasErr) {
138
+ /* aliases are best-effort; canonical name-based fields remain intact */
139
+ }
140
+
105
141
  // Diagnostic: surface any required inputs whose value is empty at extraction
106
142
  // time so downstream custom-JS validators have something concrete to report
107
143
  // instead of a generic "please fill in all required fields" message.