@superglue/cli 1.1.13 → 1.1.14

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/dist/cli.js CHANGED
@@ -9077,8 +9077,8 @@ var require_luxon = __commonJS({
9077
9077
  let current = null, currentFull = "", bracketed = false;
9078
9078
  const splits = [];
9079
9079
  for (let i = 0; i < fmt.length; i++) {
9080
- const c3 = fmt.charAt(i);
9081
- if (c3 === "'") {
9080
+ const c2 = fmt.charAt(i);
9081
+ if (c2 === "'") {
9082
9082
  if (currentFull.length > 0 || bracketed) {
9083
9083
  splits.push({
9084
9084
  literal: bracketed || /^\s+$/.test(currentFull),
@@ -9089,9 +9089,9 @@ var require_luxon = __commonJS({
9089
9089
  currentFull = "";
9090
9090
  bracketed = !bracketed;
9091
9091
  } else if (bracketed) {
9092
- currentFull += c3;
9093
- } else if (c3 === current) {
9094
- currentFull += c3;
9092
+ currentFull += c2;
9093
+ } else if (c2 === current) {
9094
+ currentFull += c2;
9095
9095
  } else {
9096
9096
  if (currentFull.length > 0) {
9097
9097
  splits.push({
@@ -9099,8 +9099,8 @@ var require_luxon = __commonJS({
9099
9099
  val: currentFull
9100
9100
  });
9101
9101
  }
9102
- currentFull = c3;
9103
- current = c3;
9102
+ currentFull = c2;
9103
+ current = c2;
9104
9104
  }
9105
9105
  }
9106
9106
  if (currentFull.length > 0) {
@@ -11782,7 +11782,7 @@ var require_luxon = __commonJS({
11782
11782
  return fixOffset(objToLocalTS(obj), offset2, zone);
11783
11783
  }
11784
11784
  function adjustTime(inst, dur) {
11785
- const oPre = inst.o, year = inst.c.year + Math.trunc(dur.years), month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3, c3 = {
11785
+ const oPre = inst.o, year = inst.c.year + Math.trunc(dur.years), month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3, c2 = {
11786
11786
  ...inst.c,
11787
11787
  year,
11788
11788
  month,
@@ -11797,7 +11797,7 @@ var require_luxon = __commonJS({
11797
11797
  minutes: dur.minutes,
11798
11798
  seconds: dur.seconds,
11799
11799
  milliseconds: dur.milliseconds
11800
- }).as("milliseconds"), localTS = objToLocalTS(c3);
11800
+ }).as("milliseconds"), localTS = objToLocalTS(c2);
11801
11801
  let [ts, o] = fixOffset(localTS, oPre, inst.zone);
11802
11802
  if (millisToAdd !== 0) {
11803
11803
  ts += millisToAdd;
@@ -11832,72 +11832,72 @@ var require_luxon = __commonJS({
11832
11832
  }
11833
11833
  function toISODate(o, extended, precision) {
11834
11834
  const longFormat = o.c.year > 9999 || o.c.year < 0;
11835
- let c3 = "";
11836
- if (longFormat && o.c.year >= 0) c3 += "+";
11837
- c3 += padStart(o.c.year, longFormat ? 6 : 4);
11838
- if (precision === "year") return c3;
11835
+ let c2 = "";
11836
+ if (longFormat && o.c.year >= 0) c2 += "+";
11837
+ c2 += padStart(o.c.year, longFormat ? 6 : 4);
11838
+ if (precision === "year") return c2;
11839
11839
  if (extended) {
11840
- c3 += "-";
11841
- c3 += padStart(o.c.month);
11842
- if (precision === "month") return c3;
11843
- c3 += "-";
11840
+ c2 += "-";
11841
+ c2 += padStart(o.c.month);
11842
+ if (precision === "month") return c2;
11843
+ c2 += "-";
11844
11844
  } else {
11845
- c3 += padStart(o.c.month);
11846
- if (precision === "month") return c3;
11845
+ c2 += padStart(o.c.month);
11846
+ if (precision === "month") return c2;
11847
11847
  }
11848
- c3 += padStart(o.c.day);
11849
- return c3;
11848
+ c2 += padStart(o.c.day);
11849
+ return c2;
11850
11850
  }
11851
11851
  function toISOTime(o, extended, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision) {
11852
- let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0, c3 = "";
11852
+ let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0, c2 = "";
11853
11853
  switch (precision) {
11854
11854
  case "day":
11855
11855
  case "month":
11856
11856
  case "year":
11857
11857
  break;
11858
11858
  default:
11859
- c3 += padStart(o.c.hour);
11859
+ c2 += padStart(o.c.hour);
11860
11860
  if (precision === "hour") break;
11861
11861
  if (extended) {
11862
- c3 += ":";
11863
- c3 += padStart(o.c.minute);
11862
+ c2 += ":";
11863
+ c2 += padStart(o.c.minute);
11864
11864
  if (precision === "minute") break;
11865
11865
  if (showSeconds) {
11866
- c3 += ":";
11867
- c3 += padStart(o.c.second);
11866
+ c2 += ":";
11867
+ c2 += padStart(o.c.second);
11868
11868
  }
11869
11869
  } else {
11870
- c3 += padStart(o.c.minute);
11870
+ c2 += padStart(o.c.minute);
11871
11871
  if (precision === "minute") break;
11872
11872
  if (showSeconds) {
11873
- c3 += padStart(o.c.second);
11873
+ c2 += padStart(o.c.second);
11874
11874
  }
11875
11875
  }
11876
11876
  if (precision === "second") break;
11877
11877
  if (showSeconds && (!suppressMilliseconds || o.c.millisecond !== 0)) {
11878
- c3 += ".";
11879
- c3 += padStart(o.c.millisecond, 3);
11878
+ c2 += ".";
11879
+ c2 += padStart(o.c.millisecond, 3);
11880
11880
  }
11881
11881
  }
11882
11882
  if (includeOffset) {
11883
11883
  if (o.isOffsetFixed && o.offset === 0 && !extendedZone) {
11884
- c3 += "Z";
11884
+ c2 += "Z";
11885
11885
  } else if (o.o < 0) {
11886
- c3 += "-";
11887
- c3 += padStart(Math.trunc(-o.o / 60));
11888
- c3 += ":";
11889
- c3 += padStart(Math.trunc(-o.o % 60));
11886
+ c2 += "-";
11887
+ c2 += padStart(Math.trunc(-o.o / 60));
11888
+ c2 += ":";
11889
+ c2 += padStart(Math.trunc(-o.o % 60));
11890
11890
  } else {
11891
- c3 += "+";
11892
- c3 += padStart(Math.trunc(o.o / 60));
11893
- c3 += ":";
11894
- c3 += padStart(Math.trunc(o.o % 60));
11891
+ c2 += "+";
11892
+ c2 += padStart(Math.trunc(o.o / 60));
11893
+ c2 += ":";
11894
+ c2 += padStart(Math.trunc(o.o % 60));
11895
11895
  }
11896
11896
  }
11897
11897
  if (extendedZone) {
11898
- c3 += "[" + o.zone.ianaName + "]";
11898
+ c2 += "[" + o.zone.ianaName + "]";
11899
11899
  }
11900
- return c3;
11900
+ return c2;
11901
11901
  }
11902
11902
  var defaultUnitValues = {
11903
11903
  month: 1,
@@ -12015,10 +12015,10 @@ var require_luxon = __commonJS({
12015
12015
  });
12016
12016
  }
12017
12017
  function diffRelative(start, end, opts) {
12018
- const round = isUndefined(opts.round) ? true : opts.round, rounding = isUndefined(opts.rounding) ? "trunc" : opts.rounding, format = (c3, unit) => {
12019
- c3 = roundTo(c3, round || opts.calendary ? 0 : 2, opts.calendary ? "round" : rounding);
12018
+ const round = isUndefined(opts.round) ? true : opts.round, rounding = isUndefined(opts.rounding) ? "trunc" : opts.rounding, format = (c2, unit) => {
12019
+ c2 = roundTo(c2, round || opts.calendary ? 0 : 2, opts.calendary ? "round" : rounding);
12020
12020
  const formatter = end.loc.clone(opts).relFormatter(opts);
12021
- return formatter.format(c3, unit);
12021
+ return formatter.format(c2, unit);
12022
12022
  }, differ = (unit) => {
12023
12023
  if (opts.calendary) {
12024
12024
  if (!end.hasSame(start, unit)) {
@@ -12059,16 +12059,16 @@ var require_luxon = __commonJS({
12059
12059
  const zone = config.zone || Settings.defaultZone;
12060
12060
  let invalid = config.invalid || (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) || (!zone.isValid ? unsupportedZone(zone) : null);
12061
12061
  this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;
12062
- let c3 = null, o = null;
12062
+ let c2 = null, o = null;
12063
12063
  if (!invalid) {
12064
12064
  const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);
12065
12065
  if (unchanged) {
12066
- [c3, o] = [config.old.c, config.old.o];
12066
+ [c2, o] = [config.old.c, config.old.o];
12067
12067
  } else {
12068
12068
  const ot = isNumber(config.o) && !config.old ? config.o : zone.offset(this.ts);
12069
- c3 = tsToObj(this.ts, ot);
12070
- invalid = Number.isNaN(c3.year) ? new Invalid("invalid input") : null;
12071
- c3 = invalid ? null : c3;
12069
+ c2 = tsToObj(this.ts, ot);
12070
+ invalid = Number.isNaN(c2.year) ? new Invalid("invalid input") : null;
12071
+ c2 = invalid ? null : c2;
12072
12072
  o = invalid ? null : ot;
12073
12073
  }
12074
12074
  }
@@ -12077,7 +12077,7 @@ var require_luxon = __commonJS({
12077
12077
  this.invalid = invalid;
12078
12078
  this.weekData = null;
12079
12079
  this.localWeekData = null;
12080
- this.c = c3;
12080
+ this.c = c2;
12081
12081
  this.o = o;
12082
12082
  this.isLuxonDateTime = true;
12083
12083
  }
@@ -12836,8 +12836,8 @@ var require_luxon = __commonJS({
12836
12836
  const ts1 = localTS - o1 * minuteMs;
12837
12837
  const ts2 = localTS - o2 * minuteMs;
12838
12838
  const c1 = tsToObj(ts1, o1);
12839
- const c22 = tsToObj(ts2, o2);
12840
- if (c1.hour === c22.hour && c1.minute === c22.minute && c1.second === c22.second && c1.millisecond === c22.millisecond) {
12839
+ const c2 = tsToObj(ts2, o2);
12840
+ if (c1.hour === c2.hour && c1.minute === c2.minute && c1.second === c2.second && c1.millisecond === c2.millisecond) {
12841
12841
  return [clone2(this, {
12842
12842
  ts: ts1
12843
12843
  }), clone2(this, {
@@ -13235,10 +13235,10 @@ var require_luxon = __commonJS({
13235
13235
  }
13236
13236
  precision = normalizeUnit(precision);
13237
13237
  const ext = format === "extended";
13238
- let c3 = toISODate(this, ext, precision);
13239
- if (orderedUnits.indexOf(precision) >= 3) c3 += "T";
13240
- c3 += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision);
13241
- return c3;
13238
+ let c2 = toISODate(this, ext, precision);
13239
+ if (orderedUnits.indexOf(precision) >= 3) c2 += "T";
13240
+ c2 += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision);
13241
+ return c2;
13242
13242
  }
13243
13243
  /**
13244
13244
  * Returns an ISO 8601-compliant string representation of this DateTime's date component
@@ -13297,8 +13297,8 @@ var require_luxon = __commonJS({
13297
13297
  return null;
13298
13298
  }
13299
13299
  precision = normalizeUnit(precision);
13300
- let c3 = includePrefix && orderedUnits.indexOf(precision) >= 3 ? "T" : "";
13301
- return c3 + toISOTime(this, format === "extended", suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision);
13300
+ let c2 = includePrefix && orderedUnits.indexOf(precision) >= 3 ? "T" : "";
13301
+ return c2 + toISOTime(this, format === "extended", suppressSeconds, suppressMilliseconds, includeOffset, extendedZone, precision);
13302
13302
  }
13303
13303
  /**
13304
13304
  * Returns an RFC 2822-compatible string representation of this DateTime
@@ -17883,7 +17883,7 @@ attempted value: ${formattedValue}
17883
17883
  type: next.type,
17884
17884
  oneOf: next._whitelist.describe(),
17885
17885
  notOneOf: next._blacklist.describe(),
17886
- tests: next.tests.filter((n, idx, list) => list.findIndex((c3) => c3.OPTIONS.name === n.OPTIONS.name) === idx).map((fn) => {
17886
+ tests: next.tests.filter((n, idx, list) => list.findIndex((c2) => c2.OPTIONS.name === n.OPTIONS.name) === idx).map((fn) => {
17887
17887
  const params = fn.OPTIONS.params && options ? resolveParams(Object.assign({}, fn.OPTIONS.params), options) : fn.OPTIONS.params;
17888
17888
  return {
17889
17889
  name: fn.OPTIONS.name,
@@ -20164,6 +20164,8 @@ var require_utils = __commonJS({
20164
20164
  var getConnectionProtocol2 = (url) => {
20165
20165
  if (url.startsWith("postgres://") || url.startsWith("postgresql://"))
20166
20166
  return "postgres";
20167
+ if (url.startsWith("mssql://") || url.startsWith("sqlserver://"))
20168
+ return "mssql";
20167
20169
  if (url.startsWith("redis://") || url.startsWith("rediss://"))
20168
20170
  return "redis";
20169
20171
  if (url.startsWith("ftp://") || url.startsWith("ftps://") || url.startsWith("sftp://"))
@@ -21499,17 +21501,18 @@ var require_superglue_client = __commonJS({
21499
21501
  options: params.options,
21500
21502
  runId: params.runId
21501
21503
  }, params.traceId ? { "X-Trace-Id": params.traceId } : void 0);
21504
+ const success3 = response.status ? response.status === "success" : response.success ?? false;
21502
21505
  return {
21503
- success: response.success,
21506
+ success: success3,
21504
21507
  data: response.data,
21505
21508
  error: response.error,
21506
- tool: response.tool,
21509
+ tool: response.tool || params.tool,
21507
21510
  stepResults: response.stepResults?.map((sr) => ({
21508
21511
  stepId: sr.stepId,
21509
21512
  success: sr.success,
21510
21513
  data: sr.data,
21511
21514
  error: sr.error
21512
- }))
21515
+ })) || []
21513
21516
  };
21514
21517
  }
21515
21518
  async abortToolExecution(runId) {
@@ -22014,14 +22017,14 @@ var package_default = {
22014
22017
  "LICENSE"
22015
22018
  ],
22016
22019
  dependencies: {
22017
- commander: "^13.0.0",
22018
- "fast-json-patch": "^3.1.1"
22020
+ commander: "15.0.0-0",
22021
+ "fast-json-patch": "3.1.1"
22019
22022
  },
22020
22023
  devDependencies: {
22021
22024
  "@superglue/shared": "file:../shared",
22022
- "@types/node": "^20.0.0",
22023
- tsup: "^8.0.0",
22024
- typescript: "^5.0.0"
22025
+ "@types/node": "25.5.0",
22026
+ tsup: "8.5.1",
22027
+ typescript: "5.8.3"
22025
22028
  },
22026
22029
  scripts: {
22027
22030
  build: "tsup && (cp -r skills dist/ 2>/dev/null || true)",
@@ -23432,13 +23435,18 @@ function registerFindCommand2(parent, getContext2) {
23432
23435
  const mode = opts.mode === "dev" || opts.mode === "prod" ? opts.mode : "all";
23433
23436
  const { items } = await client.listSystems(100, 1, { mode });
23434
23437
  table(
23435
- items.map((s) => ({
23436
- id: s.id,
23437
- name: s.name || "",
23438
- env: s.environment || "prod",
23439
- url: (s.url || "").slice(0, 50)
23440
- })),
23441
- ["id", "name", "env", "url"]
23438
+ items.map((s) => {
23439
+ const credentialKeys = Object.keys(s.credentials || {});
23440
+ const credPlaceholders = credentialKeys.map((k) => `<<${s.id}_${k}>>`).join(", ");
23441
+ return {
23442
+ id: s.id,
23443
+ name: s.name || "",
23444
+ env: s.environment || "prod",
23445
+ url: (s.url || "").slice(0, 40),
23446
+ credentials: credPlaceholders || "(none)"
23447
+ };
23448
+ }),
23449
+ ["id", "name", "env", "url", "credentials"]
23442
23450
  );
23443
23451
  } catch (err) {
23444
23452
  error(err.message);
@@ -23471,7 +23479,7 @@ function registerFindCommand2(parent, getContext2) {
23471
23479
  if (rawQuery === "*") {
23472
23480
  output({
23473
23481
  success: true,
23474
- systems: items.map((s) => ({ id: s.id, name: s.name, url: s.url }))
23482
+ systems: items.map((s) => filterSystemFields(s))
23475
23483
  });
23476
23484
  return;
23477
23485
  }
@@ -23482,7 +23490,7 @@ function registerFindCommand2(parent, getContext2) {
23482
23490
  });
23483
23491
  output({
23484
23492
  success: true,
23485
- systems: filtered.map((s) => ({ id: s.id, name: s.name, url: s.url }))
23493
+ systems: filtered.map((s) => filterSystemFields(s))
23486
23494
  });
23487
23495
  } catch (err) {
23488
23496
  error(err.message);
@@ -23505,6 +23513,46 @@ function registerCallCommand(parent, getContext2) {
23505
23513
  []
23506
23514
  ).addOption(
23507
23515
  new import_commander5.Option("--env <environment>", "Environment: dev or prod").choices(["dev", "prod"])
23516
+ ).addHelpText(
23517
+ "after",
23518
+ `
23519
+ ${c.bold}Discovering Credential Placeholders:${c.reset}
23520
+ Use 'sg system find <system-id>' to see available credential placeholders:
23521
+
23522
+ ${c.dim}$${c.reset} sg system find my_postgres
23523
+ ${c.green}\u2192 credentialPlaceholders:${c.reset} ["<<my_postgres_username>>", "<<my_postgres_password>>"]
23524
+
23525
+ Or use 'sg system list' to see credentials for all systems at once.
23526
+
23527
+ ${c.bold}Examples:${c.reset}
23528
+ ${c.dim}# HTTP API with API key${c.reset}
23529
+ sg system call --system-id stripe \\
23530
+ --url "https://api.stripe.com/v1/customers" \\
23531
+ --headers '{"Authorization": "Bearer <<stripe_api_key>>"}'
23532
+
23533
+ ${c.dim}# PostgreSQL database query${c.reset}
23534
+ sg system call --system-id my_postgres \\
23535
+ --url "postgres://<<my_postgres_username>>:<<my_postgres_password>>@localhost:5432/mydb" \\
23536
+ --body '{"query": "SELECT * FROM users WHERE id = $1", "params": [123]}'
23537
+
23538
+ ${c.dim}# Microsoft SQL Server with parameterized query${c.reset}
23539
+ sg system call --system-id azure_sql \\
23540
+ --url "mssql://<<azure_sql_username>>:<<azure_sql_password>>@server.database.windows.net:1433/mydb" \\
23541
+ --body '{"query": "SELECT * FROM orders WHERE status = @param1", "params": ["pending"]}'
23542
+
23543
+ ${c.dim}# Redis command${c.reset}
23544
+ sg system call --system-id my_redis \\
23545
+ --url "redis://<<my_redis_password>>@localhost:6379" \\
23546
+ --body '{"command": "GET", "args": ["user:123"]}'
23547
+
23548
+ ${c.dim}# SFTP list files${c.reset}
23549
+ sg system call --system-id sftp_server \\
23550
+ --url "sftp://<<sftp_server_username>>:<<sftp_server_password>>@files.example.com:22" \\
23551
+ --body '{"operation": "list", "path": "/uploads"}'
23552
+
23553
+ ${c.bold}Supported Protocols:${c.reset}
23554
+ HTTP/HTTPS, PostgreSQL, MSSQL/SQL Server, Redis, SFTP/FTP/FTPS, SMB
23555
+ `
23508
23556
  ).action(async (opts) => {
23509
23557
  const { client } = getContext2();
23510
23558
  const method = opts.method || "GET";
@@ -246,15 +246,15 @@ sg system call --url "sftp://<<my_sftp_host>>:22/data" \
246
246
  --body '{"operation":"list","path":"/"}'
247
247
  ```
248
248
 
249
- | Flag | Description |
250
- | ------------------- | ---------------------------------------------------------------------- |
251
- | `--url <url>` | Full URL with protocol (http/https/postgres/redis/rediss/sftp/ftp/smb) |
252
- | `--system-id <id>` | System ID for credential injection |
253
- | `--method <method>` | HTTP method (GET, POST, PUT, DELETE, PATCH) |
254
- | `--headers <json>` | HTTP headers JSON with credential placeholders |
255
- | `--body <string>` | Request body (JSON string) |
256
- | `--env <env>` | Environment: `dev` or `prod` |
257
- | `--file <key=path>` | File references (repeatable) |
249
+ | Flag | Description |
250
+ | ------------------- | ------------------------------------------------------------------------------------------------- |
251
+ | `--url <url>` | Full URL with protocol (http/https/postgres/postgresql/mssql/sqlserver/redis/rediss/sftp/ftp/smb) |
252
+ | `--system-id <id>` | System ID for credential injection |
253
+ | `--method <method>` | HTTP method (GET, POST, PUT, DELETE, PATCH) |
254
+ | `--headers <json>` | HTTP headers JSON with credential placeholders |
255
+ | `--body <string>` | Request body (JSON string) |
256
+ | `--env <env>` | Environment: `dev` or `prod` |
257
+ | `--file <key=path>` | File references (repeatable) |
258
258
 
259
259
  **`sg system search-docs`** — Search system documentation.
260
260
 
@@ -458,7 +458,7 @@ Empty arrays are valid — the step simply skips execution.
458
458
 
459
459
  ### Two Step Types
460
460
 
461
- **Request step** (HTTP, Postgres, Redis, FTP/SFTP/SMB) — makes an API call:
461
+ **Request step** (HTTP, PostgreSQL, MSSQL, Redis, FTP/SFTP/SMB) — makes an API call:
462
462
 
463
463
  ```json
464
464
  {
@@ -645,7 +645,7 @@ For detailed documentation on specific topics, read these files in the `referenc
645
645
  | File | When to read |
646
646
  | ------------------------------------- | ------------------------------------------------------------------------------------------------ |
647
647
  | `references/integration.md` | **READ THIS** when deploying tools to production - SDK usage, REST API, webhooks, error handling |
648
- | `references/databases.md` | Building tools that query PostgreSQL/MySQL databases |
648
+ | `references/databases.md` | Building tools that query PostgreSQL or Microsoft SQL Server (Azure SQL) databases |
649
649
  | `references/redis.md` | Building tools that interact with Redis (commands, pipelines, data types) |
650
650
  | `references/file-servers.md` | Building tools that interact with FTP/SFTP/SMB file servers |
651
651
  | `references/transforms-and-output.md` | Complex data transformations, output shaping, JS sandbox constraints |
@@ -1,6 +1,12 @@
1
- # Database Steps (PostgreSQL)
1
+ # Database Steps
2
2
 
3
- ## Step Configuration
3
+ Superglue supports both PostgreSQL and Microsoft SQL Server (including Azure SQL).
4
+
5
+ ---
6
+
7
+ ## PostgreSQL
8
+
9
+ ### Step Configuration
4
10
 
5
11
  ```typescript
6
12
  {
@@ -78,7 +84,7 @@ Returns `result.rows` — array of row objects with column names as keys.
78
84
  - `INSERT/UPDATE/DELETE` with `RETURNING`: array of returned rows
79
85
  - Mutations without `RETURNING`: empty array
80
86
 
81
- ## Batch Operations via Loop
87
+ ### Batch Operations via Loop
82
88
 
83
89
  Use a data selector returning an array to execute a query per item:
84
90
 
@@ -86,3 +92,158 @@ Use a data selector returning an array to execute a query per item:
86
92
  // dataSelector: (sourceData) => sourceData.newUsers
87
93
  // body: {"query": "INSERT INTO users (name) VALUES ($1)", "params": ["<<(sourceData) => sourceData.currentItem.name>>"]}
88
94
  ```
95
+
96
+ ---
97
+
98
+ ## Microsoft SQL Server / Azure SQL
99
+
100
+ ### Step Configuration
101
+
102
+ ```typescript
103
+ {
104
+ type: "request",
105
+ systemId: "my_azure_sql",
106
+ url: "mssql://<<my_azure_sql_user>>:<<my_azure_sql_password>>@<<my_azure_sql_host>>:<<my_azure_sql_port>>/<<my_azure_sql_database>>",
107
+ method: "POST", // ignored for MSSQL but required by schema
108
+ body: '{"query": "SELECT * FROM users WHERE id = @param1", "params": [<<userId>>]}'
109
+ }
110
+ ```
111
+
112
+ Configure the connection url depending on which credentials are saved in the stored db system - reference them accordingly.
113
+
114
+ ### URL Format
115
+
116
+ ```
117
+ mssql://user:password@host:port/database
118
+ sqlserver://user:password@host:port/database
119
+ ```
120
+
121
+ Both `mssql://` and `sqlserver://` protocols are supported. All credential variables are resolved before connecting.
122
+
123
+ #### Azure SQL Database Connection
124
+
125
+ For Azure SQL Database, use the fully qualified server name:
126
+
127
+ ```
128
+ mssql://myuser@myserver:mypassword@myserver.database.windows.net:1433/mydatabase
129
+ ```
130
+
131
+ You can include connection parameters as query strings:
132
+
133
+ ```
134
+ mssql://user:password@host:port/database?encrypt=true&trustServerCertificate=false
135
+ ```
136
+
137
+ **Connection Parameters:**
138
+
139
+ - `encrypt` (default: `true`) - Enable/disable encryption (required for Azure SQL)
140
+ - `trustServerCertificate` (default: `false`) - Whether to trust the server certificate
141
+ - `database` - Database name (can be in URL path or query parameter)
142
+
143
+ ### Body Format
144
+
145
+ JSON string with `query` and optional `params` (or `values`):
146
+
147
+ ```json
148
+ {
149
+ "query": "SELECT * FROM users WHERE age > @param1 AND status = @param2",
150
+ "params": [25, "active"]
151
+ }
152
+ ```
153
+
154
+ ### Parameterized Queries
155
+
156
+ **Always use parameterized queries** with `@param1`, `@param2`, etc. — prevents SQL injection.
157
+
158
+ ```json
159
+ { "query": "SELECT * FROM orders WHERE customer_id = @param1", "params": ["<<customerId>>"] }
160
+ ```
161
+
162
+ With expressions:
163
+
164
+ ```json
165
+ {
166
+ "query": "INSERT INTO logs (message, level) VALUES (@param1, @param2)",
167
+ "params": ["<<(sourceData) => sourceData.message>>", "error"]
168
+ }
169
+ ```
170
+
171
+ With OUTPUT (MSSQL equivalent of RETURNING):
172
+
173
+ ```json
174
+ {
175
+ "query": "INSERT INTO users (name, email) OUTPUT INSERTED.* VALUES (@param1, @param2)",
176
+ "params": [
177
+ "<<(sourceData) => sourceData.currentItem.name>>",
178
+ "<<(sourceData) => sourceData.currentItem.email>>"
179
+ ]
180
+ }
181
+ ```
182
+
183
+ **Parameter Naming**: MSSQL uses named parameters with `@` prefix. The params array maps positionally: `params[0]` → `@param1`, `params[1]` → `@param2`, etc.
184
+
185
+ ### Connection Pooling
186
+
187
+ - Pools cached by connection string, max 10 clients per pool
188
+ - 5s connection timeout, configurable request timeout
189
+ - Idle pools cleaned up automatically
190
+ - SSL/TLS encryption enabled by default for Azure SQL
191
+
192
+ ### Retry Logic
193
+
194
+ Default retries from server config. On final failure, error includes query text and params.
195
+
196
+ ### Return Value
197
+
198
+ Returns the recordset — array of row objects with column names as keys.
199
+
200
+ - `SELECT`: array of matching rows
201
+ - `INSERT/UPDATE/DELETE` with `OUTPUT`: array of returned rows
202
+ - Mutations without `OUTPUT`: empty array
203
+
204
+ ### Batch Operations via Loop
205
+
206
+ Use a data selector returning an array to execute a query per item:
207
+
208
+ ```javascript
209
+ // dataSelector: (sourceData) => sourceData.newUsers
210
+ // body: {"query": "INSERT INTO users (name) VALUES (@param1)", "params": ["<<(sourceData) => sourceData.currentItem.name>>"]}
211
+ ```
212
+
213
+ ### Common Patterns
214
+
215
+ **Simple SELECT:**
216
+
217
+ ```json
218
+ {
219
+ "query": "SELECT id, name, email FROM users WHERE status = @param1",
220
+ "params": ["active"]
221
+ }
222
+ ```
223
+
224
+ **INSERT with OUTPUT:**
225
+
226
+ ```json
227
+ {
228
+ "query": "INSERT INTO users (name, email, created_at) OUTPUT INSERTED.id, INSERTED.created_at VALUES (@param1, @param2, GETUTCDATE())",
229
+ "params": ["John Doe", "john@example.com"]
230
+ }
231
+ ```
232
+
233
+ **UPDATE with OUTPUT:**
234
+
235
+ ```json
236
+ {
237
+ "query": "UPDATE users SET last_login = GETUTCDATE() OUTPUT INSERTED.id, INSERTED.last_login WHERE id = @param1",
238
+ "params": [123]
239
+ }
240
+ ```
241
+
242
+ **Stored Procedure Call:**
243
+
244
+ ```json
245
+ {
246
+ "query": "EXEC dbo.GetUserById @param1",
247
+ "params": [123]
248
+ }
249
+ ```
@@ -82,9 +82,9 @@ Payload fields are at root level — **NEVER** use `sourceData.payload.*`.
82
82
 
83
83
  ## JS Code Constraints (all transform points)
84
84
 
85
- Runs in `isolated-vm` sandbox:
85
+ Runs in Deno subprocess sandbox:
86
86
 
87
- - 4096 MB memory limit, 10s timeout (transforms/selectors), 3s timeout (pagination stops)
87
+ - 8192 MB memory limit, 10 minute timeout per transform
88
88
  - Synchronous only — no async/await, no Promises, no setTimeout
89
89
  - No side effects — no network, filesystem, or console access
90
90
  - No external dependencies — only standard JS built-ins (Array, Object, String, Math, Date, JSON, etc.)