norn-cli 1.8.0 → 1.10.0
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/cli.js +390 -176
- package/package.json +7 -1
package/dist/cli.js
CHANGED
|
@@ -109375,6 +109375,9 @@ init_quotedString();
|
|
|
109375
109375
|
var fs8 = __toESM(require("fs"));
|
|
109376
109376
|
var path6 = __toESM(require("path"));
|
|
109377
109377
|
|
|
109378
|
+
// src/sqlBuiltInAdapters.ts
|
|
109379
|
+
var import_child_process2 = require("child_process");
|
|
109380
|
+
|
|
109378
109381
|
// node_modules/pg/esm/index.mjs
|
|
109379
109382
|
var import_lib = __toESM(require_lib2(), 1);
|
|
109380
109383
|
var Client = import_lib.default.Client;
|
|
@@ -109395,16 +109398,23 @@ var BUILT_IN_SQL_ADAPTERS = [
|
|
|
109395
109398
|
{
|
|
109396
109399
|
id: "postgres",
|
|
109397
109400
|
label: "PostgreSQL",
|
|
109398
|
-
description: "Built-in PostgreSQL adapter using
|
|
109399
|
-
connectionSetupSummary: "connectionString
|
|
109400
|
-
optionalConnectionKeys: [
|
|
109401
|
+
description: "Built-in PostgreSQL adapter using a connection string.",
|
|
109402
|
+
connectionSetupSummary: "connectionString",
|
|
109403
|
+
optionalConnectionKeys: []
|
|
109401
109404
|
},
|
|
109402
109405
|
{
|
|
109403
109406
|
id: "sqlserver",
|
|
109404
109407
|
label: "SQL Server",
|
|
109405
|
-
description: "Built-in SQL Server adapter using
|
|
109406
|
-
connectionSetupSummary: "connectionString
|
|
109407
|
-
optionalConnectionKeys: [
|
|
109408
|
+
description: "Built-in SQL Server adapter using a connection string.",
|
|
109409
|
+
connectionSetupSummary: "connectionString",
|
|
109410
|
+
optionalConnectionKeys: []
|
|
109411
|
+
},
|
|
109412
|
+
{
|
|
109413
|
+
id: "sqlserver-integrated",
|
|
109414
|
+
label: "SQL Server (Integrated Auth)",
|
|
109415
|
+
description: "Built-in SQL Server adapter using PowerShell for Windows/Trusted authentication.",
|
|
109416
|
+
connectionSetupSummary: "connectionString",
|
|
109417
|
+
optionalConnectionKeys: []
|
|
109408
109418
|
}
|
|
109409
109419
|
];
|
|
109410
109420
|
var BUILT_IN_SQL_ADAPTER_MAP = new Map(
|
|
@@ -109426,31 +109436,222 @@ function getConnectionValue(values, ...keys) {
|
|
|
109426
109436
|
function getConnectionString(values) {
|
|
109427
109437
|
return getConnectionValue(values, "connectionString");
|
|
109428
109438
|
}
|
|
109429
|
-
function
|
|
109430
|
-
|
|
109431
|
-
|
|
109439
|
+
function buildMissingConnectionError(adapterLabel, profile) {
|
|
109440
|
+
return `Missing required ${adapterLabel} connection string. Expected 'connectionString ${profile} = ...' in .nornenv.`;
|
|
109441
|
+
}
|
|
109442
|
+
var pwshAvailable2 = null;
|
|
109443
|
+
function isPwshAvailable2() {
|
|
109444
|
+
if (pwshAvailable2 !== null) {
|
|
109445
|
+
return pwshAvailable2;
|
|
109432
109446
|
}
|
|
109433
|
-
|
|
109434
|
-
|
|
109435
|
-
|
|
109447
|
+
try {
|
|
109448
|
+
const result = (0, import_child_process2.spawnSync)("pwsh", ["--version"], {
|
|
109449
|
+
timeout: 5e3,
|
|
109450
|
+
stdio: "pipe"
|
|
109451
|
+
});
|
|
109452
|
+
pwshAvailable2 = result.status === 0;
|
|
109453
|
+
} catch (error) {
|
|
109454
|
+
pwshAvailable2 = false;
|
|
109436
109455
|
}
|
|
109437
|
-
|
|
109456
|
+
return pwshAvailable2;
|
|
109457
|
+
}
|
|
109458
|
+
function getPowerShellCommand() {
|
|
109459
|
+
return isPwshAvailable2() ? "pwsh" : "powershell";
|
|
109460
|
+
}
|
|
109461
|
+
function encodePowerShellCommand(script) {
|
|
109462
|
+
return Buffer.from(script, "utf16le").toString("base64");
|
|
109463
|
+
}
|
|
109464
|
+
async function runSqlServerIntegratedViaPowerShell(request) {
|
|
109465
|
+
const script = `
|
|
109466
|
+
$ErrorActionPreference = 'Stop'
|
|
109467
|
+
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
109468
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
109469
|
+
|
|
109470
|
+
$raw = [Console]::In.ReadToEnd()
|
|
109471
|
+
if ([string]::IsNullOrWhiteSpace($raw)) {
|
|
109472
|
+
throw 'Missing SQL adapter payload.'
|
|
109473
|
+
}
|
|
109474
|
+
|
|
109475
|
+
$payload = $raw | ConvertFrom-Json -Depth 100
|
|
109476
|
+
$connectionString = [string]$payload.connection.values.connectionString
|
|
109477
|
+
if ([string]::IsNullOrWhiteSpace($connectionString)) {
|
|
109478
|
+
throw 'Missing SQL Server Integrated Auth connection string.'
|
|
109479
|
+
}
|
|
109480
|
+
|
|
109481
|
+
$sqlText = [string]$payload.operation.sql
|
|
109482
|
+
$mode = [string]$payload.mode
|
|
109483
|
+
$paramsObject = $payload.params
|
|
109484
|
+
|
|
109485
|
+
$connection = $null
|
|
109486
|
+
try {
|
|
109487
|
+
$connection = New-Object System.Data.SqlClient.SqlConnection $connectionString
|
|
109488
|
+
$command = $connection.CreateCommand()
|
|
109489
|
+
$command.CommandText = $sqlText
|
|
109490
|
+
|
|
109491
|
+
if ($null -ne $paramsObject) {
|
|
109492
|
+
foreach ($property in $paramsObject.PSObject.Properties) {
|
|
109493
|
+
$value = $property.Value
|
|
109494
|
+
if ($null -eq $value) {
|
|
109495
|
+
$value = [DBNull]::Value
|
|
109496
|
+
} elseif ($value -is [datetime]) {
|
|
109497
|
+
$value = $value.ToUniversalTime()
|
|
109498
|
+
} elseif ($value -is [System.Collections.IEnumerable] -and -not ($value -is [string]) -and -not ($value -is [byte[]])) {
|
|
109499
|
+
$value = ($value | ConvertTo-Json -Depth 100 -Compress)
|
|
109500
|
+
} elseif ($value -is [pscustomobject]) {
|
|
109501
|
+
$value = ($value | ConvertTo-Json -Depth 100 -Compress)
|
|
109502
|
+
}
|
|
109503
|
+
[void]$command.Parameters.AddWithValue("@$($property.Name)", $value)
|
|
109504
|
+
}
|
|
109505
|
+
}
|
|
109506
|
+
|
|
109507
|
+
$connection.Open()
|
|
109508
|
+
|
|
109509
|
+
if ($mode -eq 'query') {
|
|
109510
|
+
$reader = $command.ExecuteReader()
|
|
109511
|
+
$rows = @()
|
|
109512
|
+
|
|
109513
|
+
try {
|
|
109514
|
+
while ($reader.Read()) {
|
|
109515
|
+
$row = [ordered]@{}
|
|
109516
|
+
for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
|
109517
|
+
$value = $reader.GetValue($i)
|
|
109518
|
+
if ($value -is [DBNull]) {
|
|
109519
|
+
$value = $null
|
|
109520
|
+
} elseif ($value -is [datetime]) {
|
|
109521
|
+
$value = $value.ToString('o')
|
|
109522
|
+
} elseif ($value -is [byte[]]) {
|
|
109523
|
+
$value = [Convert]::ToBase64String($value)
|
|
109524
|
+
}
|
|
109525
|
+
$row[$reader.GetName($i)] = $value
|
|
109526
|
+
}
|
|
109527
|
+
$rows += [pscustomobject]$row
|
|
109528
|
+
}
|
|
109529
|
+
} finally {
|
|
109530
|
+
$reader.Close()
|
|
109531
|
+
}
|
|
109532
|
+
|
|
109533
|
+
$response = [pscustomobject]@{
|
|
109534
|
+
success = $true
|
|
109535
|
+
result = [pscustomobject]@{
|
|
109536
|
+
kind = 'rows'
|
|
109537
|
+
rowCount = @($rows).Count
|
|
109538
|
+
rows = @($rows)
|
|
109539
|
+
}
|
|
109540
|
+
}
|
|
109541
|
+
} else {
|
|
109542
|
+
$affectedRows = $command.ExecuteNonQuery()
|
|
109543
|
+
if ($affectedRows -lt 0) {
|
|
109544
|
+
$affectedRows = 0
|
|
109545
|
+
}
|
|
109546
|
+
$response = [pscustomobject]@{
|
|
109547
|
+
success = $true
|
|
109548
|
+
result = [pscustomobject]@{
|
|
109549
|
+
kind = 'exec'
|
|
109550
|
+
affectedRows = $affectedRows
|
|
109551
|
+
}
|
|
109552
|
+
}
|
|
109553
|
+
}
|
|
109554
|
+
|
|
109555
|
+
$response | ConvertTo-Json -Depth 100 -Compress
|
|
109556
|
+
}
|
|
109557
|
+
catch {
|
|
109558
|
+
$details = @()
|
|
109559
|
+
if ($_.Exception -and $_.Exception.GetType()) {
|
|
109560
|
+
$details += "type: $($_.Exception.GetType().FullName)"
|
|
109561
|
+
}
|
|
109562
|
+
if ($_.InvocationInfo -and $_.InvocationInfo.ScriptLineNumber) {
|
|
109563
|
+
$details += "line: $($_.InvocationInfo.ScriptLineNumber)"
|
|
109564
|
+
}
|
|
109565
|
+
if ($_.FullyQualifiedErrorId) {
|
|
109566
|
+
$details += "id: $($_.FullyQualifiedErrorId)"
|
|
109567
|
+
}
|
|
109568
|
+
$suffix = if ($details.Count -gt 0) { " ($($details -join ', '))" } else { '' }
|
|
109569
|
+
[Console]::Error.WriteLine("$($_.Exception.Message)$suffix")
|
|
109570
|
+
exit 1
|
|
109571
|
+
}
|
|
109572
|
+
finally {
|
|
109573
|
+
if ($null -ne $connection) {
|
|
109574
|
+
$connection.Dispose()
|
|
109575
|
+
}
|
|
109576
|
+
}
|
|
109577
|
+
`.trim();
|
|
109578
|
+
const payload = JSON.stringify(request);
|
|
109579
|
+
const command = getPowerShellCommand();
|
|
109580
|
+
const commandArgs = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-OutputFormat", "Text", "-EncodedCommand", encodePowerShellCommand(script)];
|
|
109581
|
+
return new Promise((resolve11, reject) => {
|
|
109582
|
+
const child = (0, import_child_process2.spawn)(command, commandArgs, {
|
|
109583
|
+
cwd: request.context?.workingDir || process.cwd(),
|
|
109584
|
+
shell: false,
|
|
109585
|
+
env: {
|
|
109586
|
+
...process.env
|
|
109587
|
+
}
|
|
109588
|
+
});
|
|
109589
|
+
let stdout = "";
|
|
109590
|
+
let stderr = "";
|
|
109591
|
+
child.stdout.on("data", (data) => {
|
|
109592
|
+
stdout += data.toString();
|
|
109593
|
+
});
|
|
109594
|
+
child.stderr.on("data", (data) => {
|
|
109595
|
+
stderr += data.toString();
|
|
109596
|
+
});
|
|
109597
|
+
child.on("error", (error) => {
|
|
109598
|
+
reject(new Error(`Failed to start PowerShell for SQL Server Integrated Auth: ${error.message}`));
|
|
109599
|
+
});
|
|
109600
|
+
child.on("close", (code) => {
|
|
109601
|
+
if (code !== 0) {
|
|
109602
|
+
reject(new Error((stderr || stdout || `PowerShell exited with code ${code}`).trim()));
|
|
109603
|
+
return;
|
|
109604
|
+
}
|
|
109605
|
+
try {
|
|
109606
|
+
const parsed = JSON.parse(stdout.trim());
|
|
109607
|
+
resolve11(parsed);
|
|
109608
|
+
} catch (error) {
|
|
109609
|
+
reject(new Error(`Invalid PowerShell SQL adapter response: ${error instanceof Error ? error.message : String(error)}`));
|
|
109610
|
+
}
|
|
109611
|
+
});
|
|
109612
|
+
child.stdin.write(payload);
|
|
109613
|
+
child.stdin.end();
|
|
109614
|
+
});
|
|
109615
|
+
}
|
|
109616
|
+
function shouldPassThroughError(error) {
|
|
109617
|
+
if (!(error instanceof Error)) {
|
|
109438
109618
|
return false;
|
|
109439
109619
|
}
|
|
109440
|
-
return
|
|
109620
|
+
return /^(Missing required|Missing SQL parameter|Invalid )/.test(error.message);
|
|
109441
109621
|
}
|
|
109442
|
-
function
|
|
109443
|
-
if (
|
|
109444
|
-
return
|
|
109445
|
-
}
|
|
109446
|
-
const parsed = Number(value);
|
|
109447
|
-
if (!Number.isFinite(parsed)) {
|
|
109448
|
-
fail(`Invalid ${label} '${value}'`);
|
|
109622
|
+
function formatBuiltInDriverError(adapterLabel, error) {
|
|
109623
|
+
if (shouldPassThroughError(error)) {
|
|
109624
|
+
return error;
|
|
109449
109625
|
}
|
|
109450
|
-
|
|
109451
|
-
|
|
109452
|
-
|
|
109453
|
-
|
|
109626
|
+
const candidate = error;
|
|
109627
|
+
const baseMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown database error";
|
|
109628
|
+
const details = [];
|
|
109629
|
+
const append2 = (label, value) => {
|
|
109630
|
+
if (value === void 0 || value === null || `${value}`.trim() === "") {
|
|
109631
|
+
return;
|
|
109632
|
+
}
|
|
109633
|
+
details.push(`${label}: ${value}`);
|
|
109634
|
+
};
|
|
109635
|
+
append2("code", candidate?.code);
|
|
109636
|
+
append2("number", candidate?.number);
|
|
109637
|
+
append2("state", candidate?.state);
|
|
109638
|
+
append2("severity", candidate?.severity ?? candidate?.class);
|
|
109639
|
+
append2("detail", candidate?.detail);
|
|
109640
|
+
append2("hint", candidate?.hint);
|
|
109641
|
+
append2("schema", candidate?.schema);
|
|
109642
|
+
append2("table", candidate?.table);
|
|
109643
|
+
append2("column", candidate?.column);
|
|
109644
|
+
append2("constraint", candidate?.constraint);
|
|
109645
|
+
append2("routine", candidate?.routine);
|
|
109646
|
+
append2("line", candidate?.lineNumber);
|
|
109647
|
+
append2("procedure", candidate?.procName);
|
|
109648
|
+
append2("server", candidate?.serverName);
|
|
109649
|
+
const precedingErrors = Array.isArray(candidate?.precedingErrors) ? (candidate?.precedingErrors).map((item) => item?.message).filter((message) => message !== void 0 && message !== null && `${message}`.trim() !== "") : [];
|
|
109650
|
+
if (precedingErrors.length > 0) {
|
|
109651
|
+
details.push(`precedingErrors: ${precedingErrors.join(" | ")}`);
|
|
109652
|
+
}
|
|
109653
|
+
const suffix = details.length > 0 ? ` (${details.join(", ")})` : "";
|
|
109654
|
+
return new Error(`${adapterLabel} driver error: ${baseMessage}${suffix}`);
|
|
109454
109655
|
}
|
|
109455
109656
|
function compilePostgresSql(sql, params) {
|
|
109456
109657
|
const values = [];
|
|
@@ -109485,31 +109686,12 @@ function compileSqlServerSql(sql, params) {
|
|
|
109485
109686
|
async function runBuiltInPostgresAdapter(request) {
|
|
109486
109687
|
const values = request.connection.values;
|
|
109487
109688
|
const connectionString = getConnectionString(values);
|
|
109488
|
-
|
|
109489
|
-
|
|
109490
|
-
|
|
109491
|
-
const
|
|
109492
|
-
const client = connectionString ? new Client(connectionString) : (() => {
|
|
109493
|
-
if (!host || !database || !user || !password) {
|
|
109494
|
-
fail(buildMissingConnectionError("Postgres"));
|
|
109495
|
-
}
|
|
109496
|
-
const port = parseOptionalNumber(values.port, "Postgres port") ?? 5432;
|
|
109497
|
-
return new Client({
|
|
109498
|
-
host,
|
|
109499
|
-
port,
|
|
109500
|
-
database,
|
|
109501
|
-
user,
|
|
109502
|
-
password,
|
|
109503
|
-
ssl: parseBoolean(values.ssl, false) ? { rejectUnauthorized: parseBoolean(values.rejectUnauthorized, false) } : false,
|
|
109504
|
-
application_name: values.applicationName || "norn-sql"
|
|
109505
|
-
});
|
|
109506
|
-
})();
|
|
109689
|
+
if (!connectionString) {
|
|
109690
|
+
fail(buildMissingConnectionError("Postgres", request.connection.profile));
|
|
109691
|
+
}
|
|
109692
|
+
const client = new Client(connectionString);
|
|
109507
109693
|
try {
|
|
109508
109694
|
await client.connect();
|
|
109509
|
-
if (values.schema) {
|
|
109510
|
-
const safeSchema = values.schema.replace(/"/g, '""');
|
|
109511
|
-
await client.query(`set search_path to "${safeSchema}"`);
|
|
109512
|
-
}
|
|
109513
109695
|
const compiled = compilePostgresSql(request.operation.sql, request.params || {});
|
|
109514
109696
|
const result = await client.query(compiled.text, compiled.values);
|
|
109515
109697
|
if (request.mode === "query") {
|
|
@@ -109529,6 +109711,8 @@ async function runBuiltInPostgresAdapter(request) {
|
|
|
109529
109711
|
affectedRows: result.rowCount ?? 0
|
|
109530
109712
|
}
|
|
109531
109713
|
};
|
|
109714
|
+
} catch (error) {
|
|
109715
|
+
throw formatBuiltInDriverError("Postgres", error);
|
|
109532
109716
|
} finally {
|
|
109533
109717
|
await client.end().catch(() => void 0);
|
|
109534
109718
|
}
|
|
@@ -109536,53 +109720,10 @@ async function runBuiltInPostgresAdapter(request) {
|
|
|
109536
109720
|
async function runBuiltInSqlServerAdapter(request) {
|
|
109537
109721
|
const values = request.connection.values;
|
|
109538
109722
|
const connectionString = getConnectionString(values);
|
|
109539
|
-
|
|
109540
|
-
|
|
109541
|
-
|
|
109542
|
-
|
|
109543
|
-
const password = getConnectionValue(values, "password");
|
|
109544
|
-
if (!server || !database || !user || !password) {
|
|
109545
|
-
fail(buildMissingConnectionError("SQL Server"));
|
|
109546
|
-
}
|
|
109547
|
-
const port = parseOptionalNumber(values.port, "SQL Server port");
|
|
109548
|
-
const connectionTimeout = parseOptionalNumber(values.connectionTimeout, "SQL Server connectionTimeout");
|
|
109549
|
-
const requestTimeout = parseOptionalNumber(values.requestTimeout, "SQL Server requestTimeout");
|
|
109550
|
-
const config = {
|
|
109551
|
-
server,
|
|
109552
|
-
database,
|
|
109553
|
-
user,
|
|
109554
|
-
password,
|
|
109555
|
-
options: {
|
|
109556
|
-
encrypt: parseBoolean(values.encrypt, false),
|
|
109557
|
-
trustServerCertificate: parseBoolean(values.trustServerCertificate, false)
|
|
109558
|
-
}
|
|
109559
|
-
};
|
|
109560
|
-
if (port !== void 0) {
|
|
109561
|
-
config.port = port;
|
|
109562
|
-
}
|
|
109563
|
-
if (values.instanceName) {
|
|
109564
|
-
config.options = {
|
|
109565
|
-
...config.options,
|
|
109566
|
-
instanceName: values.instanceName
|
|
109567
|
-
};
|
|
109568
|
-
}
|
|
109569
|
-
if (values.appName) {
|
|
109570
|
-
config.options = {
|
|
109571
|
-
...config.options,
|
|
109572
|
-
appName: values.appName
|
|
109573
|
-
};
|
|
109574
|
-
}
|
|
109575
|
-
if (values.domain) {
|
|
109576
|
-
config.domain = values.domain;
|
|
109577
|
-
}
|
|
109578
|
-
if (connectionTimeout !== void 0) {
|
|
109579
|
-
config.connectionTimeout = connectionTimeout;
|
|
109580
|
-
}
|
|
109581
|
-
if (requestTimeout !== void 0) {
|
|
109582
|
-
config.requestTimeout = requestTimeout;
|
|
109583
|
-
}
|
|
109584
|
-
return new mssql.ConnectionPool(config);
|
|
109585
|
-
})();
|
|
109723
|
+
if (!connectionString) {
|
|
109724
|
+
fail(buildMissingConnectionError("SQL Server", request.connection.profile));
|
|
109725
|
+
}
|
|
109726
|
+
const pool = new mssql.ConnectionPool(connectionString);
|
|
109586
109727
|
try {
|
|
109587
109728
|
await pool.connect();
|
|
109588
109729
|
const compiled = compileSqlServerSql(request.operation.sql, request.params || {});
|
|
@@ -109610,10 +109751,33 @@ async function runBuiltInSqlServerAdapter(request) {
|
|
|
109610
109751
|
affectedRows
|
|
109611
109752
|
}
|
|
109612
109753
|
};
|
|
109754
|
+
} catch (error) {
|
|
109755
|
+
throw formatBuiltInDriverError("SQL Server", error);
|
|
109613
109756
|
} finally {
|
|
109614
109757
|
await pool.close().catch(() => void 0);
|
|
109615
109758
|
}
|
|
109616
109759
|
}
|
|
109760
|
+
async function runBuiltInSqlServerIntegratedAdapter(request) {
|
|
109761
|
+
const values = request.connection.values;
|
|
109762
|
+
const connectionString = getConnectionString(values);
|
|
109763
|
+
if (!connectionString) {
|
|
109764
|
+
fail(buildMissingConnectionError("SQL Server Integrated Auth", request.connection.profile));
|
|
109765
|
+
}
|
|
109766
|
+
const compiled = compileSqlServerSql(request.operation.sql, request.params || {});
|
|
109767
|
+
const compiledRequest = {
|
|
109768
|
+
...request,
|
|
109769
|
+
operation: {
|
|
109770
|
+
...request.operation,
|
|
109771
|
+
sql: compiled.text
|
|
109772
|
+
},
|
|
109773
|
+
params: Object.fromEntries(compiled.names.map((name) => [name, request.params?.[name] ?? null]))
|
|
109774
|
+
};
|
|
109775
|
+
try {
|
|
109776
|
+
return await runSqlServerIntegratedViaPowerShell(compiledRequest);
|
|
109777
|
+
} catch (error) {
|
|
109778
|
+
throw formatBuiltInDriverError("SQL Server Integrated Auth", error);
|
|
109779
|
+
}
|
|
109780
|
+
}
|
|
109617
109781
|
function isBuiltInSqlAdapter(adapterId) {
|
|
109618
109782
|
return BUILT_IN_SQL_ADAPTER_MAP.has(adapterId.toLowerCase());
|
|
109619
109783
|
}
|
|
@@ -109623,6 +109787,8 @@ async function runBuiltInSqlAdapter(adapterId, request) {
|
|
|
109623
109787
|
return runBuiltInPostgresAdapter(request);
|
|
109624
109788
|
case "sqlserver":
|
|
109625
109789
|
return runBuiltInSqlServerAdapter(request);
|
|
109790
|
+
case "sqlserver-integrated":
|
|
109791
|
+
return runBuiltInSqlServerIntegratedAdapter(request);
|
|
109626
109792
|
default:
|
|
109627
109793
|
fail(`Built-in SQL adapter '${adapterId}' is not supported`);
|
|
109628
109794
|
}
|
|
@@ -109800,7 +109966,7 @@ function resolveSqlAdapterTarget(startPath, adapterId) {
|
|
|
109800
109966
|
}
|
|
109801
109967
|
|
|
109802
109968
|
// src/sqlAdapterRunner.ts
|
|
109803
|
-
var
|
|
109969
|
+
var import_child_process3 = require("child_process");
|
|
109804
109970
|
function isRowsResult(value) {
|
|
109805
109971
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
109806
109972
|
return false;
|
|
@@ -109840,7 +110006,7 @@ async function runExternalSqlAdapter(command, request, workingDir) {
|
|
|
109840
110006
|
throw new Error("SQL adapter command is empty");
|
|
109841
110007
|
}
|
|
109842
110008
|
return new Promise((resolve11, reject) => {
|
|
109843
|
-
const child = (0,
|
|
110009
|
+
const child = (0, import_child_process3.spawn)(command[0], command.slice(1), {
|
|
109844
110010
|
cwd: workingDir,
|
|
109845
110011
|
env: process.env,
|
|
109846
110012
|
shell: false
|
|
@@ -110161,23 +110327,35 @@ function splitNamedArgumentList(argsStr) {
|
|
|
110161
110327
|
}
|
|
110162
110328
|
return parts;
|
|
110163
110329
|
}
|
|
110164
|
-
function
|
|
110330
|
+
function parseSqlArguments(argsStr) {
|
|
110165
110331
|
if (!argsStr.trim()) {
|
|
110166
110332
|
return { args: [] };
|
|
110167
110333
|
}
|
|
110168
110334
|
const parts = splitNamedArgumentList(argsStr);
|
|
110169
110335
|
const args = [];
|
|
110336
|
+
let sawNamedArgument = false;
|
|
110170
110337
|
for (const part of parts) {
|
|
110171
|
-
const
|
|
110172
|
-
if (!
|
|
110338
|
+
const trimmed = part.trim();
|
|
110339
|
+
if (!trimmed) {
|
|
110340
|
+
continue;
|
|
110341
|
+
}
|
|
110342
|
+
const match = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
|
|
110343
|
+
if (match) {
|
|
110344
|
+
sawNamedArgument = true;
|
|
110345
|
+
args.push({
|
|
110346
|
+
name: match[1],
|
|
110347
|
+
valueExpression: match[2].trim()
|
|
110348
|
+
});
|
|
110349
|
+
continue;
|
|
110350
|
+
}
|
|
110351
|
+
if (sawNamedArgument) {
|
|
110173
110352
|
return {
|
|
110174
110353
|
args: [],
|
|
110175
|
-
error: "SQL arguments
|
|
110354
|
+
error: "Positional SQL arguments cannot follow named arguments."
|
|
110176
110355
|
};
|
|
110177
110356
|
}
|
|
110178
110357
|
args.push({
|
|
110179
|
-
|
|
110180
|
-
valueExpression: match[2].trim()
|
|
110358
|
+
valueExpression: trimmed
|
|
110181
110359
|
});
|
|
110182
110360
|
}
|
|
110183
110361
|
return { args };
|
|
@@ -110191,7 +110369,7 @@ function parseRunSqlCommand(line2) {
|
|
|
110191
110369
|
if (!match) {
|
|
110192
110370
|
return null;
|
|
110193
110371
|
}
|
|
110194
|
-
const parsedArgs =
|
|
110372
|
+
const parsedArgs = parseSqlArguments(match[3] || "");
|
|
110195
110373
|
if (parsedArgs.error) {
|
|
110196
110374
|
return {
|
|
110197
110375
|
variableName: match[1],
|
|
@@ -110206,6 +110384,52 @@ function parseRunSqlCommand(line2) {
|
|
|
110206
110384
|
args: parsedArgs.args
|
|
110207
110385
|
};
|
|
110208
110386
|
}
|
|
110387
|
+
function bindSqlArguments(parameterNames, args, runtimeVariables) {
|
|
110388
|
+
const resolvedParams = {};
|
|
110389
|
+
const boundParams = /* @__PURE__ */ new Set();
|
|
110390
|
+
let positionalIndex = 0;
|
|
110391
|
+
let sawNamedArgument = false;
|
|
110392
|
+
for (const arg of args) {
|
|
110393
|
+
if (arg.name) {
|
|
110394
|
+
sawNamedArgument = true;
|
|
110395
|
+
const declaredParam2 = parameterNames.find((param) => param.toLowerCase() === arg.name.toLowerCase());
|
|
110396
|
+
if (!declaredParam2) {
|
|
110397
|
+
return { error: `Unknown SQL parameter '${arg.name}'.` };
|
|
110398
|
+
}
|
|
110399
|
+
const declaredLower = declaredParam2.toLowerCase();
|
|
110400
|
+
if (boundParams.has(declaredLower)) {
|
|
110401
|
+
return { error: `Duplicate SQL parameter '${declaredParam2}' in run sql call.` };
|
|
110402
|
+
}
|
|
110403
|
+
const valueResult2 = evaluateSqlArgumentExpression(arg.valueExpression, runtimeVariables);
|
|
110404
|
+
if (valueResult2.error) {
|
|
110405
|
+
return { error: valueResult2.error };
|
|
110406
|
+
}
|
|
110407
|
+
resolvedParams[declaredParam2] = valueResult2.value;
|
|
110408
|
+
boundParams.add(declaredLower);
|
|
110409
|
+
continue;
|
|
110410
|
+
}
|
|
110411
|
+
if (sawNamedArgument) {
|
|
110412
|
+
return { error: "Positional SQL arguments cannot follow named arguments." };
|
|
110413
|
+
}
|
|
110414
|
+
if (positionalIndex >= parameterNames.length) {
|
|
110415
|
+
return { error: `Too many SQL arguments: expected ${parameterNames.length}.` };
|
|
110416
|
+
}
|
|
110417
|
+
const declaredParam = parameterNames[positionalIndex];
|
|
110418
|
+
const valueResult = evaluateSqlArgumentExpression(arg.valueExpression, runtimeVariables);
|
|
110419
|
+
if (valueResult.error) {
|
|
110420
|
+
return { error: valueResult.error };
|
|
110421
|
+
}
|
|
110422
|
+
resolvedParams[declaredParam] = valueResult.value;
|
|
110423
|
+
boundParams.add(declaredParam.toLowerCase());
|
|
110424
|
+
positionalIndex++;
|
|
110425
|
+
}
|
|
110426
|
+
for (const param of parameterNames) {
|
|
110427
|
+
if (!boundParams.has(param.toLowerCase())) {
|
|
110428
|
+
return { error: `Missing required SQL parameter '${param}'.` };
|
|
110429
|
+
}
|
|
110430
|
+
}
|
|
110431
|
+
return { params: resolvedParams };
|
|
110432
|
+
}
|
|
110209
110433
|
function isVarRunSequenceCommand(line2) {
|
|
110210
110434
|
const trimmed = line2.trim();
|
|
110211
110435
|
if (!/^var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*run\s+[a-zA-Z_][a-zA-Z0-9_-]*(?:\s*\([^)]*\))?(?:\s+retry\s+\d+)?(?:\s+backoff\s+\d+(?:\.\d+)?\s*(?:s|ms|seconds?|milliseconds?)?)?\s*$/i.test(trimmed)) {
|
|
@@ -111719,78 +111943,25 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
111719
111943
|
duration: Date.now() - startTime
|
|
111720
111944
|
};
|
|
111721
111945
|
}
|
|
111722
|
-
const
|
|
111723
|
-
|
|
111724
|
-
|
|
111725
|
-
|
|
111726
|
-
|
|
111727
|
-
|
|
111728
|
-
|
|
111729
|
-
|
|
111730
|
-
|
|
111731
|
-
|
|
111732
|
-
|
|
111733
|
-
|
|
111734
|
-
|
|
111735
|
-
errors,
|
|
111736
|
-
duration: Date.now() - startTime
|
|
111737
|
-
};
|
|
111738
|
-
}
|
|
111739
|
-
if (!declaredParams.has(lowerName)) {
|
|
111740
|
-
errors.push(`Unknown SQL parameter '${arg.name}'.`);
|
|
111741
|
-
return {
|
|
111742
|
-
name: "",
|
|
111743
|
-
success: false,
|
|
111744
|
-
responses,
|
|
111745
|
-
scriptResults,
|
|
111746
|
-
assertionResults,
|
|
111747
|
-
steps: orderedSteps,
|
|
111748
|
-
errors,
|
|
111749
|
-
duration: Date.now() - startTime
|
|
111750
|
-
};
|
|
111751
|
-
}
|
|
111752
|
-
const valueResult = evaluateSqlArgumentExpression(arg.valueExpression, runtimeVariables);
|
|
111753
|
-
if (valueResult.error) {
|
|
111754
|
-
errors.push(`Step ${stepIdx + 1}: ${valueResult.error}`);
|
|
111755
|
-
return {
|
|
111756
|
-
name: "",
|
|
111757
|
-
success: false,
|
|
111758
|
-
responses,
|
|
111759
|
-
scriptResults,
|
|
111760
|
-
assertionResults,
|
|
111761
|
-
steps: orderedSteps,
|
|
111762
|
-
errors,
|
|
111763
|
-
duration: Date.now() - startTime
|
|
111764
|
-
};
|
|
111765
|
-
}
|
|
111766
|
-
providedArgs.set(lowerName, valueResult.value);
|
|
111767
|
-
}
|
|
111768
|
-
for (const param of operation.parameters) {
|
|
111769
|
-
if (!providedArgs.has(param.toLowerCase())) {
|
|
111770
|
-
errors.push(`Missing required SQL parameter '${param}'.`);
|
|
111771
|
-
return {
|
|
111772
|
-
name: "",
|
|
111773
|
-
success: false,
|
|
111774
|
-
responses,
|
|
111775
|
-
scriptResults,
|
|
111776
|
-
assertionResults,
|
|
111777
|
-
steps: orderedSteps,
|
|
111778
|
-
errors,
|
|
111779
|
-
duration: Date.now() - startTime
|
|
111780
|
-
};
|
|
111781
|
-
}
|
|
111946
|
+
const boundArgs = bindSqlArguments(operation.parameters, parsed.args, runtimeVariables);
|
|
111947
|
+
if ("error" in boundArgs) {
|
|
111948
|
+
errors.push(`Step ${stepIdx + 1}: ${boundArgs.error}`);
|
|
111949
|
+
return {
|
|
111950
|
+
name: "",
|
|
111951
|
+
success: false,
|
|
111952
|
+
responses,
|
|
111953
|
+
scriptResults,
|
|
111954
|
+
assertionResults,
|
|
111955
|
+
steps: orderedSteps,
|
|
111956
|
+
errors,
|
|
111957
|
+
duration: Date.now() - startTime
|
|
111958
|
+
};
|
|
111782
111959
|
}
|
|
111783
111960
|
const adapterWorkingDir = workingDir || process.cwd();
|
|
111784
111961
|
try {
|
|
111785
111962
|
const connection = resolveSqlConnection(executionContext?.filePath || adapterWorkingDir, operation.connectionName);
|
|
111786
111963
|
const adapterTarget = resolveSqlAdapterTarget(executionContext?.filePath || adapterWorkingDir, connection.adapter);
|
|
111787
111964
|
const connectionValues = getSqlConnectionValues(connection.profile, runtimeVariables);
|
|
111788
|
-
const paramPayload = Object.fromEntries(
|
|
111789
|
-
Array.from(providedArgs.entries()).map(([lowerName, value]) => {
|
|
111790
|
-
const declaredName = operation.parameters.find((param) => param.toLowerCase() === lowerName) || lowerName;
|
|
111791
|
-
return [declaredName, value];
|
|
111792
|
-
})
|
|
111793
|
-
);
|
|
111794
111965
|
const adapterResponse = await runSqlAdapter(
|
|
111795
111966
|
adapterTarget,
|
|
111796
111967
|
{
|
|
@@ -111804,7 +111975,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
111804
111975
|
connectionName: operation.connectionName,
|
|
111805
111976
|
sourcePath: operation.sourcePath
|
|
111806
111977
|
},
|
|
111807
|
-
params:
|
|
111978
|
+
params: boundArgs.params,
|
|
111808
111979
|
connection: {
|
|
111809
111980
|
name: connection.alias,
|
|
111810
111981
|
profile: connection.profile,
|
|
@@ -114326,7 +114497,9 @@ var ENV_FILENAME = ".nornenv";
|
|
|
114326
114497
|
var importRegex = /^import\s+["']?(.+?)["']?\s*$/;
|
|
114327
114498
|
var envRegex = /^\[env:([a-zA-Z_][a-zA-Z0-9_-]*)\]$/;
|
|
114328
114499
|
var varRegex = /^var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
114500
|
+
var connectionStringRegex = /^connectionString\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
114329
114501
|
var secretRegex = /^secret\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
114502
|
+
var secretConnectionStringRegex = /^secret\s+connectionString\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
114330
114503
|
function parseEnvFile(content, sourceFilePath) {
|
|
114331
114504
|
const lines = content.split("\n");
|
|
114332
114505
|
const config = {
|
|
@@ -114367,6 +114540,27 @@ function parseEnvFile(content, sourceFilePath) {
|
|
|
114367
114540
|
config.environments.push(currentEnv);
|
|
114368
114541
|
continue;
|
|
114369
114542
|
}
|
|
114543
|
+
const secretConnectionStringMatch = trimmed.match(secretConnectionStringRegex);
|
|
114544
|
+
if (secretConnectionStringMatch) {
|
|
114545
|
+
const profileName = secretConnectionStringMatch[1];
|
|
114546
|
+
const varName = `${profileName}_connectionString`;
|
|
114547
|
+
const varValue = secretConnectionStringMatch[2].trim();
|
|
114548
|
+
config.secretDeclarations.push({
|
|
114549
|
+
name: varName,
|
|
114550
|
+
value: varValue,
|
|
114551
|
+
envName: currentEnv?.name,
|
|
114552
|
+
lineNumber: i,
|
|
114553
|
+
filePath: sourceFilePath
|
|
114554
|
+
});
|
|
114555
|
+
config.secretNames.add(varName);
|
|
114556
|
+
config.secretValues.set(varName, varValue);
|
|
114557
|
+
if (currentEnv) {
|
|
114558
|
+
currentEnv.variables[varName] = varValue;
|
|
114559
|
+
} else {
|
|
114560
|
+
config.common[varName] = varValue;
|
|
114561
|
+
}
|
|
114562
|
+
continue;
|
|
114563
|
+
}
|
|
114370
114564
|
const secretMatch = trimmed.match(secretRegex);
|
|
114371
114565
|
if (secretMatch) {
|
|
114372
114566
|
const varName = secretMatch[1];
|
|
@@ -114387,6 +114581,18 @@ function parseEnvFile(content, sourceFilePath) {
|
|
|
114387
114581
|
}
|
|
114388
114582
|
continue;
|
|
114389
114583
|
}
|
|
114584
|
+
const connectionStringMatch = trimmed.match(connectionStringRegex);
|
|
114585
|
+
if (connectionStringMatch) {
|
|
114586
|
+
const profileName = connectionStringMatch[1];
|
|
114587
|
+
const varName = `${profileName}_connectionString`;
|
|
114588
|
+
const varValue = connectionStringMatch[2].trim();
|
|
114589
|
+
if (currentEnv) {
|
|
114590
|
+
currentEnv.variables[varName] = varValue;
|
|
114591
|
+
} else {
|
|
114592
|
+
config.common[varName] = varValue;
|
|
114593
|
+
}
|
|
114594
|
+
continue;
|
|
114595
|
+
}
|
|
114390
114596
|
const varMatch = trimmed.match(varRegex);
|
|
114391
114597
|
if (varMatch) {
|
|
114392
114598
|
const varName = varMatch[1];
|
|
@@ -114661,6 +114867,7 @@ var import_process = require("process");
|
|
|
114661
114867
|
var fs14 = __toESM(require("fs"));
|
|
114662
114868
|
var path10 = __toESM(require("path"));
|
|
114663
114869
|
var envRegex2 = /^\s*\[env:([a-zA-Z_][a-zA-Z0-9_-]*)\]\s*$/;
|
|
114870
|
+
var secretConnectionStringRegex2 = /^(\s*secret\s+connectionString\s+)([a-zA-Z_][a-zA-Z0-9_]*)(\s*=\s*)(.+)$/;
|
|
114664
114871
|
var secretRegex2 = /^(\s*secret\s+)([a-zA-Z_][a-zA-Z0-9_]*)(\s*=\s*)(.+)$/;
|
|
114665
114872
|
function splitContentLines(content) {
|
|
114666
114873
|
return content.split(/\r?\n/);
|
|
@@ -114686,11 +114893,13 @@ function extractSecretLines(content, filePath) {
|
|
|
114686
114893
|
currentEnv = envMatch[1];
|
|
114687
114894
|
continue;
|
|
114688
114895
|
}
|
|
114896
|
+
const secretConnectionStringMatch = line2.match(secretConnectionStringRegex2);
|
|
114689
114897
|
const secretMatch = line2.match(secretRegex2);
|
|
114690
|
-
if (!secretMatch) {
|
|
114898
|
+
if (!secretConnectionStringMatch && !secretMatch) {
|
|
114691
114899
|
continue;
|
|
114692
114900
|
}
|
|
114693
|
-
const
|
|
114901
|
+
const secretName = secretConnectionStringMatch ? `${secretConnectionStringMatch[2]}_connectionString` : secretMatch[2];
|
|
114902
|
+
const value = (secretConnectionStringMatch?.[4] ?? secretMatch[4]).trim();
|
|
114694
114903
|
const encrypted = isEncryptedSecretValue(value);
|
|
114695
114904
|
let kid;
|
|
114696
114905
|
if (encrypted) {
|
|
@@ -114703,7 +114912,7 @@ function extractSecretLines(content, filePath) {
|
|
|
114703
114912
|
filePath,
|
|
114704
114913
|
lineNumber: i,
|
|
114705
114914
|
envName: currentEnv,
|
|
114706
|
-
name:
|
|
114915
|
+
name: secretName,
|
|
114707
114916
|
value,
|
|
114708
114917
|
encrypted,
|
|
114709
114918
|
kid
|
|
@@ -114717,11 +114926,16 @@ function updateSecretLineValue(content, lineNumber, newValue) {
|
|
|
114717
114926
|
throw new Error(`Line ${lineNumber + 1} is out of range.`);
|
|
114718
114927
|
}
|
|
114719
114928
|
const line2 = lines[lineNumber];
|
|
114929
|
+
const secretConnectionStringMatch = line2.match(secretConnectionStringRegex2);
|
|
114720
114930
|
const secretMatch = line2.match(secretRegex2);
|
|
114721
|
-
if (!secretMatch) {
|
|
114931
|
+
if (!secretConnectionStringMatch && !secretMatch) {
|
|
114722
114932
|
throw new Error(`Line ${lineNumber + 1} is not a secret declaration.`);
|
|
114723
114933
|
}
|
|
114724
|
-
|
|
114934
|
+
if (secretConnectionStringMatch) {
|
|
114935
|
+
lines[lineNumber] = `${secretConnectionStringMatch[1]}${secretConnectionStringMatch[2]}${secretConnectionStringMatch[3]}${newValue}`;
|
|
114936
|
+
} else {
|
|
114937
|
+
lines[lineNumber] = `${secretMatch[1]}${secretMatch[2]}${secretMatch[3]}${newValue}`;
|
|
114938
|
+
}
|
|
114725
114939
|
return lines.join(detectEol(content));
|
|
114726
114940
|
}
|
|
114727
114941
|
function findSecretLine(content, variableName, envName) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn - REST Client",
|
|
4
4
|
"description": "A powerful REST client for making HTTP requests with sequences, variables, scripts, and cookie support",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.10.0",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|
|
@@ -362,6 +362,12 @@
|
|
|
362
362
|
"settings": {
|
|
363
363
|
"foreground": "#85EA2C"
|
|
364
364
|
}
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"scope": "support.type.connection.nornenv",
|
|
368
|
+
"settings": {
|
|
369
|
+
"foreground": "#d7ba7d"
|
|
370
|
+
}
|
|
365
371
|
}
|
|
366
372
|
]
|
|
367
373
|
}
|