infracensus-collector 0.1.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.
Files changed (91) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +323 -0
  3. package/dist/a10-index.js +91 -0
  4. package/dist/a10-index.js.map +7 -0
  5. package/dist/azure-index.js +114 -0
  6. package/dist/azure-index.js.map +7 -0
  7. package/dist/chunk-2VVO5SGB.js +443 -0
  8. package/dist/chunk-2VVO5SGB.js.map +7 -0
  9. package/dist/chunk-3JF5HO4Y.js +372 -0
  10. package/dist/chunk-3JF5HO4Y.js.map +7 -0
  11. package/dist/chunk-3MIZN7SP.js +5160 -0
  12. package/dist/chunk-3MIZN7SP.js.map +7 -0
  13. package/dist/chunk-42NDGVLF.js +78 -0
  14. package/dist/chunk-42NDGVLF.js.map +7 -0
  15. package/dist/chunk-7EGPKFND.js +349 -0
  16. package/dist/chunk-7EGPKFND.js.map +7 -0
  17. package/dist/chunk-7ESBA5C5.js +31 -0
  18. package/dist/chunk-7ESBA5C5.js.map +7 -0
  19. package/dist/chunk-7HFPVDT3.js +1237 -0
  20. package/dist/chunk-7HFPVDT3.js.map +7 -0
  21. package/dist/chunk-DBJNP764.js +774 -0
  22. package/dist/chunk-DBJNP764.js.map +7 -0
  23. package/dist/chunk-IQU5HMXO.js +1837 -0
  24. package/dist/chunk-IQU5HMXO.js.map +7 -0
  25. package/dist/chunk-LNYJAPA2.js +28 -0
  26. package/dist/chunk-LNYJAPA2.js.map +7 -0
  27. package/dist/chunk-U2UV5MMR.js +18 -0
  28. package/dist/chunk-U2UV5MMR.js.map +7 -0
  29. package/dist/chunk-VEMGGUZ6.js +528 -0
  30. package/dist/chunk-VEMGGUZ6.js.map +7 -0
  31. package/dist/chunk-VXDH6AVJ.js +1047 -0
  32. package/dist/chunk-VXDH6AVJ.js.map +7 -0
  33. package/dist/chunk-VZFXWTU2.js +267 -0
  34. package/dist/chunk-VZFXWTU2.js.map +7 -0
  35. package/dist/chunk-XSHYWQAH.js +20 -0
  36. package/dist/chunk-XSHYWQAH.js.map +7 -0
  37. package/dist/chunk-ZCZOUDBC.js +225 -0
  38. package/dist/chunk-ZCZOUDBC.js.map +7 -0
  39. package/dist/chunk-ZIUWFLKI.js +11945 -0
  40. package/dist/chunk-ZIUWFLKI.js.map +7 -0
  41. package/dist/cli.js +193 -0
  42. package/dist/cli.js.map +7 -0
  43. package/dist/config-AA7OSIVF.js +23 -0
  44. package/dist/config-AA7OSIVF.js.map +7 -0
  45. package/dist/configimport-index.js +286 -0
  46. package/dist/configimport-index.js.map +7 -0
  47. package/dist/hyperv-index.js +105 -0
  48. package/dist/hyperv-index.js.map +7 -0
  49. package/dist/identity-index.js +105 -0
  50. package/dist/identity-index.js.map +7 -0
  51. package/dist/index.js +11444 -0
  52. package/dist/index.js.map +7 -0
  53. package/dist/linux-QMB5WPDK.js +247 -0
  54. package/dist/linux-QMB5WPDK.js.map +7 -0
  55. package/dist/pkgRoot-RFOX7XXU.js +7 -0
  56. package/dist/pkgRoot-RFOX7XXU.js.map +7 -0
  57. package/dist/sensitive-data-index.js +383 -0
  58. package/dist/sensitive-data-index.js.map +7 -0
  59. package/dist/service-U6Y7MN75.js +11 -0
  60. package/dist/service-U6Y7MN75.js.map +7 -0
  61. package/dist/sql-index.js +106 -0
  62. package/dist/sql-index.js.map +7 -0
  63. package/dist/status-SBN5WZZX.js +26 -0
  64. package/dist/status-SBN5WZZX.js.map +7 -0
  65. package/dist/tools-KT77LANU.js +93 -0
  66. package/dist/tools-KT77LANU.js.map +7 -0
  67. package/dist/vmware-index.js +118 -0
  68. package/dist/vmware-index.js.map +7 -0
  69. package/dist/voice-index.js +181 -0
  70. package/dist/voice-index.js.map +7 -0
  71. package/dist/windows-ZUCEMMDI.js +323 -0
  72. package/dist/windows-ZUCEMMDI.js.map +7 -0
  73. package/package.json +95 -0
  74. package/scripts/bundle.mjs +113 -0
  75. package/scripts/collector-ctl.mjs +143 -0
  76. package/scripts/collector.env.example +48 -0
  77. package/scripts/doctor.mjs +199 -0
  78. package/scripts/enrol.mjs +215 -0
  79. package/scripts/generate-ssh-key-fixtures.mjs +84 -0
  80. package/scripts/install-record.mjs +65 -0
  81. package/scripts/install.sh +203 -0
  82. package/scripts/lib/daemon-log.mjs +132 -0
  83. package/scripts/lib/deregister.mjs +81 -0
  84. package/scripts/lib/enrolment.mjs +266 -0
  85. package/scripts/lib/install-manifest.mjs +160 -0
  86. package/scripts/lib/plane-probe.mjs +100 -0
  87. package/scripts/lib/secure-file.mjs +71 -0
  88. package/scripts/lib/service-state.mjs +100 -0
  89. package/scripts/uninstall-local.mjs +133 -0
  90. package/scripts/uninstall.sh +84 -0
  91. package/scripts/uselog.mjs +83 -0
