Skip to content

feat(node): support connection string syntax for nodejs client creation - #3917

Open
T1B0 wants to merge 1 commit into
apache:masterfrom
T1B0:add-connection-string-support-node-sdk
Open

feat(node): support connection string syntax for nodejs client creation#3917
T1B0 wants to merge 1 commit into
apache:masterfrom
T1B0:add-connection-string-support-node-sdk

Conversation

@T1B0

@T1B0 T1B0 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR address?

this PR add node sdk support for connection string syntax on client creation
accept common url like syntax new Client("iggy://iggy:iggy@127.0.0.1:8090");

note node:url module was not used since it would have introduced small differencez with rust and other sdk parsing, so connection string is manually parsed to match others sdk behaviors.

@T1B0
T1B0 requested review from hubcio and spetz August 18, 2026 16:15
@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label Aug 18, 2026
@T1B0
T1B0 force-pushed the add-connection-string-support-node-sdk branch from 2afce42 to e9ffac3 Compare August 18, 2026 16:16
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.29787% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.13%. Comparing base (7207c64) to head (3de15b0).

Files with missing lines Patch % Lines
...oreign/node/src/client/client.connection-string.ts 98.14% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3917      +/-   ##
============================================
+ Coverage     84.11%   84.13%   +0.01%     
  Complexity     1358     1358              
============================================
  Files          1215     1216       +1     
  Lines        170001   170223     +222     
  Branches     137611   137662      +51     
============================================
+ Hits         142995   143213     +218     
- Misses        23305    23308       +3     
- Partials       3701     3702       +1     
Components Coverage Δ
Rust Core 84.94% <ø> (ø)
Java SDK 66.67% <ø> (ø)
C# SDK 76.54% <ø> (ø)
Python SDK 90.13% <ø> (ø)
PHP SDK 84.48% <ø> (ø)
Node SDK 95.85% <98.29%> (+0.04%) ⬆️
Go SDK 68.29% <ø> (ø)
Files with missing lines Coverage Δ
foreign/node/src/client/client.config.ts 100.00% <100.00%> (ø)
foreign/node/src/client/client.socket.ts 95.69% <100.00%> (+<0.01%) ⬆️
foreign/node/src/client/client.ts 97.20% <100.00%> (+0.01%) ⬆️
...oreign/node/src/client/client.connection-string.ts 98.14% <98.14%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@T1B0
T1B0 force-pushed the add-connection-string-support-node-sdk branch 5 times, most recently from 6a3b7a1 to 44edc7c Compare August 22, 2026 17:58
Comment thread foreign/node/src/client/client.connection-string.ts
@github-actions github-actions Bot added S-waiting-on-author PR is waiting on author response and removed S-waiting-on-review PR is waiting on a reviewer labels Aug 23, 2026
@T1B0
T1B0 force-pushed the add-connection-string-support-node-sdk branch from 44edc7c to be0d997 Compare August 24, 2026 10:36
slbotbm
slbotbm previously approved these changes Aug 24, 2026
@T1B0
T1B0 force-pushed the add-connection-string-support-node-sdk branch 3 times, most recently from b78121c to 3745728 Compare August 24, 2026 18:57
@T1B0
T1B0 force-pushed the add-connection-string-support-node-sdk branch from 3745728 to 3de15b0 Compare August 25, 2026 07:30

@hubcio hubcio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few notes from comparing against the rust parser. one i'd want fixed before merge - the credential echo in errors.

these landed outside the diff:

foreign/node/src/stream/consumer-stream.ts:81 and :113 - singleConsumerStream and groupConsumerStream still take ClientConfig, so passing a connection string fails to compile (TS2345). they call the widened getClient, and since getClient isn't exported they're the only place users can reach the widening.

foreign/node/src/client/client.connection.ts:86-90 - reconnect timer is never unref'd and the handle isn't kept, so unlimited means the process can't exit and destroy() can't cancel a wait already in flight. the heartbeat timer next door unrefs itself with a comment saying why. pre-existing, but this PR makes unlimited a one-word option and ships it in the new e2e.

