@withone/cli 1.20.1 → 1.20.3

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/dist/index.js CHANGED
@@ -2455,6 +2455,16 @@ ${JSON.stringify(st.example, null, 2)}
2455
2455
  - **Interpolation** (\`{{$.input.x}}\`): Use inside string values where the selector is embedded in text \u2014 e.g., \`"Hello {{$.steps.getUser.response.name}}"\`. The resolved value is always stringified. Use this in \`data\`, \`pathVars\`, and \`queryParams\` when mixing selectors with literal text.
2456
2456
  - **Rule of thumb**: If the value is purely a selector, use bare. If it's a string containing a selector, use \`{{...}}\`.
2457
2457
 
2458
+ ### Selectors vs expressions
2459
+
2460
+ Selectors in data fields (\`data\`, \`queryParams\`, \`pathVars\`, \`connectionKey\`) are **dot-path lookups only** \u2014 they do not support JavaScript operators like \`||\` or \`&&\`. For default values, use the \`default\` field on the input definition:
2461
+
2462
+ \`\`\`json
2463
+ { "inputs": { "maxResults": { "type": "number", "default": 10 } } }
2464
+ \`\`\`
2465
+
2466
+ The \`if\`, \`unless\`, \`condition.expression\`, \`while.condition\`, \`transform.expression\`, and \`code.source\` fields **do** support full JavaScript expressions (e.g., \`$.input.email && $.input.email.length > 0\`).
2467
+
2458
2468
  ### \`output\` vs \`response\` on step results
2459
2469
 
2460
2470
  Every completed step produces both \`output\` and \`response\`:
@@ -2745,11 +2755,12 @@ function validateSelectorReferences(flow2) {
2745
2755
  return ids;
2746
2756
  }
2747
2757
  const allStepIds = getAllStepIds(flow2.steps);
2758
+ const SELECTOR_TOKEN_RE = /\$\.[a-zA-Z_][\w.\[\]*]*/g;
2748
2759
  function extractSelectors(value) {
2749
2760
  const selectors = [];
2750
2761
  if (typeof value === "string") {
2751
- if (value.startsWith("$.")) {
2752
- selectors.push(value);
2762
+ for (const match of value.matchAll(SELECTOR_TOKEN_RE)) {
2763
+ selectors.push(match[0]);
2753
2764
  }
2754
2765
  const interpolated = value.matchAll(/\{\{(\$\.[^}]+)\}\}/g);
2755
2766
  for (const match of interpolated) {
@@ -2784,6 +2795,24 @@ function validateSelectorReferences(flow2) {
2784
2795
  }
2785
2796
  }
2786
2797
  }
2798
+ const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
2799
+ function checkOperatorsInSelectorField(value, path5) {
2800
+ if (typeof value === "string" && value.startsWith("$.")) {
2801
+ if (value.includes("||")) {
2802
+ errors.push({ path: path5, message: `Selector "${value}" contains unsupported operator "||". Selectors in data fields use dot-path resolution, not JS evaluation. Use the "default" field on the input definition instead, or use a "code" step for complex expressions.` });
2803
+ } else if (value.includes("&&")) {
2804
+ errors.push({ path: path5, message: `Selector "${value}" contains unsupported operator "&&". Selectors in data fields use dot-path resolution, not JS evaluation. Use a "condition" step or "code" step for complex expressions.` });
2805
+ }
2806
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2807
+ for (const [k, v] of Object.entries(value)) {
2808
+ checkOperatorsInSelectorField(v, `${path5}.${k}`);
2809
+ }
2810
+ } else if (Array.isArray(value)) {
2811
+ for (let i = 0; i < value.length; i++) {
2812
+ checkOperatorsInSelectorField(value[i], `${path5}[${i}]`);
2813
+ }
2814
+ }
2815
+ }
2787
2816
  function checkStep(step, pathPrefix) {
2788
2817
  if (step.if) checkSelectors(extractSelectors(step.if), `${pathPrefix}.if`);
2789
2818
  if (step.unless) checkSelectors(extractSelectors(step.unless), `${pathPrefix}.unless`);
@@ -2796,7 +2825,12 @@ function validateSelectorReferences(flow2) {
2796
2825
  if (fd.stepsArray) continue;
2797
2826
  const value = config[fieldName];
2798
2827
  if (value !== void 0) {
2799
- checkSelectors(extractSelectors(value), `${pathPrefix}.${descriptor.configKey}.${fieldName}`);
2828
+ const fieldKey = `${descriptor.configKey}.${fieldName}`;
2829
+ const fieldPath = `${pathPrefix}.${fieldKey}`;
2830
+ checkSelectors(extractSelectors(value), fieldPath);
2831
+ if (!EXPRESSION_FIELDS.has(fieldKey)) {
2832
+ checkOperatorsInSelectorField(value, fieldPath);
2833
+ }
2800
2834
  }
2801
2835
  }
2802
2836
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.20.1",
3
+ "version": "1.20.3",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -102,6 +102,20 @@ Connection inputs with a `connection` field auto-resolve if the user has exactly
102
102
 
103
103
  A pure `$.xxx` value resolves to the raw type. A string containing `{{$.xxx}}` does string interpolation.
104
104
 
105
+ ### Selectors vs expressions
106
+
107
+ Selectors in data fields (`data`, `queryParams`, `pathVars`, `connectionKey`) are **dot-path lookups only** — they do not support JavaScript operators like `||` or `&&`. For default values, use the `default` field on the input definition:
108
+
109
+ ```json
110
+ {
111
+ "inputs": {
112
+ "maxResults": { "type": "number", "default": 10 }
113
+ }
114
+ }
115
+ ```
116
+
117
+ The `if`, `unless`, `condition.expression`, `while.condition`, `transform.expression`, and `code.source` fields **do** support full JavaScript expressions (e.g., `$.input.email && $.input.email.length > 0`).
118
+
105
119
  ## Step Types
106
120
 
107
121
  ### `action` — Execute a One API action
@@ -172,14 +186,47 @@ A pure `$.xxx` value resolves to the raw type. A string containing `{{$.xxx}}` d
172
186
 
173
187
  ### `parallel` — Run steps concurrently
174
188
 
189
+ Use when fetching from 2+ independent data sources before combining results. Each substep must have the full step schema (`id`, `name`, `type`, and type-specific config).
190
+
175
191
  ```json
176
192
  {
177
- "id": "lookups",
193
+ "id": "fetchAll",
194
+ "name": "Fetch email and calendar data in parallel",
178
195
  "type": "parallel",
179
- "parallel": { "maxConcurrency": 5, "steps": [...] }
196
+ "parallel": {
197
+ "maxConcurrency": 5,
198
+ "steps": [
199
+ {
200
+ "id": "fetchEmails",
201
+ "name": "Fetch recent emails",
202
+ "type": "action",
203
+ "action": {
204
+ "platform": "gmail",
205
+ "actionId": "conn_mod_def::GmailListMessages::xxx",
206
+ "connectionKey": "$.input.gmailKey",
207
+ "pathVars": { "userId": "me" },
208
+ "queryParams": { "maxResults": 10 }
209
+ }
210
+ },
211
+ {
212
+ "id": "fetchEvents",
213
+ "name": "Fetch today's calendar events",
214
+ "type": "action",
215
+ "action": {
216
+ "platform": "google-calendar",
217
+ "actionId": "conn_mod_def::CalendarListEvents::xxx",
218
+ "connectionKey": "$.input.calendarKey",
219
+ "pathVars": { "calendarId": "primary" },
220
+ "queryParams": { "maxResults": 10 }
221
+ }
222
+ }
223
+ ]
224
+ }
180
225
  }
181
226
  ```
182
227
 
228
+ After a parallel step, access each substep's output by its `id`: `$.steps.fetchEmails.response`, `$.steps.fetchEvents.response`.
229
+
183
230
  ### `file-read` / `file-write` — Filesystem access
184
231
 
185
232
  ```json
@@ -247,7 +294,18 @@ Strategies: `fail` (default), `continue`, `retry`, `fallback`.
247
294
 
248
295
  Conditional execution: `"if": "$.steps.find.response.data.length > 0"`
249
296
 
250
- ## AI-Augmented Pattern: file-write -> bash -> code
297
+ ## AI-Augmented Patterns
298
+
299
+ ### When to use parallel steps
300
+
301
+ Use `parallel` when your workflow fetches from 2+ independent data sources before combining them. Common patterns:
302
+ - Fetch Gmail + Calendar + Sheets → compile into daily briefing
303
+ - Search Exa + scrape with Firecrawl → merge research data
304
+ - Query BigQuery + list Google Drive files → combine for analysis
305
+
306
+ Each substep inside `parallel.steps` must have the full step schema: `id`, `name`, `type`, and the type-specific config (`action`, `code`, etc.). Follow a parallel step with a `code` or `transform` step to combine the results.
307
+
308
+ ### file-write -> bash -> code
251
309
 
252
310
  When raw data needs analysis, use this pattern:
253
311
  1. `file-write` — save data to temp file (API responses are too large to inline)