@@ -0,0 +1,443 @@
1
+ import {
2
+ SqlIngestBody
3
+ } from "./chunk-ZIUWFLKI.js";
4
+
5
+ // src/sql/scan.ts
6
+ import sql from "mssql";
7
+ var noopLog = () => {
8
+ };
9
+ var SYSTEM_DBS = /* @__PURE__ */ new Set(["master", "model", "msdb", "tempdb"]);
10
+ function bracket(dbName) {
11
+ return `[${dbName.replace(/]/g, "]]")}]`;
12
+ }
13
+ async function scanSqlServer(opts, log = noopLog) {
14
+ const config = {
15
+ server: opts.server,
16
+ port: opts.port,
17
+ user: opts.user,
18
+ password: opts.pass,
19
+ connectionTimeout: 15e3,
20
+ requestTimeout: 3e4,
21
+ options: {
22
+ encrypt: opts.encrypt,
23
+ trustServerCertificate: opts.trustServerCertificate
24
+ }
25
+ };
26
+ const pool = new sql.ConnectionPool(config);
27
+ try {
28
+ await pool.connect();
29
+ } catch (err) {
30
+ throw new Error(
31
+ `Could not connect to SQL Server "${opts.server}:${opts.port}": ${err.message} \u2014 check reachability, SQL_USER/SQL_PASS, and SQL_ENCRYPT/SQL_TRUST_CERT.`
32
+ );
33
+ }
34
+ try {
35
+ const instance = await scanInstance(pool, opts, log);
36
+ const databases = await scanDatabases(pool, log);
37
+ const jobs = await scanJobs(pool, log);
38
+ const userDbs = databases.filter((db) => !SYSTEM_DBS.has(db.name.toLowerCase()) && (db.state ?? "ONLINE") === "ONLINE").map((db) => db.name);
39
+ const { tables, foreignKeys } = await scanSchema(pool, userDbs, log);
40
+ log(
41
+ "info",
42
+ `SQL Server returned instance ${instance.hostName}\\${instance.instanceName}, ${databases.length} database(s), ${jobs.length} job(s), ${tables.length} table(s), ${foreignKeys.length} FK(s)`
43
+ );
44
+ return { instance, databases, jobs, tables, foreignKeys };
45
+ } finally {
46
+ await pool.close().catch(() => {
47
+ });
48
+ }
49
+ }
50
+ function formatType(name, maxLength, prec, scale) {
51
+ const t = name.toLowerCase();
52
+ if (["varchar", "char", "varbinary", "binary"].includes(t)) {
53
+ return maxLength === -1 ? `${name}(max)` : `${name}(${maxLength})`;
54
+ }
55
+ if (["nvarchar", "nchar"].includes(t)) {
56
+ return maxLength === -1 ? `${name}(max)` : `${name}(${Math.floor(maxLength / 2)})`;
57
+ }
58
+ if (["decimal", "numeric"].includes(t)) return `${name}(${prec},${scale})`;
59
+ if (["datetime2", "time", "datetimeoffset"].includes(t)) return `${name}(${scale})`;
60
+ return name;
61
+ }
62
+ async function scanSchema(pool, dbNames, log) {
63
+ const tables = [];
64
+ const foreignKeys = [];
65
+ for (const dbName of dbNames) {
66
+ const B = bracket(dbName);
67
+ try {
68
+ const colRes = await pool.request().query(
69
+ `SELECT s.name AS schemaName, t.name AS tableName, c.name AS columnName,
70
+ c.column_id AS columnId, ty.name AS typeName,
71
+ c.max_length AS maxLength, c.precision AS prec, c.scale AS scale,
72
+ c.is_nullable AS isNullable,
73
+ CASE WHEN ic.column_id IS NOT NULL THEN 1 ELSE 0 END AS isPk,
74
+ CASE WHEN fkc.parent_column_id IS NOT NULL THEN 1 ELSE 0 END AS isFk
75
+ FROM ${B}.sys.tables t
76
+ JOIN ${B}.sys.schemas s ON s.schema_id = t.schema_id
77
+ JOIN ${B}.sys.columns c ON c.object_id = t.object_id
78
+ JOIN ${B}.sys.types ty ON ty.user_type_id = c.user_type_id
79
+ LEFT JOIN ${B}.sys.indexes pk ON pk.object_id = t.object_id AND pk.is_primary_key = 1
80
+ LEFT JOIN ${B}.sys.index_columns ic
81
+ ON ic.object_id = t.object_id AND ic.index_id = pk.index_id AND ic.column_id = c.column_id
82
+ LEFT JOIN (SELECT DISTINCT parent_object_id, parent_column_id FROM ${B}.sys.foreign_key_columns) fkc
83
+ ON fkc.parent_object_id = t.object_id AND fkc.parent_column_id = c.column_id
84
+ WHERE t.is_ms_shipped = 0
85
+ ORDER BY s.name, t.name, c.column_id`
86
+ );
87
+ const byTable = /* @__PURE__ */ new Map();
88
+ for (const row of rows(colRes)) {
89
+ const schema = str(row, "schemaName") ?? "dbo";
90
+ const tableName = str(row, "tableName");
91
+ const columnName = str(row, "columnName");
92
+ if (!tableName || !columnName) continue;
93
+ const key = `${schema}.${tableName}`;
94
+ let tbl = byTable.get(key);
95
+ if (!tbl) {
96
+ tbl = { database: dbName, schema, name: tableName, columns: [] };
97
+ byTable.set(key, tbl);
98
+ }
99
+ const typeName = str(row, "typeName");
100
+ tbl.columns.push({
101
+ name: columnName,
102
+ ...typeName ? {
103
+ dataType: formatType(
104
+ typeName,
105
+ int(row, "maxLength") ?? 0,
106
+ int(row, "prec") ?? 0,
107
+ int(row, "scale") ?? 0
108
+ )
109
+ } : {},
110
+ nullable: bool(row, "isNullable") ?? true,
111
+ ...int(row, "isPk") ? { isPrimaryKey: true } : {},
112
+ ...int(row, "isFk") ? { isForeignKey: true } : {},
113
+ ...defined(int(row, "columnId"), (v) => ({ ordinal: v }))
114
+ });
115
+ }
116
+ for (const tbl of byTable.values()) tables.push(tbl);
117
+ const fkRes = await pool.request().query(
118
+ `SELECT fk.name AS fkName,
119
+ sp.name AS fromSchema, tp.name AS fromTable, cpar.name AS fromColumn,
120
+ sr.name AS toSchema, tr.name AS toTable, cref.name AS toColumn,
121
+ fkc.constraint_column_id AS ord
122
+ FROM ${B}.sys.foreign_keys fk
123
+ JOIN ${B}.sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
124
+ JOIN ${B}.sys.tables tp ON tp.object_id = fk.parent_object_id
125
+ JOIN ${B}.sys.schemas sp ON sp.schema_id = tp.schema_id
126
+ JOIN ${B}.sys.columns cpar ON cpar.object_id = fk.parent_object_id AND cpar.column_id = fkc.parent_column_id
127
+ JOIN ${B}.sys.tables tr ON tr.object_id = fk.referenced_object_id
128
+ JOIN ${B}.sys.schemas sr ON sr.schema_id = tr.schema_id
129
+ JOIN ${B}.sys.columns cref ON cref.object_id = fk.referenced_object_id AND cref.column_id = fkc.referenced_column_id
130
+ ORDER BY fk.name, fkc.constraint_column_id`
131
+ );
132
+ const byFk = /* @__PURE__ */ new Map();
133
+ for (const row of rows(fkRes)) {
134
+ const fkName = str(row, "fkName");
135
+ const fromTable = str(row, "fromTable");
136
+ const toTable = str(row, "toTable");
137
+ if (!fkName || !fromTable || !toTable) continue;
138
+ let fk = byFk.get(fkName);
139
+ if (!fk) {
140
+ fk = {
141
+ database: dbName,
142
+ name: fkName,
143
+ fromSchema: str(row, "fromSchema") ?? "dbo",
144
+ fromTable,
145
+ fromColumns: [],
146
+ toSchema: str(row, "toSchema") ?? "dbo",
147
+ toTable,
148
+ toColumns: []
149
+ };
150
+ byFk.set(fkName, fk);
151
+ }
152
+ const fromCol = str(row, "fromColumn");
153
+ const toCol = str(row, "toColumn");
154
+ if (fromCol) fk.fromColumns.push(fromCol);
155
+ if (toCol) fk.toColumns.push(toCol);
156
+ }
157
+ for (const fk of byFk.values()) foreignKeys.push(fk);
158
+ log("debug", `schema ${dbName}: ${byTable.size} table(s), ${byFk.size} FK(s)`);
159
+ } catch (err) {
160
+ log("info", `schema scan skipped for database ${dbName} (no access?): ${err.message}`);
161
+ }
162
+ }
163
+ return { tables, foreignKeys };
164
+ }
165
+ async function scanInstance(pool, opts, log) {
166
+ const fallbackHost = opts.server.split("\\")[0] ?? opts.server;
167
+ try {
168
+ const res = await pool.request().query(
169
+ `SELECT
170
+ CAST(SERVERPROPERTY('Edition') AS nvarchar(256)) AS Edition,
171
+ CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(256)) AS ProductVersion,
172
+ CAST(SERVERPROPERTY('ProductLevel') AS nvarchar(256)) AS ProductLevel,
173
+ CAST(SERVERPROPERTY('InstanceName') AS nvarchar(256)) AS InstanceName,
174
+ CAST(SERVERPROPERTY('MachineName') AS nvarchar(256)) AS MachineName`
175
+ );
176
+ const row = firstRow(res);
177
+ const hostName = str(row, "MachineName") ?? fallbackHost;
178
+ const instanceName = str(row, "InstanceName") ?? "MSSQLSERVER";
179
+ return {
180
+ hostName,
181
+ instanceName,
182
+ ...defined(str(row, "Edition"), (v) => ({ edition: v })),
183
+ ...defined(str(row, "ProductVersion"), (v) => ({ version: v })),
184
+ ...defined(str(row, "ProductLevel"), (v) => ({ productLevel: v })),
185
+ port: opts.port
186
+ };
187
+ } catch (err) {
188
+ log("warn", `SERVERPROPERTY query failed: ${err.message}`);
189
+ return { hostName: fallbackHost, instanceName: "MSSQLSERVER", port: opts.port };
190
+ }
191
+ }
192
+ async function scanDatabases(pool, log) {
193
+ try {
194
+ const res = await pool.request().query(
195
+ `SELECT
196
+ d.name AS name,
197
+ d.state_desc AS state,
198
+ d.recovery_model_desc AS recoveryModel,
199
+ d.compatibility_level AS compatibilityLevel,
200
+ CAST(SUM(CAST(mf.size AS bigint)) * 8.0 / 1024 AS float) AS sizeMB,
201
+ CAST(SUM(CASE WHEN mf.type = 0 THEN CAST(mf.size AS bigint) ELSE 0 END) * 8.0 / 1024 AS float) AS dataMB,
202
+ CAST(SUM(CASE WHEN mf.type = 1 THEN CAST(mf.size AS bigint) ELSE 0 END) * 8.0 / 1024 AS float) AS logMB
203
+ FROM sys.databases d
204
+ LEFT JOIN sys.master_files mf ON mf.database_id = d.database_id
205
+ GROUP BY d.name, d.state_desc, d.recovery_model_desc, d.compatibility_level`
206
+ );
207
+ const out = [];
208
+ for (const row of rows(res)) {
209
+ const name = str(row, "name");
210
+ if (!name) continue;
211
+ out.push({
212
+ name,
213
+ ...defined(str(row, "state"), (v) => ({ state: v })),
214
+ ...defined(round2(num(row, "sizeMB")), (v) => ({ sizeMB: v })),
215
+ ...defined(round2(num(row, "dataMB")), (v) => ({ dataFileMB: v })),
216
+ ...defined(round2(num(row, "logMB")), (v) => ({ logFileMB: v })),
217
+ ...defined(str(row, "recoveryModel"), (v) => ({ recoveryModel: v })),
218
+ ...defined(int(row, "compatibilityLevel"), (v) => ({ compatibilityLevel: v }))
219
+ });
220
+ }
221
+ return out;
222
+ } catch (err) {
223
+ log("warn", `sys.databases query failed: ${err.message}`);
224
+ return [];
225
+ }
226
+ }
227
+ async function scanJobs(pool, log) {
228
+ try {
229
+ const res = await pool.request().query(
230
+ `SELECT
231
+ j.name AS name,
232
+ j.enabled AS enabled,
233
+ h.run_status AS runStatus,
234
+ h.run_date AS runDate,
235
+ h.run_time AS runTime
236
+ FROM msdb.dbo.sysjobs j
237
+ OUTER APPLY (
238
+ SELECT TOP 1 sh.run_status, sh.run_date, sh.run_time
239
+ FROM msdb.dbo.sysjobhistory sh
240
+ WHERE sh.job_id = j.job_id AND sh.step_id = 0
241
+ ORDER BY sh.run_date DESC, sh.run_time DESC
242
+ ) h`
243
+ );
244
+ const out = [];
245
+ for (const row of rows(res)) {
246
+ const name = str(row, "name");
247
+ if (!name) continue;
248
+ const status = mapRunStatus(int(row, "runStatus"));
249
+ const lastRunAt = agentDateTimeToIso(int(row, "runDate"), int(row, "runTime"));
250
+ out.push({
251
+ name,
252
+ ...defined(bool(row, "enabled"), (v) => ({ enabled: v })),
253
+ ...defined(status, (v) => ({ lastRunStatus: v })),
254
+ ...defined(lastRunAt, (v) => ({ lastRunAt: v }))
255
+ });
256
+ }
257
+ return out;
258
+ } catch (err) {
259
+ log("info", `msdb job query skipped (no access?): ${err.message}`);
260
+ return [];
261
+ }
262
+ }
263
+ function mapRunStatus(code) {
264
+ switch (code) {
265
+ case 0:
266
+ return "failed";
267
+ case 1:
268
+ return "succeeded";
269
+ case 2:
270
+ return "retry";
271
+ case 3:
272
+ return "canceled";
273
+ case 4:
274
+ return "in-progress";
275
+ default:
276
+ return void 0;
277
+ }
278
+ }
279
+ function agentDateTimeToIso(date, time) {
280
+ if (date === void 0 || date <= 0) return void 0;
281
+ const year = Math.floor(date / 1e4);
282
+ const month = Math.floor(date % 1e4 / 100);
283
+ const day = date % 100;
284
+ const t = time ?? 0;
285
+ const hour = Math.floor(t / 1e4);
286
+ const minute = Math.floor(t % 1e4 / 100);
287
+ const second = t % 100;
288
+ if (month < 1 || month > 12 || day < 1 || day > 31) return void 0;
289
+ const d = new Date(year, month - 1, day, hour, minute, second);
290
+ return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
291
+ }
292
+ function firstRow(res) {
293
+ const r = res.recordset;
294
+ return r && r.length > 0 ? r[0] : {};
295
+ }
296
+ function rows(res) {
297
+ return res.recordset ?? [];
298
+ }
299
+ function str(o, key) {
300
+ const v = o[key];
301
+ if (typeof v === "string" && v.length > 0) return v;
302
+ return void 0;
303
+ }
304
+ function num(o, key) {
305
+ const v = o[key];
306
+ if (typeof v === "number" && Number.isFinite(v)) return v;
307
+ if (typeof v === "string" && v.trim() !== "") {
308
+ const n = Number(v);
309
+ if (Number.isFinite(n)) return n;
310
+ }
311
+ return void 0;
312
+ }
313
+ function int(o, key) {
314
+ const n = num(o, key);
315
+ return n === void 0 ? void 0 : Math.round(n);
316
+ }
317
+ function bool(o, key) {
318
+ const v = o[key];
319
+ if (typeof v === "boolean") return v;
320
+ if (typeof v === "number") return v !== 0;
321
+ return void 0;
322
+ }
323
+ function round2(n) {
324
+ if (n === void 0) return void 0;
325
+ const r = Math.round(n * 100) / 100;
326
+ return r >= 0 ? r : void 0;
327
+ }
328
+ function defined(v, build) {
329
+ return v === void 0 ? {} : build(v);
330
+ }
331
+
332
+ // src/sql/plugin.ts
333
+ import { randomUUID } from "node:crypto";
334
+ var noopLog2 = () => {
335
+ };
336
+ function buildSqlInventory(scan, stamp, log = noopLog2) {
337
+ const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
338
+ const tenantId = stamp.tenantId;
339
+ const siteId = stamp.siteId;
340
+ const inst = scan.instance;
341
+ const instanceId = `${inst.hostName}\\${inst.instanceName}`;
342
+ const databases = [];
343
+ for (const d of scan.databases) {
344
+ databases.push({
345
+ id: d.name ? `${instanceId}::db::${d.name}` : randomUUID(),
346
+ tenantId,
347
+ siteId,
348
+ instanceId,
349
+ name: d.name,
350
+ ...d.state !== void 0 ? { state: d.state } : {},
351
+ ...d.sizeMB !== void 0 ? { sizeMB: d.sizeMB } : {},
352
+ ...d.dataFileMB !== void 0 ? { dataFileMB: d.dataFileMB } : {},
353
+ ...d.logFileMB !== void 0 ? { logFileMB: d.logFileMB } : {},
354
+ ...d.recoveryModel !== void 0 ? { recoveryModel: d.recoveryModel } : {},
355
+ ...d.compatibilityLevel !== void 0 ? { compatibilityLevel: d.compatibilityLevel } : {}
356
+ });
357
+ }
358
+ const jobs = [];
359
+ for (const j of scan.jobs) {
360
+ jobs.push({
361
+ id: j.name ? `${instanceId}::job::${j.name}` : randomUUID(),
362
+ tenantId,
363
+ siteId,
364
+ instanceId,
365
+ name: j.name,
366
+ ...j.enabled !== void 0 ? { enabled: j.enabled } : {},
367
+ ...j.lastRunStatus !== void 0 ? { lastRunStatus: j.lastRunStatus } : {},
368
+ ...j.lastRunAt !== void 0 ? { lastRunAt: j.lastRunAt } : {}
369
+ });
370
+ }
371
+ const tables = [];
372
+ for (const t of scan.tables) {
373
+ tables.push({
374
+ id: `${instanceId}::tbl::${t.database}.${t.schema}.${t.name}`,
375
+ tenantId,
376
+ siteId,
377
+ instanceId,
378
+ databaseName: t.database,
379
+ schema: t.schema,
380
+ name: t.name,
381
+ columns: t.columns.map((c) => ({
382
+ name: c.name,
383
+ ...c.dataType !== void 0 ? { dataType: c.dataType } : {},
384
+ ...c.nullable !== void 0 ? { nullable: c.nullable } : {},
385
+ ...c.isPrimaryKey ? { isPrimaryKey: true } : {},
386
+ ...c.isForeignKey ? { isForeignKey: true } : {},
387
+ ...c.ordinal !== void 0 ? { ordinal: c.ordinal } : {}
388
+ }))
389
+ });
390
+ }
391
+ const foreignKeys = [];
392
+ for (const f of scan.foreignKeys) {
393
+ foreignKeys.push({
394
+ id: `${instanceId}::fk::${f.database}.${f.name}`,
395
+ tenantId,
396
+ siteId,
397
+ instanceId,
398
+ databaseName: f.database,
399
+ name: f.name,
400
+ fromSchema: f.fromSchema,
401
+ fromTable: f.fromTable,
402
+ fromColumns: f.fromColumns,
403
+ toSchema: f.toSchema,
404
+ toTable: f.toTable,
405
+ toColumns: f.toColumns
406
+ });
407
+ }
408
+ const instance = {
409
+ id: instanceId,
410
+ tenantId,
411
+ siteId,
412
+ hostName: inst.hostName,
413
+ instanceName: inst.instanceName,
414
+ ...inst.edition !== void 0 ? { edition: inst.edition } : {},
415
+ ...inst.version !== void 0 ? { version: inst.version } : {},
416
+ ...inst.productLevel !== void 0 ? { productLevel: inst.productLevel } : {},
417
+ ...inst.port !== void 0 ? { port: inst.port } : {},
418
+ databaseCount: databases.length
419
+ };
420
+ log(
421
+ "info",
422
+ `built SqlInventory: 1 instance, ${databases.length} database(s), ${jobs.length} job(s), ${tables.length} table(s), ${foreignKeys.length} FK(s)`
423
+ );
424
+ return SqlIngestBody.parse({
425
+ schemaVersion: 1,
426
+ collectorId: stamp.collectorId,
427
+ tenantId: stamp.tenantId,
428
+ orgId: stamp.orgId,
429
+ siteId: stamp.siteId,
430
+ capturedAt,
431
+ instances: [instance],
432
+ databases,
433
+ jobs,
434
+ tables,
435
+ foreignKeys
436
+ });
437
+ }
438
+
439
+ export {
440
+ scanSqlServer,
441
+ buildSqlInventory
442
+ };
443
+ //# sourceMappingURL=chunk-2VVO5SGB.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/sql/scan.ts", "../src/sql/plugin.ts"],
4
+ "sourcesContent": ["/**\r\n * SQL Server scanner \u2014 connects over TDS (the `mssql` driver, which bundles\r\n * tedious) and inventories one SQL Server instance: server properties, its\r\n * databases, and its Agent jobs.\r\n *\r\n * Queries (each run best-effort \u2014 one failing query never aborts the scan):\r\n * - SERVERPROPERTY(...) \u2192 edition / version / product level / names\r\n * - sys.databases + sys.master_files \u2192 per-database state / recovery / size\r\n * - msdb.dbo.sysjobs + sysjobhistory \u2192 Agent jobs + last-run outcome/time\r\n * The msdb query is tolerated failing (a least-privilege login often can't read\r\n * msdb): jobs come back empty rather than failing the run.\r\n *\r\n * SECURITY: SQL_USER / SQL_PASS are resolved by config.ts and passed to the\r\n * driver here only. They are never logged and never placed in the payload.\r\n *\r\n * A connection/auth failure throws a clear error so the CLI exits non-zero \u2014 the\r\n * expected outcome when no SQL Server is reachable.\r\n */\r\nimport sql from \"mssql\";\r\n\r\n/** The mssql connection-pool instance type, derived from the value (no named\r\n * type import \u2014 robust across the driver's CJS/ESM type layouts). */\r\ntype Pool = InstanceType<typeof sql.ConnectionPool>;\r\n/** Minimal structural view of a query result we rely on. */\r\ntype QueryResult = { recordset?: ReadonlyArray<Record<string, unknown>> };\r\n\r\nexport type LogFn = (\r\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\r\n msg: string,\r\n meta?: unknown,\r\n) => void;\r\n\r\nconst noopLog: LogFn = () => {};\r\n\r\n/** Connection settings resolved locally (password is a secret). */\r\nexport interface SqlScanOptions {\r\n server: string;\r\n port: number;\r\n user: string;\r\n /** SECRET \u2014 handed to the driver only. */\r\n pass: string;\r\n encrypt: boolean;\r\n trustServerCertificate: boolean;\r\n}\r\n\r\nexport interface ScannedSqlInstance {\r\n hostName: string;\r\n instanceName: string;\r\n edition?: string;\r\n version?: string;\r\n productLevel?: string;\r\n port?: number;\r\n}\r\n\r\nexport interface ScannedSqlDatabase {\r\n name: string;\r\n state?: string;\r\n sizeMB?: number;\r\n dataFileMB?: number;\r\n logFileMB?: number;\r\n recoveryModel?: string;\r\n compatibilityLevel?: number;\r\n}\r\n\r\nexport interface ScannedSqlJob {\r\n name: string;\r\n enabled?: boolean;\r\n lastRunStatus?: string;\r\n /** ISO-8601 last-run timestamp, when derivable from sysjobhistory. */\r\n lastRunAt?: string;\r\n}\r\n\r\nexport interface ScannedSqlColumn {\r\n name: string;\r\n dataType?: string;\r\n nullable?: boolean;\r\n isPrimaryKey?: boolean;\r\n isForeignKey?: boolean;\r\n ordinal?: number;\r\n}\r\n\r\nexport interface ScannedSqlTable {\r\n database: string;\r\n schema: string;\r\n name: string;\r\n columns: ScannedSqlColumn[];\r\n}\r\n\r\nexport interface ScannedSqlForeignKey {\r\n database: string;\r\n name: string;\r\n fromSchema: string;\r\n fromTable: string;\r\n fromColumns: string[];\r\n toSchema: string;\r\n toTable: string;\r\n toColumns: string[];\r\n}\r\n\r\nexport interface SqlScanResult {\r\n instance: ScannedSqlInstance;\r\n databases: ScannedSqlDatabase[];\r\n jobs: ScannedSqlJob[];\r\n /** Per-database schema for the ER diagrams (user databases only). */\r\n tables: ScannedSqlTable[];\r\n foreignKeys: ScannedSqlForeignKey[];\r\n}\r\n\r\n/** System databases we never diagram. */\r\nconst SYSTEM_DBS = new Set([\"master\", \"model\", \"msdb\", \"tempdb\"]);\r\n\r\n/** Quote a database name for a 3-part catalog reference (]-escaped). */\r\nfunction bracket(dbName: string): string {\r\n return `[${dbName.replace(/]/g, \"]]\")}]`;\r\n}\r\n\r\n/** Connect, inventory the instance, and close the pool. Throws on connect failure. */\r\nexport async function scanSqlServer(\r\n opts: SqlScanOptions,\r\n log: LogFn = noopLog,\r\n): Promise<SqlScanResult> {\r\n // `server` may be \"host\" or \"host\\\\instance\"; mssql accepts the instance in\r\n // options.instanceName, but passing it as the server name also works. When an\r\n // explicit instance is given SQL Browser resolves the port, so we still send\r\n // the configured port as the default for the default instance.\r\n // Keep the scan snappy; a slow/blocked server should fail fast, not hang.\r\n // (server/port/user/password/timeouts are top-level mssql config fields;\r\n // encrypt/trustServerCertificate live under `options`.)\r\n const config = {\r\n server: opts.server,\r\n port: opts.port,\r\n user: opts.user,\r\n password: opts.pass,\r\n connectionTimeout: 15000,\r\n requestTimeout: 30000,\r\n options: {\r\n encrypt: opts.encrypt,\r\n trustServerCertificate: opts.trustServerCertificate,\r\n },\r\n };\r\n\r\n const pool = new sql.ConnectionPool(config);\r\n try {\r\n await pool.connect();\r\n } catch (err) {\r\n throw new Error(\r\n `Could not connect to SQL Server \"${opts.server}:${opts.port}\": ${(err as Error).message} ` +\r\n `\u2014 check reachability, SQL_USER/SQL_PASS, and SQL_ENCRYPT/SQL_TRUST_CERT.`,\r\n );\r\n }\r\n\r\n try {\r\n const instance = await scanInstance(pool, opts, log);\r\n const databases = await scanDatabases(pool, log);\r\n const jobs = await scanJobs(pool, log);\r\n const userDbs = databases\r\n .filter((db) => !SYSTEM_DBS.has(db.name.toLowerCase()) && (db.state ?? \"ONLINE\") === \"ONLINE\")\r\n .map((db) => db.name);\r\n const { tables, foreignKeys } = await scanSchema(pool, userDbs, log);\r\n log(\r\n \"info\",\r\n `SQL Server returned instance ${instance.hostName}\\\\${instance.instanceName}, ` +\r\n `${databases.length} database(s), ${jobs.length} job(s), ${tables.length} table(s), ${foreignKeys.length} FK(s)`,\r\n );\r\n return { instance, databases, jobs, tables, foreignKeys };\r\n } finally {\r\n await pool.close().catch(() => {});\r\n }\r\n}\r\n\r\n/** Render a SQL type with its length/precision, matching SSMS (e.g. nvarchar(256)). */\r\nfunction formatType(name: string, maxLength: number, prec: number, scale: number): string {\r\n const t = name.toLowerCase();\r\n if ([\"varchar\", \"char\", \"varbinary\", \"binary\"].includes(t)) {\r\n return maxLength === -1 ? `${name}(max)` : `${name}(${maxLength})`;\r\n }\r\n if ([\"nvarchar\", \"nchar\"].includes(t)) {\r\n return maxLength === -1 ? `${name}(max)` : `${name}(${Math.floor(maxLength / 2)})`;\r\n }\r\n if ([\"decimal\", \"numeric\"].includes(t)) return `${name}(${prec},${scale})`;\r\n if ([\"datetime2\", \"time\", \"datetimeoffset\"].includes(t)) return `${name}(${scale})`;\r\n return name;\r\n}\r\n\r\n/**\r\n * Read the schema (tables \u2192 columns with PK/FK flags, and foreign keys) of each\r\n * USER database via 3-part catalog references. Read-only metadata only \u2014 no data\r\n * rows. Every database is best-effort: a least-privilege login that can't read a\r\n * given database is skipped (logged), never aborting the whole scan.\r\n */\r\nasync function scanSchema(\r\n pool: Pool,\r\n dbNames: string[],\r\n log: LogFn,\r\n): Promise<{ tables: ScannedSqlTable[]; foreignKeys: ScannedSqlForeignKey[] }> {\r\n const tables: ScannedSqlTable[] = [];\r\n const foreignKeys: ScannedSqlForeignKey[] = [];\r\n\r\n for (const dbName of dbNames) {\r\n const B = bracket(dbName);\r\n try {\r\n // Columns of every base table, with PK / FK membership flags.\r\n const colRes = await pool.request().query(\r\n `SELECT s.name AS schemaName, t.name AS tableName, c.name AS columnName,\r\n c.column_id AS columnId, ty.name AS typeName,\r\n c.max_length AS maxLength, c.precision AS prec, c.scale AS scale,\r\n c.is_nullable AS isNullable,\r\n CASE WHEN ic.column_id IS NOT NULL THEN 1 ELSE 0 END AS isPk,\r\n CASE WHEN fkc.parent_column_id IS NOT NULL THEN 1 ELSE 0 END AS isFk\r\n FROM ${B}.sys.tables t\r\n JOIN ${B}.sys.schemas s ON s.schema_id = t.schema_id\r\n JOIN ${B}.sys.columns c ON c.object_id = t.object_id\r\n JOIN ${B}.sys.types ty ON ty.user_type_id = c.user_type_id\r\n LEFT JOIN ${B}.sys.indexes pk ON pk.object_id = t.object_id AND pk.is_primary_key = 1\r\n LEFT JOIN ${B}.sys.index_columns ic\r\n ON ic.object_id = t.object_id AND ic.index_id = pk.index_id AND ic.column_id = c.column_id\r\n LEFT JOIN (SELECT DISTINCT parent_object_id, parent_column_id FROM ${B}.sys.foreign_key_columns) fkc\r\n ON fkc.parent_object_id = t.object_id AND fkc.parent_column_id = c.column_id\r\n WHERE t.is_ms_shipped = 0\r\n ORDER BY s.name, t.name, c.column_id`,\r\n );\r\n\r\n // Group column rows into tables keyed by schema.table.\r\n const byTable = new Map<string, ScannedSqlTable>();\r\n for (const row of rows(colRes)) {\r\n const schema = str(row, \"schemaName\") ?? \"dbo\";\r\n const tableName = str(row, \"tableName\");\r\n const columnName = str(row, \"columnName\");\r\n if (!tableName || !columnName) continue;\r\n const key = `${schema}.${tableName}`;\r\n let tbl = byTable.get(key);\r\n if (!tbl) {\r\n tbl = { database: dbName, schema, name: tableName, columns: [] };\r\n byTable.set(key, tbl);\r\n }\r\n const typeName = str(row, \"typeName\");\r\n tbl.columns.push({\r\n name: columnName,\r\n ...(typeName\r\n ? {\r\n dataType: formatType(\r\n typeName,\r\n int(row, \"maxLength\") ?? 0,\r\n int(row, \"prec\") ?? 0,\r\n int(row, \"scale\") ?? 0,\r\n ),\r\n }\r\n : {}),\r\n nullable: bool(row, \"isNullable\") ?? true,\r\n ...(int(row, \"isPk\") ? { isPrimaryKey: true } : {}),\r\n ...(int(row, \"isFk\") ? { isForeignKey: true } : {}),\r\n ...(defined(int(row, \"columnId\"), (v) => ({ ordinal: v }))),\r\n });\r\n }\r\n for (const tbl of byTable.values()) tables.push(tbl);\r\n\r\n // Foreign keys \u2014 one edge per constraint, columns ordered by position.\r\n const fkRes = await pool.request().query(\r\n `SELECT fk.name AS fkName,\r\n sp.name AS fromSchema, tp.name AS fromTable, cpar.name AS fromColumn,\r\n sr.name AS toSchema, tr.name AS toTable, cref.name AS toColumn,\r\n fkc.constraint_column_id AS ord\r\n FROM ${B}.sys.foreign_keys fk\r\n JOIN ${B}.sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id\r\n JOIN ${B}.sys.tables tp ON tp.object_id = fk.parent_object_id\r\n JOIN ${B}.sys.schemas sp ON sp.schema_id = tp.schema_id\r\n JOIN ${B}.sys.columns cpar ON cpar.object_id = fk.parent_object_id AND cpar.column_id = fkc.parent_column_id\r\n JOIN ${B}.sys.tables tr ON tr.object_id = fk.referenced_object_id\r\n JOIN ${B}.sys.schemas sr ON sr.schema_id = tr.schema_id\r\n JOIN ${B}.sys.columns cref ON cref.object_id = fk.referenced_object_id AND cref.column_id = fkc.referenced_column_id\r\n ORDER BY fk.name, fkc.constraint_column_id`,\r\n );\r\n const byFk = new Map<string, ScannedSqlForeignKey>();\r\n for (const row of rows(fkRes)) {\r\n const fkName = str(row, \"fkName\");\r\n const fromTable = str(row, \"fromTable\");\r\n const toTable = str(row, \"toTable\");\r\n if (!fkName || !fromTable || !toTable) continue;\r\n let fk = byFk.get(fkName);\r\n if (!fk) {\r\n fk = {\r\n database: dbName,\r\n name: fkName,\r\n fromSchema: str(row, \"fromSchema\") ?? \"dbo\",\r\n fromTable,\r\n fromColumns: [],\r\n toSchema: str(row, \"toSchema\") ?? \"dbo\",\r\n toTable,\r\n toColumns: [],\r\n };\r\n byFk.set(fkName, fk);\r\n }\r\n const fromCol = str(row, \"fromColumn\");\r\n const toCol = str(row, \"toColumn\");\r\n if (fromCol) fk.fromColumns.push(fromCol);\r\n if (toCol) fk.toColumns.push(toCol);\r\n }\r\n for (const fk of byFk.values()) foreignKeys.push(fk);\r\n\r\n log(\"debug\", `schema ${dbName}: ${byTable.size} table(s), ${byFk.size} FK(s)`);\r\n } catch (err) {\r\n log(\"info\", `schema scan skipped for database ${dbName} (no access?): ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n return { tables, foreignKeys };\r\n}\r\n\r\nasync function scanInstance(\r\n pool: Pool,\r\n opts: SqlScanOptions,\r\n log: LogFn,\r\n): Promise<ScannedSqlInstance> {\r\n const fallbackHost = opts.server.split(\"\\\\\")[0] ?? opts.server;\r\n try {\r\n const res = await pool.request().query(\r\n `SELECT\r\n CAST(SERVERPROPERTY('Edition') AS nvarchar(256)) AS Edition,\r\n CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(256)) AS ProductVersion,\r\n CAST(SERVERPROPERTY('ProductLevel') AS nvarchar(256)) AS ProductLevel,\r\n CAST(SERVERPROPERTY('InstanceName') AS nvarchar(256)) AS InstanceName,\r\n CAST(SERVERPROPERTY('MachineName') AS nvarchar(256)) AS MachineName`,\r\n );\r\n const row = firstRow(res);\r\n const hostName = str(row, \"MachineName\") ?? fallbackHost;\r\n // A default instance reports a NULL InstanceName \u2192 canonical \"MSSQLSERVER\".\r\n const instanceName = str(row, \"InstanceName\") ?? \"MSSQLSERVER\";\r\n return {\r\n hostName,\r\n instanceName,\r\n ...(defined(str(row, \"Edition\"), (v) => ({ edition: v }))),\r\n ...(defined(str(row, \"ProductVersion\"), (v) => ({ version: v }))),\r\n ...(defined(str(row, \"ProductLevel\"), (v) => ({ productLevel: v }))),\r\n port: opts.port,\r\n };\r\n } catch (err) {\r\n log(\"warn\", `SERVERPROPERTY query failed: ${(err as Error).message}`);\r\n // Even without server properties we can still produce an identifiable instance.\r\n return { hostName: fallbackHost, instanceName: \"MSSQLSERVER\", port: opts.port };\r\n }\r\n}\r\n\r\nasync function scanDatabases(pool: Pool, log: LogFn): Promise<ScannedSqlDatabase[]> {\r\n try {\r\n const res = await pool.request().query(\r\n `SELECT\r\n d.name AS name,\r\n d.state_desc AS state,\r\n d.recovery_model_desc AS recoveryModel,\r\n d.compatibility_level AS compatibilityLevel,\r\n CAST(SUM(CAST(mf.size AS bigint)) * 8.0 / 1024 AS float) AS sizeMB,\r\n CAST(SUM(CASE WHEN mf.type = 0 THEN CAST(mf.size AS bigint) ELSE 0 END) * 8.0 / 1024 AS float) AS dataMB,\r\n CAST(SUM(CASE WHEN mf.type = 1 THEN CAST(mf.size AS bigint) ELSE 0 END) * 8.0 / 1024 AS float) AS logMB\r\n FROM sys.databases d\r\n LEFT JOIN sys.master_files mf ON mf.database_id = d.database_id\r\n GROUP BY d.name, d.state_desc, d.recovery_model_desc, d.compatibility_level`,\r\n );\r\n const out: ScannedSqlDatabase[] = [];\r\n for (const row of rows(res)) {\r\n const name = str(row, \"name\");\r\n if (!name) continue;\r\n out.push({\r\n name,\r\n ...(defined(str(row, \"state\"), (v) => ({ state: v }))),\r\n ...(defined(round2(num(row, \"sizeMB\")), (v) => ({ sizeMB: v }))),\r\n ...(defined(round2(num(row, \"dataMB\")), (v) => ({ dataFileMB: v }))),\r\n ...(defined(round2(num(row, \"logMB\")), (v) => ({ logFileMB: v }))),\r\n ...(defined(str(row, \"recoveryModel\"), (v) => ({ recoveryModel: v }))),\r\n ...(defined(int(row, \"compatibilityLevel\"), (v) => ({ compatibilityLevel: v }))),\r\n });\r\n }\r\n return out;\r\n } catch (err) {\r\n log(\"warn\", `sys.databases query failed: ${(err as Error).message}`);\r\n return [];\r\n }\r\n}\r\n\r\nasync function scanJobs(pool: Pool, log: LogFn): Promise<ScannedSqlJob[]> {\r\n try {\r\n const res = await pool.request().query(\r\n `SELECT\r\n j.name AS name,\r\n j.enabled AS enabled,\r\n h.run_status AS runStatus,\r\n h.run_date AS runDate,\r\n h.run_time AS runTime\r\n FROM msdb.dbo.sysjobs j\r\n OUTER APPLY (\r\n SELECT TOP 1 sh.run_status, sh.run_date, sh.run_time\r\n FROM msdb.dbo.sysjobhistory sh\r\n WHERE sh.job_id = j.job_id AND sh.step_id = 0\r\n ORDER BY sh.run_date DESC, sh.run_time DESC\r\n ) h`,\r\n );\r\n const out: ScannedSqlJob[] = [];\r\n for (const row of rows(res)) {\r\n const name = str(row, \"name\");\r\n if (!name) continue;\r\n const status = mapRunStatus(int(row, \"runStatus\"));\r\n const lastRunAt = agentDateTimeToIso(int(row, \"runDate\"), int(row, \"runTime\"));\r\n out.push({\r\n name,\r\n ...(defined(bool(row, \"enabled\"), (v) => ({ enabled: v }))),\r\n ...(defined(status, (v) => ({ lastRunStatus: v }))),\r\n ...(defined(lastRunAt, (v) => ({ lastRunAt: v }))),\r\n });\r\n }\r\n return out;\r\n } catch (err) {\r\n // msdb is frequently off-limits to a least-privilege login \u2014 tolerate it.\r\n log(\"info\", `msdb job query skipped (no access?): ${(err as Error).message}`);\r\n return [];\r\n }\r\n}\r\n\r\n/** SQL Agent run_status \u2192 human text (0 Failed, 1 Succeeded, 2 Retry, 3 Canceled, 4 In progress). */\r\nfunction mapRunStatus(code: number | undefined): string | undefined {\r\n switch (code) {\r\n case 0:\r\n return \"failed\";\r\n case 1:\r\n return \"succeeded\";\r\n case 2:\r\n return \"retry\";\r\n case 3:\r\n return \"canceled\";\r\n case 4:\r\n return \"in-progress\";\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Convert SQL Agent's split integer date/time (run_date=yyyymmdd, run_time=hhmmss)\r\n * into an ISO-8601 string. Returns undefined for the sentinel/absent 0 values.\r\n */\r\nfunction agentDateTimeToIso(date: number | undefined, time: number | undefined): string | undefined {\r\n if (date === undefined || date <= 0) return undefined;\r\n const year = Math.floor(date / 10000);\r\n const month = Math.floor((date % 10000) / 100);\r\n const day = date % 100;\r\n const t = time ?? 0;\r\n const hour = Math.floor(t / 10000);\r\n const minute = Math.floor((t % 10000) / 100);\r\n const second = t % 100;\r\n if (month < 1 || month > 12 || day < 1 || day > 31) return undefined;\r\n const d = new Date(year, month - 1, day, hour, minute, second);\r\n return Number.isNaN(d.getTime()) ? undefined : d.toISOString();\r\n}\r\n\r\n// --- tiny typed accessors over the loose recordset rows ----------------------\r\n\r\ntype Row = Record<string, unknown>;\r\n\r\nfunction firstRow(res: QueryResult): Row {\r\n const r = res.recordset;\r\n return r && r.length > 0 ? (r[0] as Row) : {};\r\n}\r\n\r\nfunction rows(res: QueryResult): Row[] {\r\n return (res.recordset ?? []) as Row[];\r\n}\r\n\r\nfunction str(o: Row, key: string): string | undefined {\r\n const v = o[key];\r\n if (typeof v === \"string\" && v.length > 0) return v;\r\n return undefined;\r\n}\r\n\r\nfunction num(o: Row, key: string): number | undefined {\r\n const v = o[key];\r\n if (typeof v === \"number\" && Number.isFinite(v)) return v;\r\n if (typeof v === \"string\" && v.trim() !== \"\") {\r\n const n = Number(v);\r\n if (Number.isFinite(n)) return n;\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction int(o: Row, key: string): number | undefined {\r\n const n = num(o, key);\r\n return n === undefined ? undefined : Math.round(n);\r\n}\r\n\r\nfunction bool(o: Row, key: string): boolean | undefined {\r\n const v = o[key];\r\n if (typeof v === \"boolean\") return v;\r\n if (typeof v === \"number\") return v !== 0;\r\n return undefined;\r\n}\r\n\r\nfunction round2(n: number | undefined): number | undefined {\r\n if (n === undefined) return undefined;\r\n const r = Math.round(n * 100) / 100;\r\n return r >= 0 ? r : undefined;\r\n}\r\n\r\nfunction defined<T, O extends object>(v: T | undefined, build: (v: T) => O): Partial<O> {\r\n return v === undefined ? {} : build(v);\r\n}\r\n", "/**\r\n * SQL Server collector mapper.\r\n *\r\n * Consumes the normalized `SqlScanResult` from the TDS scanner and maps it onto\r\n * the shared canonical model (instances \u2192 databases + jobs), producing a\r\n * validated `SqlInventory` ready to POST to /ingest/sql.\r\n *\r\n * Design notes (parallels the VMware/Hyper-V mappers):\r\n * - Instance id is derived from \"host\\\\instance\" (stable across runs); database\r\n * and job ids are stable \"instanceId::name\" keys, so re-scans line up. If a\r\n * name were somehow blank we fall back to a random UUID.\r\n * - `databaseCount` is stamped onto the instance from the mapped databases.\r\n * - Every optional field is best-effort: whatever the scan didn't yield is\r\n * omitted. tenantId / siteId are stamped on every entity.\r\n */\r\nimport { randomUUID } from \"node:crypto\";\r\nimport type {\r\n SqlInventory,\r\n SqlInstance,\r\n SqlDatabase,\r\n SqlJob,\r\n SqlTable,\r\n SqlForeignKey,\r\n} from \"@netdoc/canonical-model\";\r\nimport { SqlIngestBody } from \"@netdoc/collector-contract\";\r\nimport type { SqlScanResult, LogFn } from \"./scan.js\";\r\n\r\nconst noopLog: LogFn = () => {};\r\n\r\n/** Envelope identifiers stamped onto every entity + the payload. */\r\nexport interface SqlStamp {\r\n collectorId: string;\r\n tenantId: string;\r\n orgId: string;\r\n siteId: string;\r\n}\r\n\r\n/** Map a scan result \u2192 a validated SqlInventory. */\r\nexport function buildSqlInventory(\r\n scan: SqlScanResult,\r\n stamp: SqlStamp,\r\n log: LogFn = noopLog,\r\n): SqlInventory {\r\n const capturedAt = new Date().toISOString();\r\n const tenantId = stamp.tenantId;\r\n const siteId = stamp.siteId;\r\n\r\n const inst = scan.instance;\r\n const instanceId = `${inst.hostName}\\\\${inst.instanceName}`;\r\n\r\n const databases: SqlDatabase[] = [];\r\n for (const d of scan.databases) {\r\n databases.push({\r\n id: d.name ? `${instanceId}::db::${d.name}` : randomUUID(),\r\n tenantId,\r\n siteId,\r\n instanceId,\r\n name: d.name,\r\n ...(d.state !== undefined ? { state: d.state } : {}),\r\n ...(d.sizeMB !== undefined ? { sizeMB: d.sizeMB } : {}),\r\n ...(d.dataFileMB !== undefined ? { dataFileMB: d.dataFileMB } : {}),\r\n ...(d.logFileMB !== undefined ? { logFileMB: d.logFileMB } : {}),\r\n ...(d.recoveryModel !== undefined ? { recoveryModel: d.recoveryModel } : {}),\r\n ...(d.compatibilityLevel !== undefined ? { compatibilityLevel: d.compatibilityLevel } : {}),\r\n });\r\n }\r\n\r\n const jobs: SqlJob[] = [];\r\n for (const j of scan.jobs) {\r\n jobs.push({\r\n id: j.name ? `${instanceId}::job::${j.name}` : randomUUID(),\r\n tenantId,\r\n siteId,\r\n instanceId,\r\n name: j.name,\r\n ...(j.enabled !== undefined ? { enabled: j.enabled } : {}),\r\n ...(j.lastRunStatus !== undefined ? { lastRunStatus: j.lastRunStatus } : {}),\r\n ...(j.lastRunAt !== undefined ? { lastRunAt: j.lastRunAt } : {}),\r\n });\r\n }\r\n\r\n const tables: SqlTable[] = [];\r\n for (const t of scan.tables) {\r\n tables.push({\r\n id: `${instanceId}::tbl::${t.database}.${t.schema}.${t.name}`,\r\n tenantId,\r\n siteId,\r\n instanceId,\r\n databaseName: t.database,\r\n schema: t.schema,\r\n name: t.name,\r\n columns: t.columns.map((c) => ({\r\n name: c.name,\r\n ...(c.dataType !== undefined ? { dataType: c.dataType } : {}),\r\n ...(c.nullable !== undefined ? { nullable: c.nullable } : {}),\r\n ...(c.isPrimaryKey ? { isPrimaryKey: true } : {}),\r\n ...(c.isForeignKey ? { isForeignKey: true } : {}),\r\n ...(c.ordinal !== undefined ? { ordinal: c.ordinal } : {}),\r\n })),\r\n });\r\n }\r\n\r\n const foreignKeys: SqlForeignKey[] = [];\r\n for (const f of scan.foreignKeys) {\r\n foreignKeys.push({\r\n id: `${instanceId}::fk::${f.database}.${f.name}`,\r\n tenantId,\r\n siteId,\r\n instanceId,\r\n databaseName: f.database,\r\n name: f.name,\r\n fromSchema: f.fromSchema,\r\n fromTable: f.fromTable,\r\n fromColumns: f.fromColumns,\r\n toSchema: f.toSchema,\r\n toTable: f.toTable,\r\n toColumns: f.toColumns,\r\n });\r\n }\r\n\r\n const instance: SqlInstance = {\r\n id: instanceId,\r\n tenantId,\r\n siteId,\r\n hostName: inst.hostName,\r\n instanceName: inst.instanceName,\r\n ...(inst.edition !== undefined ? { edition: inst.edition } : {}),\r\n ...(inst.version !== undefined ? { version: inst.version } : {}),\r\n ...(inst.productLevel !== undefined ? { productLevel: inst.productLevel } : {}),\r\n ...(inst.port !== undefined ? { port: inst.port } : {}),\r\n databaseCount: databases.length,\r\n };\r\n\r\n log(\r\n \"info\",\r\n `built SqlInventory: 1 instance, ${databases.length} database(s), ${jobs.length} job(s), ` +\r\n `${tables.length} table(s), ${foreignKeys.length} FK(s)`,\r\n );\r\n\r\n // Validate the whole payload (SqlIngestBody === SqlInventory) before push.\r\n return SqlIngestBody.parse({\r\n schemaVersion: 1,\r\n collectorId: stamp.collectorId,\r\n tenantId: stamp.tenantId,\r\n orgId: stamp.orgId,\r\n siteId: stamp.siteId,\r\n capturedAt,\r\n instances: [instance],\r\n databases,\r\n jobs,\r\n tables,\r\n foreignKeys,\r\n });\r\n}\r\n"],
5
+ "mappings": "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",
6
+ "names": ["noopLog"]
7
+ }