@utilix-tech/sdk 0.39.0 → 0.41.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/CHANGELOG.md +8 -0
- package/README.md +30 -5
- package/dist/{chunk-47RI5V3B.js → chunk-2MTHD2LG.js} +62 -1
- package/dist/{chunk-Y4BVU76A.js → chunk-7WSFJTYP.js} +116 -1
- package/dist/{chunk-RZUNTYEH.js → chunk-JR354NH4.js} +61 -1
- package/dist/{chunk-VEVNIGJ6.js → chunk-PET2CGTH.js} +21 -1
- package/dist/{data-BCf3dm-E.d.cts → data-BKpEOcOX.d.cts} +35 -2
- package/dist/{data-BCf3dm-E.d.ts → data-BKpEOcOX.d.ts} +35 -2
- package/dist/{hashing-Dnj0IAZf.d.cts → hashing-CJjlvqS_.d.cts} +28 -2
- package/dist/{hashing-Dnj0IAZf.d.ts → hashing-CJjlvqS_.d.ts} +28 -2
- package/dist/index.cjs +256 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/{network-CKPoesdD.d.ts → network-BC8McQOh.d.cts} +60 -2
- package/dist/{network-CKPoesdD.d.cts → network-BC8McQOh.d.ts} +60 -2
- package/dist/tools/data.cjs +60 -0
- package/dist/tools/data.d.cts +1 -1
- package/dist/tools/data.d.ts +1 -1
- package/dist/tools/data.js +1 -1
- package/dist/tools/hashing.cjs +20 -0
- package/dist/tools/hashing.d.cts +1 -1
- package/dist/tools/hashing.d.ts +1 -1
- package/dist/tools/hashing.js +1 -1
- package/dist/tools/network.cjs +115 -0
- package/dist/tools/network.d.cts +1 -1
- package/dist/tools/network.d.ts +1 -1
- package/dist/tools/network.js +1 -1
- package/dist/tools/units.cjs +61 -0
- package/dist/tools/units.d.cts +1 -1
- package/dist/tools/units.d.ts +1 -1
- package/dist/tools/units.js +1 -1
- package/dist/{units-HQrMCiNM.d.ts → units-CzPwRC03.d.cts} +50 -2
- package/dist/{units-HQrMCiNM.d.cts → units-CzPwRC03.d.ts} +50 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Full release notes for all Utilix surfaces (browser tools, REST API, SDKs, MCP server) live at [utilix.tech/changelog](https://utilix.tech/changelog). This file tracks just this package.
|
|
4
4
|
|
|
5
|
+
## 0.41.0
|
|
6
|
+
- Add `buildRangeHeader`, `parseContentRangeHeader` (http-range-header-builder): build an HTTP `Range` request header from explicit, open-ended, or suffix ("last N bytes") byte ranges, and parse a server's `Content-Range` response to verify what it actually sent back.
|
|
7
|
+
- Add `findCsvDuplicateRows` (csv-duplicate-row-finder): group CSV/TSV rows that share the same value across a chosen set of key columns, or the entire row, and report each duplicate group's occurrence count and data row numbers.
|
|
8
|
+
|
|
9
|
+
## 0.40.0
|
|
10
|
+
- Add `calculateCashBreakdown`, `calculateChange` (cash-till-calculator): break a cash amount down into the minimum count of bills/coins from a set of denominations (largest first), or compute change owed for a payment/tender pair and its breakdown.
|
|
11
|
+
- Add `generateEtag` (etag-generator): compute a content-hash ETag (MD5/SHA-1/SHA-256) for a response header, with an optional weak-validator (W/) prefix.
|
|
12
|
+
|
|
5
13
|
## 0.39.0
|
|
6
14
|
- Add `generateSri`, `generateSriAll` (sri-hash-generator): compute SHA-256/384/512 Subresource Integrity hashes for a script or stylesheet's content, formatted as the `<algorithm>-<base64>` string an HTML `integrity` attribute expects.
|
|
7
15
|
- Add `analyzeEmailHeaders` (email-header-analyzer): parse a raw email header block into structured From/To/Subject fields, the Received hop chain in order, and SPF/DKIM/DMARC results from an Authentication-Results header.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**618 developer utility functions for Node.js: runs locally, no API key required.**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@utilix-tech/sdk)
|
|
6
6
|
[](https://www.npmjs.com/package/@utilix-tech/sdk)
|
|
@@ -135,6 +135,10 @@ generateHtpasswdFile([{ username: "alice", password: "secret" }]);
|
|
|
135
135
|
// Subresource Integrity hashes for a script/stylesheet's content (SHA-256/384/512)
|
|
136
136
|
generateSriAll("console.log(1)");
|
|
137
137
|
// [{ algorithm: "SHA-256", base64: "...", integrity: "sha256-..." }, ...]
|
|
138
|
+
|
|
139
|
+
// Content-hash ETag header value (MD5/SHA-1/SHA-256), with an optional weak (W/) prefix
|
|
140
|
+
generateEtag("console.log(1)");
|
|
141
|
+
// { algorithm: "SHA-1", weak: false, hash: "...", etag: "\"...\"", snippet: "ETag: \"...\"" }
|
|
138
142
|
```
|
|
139
143
|
|
|
140
144
|
---
|
|
@@ -197,7 +201,7 @@ import { parseCsv, csvToJson, validateYaml, tomlToJson, jsonToToml,
|
|
|
197
201
|
formatXml, xmlToJson, jsonToXml, parseIni,
|
|
198
202
|
validateNdjson, formatNdjson, ndjsonToJsonArray, diffEnvExample,
|
|
199
203
|
parseSubtitles, convertSubtitles, shiftSubtitles, parseFrontMatter,
|
|
200
|
-
generateMarkdownToc, csvToSqlInsert } from "@utilix-tech/sdk/data";
|
|
204
|
+
generateMarkdownToc, csvToSqlInsert, findCsvDuplicateRows } from "@utilix-tech/sdk/data";
|
|
201
205
|
|
|
202
206
|
// CSV
|
|
203
207
|
const rows = parseCsv("name,age\nalice,30\nbob,25");
|
|
@@ -243,6 +247,10 @@ generateMarkdownToc("# Title\n\n## Section One\n\n## Section Two\n");
|
|
|
243
247
|
// CSV to SQL INSERT statements, with basic type inference and identifier quoting
|
|
244
248
|
csvToSqlInsert("name,age\nAlice,30\nBob,25", { tableName: "users" });
|
|
245
249
|
// { sql: 'INSERT INTO "users" ("name", "age") VALUES (\'Alice\', 30);\n...', rowCount: 2, ... }
|
|
250
|
+
|
|
251
|
+
// Find duplicate rows in CSV/TSV data, by the whole row or a chosen key column set
|
|
252
|
+
findCsvDuplicateRows("name,email\nAlice,a@x.com\nBob,b@x.com\nAlice,a@x.com");
|
|
253
|
+
// { duplicateGroups: [{ key: ["Alice", "a@x.com"], rowNumbers: [1, 3], count: 2 }], duplicateRowCount: 2, ... }
|
|
246
254
|
```
|
|
247
255
|
|
|
248
256
|
---
|
|
@@ -406,6 +414,14 @@ calculateRoi({ initialValue: 1000, finalValue: 1610.51, years: 5 });
|
|
|
406
414
|
// Unit price comparator: true cost per unit across package options, flags the cheapest
|
|
407
415
|
compareUnitPrices([{ label: "12-pack", price: 6, quantity: 12 }, { label: "24-pack", price: 10, quantity: 24 }]);
|
|
408
416
|
// { items: [...], bestLabel: "24-pack" }
|
|
417
|
+
|
|
418
|
+
// Cash till breakdown: minimum count of bills/coins from a set of denominations
|
|
419
|
+
calculateCashBreakdown(53.75, USD_DENOMINATIONS);
|
|
420
|
+
// { breakdown: [{ denomination: 50, count: 1, subtotal: 50 }, ...], totalCount: 7, remainder: 0 }
|
|
421
|
+
|
|
422
|
+
// Change owed for a payment/tender pair, broken down the same way
|
|
423
|
+
calculateChange(4.65, 5.0, USD_DENOMINATIONS);
|
|
424
|
+
// { changeDue: 0.35, breakdown: [{ denomination: 0.25, count: 1, subtotal: 0.25 }, ...], totalCount: 2 }
|
|
409
425
|
```
|
|
410
426
|
|
|
411
427
|
---
|
|
@@ -416,7 +432,8 @@ compareUnitPrices([{ label: "12-pack", price: 6, quantity: 12 }, { label: "24-pa
|
|
|
416
432
|
import { getStatusCode, isValidIp, isValidIpv4, isValidIpv6,
|
|
417
433
|
searchStatusCodes, buildGeoUrl, countryCodeToFlag,
|
|
418
434
|
parseHar, validateSitemap, validateRobotsTxt,
|
|
419
|
-
parseUserAgent, buildCacheControl, buildReferrerPolicy, parseIpv6Address, buildLinkHeader, checkSetCookieHeader, analyzeEmailHeaders
|
|
435
|
+
parseUserAgent, buildCacheControl, buildReferrerPolicy, parseIpv6Address, buildLinkHeader, checkSetCookieHeader, analyzeEmailHeaders,
|
|
436
|
+
buildRangeHeader, parseContentRangeHeader } from "@utilix-tech/sdk/network";
|
|
420
437
|
|
|
421
438
|
getStatusCode(404);
|
|
422
439
|
// { code: 404, text: "Not Found", description: "...", category: "Client Error" }
|
|
@@ -471,6 +488,14 @@ checkSetCookieHeader("session=abc123; SameSite=None");
|
|
|
471
488
|
// Parse a raw email header block: fields, Received hop chain, SPF/DKIM/DMARC results
|
|
472
489
|
analyzeEmailHeaders(rawHeaderText);
|
|
473
490
|
// { from: "...", to: "...", receivedHops: [...], hopCount: 2, spf: "pass", dkim: "pass", dmarc: "pass", warnings: [] }
|
|
491
|
+
|
|
492
|
+
// Build a Range request header from explicit, open-ended, or suffix ("last N bytes") byte ranges
|
|
493
|
+
buildRangeHeader([{ start: 0, end: 1023 }], { totalSize: 146515 });
|
|
494
|
+
// { header: "Range: bytes=0-1023", ranges: [...], totalBytesRequested: 1024, warnings: [] }
|
|
495
|
+
|
|
496
|
+
// Parse a Content-Range response header to verify what a server actually sent back
|
|
497
|
+
parseContentRangeHeader("bytes 200-1000/67589");
|
|
498
|
+
// { unit: "bytes", start: 200, end: 1000, total: 67589, byteLength: 801, isUnsatisfiable: false }
|
|
474
499
|
```
|
|
475
500
|
|
|
476
501
|
---
|
|
@@ -703,12 +728,12 @@ readVideoInfo(new Uint8Array(videoBytes));
|
|
|
703
728
|
|---|---|
|
|
704
729
|
| `@utilix-tech/sdk/json` | Format, minify, diff, validate, JSONPath, CSV↔JSON, YAML↔JSON, TS types from data or from a JSON Schema |
|
|
705
730
|
| `@utilix-tech/sdk/encoding` | Base64, URL, HTML entities, Base32/Base58/Base62 encode/decode |
|
|
706
|
-
| `@utilix-tech/sdk/hashing` | MD5, SHA-1/256/512, bcrypt, HMAC, .htpasswd |
|
|
731
|
+
| `@utilix-tech/sdk/hashing` | MD5, SHA-1/256/512, bcrypt, HMAC, .htpasswd, SRI hashes, content-hash ETag generation |
|
|
707
732
|
| `@utilix-tech/sdk/text` | Case convert, slugify, word count, lorem ipsum, line ops, HTML→Markdown, Levenshtein/Damerau string distance, readability scoring, sentence length distribution, emoji lookup |
|
|
708
733
|
| `@utilix-tech/sdk/data` | CSV, YAML, TOML, XML, INI, NDJSON parse and convert, SRT/WebVTT subtitles, Markdown front matter parser |
|
|
709
734
|
| `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, entropy estimator, fake data |
|
|
710
735
|
| `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
|
|
711
|
-
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, EMI calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator, Rule of 72 calculator, savings goal calculator, ROI calculator |
|
|
736
|
+
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, EMI calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator, Rule of 72 calculator, savings goal calculator, ROI calculator, cash denomination/till calculator |
|
|
712
737
|
| `@utilix-tech/sdk/network` | HTTP status codes, IP validation, country flags, geolocation URLs, HAR file parsing, User-Agent parsing, IPv6 address compress/expand |
|
|
713
738
|
| `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header |
|
|
714
739
|
| `@utilix-tech/sdk/code` | SQL format/minify, HTML format/minify, regex tester, GraphQL format, MIME type lookup, GitHub URL parsing, regex ReDoS detector, Conventional Commits linter |
|
|
@@ -14,12 +14,15 @@ __export(units_exports, {
|
|
|
14
14
|
COMMON_TAX_RATES: () => COMMON_TAX_RATES,
|
|
15
15
|
COMMON_TIP_PERCENTAGES: () => COMMON_TIP_PERCENTAGES,
|
|
16
16
|
COMMON_VIEWPORTS: () => COMMON_VIEWPORTS,
|
|
17
|
+
USD_DENOMINATIONS: () => USD_DENOMINATIONS,
|
|
17
18
|
applySuccessiveDiscounts: () => applySuccessiveDiscounts,
|
|
18
19
|
autoFormat: () => autoFormat,
|
|
19
20
|
bumpVersion: () => bumpVersion,
|
|
20
21
|
calcAspectRatio: () => calcAspectRatio,
|
|
21
22
|
calcSubnet: () => calcSubnet,
|
|
22
23
|
calculateBreakEven: () => calculateBreakEven,
|
|
24
|
+
calculateCashBreakdown: () => calculateCashBreakdown,
|
|
25
|
+
calculateChange: () => calculateChange,
|
|
23
26
|
calculateCompoundInterest: () => calculateCompoundInterest,
|
|
24
27
|
calculateDiscount: () => calculateDiscount,
|
|
25
28
|
calculateEmi: () => calculateEmi,
|
|
@@ -1867,5 +1870,63 @@ function compareUnitPrices(items) {
|
|
|
1867
1870
|
const bestLabel = results.find((r) => r.isBest).label;
|
|
1868
1871
|
return { items: results, bestLabel };
|
|
1869
1872
|
}
|
|
1873
|
+
var USD_DENOMINATIONS = [100, 50, 20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01];
|
|
1874
|
+
function _cashToCents(n) {
|
|
1875
|
+
return Math.round(n * 100);
|
|
1876
|
+
}
|
|
1877
|
+
function calculateCashBreakdown(amount, denominations) {
|
|
1878
|
+
if (!Number.isFinite(amount) || amount < 0) return { error: "Amount must be zero or a positive number" };
|
|
1879
|
+
if (!Array.isArray(denominations) || denominations.length === 0) {
|
|
1880
|
+
return { error: "At least one denomination is required" };
|
|
1881
|
+
}
|
|
1882
|
+
for (const d of denominations) {
|
|
1883
|
+
if (typeof d !== "number" || !Number.isFinite(d) || d <= 0) {
|
|
1884
|
+
return { error: "Each denomination must be a positive number" };
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
const round22 = (n) => Math.round(n * 100) / 100;
|
|
1888
|
+
const sorted = Array.from(new Set(denominations)).sort((a, b) => b - a);
|
|
1889
|
+
let remainingCents = _cashToCents(amount);
|
|
1890
|
+
const breakdown = [];
|
|
1891
|
+
let totalCount = 0;
|
|
1892
|
+
for (const d of sorted) {
|
|
1893
|
+
const dCents = _cashToCents(d);
|
|
1894
|
+
const count = Math.floor(remainingCents / dCents);
|
|
1895
|
+
if (count > 0) {
|
|
1896
|
+
breakdown.push({ denomination: d, count, subtotal: round22(count * d) });
|
|
1897
|
+
remainingCents -= count * dCents;
|
|
1898
|
+
totalCount += count;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return {
|
|
1902
|
+
amount: round22(amount),
|
|
1903
|
+
denominations: sorted,
|
|
1904
|
+
breakdown,
|
|
1905
|
+
totalCount,
|
|
1906
|
+
remainder: round22(remainingCents / 100)
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
function calculateChange(amountDue, amountTendered, denominations) {
|
|
1910
|
+
if (!Number.isFinite(amountDue) || amountDue < 0) return { error: "Amount due must be zero or a positive number" };
|
|
1911
|
+
if (!Number.isFinite(amountTendered) || amountTendered < 0) {
|
|
1912
|
+
return { error: "Amount tendered must be zero or a positive number" };
|
|
1913
|
+
}
|
|
1914
|
+
if (_cashToCents(amountTendered) < _cashToCents(amountDue)) {
|
|
1915
|
+
return { error: "Amount tendered must be greater than or equal to the amount due" };
|
|
1916
|
+
}
|
|
1917
|
+
const round22 = (n) => Math.round(n * 100) / 100;
|
|
1918
|
+
const changeDue = round22(amountTendered - amountDue);
|
|
1919
|
+
const breakdown = calculateCashBreakdown(changeDue, denominations);
|
|
1920
|
+
if ("error" in breakdown) return breakdown;
|
|
1921
|
+
return {
|
|
1922
|
+
amountDue: round22(amountDue),
|
|
1923
|
+
amountTendered: round22(amountTendered),
|
|
1924
|
+
changeDue,
|
|
1925
|
+
denominations: breakdown.denominations,
|
|
1926
|
+
breakdown: breakdown.breakdown,
|
|
1927
|
+
totalCount: breakdown.totalCount,
|
|
1928
|
+
remainder: breakdown.remainder
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1870
1931
|
|
|
1871
|
-
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBreakEven, calculateCompoundInterest, calculateDiscount, calculateEmi, calculateLoan, calculateMarginMarkup, calculatePercentageChange, calculateRoi, calculateRuleOf72, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, viewportToPx };
|
|
1932
|
+
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, USD_DENOMINATIONS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBreakEven, calculateCashBreakdown, calculateChange, calculateCompoundInterest, calculateDiscount, calculateEmi, calculateLoan, calculateMarginMarkup, calculatePercentageChange, calculateRoi, calculateRuleOf72, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, viewportToPx };
|
|
@@ -14,6 +14,7 @@ __export(network_exports, {
|
|
|
14
14
|
buildGeoUrl: () => buildGeoUrl,
|
|
15
15
|
buildLinkHeader: () => buildLinkHeader,
|
|
16
16
|
buildMapUrl: () => buildMapUrl,
|
|
17
|
+
buildRangeHeader: () => buildRangeHeader,
|
|
17
18
|
buildReferrerPolicy: () => buildReferrerPolicy,
|
|
18
19
|
checkSetCookieHeader: () => checkSetCookieHeader,
|
|
19
20
|
countryCodeToFlag: () => countryCodeToFlag,
|
|
@@ -30,6 +31,7 @@ __export(network_exports, {
|
|
|
30
31
|
isValidIp: () => isValidIp,
|
|
31
32
|
isValidIpv4: () => isValidIpv4,
|
|
32
33
|
isValidIpv6: () => isValidIpv6,
|
|
34
|
+
parseContentRangeHeader: () => parseContentRangeHeader,
|
|
33
35
|
parseDnsResponse: () => parseDnsResponse,
|
|
34
36
|
parseGeoResponse: () => parseGeoResponse,
|
|
35
37
|
parseHar: () => parseHar,
|
|
@@ -2209,5 +2211,118 @@ function analyzeEmailHeaders(raw) {
|
|
|
2209
2211
|
warnings
|
|
2210
2212
|
};
|
|
2211
2213
|
}
|
|
2214
|
+
function _round2(n) {
|
|
2215
|
+
return Math.round(n * 100) / 100;
|
|
2216
|
+
}
|
|
2217
|
+
function buildRangeHeader(entries, options) {
|
|
2218
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
2219
|
+
return { error: "At least one range is required" };
|
|
2220
|
+
}
|
|
2221
|
+
const unit = options?.unit?.trim() || "bytes";
|
|
2222
|
+
const totalSize = typeof options?.totalSize === "number" && Number.isFinite(options.totalSize) ? options.totalSize : void 0;
|
|
2223
|
+
if (totalSize !== void 0 && totalSize <= 0) {
|
|
2224
|
+
return { error: "totalSize must be a positive number when provided" };
|
|
2225
|
+
}
|
|
2226
|
+
const parsed = [];
|
|
2227
|
+
for (const entry of entries) {
|
|
2228
|
+
if (entry.suffixLength !== void 0) {
|
|
2229
|
+
if (!Number.isFinite(entry.suffixLength) || entry.suffixLength <= 0) {
|
|
2230
|
+
return { error: "suffixLength must be a positive number" };
|
|
2231
|
+
}
|
|
2232
|
+
const suffixLength = Math.floor(entry.suffixLength);
|
|
2233
|
+
const byteLength = totalSize !== void 0 ? Math.min(suffixLength, totalSize) : suffixLength;
|
|
2234
|
+
parsed.push({
|
|
2235
|
+
spec: `-${suffixLength}`,
|
|
2236
|
+
start: null,
|
|
2237
|
+
end: null,
|
|
2238
|
+
suffixLength,
|
|
2239
|
+
byteLength,
|
|
2240
|
+
percentOfTotal: totalSize !== void 0 ? _round2(byteLength / totalSize * 100) : null
|
|
2241
|
+
});
|
|
2242
|
+
continue;
|
|
2243
|
+
}
|
|
2244
|
+
if (entry.start === void 0) {
|
|
2245
|
+
return { error: "Each range requires either a start byte or a suffixLength" };
|
|
2246
|
+
}
|
|
2247
|
+
if (!Number.isFinite(entry.start) || entry.start < 0) {
|
|
2248
|
+
return { error: "start must be a non-negative number" };
|
|
2249
|
+
}
|
|
2250
|
+
const start = Math.floor(entry.start);
|
|
2251
|
+
if (totalSize !== void 0 && start >= totalSize) {
|
|
2252
|
+
return { error: `start byte ${start} is beyond totalSize (${totalSize})` };
|
|
2253
|
+
}
|
|
2254
|
+
if (entry.end !== void 0) {
|
|
2255
|
+
if (!Number.isFinite(entry.end) || entry.end < 0) {
|
|
2256
|
+
return { error: "end must be a non-negative number" };
|
|
2257
|
+
}
|
|
2258
|
+
const end = Math.floor(entry.end);
|
|
2259
|
+
if (end < start) {
|
|
2260
|
+
return { error: `range end (${end}) must be >= start (${start})` };
|
|
2261
|
+
}
|
|
2262
|
+
if (totalSize !== void 0 && end >= totalSize) {
|
|
2263
|
+
return { error: `end byte ${end} is beyond totalSize (${totalSize})` };
|
|
2264
|
+
}
|
|
2265
|
+
const byteLength = end - start + 1;
|
|
2266
|
+
parsed.push({
|
|
2267
|
+
spec: `${start}-${end}`,
|
|
2268
|
+
start,
|
|
2269
|
+
end,
|
|
2270
|
+
suffixLength: null,
|
|
2271
|
+
byteLength,
|
|
2272
|
+
percentOfTotal: totalSize !== void 0 ? _round2(byteLength / totalSize * 100) : null
|
|
2273
|
+
});
|
|
2274
|
+
} else {
|
|
2275
|
+
const byteLength = totalSize !== void 0 ? totalSize - start : null;
|
|
2276
|
+
parsed.push({
|
|
2277
|
+
spec: `${start}-`,
|
|
2278
|
+
start,
|
|
2279
|
+
end: null,
|
|
2280
|
+
suffixLength: null,
|
|
2281
|
+
byteLength,
|
|
2282
|
+
percentOfTotal: byteLength !== null && totalSize !== void 0 ? _round2(byteLength / totalSize * 100) : null
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
const warnings = [];
|
|
2287
|
+
const resolvable = parsed.map((r, i) => ({ i, start: r.start ?? (totalSize !== void 0 && r.suffixLength !== null ? totalSize - Math.min(r.suffixLength, totalSize) : null), end: r.end ?? (totalSize !== void 0 ? totalSize - 1 : null) })).filter((r) => r.start !== null && r.end !== null).sort((a, b) => a.start - b.start);
|
|
2288
|
+
for (let i = 1; i < resolvable.length; i++) {
|
|
2289
|
+
if (resolvable[i].start <= resolvable[i - 1].end) {
|
|
2290
|
+
warnings.push(`ranges "${parsed[resolvable[i - 1].i].spec}" and "${parsed[resolvable[i].i].spec}" overlap - servers may reject overlapping multi-range requests`);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
if (parsed.length > 1 && parsed.some((r) => r.byteLength === null)) {
|
|
2294
|
+
warnings.push("totalSize was not provided, so open-ended ranges cannot be checked for overlap or total byte count");
|
|
2295
|
+
}
|
|
2296
|
+
const totalBytesRequested = parsed.every((r) => r.byteLength !== null) ? parsed.reduce((sum, r) => sum + (r.byteLength ?? 0), 0) : null;
|
|
2297
|
+
const headerValue = `${unit}=${parsed.map((r) => r.spec).join(",")}`;
|
|
2298
|
+
return {
|
|
2299
|
+
header: `Range: ${headerValue}`,
|
|
2300
|
+
headerValue,
|
|
2301
|
+
ranges: parsed,
|
|
2302
|
+
totalBytesRequested,
|
|
2303
|
+
warnings
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
function parseContentRangeHeader(header) {
|
|
2307
|
+
if (!header || header.trim() === "") return { error: "Input is empty" };
|
|
2308
|
+
const match = header.trim().match(/^(\S+)\s+(\*|\d+-\d+)\/(\*|\d+)$/);
|
|
2309
|
+
if (!match) {
|
|
2310
|
+
return { error: 'Could not parse Content-Range header. Expected a format like "bytes 200-1000/67589" or "bytes */67589"' };
|
|
2311
|
+
}
|
|
2312
|
+
const [, unit, range, totalRaw] = match;
|
|
2313
|
+
const isUnsatisfiable = range === "*";
|
|
2314
|
+
const isTotalUnknown = totalRaw === "*";
|
|
2315
|
+
const total = isTotalUnknown ? null : Number(totalRaw);
|
|
2316
|
+
if (isUnsatisfiable) {
|
|
2317
|
+
return { unit, start: null, end: null, total, isUnsatisfiable, isTotalUnknown, byteLength: null };
|
|
2318
|
+
}
|
|
2319
|
+
const [startRaw, endRaw] = range.split("-");
|
|
2320
|
+
const start = Number(startRaw);
|
|
2321
|
+
const end = Number(endRaw);
|
|
2322
|
+
if (end < start) {
|
|
2323
|
+
return { error: `Invalid range: end (${end}) is before start (${start})` };
|
|
2324
|
+
}
|
|
2325
|
+
return { unit, start, end, total, isUnsatisfiable, isTotalUnknown, byteLength: end - start + 1 };
|
|
2326
|
+
}
|
|
2212
2327
|
|
|
2213
|
-
export { DNS_RECORD_TYPES, HTTP_HEADERS, LINK_REL_VALUES, REFERRER_POLICY_VALUES, STATUS_CODES, analyzeEmailHeaders, buildCacheControl, buildDnsQueryUrl, buildGeoUrl, buildLinkHeader, buildMapUrl, buildReferrerPolicy, checkSetCookieHeader, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseDnsResponse, parseGeoResponse, parseHar, parseIpv6Address, parseUserAgent, searchHeaders, searchStatusCodes, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateRobotsTxt, validateSitemap };
|
|
2328
|
+
export { DNS_RECORD_TYPES, HTTP_HEADERS, LINK_REL_VALUES, REFERRER_POLICY_VALUES, STATUS_CODES, analyzeEmailHeaders, buildCacheControl, buildDnsQueryUrl, buildGeoUrl, buildLinkHeader, buildMapUrl, buildRangeHeader, buildReferrerPolicy, checkSetCookieHeader, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseContentRangeHeader, parseDnsResponse, parseGeoResponse, parseHar, parseIpv6Address, parseUserAgent, searchHeaders, searchStatusCodes, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateRobotsTxt, validateSitemap };
|
|
@@ -13,6 +13,7 @@ __export(data_exports, {
|
|
|
13
13
|
diffEnvExample: () => diffEnvExample,
|
|
14
14
|
envToExport: () => envToExport,
|
|
15
15
|
envToJson: () => envToJson,
|
|
16
|
+
findCsvDuplicateRows: () => findCsvDuplicateRows,
|
|
16
17
|
formatNdjson: () => formatNdjson,
|
|
17
18
|
formatSubtitleTimestamp: () => formatSubtitleTimestamp,
|
|
18
19
|
formatToml: () => formatToml,
|
|
@@ -1500,5 +1501,64 @@ function csvToSqlInsert(input, options) {
|
|
|
1500
1501
|
columns
|
|
1501
1502
|
};
|
|
1502
1503
|
}
|
|
1504
|
+
function _normalizeDuplicateValue(value, caseSensitive, trimWhitespace) {
|
|
1505
|
+
let v = value;
|
|
1506
|
+
if (trimWhitespace) v = v.trim();
|
|
1507
|
+
if (!caseSensitive) v = v.toLowerCase();
|
|
1508
|
+
return v;
|
|
1509
|
+
}
|
|
1510
|
+
function findCsvDuplicateRows(input, options) {
|
|
1511
|
+
const parsed = parseCsv(input, {
|
|
1512
|
+
delimiter: options?.delimiter ?? ",",
|
|
1513
|
+
hasHeader: options?.hasHeader !== false,
|
|
1514
|
+
trimWhitespace: false
|
|
1515
|
+
});
|
|
1516
|
+
if ("error" in parsed) return parsed;
|
|
1517
|
+
if (parsed.rows.length === 0) return { error: "No data rows found" };
|
|
1518
|
+
const caseSensitive = options?.caseSensitive !== false;
|
|
1519
|
+
const trimWhitespace = options?.trimWhitespace !== false;
|
|
1520
|
+
let keyIndexes;
|
|
1521
|
+
if (options?.keyColumns && options.keyColumns.length > 0) {
|
|
1522
|
+
keyIndexes = [];
|
|
1523
|
+
for (const col of options.keyColumns) {
|
|
1524
|
+
const idx = parsed.headers.indexOf(col);
|
|
1525
|
+
if (idx === -1) {
|
|
1526
|
+
return { error: `Column "${col}" not found. Available columns: ${parsed.headers.join(", ")}` };
|
|
1527
|
+
}
|
|
1528
|
+
keyIndexes.push(idx);
|
|
1529
|
+
}
|
|
1530
|
+
} else {
|
|
1531
|
+
keyIndexes = parsed.headers.map((_, idx) => idx);
|
|
1532
|
+
}
|
|
1533
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1534
|
+
parsed.rows.forEach((row, i) => {
|
|
1535
|
+
const key = keyIndexes.map((idx) => row[idx] ?? "");
|
|
1536
|
+
const normalizedKey = keyIndexes.map((idx) => _normalizeDuplicateValue(row[idx] ?? "", caseSensitive, trimWhitespace));
|
|
1537
|
+
const mapKey = JSON.stringify(normalizedKey);
|
|
1538
|
+
const rowNumber = i + 1;
|
|
1539
|
+
const existing = groups.get(mapKey);
|
|
1540
|
+
if (existing) {
|
|
1541
|
+
existing.rowNumbers.push(rowNumber);
|
|
1542
|
+
} else {
|
|
1543
|
+
groups.set(mapKey, { key, rowNumbers: [rowNumber] });
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
const duplicateGroups = [];
|
|
1547
|
+
let duplicateRowCount = 0;
|
|
1548
|
+
for (const group of groups.values()) {
|
|
1549
|
+
if (group.rowNumbers.length > 1) {
|
|
1550
|
+
duplicateGroups.push({ key: group.key, rowNumbers: group.rowNumbers, count: group.rowNumbers.length });
|
|
1551
|
+
duplicateRowCount += group.rowNumbers.length;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
duplicateGroups.sort((a, b) => b.count - a.count);
|
|
1555
|
+
return {
|
|
1556
|
+
headers: parsed.headers,
|
|
1557
|
+
totalRows: parsed.rows.length,
|
|
1558
|
+
duplicateGroups,
|
|
1559
|
+
duplicateRowCount,
|
|
1560
|
+
uniqueRowCount: parsed.rows.length - duplicateRowCount + duplicateGroups.length
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1503
1563
|
|
|
1504
|
-
export { convertSubtitles, csvToJson, csvToSqlInsert, data_exports, detectDelimiter, detectSubtitleFormat, diffEnvExample, envToExport, envToJson, formatNdjson, formatSubtitleTimestamp, formatToml, formatXml, generateMarkdownToc, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseFrontMatter, parseIni, parseSubtitleTimestamp, parseSubtitles, parseXmlToObject, serializeSubtitles, shiftSubtitles, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson };
|
|
1564
|
+
export { convertSubtitles, csvToJson, csvToSqlInsert, data_exports, detectDelimiter, detectSubtitleFormat, diffEnvExample, envToExport, envToJson, findCsvDuplicateRows, formatNdjson, formatSubtitleTimestamp, formatToml, formatXml, generateMarkdownToc, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseFrontMatter, parseIni, parseSubtitleTimestamp, parseSubtitles, parseXmlToObject, serializeSubtitles, shiftSubtitles, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson };
|
|
@@ -6,12 +6,14 @@ import bcrypt from 'bcryptjs';
|
|
|
6
6
|
// src/tools/hashing.ts
|
|
7
7
|
var hashing_exports = {};
|
|
8
8
|
__export(hashing_exports, {
|
|
9
|
+
ETAG_ALGORITHMS: () => ETAG_ALGORITHMS,
|
|
9
10
|
HASH_ALGORITHMS: () => HASH_ALGORITHMS,
|
|
10
11
|
HASH_BITS: () => HASH_BITS,
|
|
11
12
|
ROUND_OPTIONS: () => ROUND_OPTIONS,
|
|
12
13
|
SRI_ALGORITHMS: () => SRI_ALGORITHMS,
|
|
13
14
|
createEntry: () => createEntry,
|
|
14
15
|
generateBcryptHash: () => generateBcryptHash,
|
|
16
|
+
generateEtag: () => generateEtag,
|
|
15
17
|
generateHtpasswdFile: () => generateHtpasswdFile,
|
|
16
18
|
generateMd5Hash: () => generateMd5Hash,
|
|
17
19
|
generatePlain: () => generatePlain,
|
|
@@ -272,5 +274,23 @@ function generateSriAll(content) {
|
|
|
272
274
|
}
|
|
273
275
|
return SRI_ALGORITHMS.map((alg) => generateSri(content, alg));
|
|
274
276
|
}
|
|
277
|
+
var ETAG_ALGORITHMS = ["MD5", "SHA-1", "SHA-256"];
|
|
278
|
+
var ETAG_NODE_ALG_MAP = {
|
|
279
|
+
"MD5": "md5",
|
|
280
|
+
"SHA-1": "sha1",
|
|
281
|
+
"SHA-256": "sha256"
|
|
282
|
+
};
|
|
283
|
+
function generateEtag(content, algorithm = "SHA-1", weak = false) {
|
|
284
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
285
|
+
return { error: "Content is required" };
|
|
286
|
+
}
|
|
287
|
+
if (!ETAG_ALGORITHMS.includes(algorithm)) {
|
|
288
|
+
return { error: `Unknown algorithm: ${algorithm}. Must be one of ${ETAG_ALGORITHMS.join(", ")}` };
|
|
289
|
+
}
|
|
290
|
+
const hash = createHash(ETAG_NODE_ALG_MAP[algorithm]).update(content, "utf8").digest("hex");
|
|
291
|
+
const quoted = `"${hash}"`;
|
|
292
|
+
const etag = weak ? `W/${quoted}` : quoted;
|
|
293
|
+
return { algorithm, weak, hash, etag, headerName: "ETag", snippet: `ETag: ${etag}` };
|
|
294
|
+
}
|
|
275
295
|
|
|
276
|
-
export { HASH_ALGORITHMS, HASH_BITS, ROUND_OPTIONS, SRI_ALGORITHMS, createEntry, generateBcryptHash, generateHtpasswdFile, generateMd5Hash, generatePlain, generateSha1Hash, generateSri, generateSriAll, hashAll, hashOne, hashPassword, hashWithTiming, hashing_exports, parseHtpasswdFile, validateRounds, validateUsername, verifyPassword };
|
|
296
|
+
export { ETAG_ALGORITHMS, HASH_ALGORITHMS, HASH_BITS, ROUND_OPTIONS, SRI_ALGORITHMS, createEntry, generateBcryptHash, generateEtag, generateHtpasswdFile, generateMd5Hash, generatePlain, generateSha1Hash, generateSri, generateSriAll, hashAll, hashOne, hashPassword, hashWithTiming, hashing_exports, parseHtpasswdFile, validateRounds, validateUsername, verifyPassword };
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - markdown-front-matter-parser: parseFrontMatter
|
|
17
17
|
* - markdown-toc-generator: generateMarkdownToc
|
|
18
18
|
* - csv-to-sql-insert-generator: csvToSqlInsert
|
|
19
|
+
* - csv-duplicate-row-finder: findCsvDuplicateRows
|
|
19
20
|
*/
|
|
20
21
|
interface ValidationResult {
|
|
21
22
|
valid: boolean;
|
|
@@ -343,8 +344,39 @@ interface CsvToSqlResult {
|
|
|
343
344
|
declare function csvToSqlInsert(input: string, options?: CsvToSqlOptions): CsvToSqlResult | {
|
|
344
345
|
error: string;
|
|
345
346
|
};
|
|
347
|
+
interface CsvDuplicateOptions {
|
|
348
|
+
delimiter?: string;
|
|
349
|
+
hasHeader?: boolean;
|
|
350
|
+
keyColumns?: string[];
|
|
351
|
+
caseSensitive?: boolean;
|
|
352
|
+
trimWhitespace?: boolean;
|
|
353
|
+
}
|
|
354
|
+
interface CsvDuplicateGroup {
|
|
355
|
+
key: string[];
|
|
356
|
+
rowNumbers: number[];
|
|
357
|
+
count: number;
|
|
358
|
+
}
|
|
359
|
+
interface CsvDuplicateResult {
|
|
360
|
+
headers: string[];
|
|
361
|
+
totalRows: number;
|
|
362
|
+
duplicateGroups: CsvDuplicateGroup[];
|
|
363
|
+
duplicateRowCount: number;
|
|
364
|
+
uniqueRowCount: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Parse CSV/TSV data and group rows that share the same value across a
|
|
368
|
+
* chosen set of key columns (or the entire row, if no key columns are
|
|
369
|
+
* given), so you can spot and clean up accidental duplicates before an
|
|
370
|
+
* import.
|
|
371
|
+
*/
|
|
372
|
+
declare function findCsvDuplicateRows(input: string, options?: CsvDuplicateOptions): CsvDuplicateResult | {
|
|
373
|
+
error: string;
|
|
374
|
+
};
|
|
346
375
|
|
|
347
376
|
type data_ConvertedSubtitles = ConvertedSubtitles;
|
|
377
|
+
type data_CsvDuplicateGroup = CsvDuplicateGroup;
|
|
378
|
+
type data_CsvDuplicateOptions = CsvDuplicateOptions;
|
|
379
|
+
type data_CsvDuplicateResult = CsvDuplicateResult;
|
|
348
380
|
type data_CsvParseResult = CsvParseResult;
|
|
349
381
|
type data_CsvToSqlOptions = CsvToSqlOptions;
|
|
350
382
|
type data_CsvToSqlResult = CsvToSqlResult;
|
|
@@ -377,6 +409,7 @@ declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
|
|
|
377
409
|
declare const data_diffEnvExample: typeof diffEnvExample;
|
|
378
410
|
declare const data_envToExport: typeof envToExport;
|
|
379
411
|
declare const data_envToJson: typeof envToJson;
|
|
412
|
+
declare const data_findCsvDuplicateRows: typeof findCsvDuplicateRows;
|
|
380
413
|
declare const data_formatNdjson: typeof formatNdjson;
|
|
381
414
|
declare const data_formatSubtitleTimestamp: typeof formatSubtitleTimestamp;
|
|
382
415
|
declare const data_formatToml: typeof formatToml;
|
|
@@ -416,7 +449,7 @@ declare const data_validateXml: typeof validateXml;
|
|
|
416
449
|
declare const data_validateYaml: typeof validateYaml;
|
|
417
450
|
declare const data_xmlToJson: typeof xmlToJson;
|
|
418
451
|
declare namespace data {
|
|
419
|
-
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvParseResult as CsvParseResult, type data_CsvToSqlOptions as CsvToSqlOptions, type data_CsvToSqlResult as CsvToSqlResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_FrontMatterError as FrontMatterError, type data_FrontMatterResult as FrontMatterResult, type data_IniSection as IniSection, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_generateMarkdownToc as generateMarkdownToc, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseFrontMatter as parseFrontMatter, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
452
|
+
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvDuplicateGroup as CsvDuplicateGroup, type data_CsvDuplicateOptions as CsvDuplicateOptions, type data_CsvDuplicateResult as CsvDuplicateResult, type data_CsvParseResult as CsvParseResult, type data_CsvToSqlOptions as CsvToSqlOptions, type data_CsvToSqlResult as CsvToSqlResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_FrontMatterError as FrontMatterError, type data_FrontMatterResult as FrontMatterResult, type data_IniSection as IniSection, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_findCsvDuplicateRows as findCsvDuplicateRows, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_generateMarkdownToc as generateMarkdownToc, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseFrontMatter as parseFrontMatter, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
420
453
|
}
|
|
421
454
|
|
|
422
|
-
export {
|
|
455
|
+
export { minifyNdjson as $, findCsvDuplicateRows as A, formatNdjson as B, type ConvertedSubtitles as C, formatSubtitleTimestamp as D, type EnvDiffError as E, type FrontMatterError as F, formatToml as G, formatXml as H, type IniSection as I, generateMarkdownToc as J, getSection as K, getSectionNames as L, type MarkdownTocEntry as M, type NdjsonLineResult as N, getTomlStats as O, type ParseOptions as P, getXmlStats as Q, getYamlStats as R, type SubtitleCue as S, type TomlFormatResult as T, iniToJson as U, type ValidationResult as V, jsonArrayToNdjson as W, type XmlFormatResult as X, jsonToIni as Y, jsonToToml as Z, jsonToXml as _, type CsvDuplicateGroup as a, minifyToml as a0, minifyXml as a1, ndjsonToJsonArray as a2, parseCsv as a3, parseEnv as a4, parseFrontMatter as a5, parseIni as a6, parseSubtitleTimestamp as a7, parseSubtitles as a8, parseXmlToObject as a9, serializeSubtitles as aa, shiftSubtitles as ab, stringifyIni as ac, toCsvString as ad, tomlToJson as ae, validateEnvKey as af, validateIni as ag, validateNdjson as ah, validateToml as ai, validateXml as aj, validateYaml as ak, xmlToJson as al, type CsvDuplicateOptions as b, type CsvDuplicateResult as c, data as d, type CsvParseResult as e, type CsvToSqlOptions as f, type CsvToSqlResult as g, type EnvDiffResult as h, type EnvEntry as i, type EnvParseResult as j, type FrontMatterResult as k, type MarkdownTocOptions as l, type MarkdownTocResult as m, type NdjsonValidateResult as n, type ParsedIni as o, type ParsedSubtitles as p, type SubtitleFormat as q, type XmlToJsonOptions as r, convertSubtitles as s, csvToJson as t, csvToSqlInsert as u, detectDelimiter as v, detectSubtitleFormat as w, diffEnvExample as x, envToExport as y, envToJson as z };
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - markdown-front-matter-parser: parseFrontMatter
|
|
17
17
|
* - markdown-toc-generator: generateMarkdownToc
|
|
18
18
|
* - csv-to-sql-insert-generator: csvToSqlInsert
|
|
19
|
+
* - csv-duplicate-row-finder: findCsvDuplicateRows
|
|
19
20
|
*/
|
|
20
21
|
interface ValidationResult {
|
|
21
22
|
valid: boolean;
|
|
@@ -343,8 +344,39 @@ interface CsvToSqlResult {
|
|
|
343
344
|
declare function csvToSqlInsert(input: string, options?: CsvToSqlOptions): CsvToSqlResult | {
|
|
344
345
|
error: string;
|
|
345
346
|
};
|
|
347
|
+
interface CsvDuplicateOptions {
|
|
348
|
+
delimiter?: string;
|
|
349
|
+
hasHeader?: boolean;
|
|
350
|
+
keyColumns?: string[];
|
|
351
|
+
caseSensitive?: boolean;
|
|
352
|
+
trimWhitespace?: boolean;
|
|
353
|
+
}
|
|
354
|
+
interface CsvDuplicateGroup {
|
|
355
|
+
key: string[];
|
|
356
|
+
rowNumbers: number[];
|
|
357
|
+
count: number;
|
|
358
|
+
}
|
|
359
|
+
interface CsvDuplicateResult {
|
|
360
|
+
headers: string[];
|
|
361
|
+
totalRows: number;
|
|
362
|
+
duplicateGroups: CsvDuplicateGroup[];
|
|
363
|
+
duplicateRowCount: number;
|
|
364
|
+
uniqueRowCount: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Parse CSV/TSV data and group rows that share the same value across a
|
|
368
|
+
* chosen set of key columns (or the entire row, if no key columns are
|
|
369
|
+
* given), so you can spot and clean up accidental duplicates before an
|
|
370
|
+
* import.
|
|
371
|
+
*/
|
|
372
|
+
declare function findCsvDuplicateRows(input: string, options?: CsvDuplicateOptions): CsvDuplicateResult | {
|
|
373
|
+
error: string;
|
|
374
|
+
};
|
|
346
375
|
|
|
347
376
|
type data_ConvertedSubtitles = ConvertedSubtitles;
|
|
377
|
+
type data_CsvDuplicateGroup = CsvDuplicateGroup;
|
|
378
|
+
type data_CsvDuplicateOptions = CsvDuplicateOptions;
|
|
379
|
+
type data_CsvDuplicateResult = CsvDuplicateResult;
|
|
348
380
|
type data_CsvParseResult = CsvParseResult;
|
|
349
381
|
type data_CsvToSqlOptions = CsvToSqlOptions;
|
|
350
382
|
type data_CsvToSqlResult = CsvToSqlResult;
|
|
@@ -377,6 +409,7 @@ declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
|
|
|
377
409
|
declare const data_diffEnvExample: typeof diffEnvExample;
|
|
378
410
|
declare const data_envToExport: typeof envToExport;
|
|
379
411
|
declare const data_envToJson: typeof envToJson;
|
|
412
|
+
declare const data_findCsvDuplicateRows: typeof findCsvDuplicateRows;
|
|
380
413
|
declare const data_formatNdjson: typeof formatNdjson;
|
|
381
414
|
declare const data_formatSubtitleTimestamp: typeof formatSubtitleTimestamp;
|
|
382
415
|
declare const data_formatToml: typeof formatToml;
|
|
@@ -416,7 +449,7 @@ declare const data_validateXml: typeof validateXml;
|
|
|
416
449
|
declare const data_validateYaml: typeof validateYaml;
|
|
417
450
|
declare const data_xmlToJson: typeof xmlToJson;
|
|
418
451
|
declare namespace data {
|
|
419
|
-
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvParseResult as CsvParseResult, type data_CsvToSqlOptions as CsvToSqlOptions, type data_CsvToSqlResult as CsvToSqlResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_FrontMatterError as FrontMatterError, type data_FrontMatterResult as FrontMatterResult, type data_IniSection as IniSection, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_generateMarkdownToc as generateMarkdownToc, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseFrontMatter as parseFrontMatter, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
452
|
+
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvDuplicateGroup as CsvDuplicateGroup, type data_CsvDuplicateOptions as CsvDuplicateOptions, type data_CsvDuplicateResult as CsvDuplicateResult, type data_CsvParseResult as CsvParseResult, type data_CsvToSqlOptions as CsvToSqlOptions, type data_CsvToSqlResult as CsvToSqlResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_FrontMatterError as FrontMatterError, type data_FrontMatterResult as FrontMatterResult, type data_IniSection as IniSection, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_findCsvDuplicateRows as findCsvDuplicateRows, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_generateMarkdownToc as generateMarkdownToc, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseFrontMatter as parseFrontMatter, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
420
453
|
}
|
|
421
454
|
|
|
422
|
-
export {
|
|
455
|
+
export { minifyNdjson as $, findCsvDuplicateRows as A, formatNdjson as B, type ConvertedSubtitles as C, formatSubtitleTimestamp as D, type EnvDiffError as E, type FrontMatterError as F, formatToml as G, formatXml as H, type IniSection as I, generateMarkdownToc as J, getSection as K, getSectionNames as L, type MarkdownTocEntry as M, type NdjsonLineResult as N, getTomlStats as O, type ParseOptions as P, getXmlStats as Q, getYamlStats as R, type SubtitleCue as S, type TomlFormatResult as T, iniToJson as U, type ValidationResult as V, jsonArrayToNdjson as W, type XmlFormatResult as X, jsonToIni as Y, jsonToToml as Z, jsonToXml as _, type CsvDuplicateGroup as a, minifyToml as a0, minifyXml as a1, ndjsonToJsonArray as a2, parseCsv as a3, parseEnv as a4, parseFrontMatter as a5, parseIni as a6, parseSubtitleTimestamp as a7, parseSubtitles as a8, parseXmlToObject as a9, serializeSubtitles as aa, shiftSubtitles as ab, stringifyIni as ac, toCsvString as ad, tomlToJson as ae, validateEnvKey as af, validateIni as ag, validateNdjson as ah, validateToml as ai, validateXml as aj, validateYaml as ak, xmlToJson as al, type CsvDuplicateOptions as b, type CsvDuplicateResult as c, data as d, type CsvParseResult as e, type CsvToSqlOptions as f, type CsvToSqlResult as g, type EnvDiffResult as h, type EnvEntry as i, type EnvParseResult as j, type FrontMatterResult as k, type MarkdownTocOptions as l, type MarkdownTocResult as m, type NdjsonValidateResult as n, type ParsedIni as o, type ParsedSubtitles as p, type SubtitleFormat as q, type XmlToJsonOptions as r, convertSubtitles as s, csvToJson as t, csvToSqlInsert as u, detectDelimiter as v, detectSubtitleFormat as w, diffEnvExample as x, envToExport as y, envToJson as z };
|