foreign/node/src/client/client.config.ts:61 - error says heartbeatInterval in raw ms at someone who typed heartbeat_interval=1000h.

nothing tests the tls branch - the pre-merge action runs tls.system.e2e.ts, which builds from an object config, so the parser never gets hit.


const protocolParts = connectionString.split('://');
if (protocolParts.length !== 2)
throw new TypeError(`invalid connection string: "${connectionString}"`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

every parse failure echoes the whole connection string, which holds the password or PAT in plaintext. same at 63, 72, 77, 82, 86, 92, 96, 100, 104, 148, and worded differently at 186, 200, 212. rust returns an opaque error here for that reason - drop the interpolation, or echo scheme + host:port only.

throw new TypeError(`invalid connection string: "${connectionString}"`);

const serverAddress = serverAndOptions[0];
if (serverAddress.length === 0 ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastIndexOf(':') guesses wrong on multi-colon authorities - 2001:db8::1 parses as {host:"2001:db8:", port:1}, and host:8090:9090 gives port 9090 where rust reads 8090. one regex replaces the whole block: /^(?:\[([^\]]+)\]|([^:\[\]]+)):(\d+)$/ with host = m[1] ?? m[2], keeping the > 65535 check.

parsed.servername = value;
break;
case 'tls_ca_file':
parsed.ca = readFileSync(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reads the file during parsing even when the transport ends up plain TCP, and a bad path throws a raw ENOENT Error instead of the TypeError the test asserts. rust stores the path and reads it at connect.

case 'nodelay':
parsed.noDelay = parseBoolean(name, value, connectionString);
break;
case 'tls_domain':

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tls=true without tls_domain sends no SNI - node won't derive servername from host, rust falls back to it. fine against iggy's own server, breaks behind an SNI-routing terminator. default it to host when net.isIP(host) === 0.


/** Parses a duration such as "500ms" or "5s" into milliseconds. */
export const parseDuration = (value: string): number => {
const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)$/.exec(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict subset of IggyDuration::from_str - 0, unlimited, disabled, none, 1d, 5sec, 1h 30m all work in rust and throw here. heartbeat_interval=0 is the documented disable and only 0ms gets through. also 1.005s gives 1004.9999999999999, which normalizeClientConfig then rejects - Math.round the product.

throw new TypeError(`invalid connection string: "${connectionString}"`);
const host = bracketed ? bracketed[1] : rawHost;

const options: ParsedConnectionOptions = serverAndOptions[1]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

truthiness means a trailing ? parses while ?& throws. rust rejects both. serverAndOptions.length === 2.

interval: parsed.reconnect?.interval ?? 5000,
maxRetries: value === 'unlimited'
? Number.MAX_SAFE_INTEGER
: parseNumber(name, value, connectionString)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no upper bound - reconnection_retries=99999999999999 is accepted where rust errors on u32 overflow. second undocumented spelling of unlimited.


export { Client, SimpleClient, SingleClient } from './client.js'
export * from './client.config.js';
export * from './client.connection-string.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export * makes parseDuration public api. it only needs the module export for its own test - export { parseConnectionString } from './client.connection-string.js';

Comment thread foreign/node/README.md

### Connection strings

Every client constructor also accepts a connection string instead of a config

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleClient takes a RawClient, so not every constructor. worth also listing the reconnect and heartbeat defaults a connection string implies, and that ipv6 literals need brackets.

import { Client } from '../client/client.js';
import { getIggyAddress } from '../tcp.sm.utils.js';

const dummyOpt = 'nodelay=true' +

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reconnection_retries is set twice - last wins, so =1 is dead and this actually runs unlimited. only ping() is asserted so no option is checked, and destroy() on line 39 isn't awaited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author PR is waiting on author response

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants