@teleporthq/teleport-plugin-next-workflows 0.43.21 → 0.43.22

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.
@@ -125,6 +125,102 @@ function isSecretRef(value) {
125
125
  );
126
126
  }
127
127
 
128
+ // Resolve the inline workflow-context placeholders the rich-text email-body
129
+ // editor embeds for every dynamic value:
130
+ // <span class="context-value-inline" data-ctx-node-id="<id>"
131
+ // data-ctx-path='["<id>","result"]'>Label</span>
132
+ // resolveConfig leaves plain strings untouched, so without this the literal
133
+ // placeholder label (e.g. "Reset URL") would be delivered in the email instead
134
+ // of the resolved value. Handles both the raw single-quoted attribute form and
135
+ // the Quill-serialized &quot;-escaped form, plus the nested inner <span> Quill
136
+ // wraps the label in.
137
+ function resolveRichTextContext(html, context) {
138
+ if (typeof html !== 'string' || html.indexOf('context-value-inline') === -1) {
139
+ return html;
140
+ }
141
+ var out = '';
142
+ var i = 0;
143
+ while (i < html.length) {
144
+ var spanStart = html.indexOf('<span', i);
145
+ if (spanStart === -1) { out += html.slice(i); break; }
146
+ var openEnd = html.indexOf('>', spanStart);
147
+ if (openEnd === -1) { out += html.slice(i); break; }
148
+ var openTag = html.slice(spanStart, openEnd + 1);
149
+ if (openTag.indexOf('context-value-inline') === -1) {
150
+ // Unrelated <span> — copy it through verbatim and keep scanning.
151
+ out += html.slice(i, openEnd + 1);
152
+ i = openEnd + 1;
153
+ continue;
154
+ }
155
+ // Copy everything before the embed, then replace the whole embed span
156
+ // (including any nested inner spans) with the resolved value.
157
+ out += html.slice(i, spanStart);
158
+ var depth = 1;
159
+ var j = openEnd + 1;
160
+ while (j < html.length && depth > 0) {
161
+ var nextOpen = html.indexOf('<span', j);
162
+ var nextClose = html.indexOf('</span>', j);
163
+ if (nextClose === -1) { j = html.length; break; }
164
+ if (nextOpen !== -1 && nextOpen < nextClose) {
165
+ var innerEnd = html.indexOf('>', nextOpen);
166
+ j = innerEnd === -1 ? html.length : innerEnd + 1;
167
+ depth++;
168
+ } else {
169
+ j = nextClose + 7; // '</span>'.length
170
+ depth--;
171
+ }
172
+ }
173
+ out += resolveRichTextContextSpan(openTag, context);
174
+ i = j;
175
+ }
176
+ return out;
177
+ }
178
+
179
+ function resolveRichTextContextSpan(openTag, context) {
180
+ var nodeId = extractHtmlAttr(openTag, 'data-ctx-node-id');
181
+ var pathRaw = extractHtmlAttr(openTag, 'data-ctx-path');
182
+ var path = null;
183
+ if (pathRaw) {
184
+ var decoded = pathRaw.replace(/&quot;/g, '"').replace(/&#34;/g, '"').replace(/&amp;/g, '&');
185
+ try { path = JSON.parse(decoded); } catch (e) { path = null; }
186
+ }
187
+ if (!Array.isArray(path) || path.length === 0) {
188
+ path = nodeId ? [nodeId] : [];
189
+ }
190
+ if (!nodeId && path.length > 0) { nodeId = path[0]; }
191
+ if (!nodeId) { return ''; }
192
+ var value = resolveContextRef({ type: 'workflowContext', nodeId: nodeId, path: path }, context);
193
+ if (value === undefined || value === null) { return ''; }
194
+ return escapeHtmlText(String(value));
195
+ }
196
+
197
+ // Read a quoted HTML attribute value (double- or single-quoted) without a regex
198
+ // so the surrounding template literal needs no backslash escaping.
199
+ function extractHtmlAttr(tag, name) {
200
+ var key = name + '=';
201
+ var at = tag.indexOf(key);
202
+ if (at === -1) { return ''; }
203
+ var quote = tag.charAt(at + key.length);
204
+ if (quote !== '"' && quote !== "'") { return ''; }
205
+ var start = at + key.length + 1;
206
+ var end = tag.indexOf(quote, start);
207
+ if (end === -1) { return ''; }
208
+ return tag.slice(start, end);
209
+ }
210
+
211
+ function escapeHtmlText(value) {
212
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
213
+ }
214
+
215
+ // Scalar config values are normally just secret-resolved. Rich-text strings
216
+ // (email bodies) additionally need their inline context placeholders resolved.
217
+ function resolveScalarValue(value, context) {
218
+ if (typeof value === 'string' && value.indexOf('context-value-inline') !== -1) {
219
+ return resolveRichTextContext(value, context);
220
+ }
221
+ return resolveSecret(value, context);
222
+ }
223
+
128
224
  function resolveConfig(config, context) {
129
225
  if (!config || typeof config !== 'object') return config;
130
226
  const resolved = {};
@@ -154,7 +250,7 @@ function resolveConfig(config, context) {
154
250
  if (item && typeof item === 'object' && !Array.isArray(item)) {
155
251
  return resolveConfig(item, context);
156
252
  }
157
- return resolveSecret(item, context);
253
+ return resolveScalarValue(item, context);
158
254
  });
159
255
  } else {
160
256
  resolved[key] = val.map(function(item) {
@@ -164,7 +260,7 @@ function resolveConfig(config, context) {
164
260
  if (item && typeof item === 'object' && !Array.isArray(item)) {
165
261
  return resolveConfig(item, context);
166
262
  }
167
- return resolveSecret(item, context);
263
+ return resolveScalarValue(item, context);
168
264
  });
169
265
  }
170
266
  } else if (val && typeof val === 'object' && val.type === 'workflowContext') {
@@ -176,7 +272,7 @@ function resolveConfig(config, context) {
176
272
  } else if (val && typeof val === 'object' && !Array.isArray(val)) {
177
273
  resolved[key] = resolveConfig(val, context);
178
274
  } else {
179
- resolved[key] = resolveSecret(val, context);
275
+ resolved[key] = resolveScalarValue(val, context);
180
276
  }
181
277
  }
182
278
  return resolved;
@@ -741,6 +837,7 @@ module.exports = {
741
837
  resolveSecret,
742
838
  resolveConfig,
743
839
  resolveContextRef,
840
+ resolveRichTextContext,
744
841
  unwrapWorkflowCollection,
745
842
  evaluateCondition,
746
843
  evaluateSingleComparison,
@@ -244,6 +244,20 @@ async function general_custom_js(config: any, context: Record<string, unknown>)
244
244
  const argValues: Record<string, unknown> = {
245
245
  previousContext,
246
246
  params,
247
+ // The workflow editor + AI generator advertise the top-level regular
248
+ // signature as `customHandler(params, inputs, workflowContext)` (see
249
+ // teleport-services-worker custom-js-contract.ts) where `workflowContext`
250
+ // is documented as "prior node results by node index" and `inputs` is a
251
+ // reserved positional. At top level that array IS `params` (built above in
252
+ // execution order: params[0] = trigger, params[1] = first node after the
253
+ // trigger). Without these aliases a handler that follows the advertised
254
+ // contract and reads `workflowContext[1]` gets `undefined[1]` → TypeError
255
+ // at runtime (the 2026-06-19 Sugarpost homepage "Something went wrong"
256
+ // page-load crash). Aliasing both to `params` honours the documented
257
+ // contract; the legacy `(params)` form is unaffected since it never names
258
+ // them, and inside a loop the still-distinct `innerParams*` win below.
259
+ inputs: params,
260
+ workflowContext: params,
247
261
  }
248
262
  for (let i = 0; i < innerParamsList.length; i++) {
249
263
  const name = i === 0 ? 'innerParams' : 'innerParams' + (i + 1)