@utilix-tech/sdk 0.57.0 → 0.60.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 CHANGED
@@ -2,6 +2,15 @@
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.60.0
6
+ - Add `buildRetryAfterHeader`, `parseRetryAfterHeader` (retry-after-builder): build an HTTP Retry-After response header from a delay in seconds or a target date, or parse an existing one, per RFC 9110.
7
+ - Add `calculateFreelancerRate` (freelancer-rate-calculator): solve for the minimum hourly rate a freelancer must charge to hit a target annual income, given billable hours, weeks worked, overhead, and a profit margin.
8
+
9
+ ## 0.59.0
10
+ - Add `buildPreferHeader`, `parsePreferHeader` (prefer-header): build an HTTP Prefer request-header value from preferences with optional values and parameters per RFC 7240, or parse an existing one into the same shape.
11
+ - Add `dedupeNdjson` (ndjson-dedup): remove duplicate records from NDJSON input, either by exact structural equality or by a chosen key field, keeping the first occurrence of each duplicate.
12
+ - Corrects a version-field drift: package.json was left at 0.57.0 after the prior release's changes actually shipped as 0.58.0 below.
13
+
5
14
  ## 0.57.0
6
15
  - Add `mergeJsonPatch`, `applyJsonMergePatch` (json-merge-patch): apply an RFC 7396 JSON Merge Patch document to a target JSON document, with null-valued patch keys removing the matching target key.
7
16
  - Add `generateCspNonce` (csp-nonce-generator): generate a cryptographically random, base64-encoded Content-Security-Policy nonce with ready-to-paste header directive and script/style tag snippets.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/sdk
2
2
 
3
- **659 developer utility functions for Node.js: runs locally, no API key required.**
3
+ **665 developer utility functions for Node.js: runs locally, no API key required.**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@utilix-tech/sdk?style=flat-square&color=cb3837)](https://www.npmjs.com/package/@utilix-tech/sdk)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@utilix-tech/sdk?style=flat-square)](https://www.npmjs.com/package/@utilix-tech/sdk)
@@ -210,7 +210,7 @@ import { parseCsv, csvToJson, validateYaml, resolveYamlAnchors, tomlToJson, json
210
210
  validateNdjson, formatNdjson, ndjsonToJsonArray, diffEnvExample,
211
211
  parseSubtitles, convertSubtitles, shiftSubtitles, analyzeCaptionSpeed, parseFrontMatter,
212
212
  generateMarkdownToc, csvToSqlInsert, findCsvDuplicateRows, calculateMarkdownTaskListProgress,
213
- inferCsvColumnTypes } from "@utilix-tech/sdk/data";
213
+ inferCsvColumnTypes, dedupeNdjson } from "@utilix-tech/sdk/data";
214
214
 
215
215
  // CSV
216
216
  const rows = parseCsv("name,age\nalice,30\nbob,25");
@@ -237,6 +237,10 @@ validateNdjson('{"id":1}\n{"id":2}'); // { valid: true, totalLines: 2, ...
237
237
  formatNdjson('{"id":1}'); // '{\n "id": 1\n}'
238
238
  ndjsonToJsonArray('{"id":1}\n{"id":2}'); // '[\n { "id": 1 },\n { "id": 2 }\n]'
239
239
 
240
+ // Remove duplicate NDJSON records, by exact match or by a key field
241
+ dedupeNdjson('{"id":1}\n{"id":2}\n{"id":1}');
242
+ // { totalLines: 3, uniqueLines: 2, duplicatesRemoved: 1, output: "{\"id\":1}\n{\"id\":2}", removedLines: [3] }
243
+
240
244
  // Diff a .env file against its .env.example template
241
245
  diffEnvExample("FOO=1\nBAR=2", "FOO=\nBAR=y\nBAZ=z");
242
246
  // { missingInExample: [], missingInEnv: ["BAZ"], emptyValueInExample: ["FOO"], keysInBoth: 2 }
@@ -339,7 +343,7 @@ formatIso8601Duration(90061); // { iso: "P1DT1H1M1S", ... }
339
343
  ### `/units`: Conversions
340
344
 
341
345
  ```ts
342
- import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue, calculatePercentageChange, convertAprApy, calculateMarginMarkup, calculateDiscount, applySuccessiveDiscounts, calculateBreakEven, calculateVideoBitrate, calculateRuleOf72, calculateEmi, calculateRoi, compareUnitPrices, calculatePaybackPeriod, calculateCdMaturity, calculateDepreciationSchedule, calculateCashRounding, calculateMovingAverages, calculateGpa, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateBlendedLoanRate } from "@utilix-tech/sdk/units";
346
+ import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue, calculatePercentageChange, convertAprApy, calculateMarginMarkup, calculateDiscount, applySuccessiveDiscounts, calculateBreakEven, calculateVideoBitrate, calculateRuleOf72, calculateEmi, calculateRoi, compareUnitPrices, calculatePaybackPeriod, calculateCdMaturity, calculateDepreciationSchedule, calculateCashRounding, calculateMovingAverages, calculateGpa, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateBlendedLoanRate, calculateFreelancerRate } from "@utilix-tech/sdk/units";
343
347
 
344
348
  // File sizes
345
349
  convertBytes(1073741824, "GB"); // 1
@@ -481,6 +485,10 @@ calculateCreditUtilization([{ label: "Visa", balance: 500, limit: 5000 }, { labe
481
485
  // Debt-to-income (DTI) ratio, plus a qualitative tier matching common mortgage-lending guidance
482
486
  calculateDebtToIncomeRatio([{ label: "Rent", amount: 1500 }, { label: "Car loan", amount: 400 }], 5000);
483
487
  // { dtiPercent: 38, tier: "moderate", totalMonthlyDebt: 1900, grossMonthlyIncome: 5000, debts: [...] }
488
+
489
+ // Minimum hourly rate to hit a target annual income, given billable hours, weeks worked, overhead, and margin
490
+ calculateFreelancerRate({ targetAnnualIncome: 80000, billableHoursPerWeek: 25, weeksPerYear: 48 });
491
+ // { hourlyRate: 66.67, dailyRate: 533.33, requiredAnnualRevenue: 80000, annualBillableHours: 1200, monthlyRevenueTarget: 6666.67 }
484
492
  ```
485
493
 
486
494
  ---
@@ -496,7 +504,9 @@ import { getStatusCode, isValidIp, isValidIpv4, isValidIpv6,
496
504
  buildPermissionsPolicy, parsePermissionsPolicy, buildClientHints, parseClientHintHeaders,
497
505
  buildContentDisposition, parseContentDisposition,
498
506
  buildXContentTypeOptionsHeader, parseXContentTypeOptionsHeader, assessContentTypeRisk,
499
- buildAcceptLanguage, parseAcceptLanguage } from "@utilix-tech/sdk/network";
507
+ buildAcceptLanguage, parseAcceptLanguage,
508
+ buildPreferHeader, parsePreferHeader,
509
+ buildRetryAfterHeader, parseRetryAfterHeader } from "@utilix-tech/sdk/network";
500
510
 
501
511
  getStatusCode(404);
502
512
  // { code: 404, text: "Not Found", description: "...", category: "Client Error" }
@@ -613,6 +623,22 @@ parseXContentTypeOptionsHeader("nosniff");
613
623
  // Assess how risky it is to serve a given Content-Type without X-Content-Type-Options: nosniff
614
624
  assessContentTypeRisk("text/plain");
615
625
  // { contentType: "text/plain", risk: "high", reason: "..." }
626
+
627
+ // Build a Prefer request header from preferences with optional values/params, per RFC 7240
628
+ buildPreferHeader([{ token: "respond-async" }, { token: "wait", value: "100" }]);
629
+ // { header: "Prefer: respond-async, wait=100", headerValue: "respond-async, wait=100", preferences: [...] }
630
+
631
+ // Parse an existing Prefer header value into the same shape
632
+ parsePreferHeader("return=minimal;handling=lenient");
633
+ // { header: "Prefer: return=minimal;handling=lenient", headerValue: "return=minimal;handling=lenient", preferences: [...] }
634
+
635
+ // Build a Retry-After response header from a delay in seconds or a target date, per RFC 9110
636
+ buildRetryAfterHeader({ seconds: 120 });
637
+ // { header: "Retry-After: 120", headerValue: "120", type: "seconds", seconds: 120 }
638
+
639
+ // Parse an existing Retry-After header value into the same shape
640
+ parseRetryAfterHeader("Retry-After: 120");
641
+ // { header: "Retry-After: 120", headerValue: "120", type: "seconds", seconds: 120 }
616
642
  ```
617
643
 
618
644
  ---
@@ -868,11 +894,11 @@ inspectGifFrames(new Uint8Array(gifBytes));
868
894
  | `@utilix-tech/sdk/encoding` | Base64, URL, HTML entities, Base32/Base58/Base62 encode/decode |
869
895
  | `@utilix-tech/sdk/hashing` | MD5, SHA-1/256/512, bcrypt, HMAC, .htpasswd, SRI hashes, content-hash ETag generation |
870
896
  | `@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 |
871
- | `@utilix-tech/sdk/data` | CSV, YAML, TOML, XML, INI, NDJSON parse and convert, SRT/WebVTT subtitles, Markdown front matter parser, YAML anchor/alias resolver, CSV column type inferencer |
897
+ | `@utilix-tech/sdk/data` | CSV, YAML, TOML, XML, INI, NDJSON parse and convert, NDJSON record deduplicator, SRT/WebVTT subtitles, Markdown front matter parser, YAML anchor/alias resolver, CSV column type inferencer |
872
898
  | `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, entropy estimator, fake data, CSP nonce generator |
873
899
  | `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
874
- | `@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, payback period calculator, CD maturity calculator, debt-to-income ratio calculator |
875
- | `@utilix-tech/sdk/network` | HTTP status codes, IP validation, country flags, geolocation URLs, HAR file parsing, User-Agent parsing, IPv6 address compress/expand, Permissions-Policy header builder, Client Hints header builder/parser, Content-Disposition header builder/parser, X-Content-Type-Options header builder/risk assessor, sitemap.xml validator, sitemap index splitter |
900
+ | `@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, payback period calculator, CD maturity calculator, debt-to-income ratio calculator, freelancer hourly rate calculator |
901
+ | `@utilix-tech/sdk/network` | HTTP status codes, IP validation, country flags, geolocation URLs, HAR file parsing, User-Agent parsing, IPv6 address compress/expand, Permissions-Policy header builder, Client Hints header builder/parser, Content-Disposition header builder/parser, X-Content-Type-Options header builder/risk assessor, sitemap.xml validator, sitemap index splitter, Prefer header builder/parser (RFC 7240), Retry-After header builder/parser (RFC 9110) |
876
902
  | `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header |
877
903
  | `@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, package.json exports field validator |
878
904
  | `@utilix-tech/sdk/color` | Hex/RGB/HSL/HSV/CMYK convert, palette generation, contrast check |
@@ -22,8 +22,10 @@ __export(network_exports, {
22
22
  buildLinkHeader: () => buildLinkHeader,
23
23
  buildMapUrl: () => buildMapUrl,
24
24
  buildPermissionsPolicy: () => buildPermissionsPolicy,
25
+ buildPreferHeader: () => buildPreferHeader,
25
26
  buildRangeHeader: () => buildRangeHeader,
26
27
  buildReferrerPolicy: () => buildReferrerPolicy,
28
+ buildRetryAfterHeader: () => buildRetryAfterHeader,
27
29
  buildVaryHeader: () => buildVaryHeader,
28
30
  buildXContentTypeOptionsHeader: () => buildXContentTypeOptionsHeader,
29
31
  checkSetCookieHeader: () => checkSetCookieHeader,
@@ -50,6 +52,8 @@ __export(network_exports, {
50
52
  parseHar: () => parseHar,
51
53
  parseIpv6Address: () => parseIpv6Address,
52
54
  parsePermissionsPolicy: () => parsePermissionsPolicy,
55
+ parsePreferHeader: () => parsePreferHeader,
56
+ parseRetryAfterHeader: () => parseRetryAfterHeader,
53
57
  parseUserAgent: () => parseUserAgent,
54
58
  parseVaryHeader: () => parseVaryHeader,
55
59
  parseXContentTypeOptionsHeader: () => parseXContentTypeOptionsHeader,
@@ -2886,5 +2890,134 @@ function parseAcceptLanguage(raw) {
2886
2890
  const headerValue = formatAcceptLanguageEntries(sorted);
2887
2891
  return { header: `Accept-Language: ${headerValue}`, headerValue, entries: sorted };
2888
2892
  }
2893
+ var PREFER_TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
2894
+ function isPreferToken(s) {
2895
+ return PREFER_TOKEN_RE.test(s);
2896
+ }
2897
+ function splitPreferTopLevel(input, delim) {
2898
+ const parts = [];
2899
+ let current = "";
2900
+ let inQuotes = false;
2901
+ for (const ch of input) {
2902
+ if (ch === '"') inQuotes = !inQuotes;
2903
+ if (ch === delim && !inQuotes) {
2904
+ parts.push(current);
2905
+ current = "";
2906
+ } else {
2907
+ current += ch;
2908
+ }
2909
+ }
2910
+ parts.push(current);
2911
+ return parts;
2912
+ }
2913
+ function parsePreferTokenValue(part) {
2914
+ const trimmed = part.trim();
2915
+ if (!trimmed) return null;
2916
+ const eq = trimmed.indexOf("=");
2917
+ if (eq === -1) return { token: trimmed };
2918
+ const token = trimmed.slice(0, eq).trim();
2919
+ let value = trimmed.slice(eq + 1).trim();
2920
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
2921
+ value = value.slice(1, -1);
2922
+ }
2923
+ return { token, value };
2924
+ }
2925
+ function formatPreferWord(value) {
2926
+ return isPreferToken(value) ? value : `"${value.replace(/"/g, '\\"')}"`;
2927
+ }
2928
+ function formatPreferPreferences(preferences) {
2929
+ return preferences.map((p) => {
2930
+ let s = p.token;
2931
+ if (p.value !== void 0) s += `=${formatPreferWord(p.value)}`;
2932
+ for (const param of p.params ?? []) {
2933
+ s += `;${param.name}`;
2934
+ if (param.value !== void 0) s += `=${formatPreferWord(param.value)}`;
2935
+ }
2936
+ return s;
2937
+ }).join(", ");
2938
+ }
2939
+ function buildPreferHeader(preferences) {
2940
+ if (!Array.isArray(preferences) || preferences.length === 0) {
2941
+ return { error: "At least one preference is required" };
2942
+ }
2943
+ const built = [];
2944
+ for (let i = 0; i < preferences.length; i++) {
2945
+ const { token, value, params } = preferences[i];
2946
+ if (typeof token !== "string" || !isPreferToken(token.trim())) {
2947
+ return { error: `preference ${i + 1}: token is required and must be a valid HTTP token` };
2948
+ }
2949
+ const cleanParams = [];
2950
+ if (params !== void 0) {
2951
+ if (!Array.isArray(params)) return { error: `preference ${i + 1}: params must be an array` };
2952
+ for (const param of params) {
2953
+ if (typeof param?.name !== "string" || !isPreferToken(param.name.trim())) {
2954
+ return { error: `preference ${i + 1}: parameter name is required and must be a valid HTTP token` };
2955
+ }
2956
+ cleanParams.push({ name: param.name.trim(), value: param.value !== void 0 ? String(param.value) : void 0 });
2957
+ }
2958
+ }
2959
+ built.push({
2960
+ token: token.trim(),
2961
+ value: value !== void 0 ? String(value) : void 0,
2962
+ params: cleanParams.length > 0 ? cleanParams : void 0
2963
+ });
2964
+ }
2965
+ const headerValue = formatPreferPreferences(built);
2966
+ return { header: `Prefer: ${headerValue}`, headerValue, preferences: built };
2967
+ }
2968
+ function parsePreferHeader(raw) {
2969
+ if (!raw || raw.trim() === "") return { error: "Input is empty" };
2970
+ const withoutLabel = raw.trim().replace(/^prefer:\s*/i, "");
2971
+ const parts = splitPreferTopLevel(withoutLabel, ",").map((p) => p.trim()).filter((p) => p.length > 0);
2972
+ if (parts.length === 0) return { error: "Input is empty" };
2973
+ const preferences = [];
2974
+ for (const part of parts) {
2975
+ const segments = splitPreferTopLevel(part, ";").map((s) => s.trim()).filter((s) => s.length > 0);
2976
+ if (segments.length === 0) return { error: `Invalid preference: "${part}"` };
2977
+ const main = parsePreferTokenValue(segments[0]);
2978
+ if (!main || !isPreferToken(main.token)) return { error: `Invalid preference token in "${part}"` };
2979
+ const params = [];
2980
+ for (const seg of segments.slice(1)) {
2981
+ const p = parsePreferTokenValue(seg);
2982
+ if (!p || !isPreferToken(p.token)) return { error: `Invalid parameter in "${part}"` };
2983
+ params.push({ name: p.token, value: p.value });
2984
+ }
2985
+ preferences.push({ token: main.token, value: main.value, params: params.length > 0 ? params : void 0 });
2986
+ }
2987
+ const headerValue = formatPreferPreferences(preferences);
2988
+ return { header: `Prefer: ${headerValue}`, headerValue, preferences };
2989
+ }
2990
+ function buildRetryAfterHeader(input) {
2991
+ const { seconds, date } = input;
2992
+ const hasSeconds = seconds !== void 0;
2993
+ const hasDate = date !== void 0 && date !== "";
2994
+ if (hasSeconds && hasDate) return { error: "Provide either seconds or date, not both" };
2995
+ if (!hasSeconds && !hasDate) return { error: "seconds or date is required" };
2996
+ if (hasSeconds) {
2997
+ if (typeof seconds !== "number" || !Number.isFinite(seconds) || !Number.isInteger(seconds) || seconds < 0) {
2998
+ return { error: "seconds must be a non-negative integer" };
2999
+ }
3000
+ return { header: `Retry-After: ${seconds}`, headerValue: String(seconds), type: "seconds", seconds };
3001
+ }
3002
+ const parsed = new Date(date);
3003
+ if (Number.isNaN(parsed.getTime())) return { error: "date must be a valid date/time string" };
3004
+ const httpDate = parsed.toUTCString();
3005
+ return { header: `Retry-After: ${httpDate}`, headerValue: httpDate, type: "http-date", httpDate, iso: parsed.toISOString() };
3006
+ }
3007
+ function parseRetryAfterHeader(raw) {
3008
+ if (!raw || raw.trim() === "") return { error: "Input is empty" };
3009
+ const value = raw.trim().replace(/^retry-after:\s*/i, "").trim();
3010
+ if (value === "") return { error: "Input is empty" };
3011
+ if (/^\d+$/.test(value)) {
3012
+ const seconds = parseInt(value, 10);
3013
+ return { header: `Retry-After: ${seconds}`, headerValue: String(seconds), type: "seconds", seconds };
3014
+ }
3015
+ const parsed = new Date(value);
3016
+ if (Number.isNaN(parsed.getTime())) {
3017
+ return { error: "Value is neither a non-negative integer (seconds) nor a valid HTTP-date" };
3018
+ }
3019
+ const httpDate = parsed.toUTCString();
3020
+ return { header: `Retry-After: ${httpDate}`, headerValue: httpDate, type: "http-date", httpDate, iso: parsed.toISOString() };
3021
+ }
2889
3022
 
2890
- export { CLIENT_HINTS, DNS_RECORD_TYPES, HTTP_HEADERS, LINK_REL_VALUES, PERMISSIONS_POLICY_FEATURES, REFERRER_POLICY_VALUES, STATUS_CODES, X_CONTENT_TYPE_OPTIONS_HEADER, analyzeEmailHeaders, assessContentTypeRisk, buildAcceptLanguage, buildCacheControl, buildClientHints, buildContentDisposition, buildDnsQueryUrl, buildGeoUrl, buildLinkHeader, buildMapUrl, buildPermissionsPolicy, buildRangeHeader, buildReferrerPolicy, buildVaryHeader, buildXContentTypeOptionsHeader, checkSetCookieHeader, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseAcceptLanguage, parseClientHintHeaders, parseContentDisposition, parseContentRangeHeader, parseDnsResponse, parseGeoResponse, parseHar, parseIpv6Address, parsePermissionsPolicy, parseUserAgent, parseVaryHeader, parseXContentTypeOptionsHeader, searchHeaders, searchStatusCodes, splitSitemap, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateManifest, validateRobotsTxt, validateSitemap };
3023
+ export { CLIENT_HINTS, DNS_RECORD_TYPES, HTTP_HEADERS, LINK_REL_VALUES, PERMISSIONS_POLICY_FEATURES, REFERRER_POLICY_VALUES, STATUS_CODES, X_CONTENT_TYPE_OPTIONS_HEADER, analyzeEmailHeaders, assessContentTypeRisk, buildAcceptLanguage, buildCacheControl, buildClientHints, buildContentDisposition, buildDnsQueryUrl, buildGeoUrl, buildLinkHeader, buildMapUrl, buildPermissionsPolicy, buildPreferHeader, buildRangeHeader, buildReferrerPolicy, buildRetryAfterHeader, buildVaryHeader, buildXContentTypeOptionsHeader, checkSetCookieHeader, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseAcceptLanguage, parseClientHintHeaders, parseContentDisposition, parseContentRangeHeader, parseDnsResponse, parseGeoResponse, parseHar, parseIpv6Address, parsePermissionsPolicy, parsePreferHeader, parseRetryAfterHeader, parseUserAgent, parseVaryHeader, parseXContentTypeOptionsHeader, searchHeaders, searchStatusCodes, splitSitemap, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateManifest, validateRobotsTxt, validateSitemap };
@@ -33,6 +33,7 @@ __export(units_exports, {
33
33
  calculateDepreciationSchedule: () => calculateDepreciationSchedule,
34
34
  calculateDiscount: () => calculateDiscount,
35
35
  calculateEmi: () => calculateEmi,
36
+ calculateFreelancerRate: () => calculateFreelancerRate,
36
37
  calculateGpa: () => calculateGpa,
37
38
  calculateLoan: () => calculateLoan,
38
39
  calculateMarginMarkup: () => calculateMarginMarkup,
@@ -2311,5 +2312,40 @@ function calculateBlendedLoanRate(loans) {
2311
2312
  totalAnnualInterest: round22(totalMonthlyInterest * 12)
2312
2313
  };
2313
2314
  }
2315
+ function calculateFreelancerRate(input) {
2316
+ const { targetAnnualIncome, billableHoursPerWeek, weeksPerYear } = input;
2317
+ const annualOverheadCosts = input.annualOverheadCosts ?? 0;
2318
+ const profitMarginPercent = input.profitMarginPercent ?? 0;
2319
+ if (!Number.isFinite(targetAnnualIncome) || targetAnnualIncome < 0) {
2320
+ return { error: "targetAnnualIncome must be a finite, non-negative number" };
2321
+ }
2322
+ if (!Number.isFinite(billableHoursPerWeek) || billableHoursPerWeek <= 0 || billableHoursPerWeek > 168) {
2323
+ return { error: "billableHoursPerWeek must be a finite number greater than zero and at most 168" };
2324
+ }
2325
+ if (!Number.isFinite(weeksPerYear) || weeksPerYear <= 0 || weeksPerYear > 52) {
2326
+ return { error: "weeksPerYear must be a finite number greater than zero and at most 52" };
2327
+ }
2328
+ if (!Number.isFinite(annualOverheadCosts) || annualOverheadCosts < 0) {
2329
+ return { error: "annualOverheadCosts must be a finite, non-negative number" };
2330
+ }
2331
+ if (!Number.isFinite(profitMarginPercent) || profitMarginPercent < 0) {
2332
+ return { error: "profitMarginPercent must be a finite, non-negative number" };
2333
+ }
2334
+ const round22 = (n) => Math.round(n * 100) / 100;
2335
+ const annualBillableHours = billableHoursPerWeek * weeksPerYear;
2336
+ const baseRevenueNeeded = targetAnnualIncome + annualOverheadCosts;
2337
+ const requiredAnnualRevenue = baseRevenueNeeded * (1 + profitMarginPercent / 100);
2338
+ const hourlyRate = requiredAnnualRevenue / annualBillableHours;
2339
+ return {
2340
+ targetAnnualIncome: round22(targetAnnualIncome),
2341
+ annualOverheadCosts: round22(annualOverheadCosts),
2342
+ profitMarginPercent: round22(profitMarginPercent),
2343
+ annualBillableHours: round22(annualBillableHours),
2344
+ requiredAnnualRevenue: round22(requiredAnnualRevenue),
2345
+ hourlyRate: round22(hourlyRate),
2346
+ dailyRate: round22(hourlyRate * 8),
2347
+ monthlyRevenueTarget: round22(requiredAnnualRevenue / 12)
2348
+ };
2349
+ }
2314
2350
 
2315
- 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, GPA_GRADE_POINTS, USD_DENOMINATIONS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateGpa, calculateLoan, calculateMarginMarkup, calculateMovingAverages, calculatePaybackPeriod, 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 };
2351
+ 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, GPA_GRADE_POINTS, USD_DENOMINATIONS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateFreelancerRate, calculateGpa, calculateLoan, calculateMarginMarkup, calculateMovingAverages, calculatePaybackPeriod, 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 };
@@ -10,6 +10,7 @@ __export(data_exports, {
10
10
  convertSubtitles: () => convertSubtitles,
11
11
  csvToJson: () => csvToJson,
12
12
  csvToSqlInsert: () => csvToSqlInsert,
13
+ dedupeNdjson: () => dedupeNdjson,
13
14
  detectDelimiter: () => detectDelimiter,
14
15
  detectSubtitleFormat: () => detectSubtitleFormat,
15
16
  diffEnvExample: () => diffEnvExample,
@@ -1172,6 +1173,38 @@ function jsonArrayToNdjson(input) {
1172
1173
  if (!Array.isArray(parsed)) return { error: "Input must be a JSON array" };
1173
1174
  return parsed.map((item) => JSON.stringify(item)).join("\n");
1174
1175
  }
1176
+ function dedupeNdjson(input, keyField) {
1177
+ const validated = validateNdjson(input);
1178
+ if (!validated.valid) return { error: _firstNdjsonError(validated) };
1179
+ const seen = /* @__PURE__ */ new Set();
1180
+ const keptRaw = [];
1181
+ const removedLines = [];
1182
+ for (const l of validated.lines) {
1183
+ const parsed = JSON.parse(l.raw);
1184
+ let key;
1185
+ if (keyField) {
1186
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || !(keyField in parsed)) {
1187
+ return { error: `Line ${l.line}: missing key field "${keyField}"` };
1188
+ }
1189
+ key = JSON.stringify(parsed[keyField]);
1190
+ } else {
1191
+ key = JSON.stringify(parsed);
1192
+ }
1193
+ if (seen.has(key)) {
1194
+ removedLines.push(l.line);
1195
+ } else {
1196
+ seen.add(key);
1197
+ keptRaw.push(l.raw.trim());
1198
+ }
1199
+ }
1200
+ return {
1201
+ totalLines: validated.totalLines,
1202
+ uniqueLines: keptRaw.length,
1203
+ duplicatesRemoved: removedLines.length,
1204
+ output: keptRaw.join("\n"),
1205
+ removedLines
1206
+ };
1207
+ }
1175
1208
  var _SUBTITLE_TIMESTAMP_RE = /(\d+):(\d{1,2}):(\d{1,2})[.,](\d{1,3})|(\d{1,3}):(\d{1,2})[.,](\d{1,3})/;
1176
1209
  var _VTT_BLOCK_KEYWORDS = ["NOTE", "STYLE", "REGION"];
1177
1210
  function parseSubtitleTimestamp(raw) {
@@ -1775,4 +1808,4 @@ function calculateMarkdownTaskListProgress(markdown) {
1775
1808
  return { totalItems, completedItems, percentComplete, sections, items };
1776
1809
  }
1777
1810
 
1778
- export { analyzeCaptionSpeed, calculateMarkdownTaskListProgress, convertSubtitles, csvToJson, csvToSqlInsert, data_exports, detectDelimiter, detectSubtitleFormat, diffEnvExample, envToExport, envToJson, findCsvDuplicateRows, formatNdjson, formatSubtitleTimestamp, formatToml, formatXml, generateMarkdownToc, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, inferCsvColumnTypes, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseFrontMatter, parseIni, parseSubtitleTimestamp, parseSubtitles, parseXmlToObject, resolveYamlAnchors, serializeSubtitles, shiftSubtitles, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson };
1811
+ export { analyzeCaptionSpeed, calculateMarkdownTaskListProgress, convertSubtitles, csvToJson, csvToSqlInsert, data_exports, dedupeNdjson, detectDelimiter, detectSubtitleFormat, diffEnvExample, envToExport, envToJson, findCsvDuplicateRows, formatNdjson, formatSubtitleTimestamp, formatToml, formatXml, generateMarkdownToc, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, inferCsvColumnTypes, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseFrontMatter, parseIni, parseSubtitleTimestamp, parseSubtitles, parseXmlToObject, resolveYamlAnchors, serializeSubtitles, shiftSubtitles, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson };
@@ -12,6 +12,7 @@
12
12
  * - env-parser : parseEnv, envToJson, envToExport, validateEnvKey
13
13
  * - env-diff-checker: diffEnvExample
14
14
  * - ndjson-formatter: validateNdjson, minifyNdjson, formatNdjson, ndjsonToJsonArray, jsonArrayToNdjson
15
+ * - ndjson-dedup: dedupeNdjson
15
16
  * - subtitle-converter: parseSubtitleTimestamp, formatSubtitleTimestamp, detectSubtitleFormat, parseSubtitles, serializeSubtitles, convertSubtitles, shiftSubtitles
16
17
  * - markdown-front-matter-parser: parseFrontMatter
17
18
  * - markdown-toc-generator: generateMarkdownToc
@@ -216,6 +217,23 @@ declare function ndjsonToJsonArray(input: string): string | {
216
217
  declare function jsonArrayToNdjson(input: string): string | {
217
218
  error: string;
218
219
  };
220
+ interface NdjsonDedupeResult {
221
+ totalLines: number;
222
+ uniqueLines: number;
223
+ duplicatesRemoved: number;
224
+ output: string;
225
+ removedLines: number[];
226
+ }
227
+ /**
228
+ * Remove duplicate records from NDJSON input. With no `keyField`, two lines
229
+ * are duplicates when their parsed JSON values are structurally identical
230
+ * (compared via a canonical JSON.stringify, so key order matters). With a
231
+ * `keyField`, two lines are duplicates when that field's value matches; the
232
+ * first occurrence of each key is kept, later ones are dropped.
233
+ */
234
+ declare function dedupeNdjson(input: string, keyField?: string): NdjsonDedupeResult | {
235
+ error: string;
236
+ };
219
237
  type SubtitleFormat = "srt" | "vtt";
220
238
  interface SubtitleCue {
221
239
  index: number;
@@ -513,6 +531,7 @@ type data_MarkdownTaskListSectionSummary = MarkdownTaskListSectionSummary;
513
531
  type data_MarkdownTocEntry = MarkdownTocEntry;
514
532
  type data_MarkdownTocOptions = MarkdownTocOptions;
515
533
  type data_MarkdownTocResult = MarkdownTocResult;
534
+ type data_NdjsonDedupeResult = NdjsonDedupeResult;
516
535
  type data_NdjsonLineResult = NdjsonLineResult;
517
536
  type data_NdjsonValidateResult = NdjsonValidateResult;
518
537
  type data_ParseOptions = ParseOptions;
@@ -532,6 +551,7 @@ declare const data_calculateMarkdownTaskListProgress: typeof calculateMarkdownTa
532
551
  declare const data_convertSubtitles: typeof convertSubtitles;
533
552
  declare const data_csvToJson: typeof csvToJson;
534
553
  declare const data_csvToSqlInsert: typeof csvToSqlInsert;
554
+ declare const data_dedupeNdjson: typeof dedupeNdjson;
535
555
  declare const data_detectDelimiter: typeof detectDelimiter;
536
556
  declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
537
557
  declare const data_diffEnvExample: typeof diffEnvExample;
@@ -579,7 +599,7 @@ declare const data_validateXml: typeof validateXml;
579
599
  declare const data_validateYaml: typeof validateYaml;
580
600
  declare const data_xmlToJson: typeof xmlToJson;
581
601
  declare namespace data {
582
- export { type data_CaptionSpeedCue as CaptionSpeedCue, type data_CaptionSpeedResult as CaptionSpeedResult, type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvColumnType as CsvColumnType, type data_CsvColumnTypeInfo as CsvColumnTypeInfo, type data_CsvColumnTypeOptions as CsvColumnTypeOptions, type data_CsvColumnTypeResult as CsvColumnTypeResult, 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_MarkdownTaskListItem as MarkdownTaskListItem, type data_MarkdownTaskListProgressResult as MarkdownTaskListProgressResult, type data_MarkdownTaskListSectionSummary as MarkdownTaskListSectionSummary, 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, type data_YamlAnchorInfo as YamlAnchorInfo, type data_YamlAnchorResolveError as YamlAnchorResolveError, type data_YamlAnchorResolveResult as YamlAnchorResolveResult, data_analyzeCaptionSpeed as analyzeCaptionSpeed, data_calculateMarkdownTaskListProgress as calculateMarkdownTaskListProgress, 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_inferCsvColumnTypes as inferCsvColumnTypes, 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_resolveYamlAnchors as resolveYamlAnchors, 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 };
602
+ export { type data_CaptionSpeedCue as CaptionSpeedCue, type data_CaptionSpeedResult as CaptionSpeedResult, type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvColumnType as CsvColumnType, type data_CsvColumnTypeInfo as CsvColumnTypeInfo, type data_CsvColumnTypeOptions as CsvColumnTypeOptions, type data_CsvColumnTypeResult as CsvColumnTypeResult, 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_MarkdownTaskListItem as MarkdownTaskListItem, type data_MarkdownTaskListProgressResult as MarkdownTaskListProgressResult, type data_MarkdownTaskListSectionSummary as MarkdownTaskListSectionSummary, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonDedupeResult as NdjsonDedupeResult, 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, type data_YamlAnchorInfo as YamlAnchorInfo, type data_YamlAnchorResolveError as YamlAnchorResolveError, type data_YamlAnchorResolveResult as YamlAnchorResolveResult, data_analyzeCaptionSpeed as analyzeCaptionSpeed, data_calculateMarkdownTaskListProgress as calculateMarkdownTaskListProgress, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_dedupeNdjson as dedupeNdjson, 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_inferCsvColumnTypes as inferCsvColumnTypes, 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_resolveYamlAnchors as resolveYamlAnchors, 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 };
583
603
  }
584
604
 
585
- export { formatSubtitleTimestamp as $, type XmlToJsonOptions as A, type YamlAnchorResolveError as B, type CaptionSpeedCue as C, type YamlAnchorResolveResult as D, type EnvDiffError as E, type FrontMatterError as F, analyzeCaptionSpeed as G, calculateMarkdownTaskListProgress as H, type IniSection as I, convertSubtitles as J, csvToJson as K, csvToSqlInsert as L, type MarkdownTaskListItem as M, type NdjsonLineResult as N, detectDelimiter as O, type ParseOptions as P, detectSubtitleFormat as Q, diffEnvExample as R, type SubtitleCue as S, type TomlFormatResult as T, envToExport as U, type ValidationResult as V, envToJson as W, type XmlFormatResult as X, type YamlAnchorInfo as Y, findCsvDuplicateRows as Z, formatNdjson as _, type CaptionSpeedResult as a, formatToml as a0, formatXml as a1, generateMarkdownToc as a2, getSection as a3, getSectionNames as a4, getTomlStats as a5, getXmlStats as a6, getYamlStats as a7, inferCsvColumnTypes as a8, iniToJson as a9, validateYaml as aA, xmlToJson as aB, jsonArrayToNdjson as aa, jsonToIni as ab, jsonToToml as ac, jsonToXml as ad, minifyNdjson as ae, minifyToml as af, minifyXml as ag, ndjsonToJsonArray as ah, parseCsv as ai, parseEnv as aj, parseFrontMatter as ak, parseIni as al, parseSubtitleTimestamp as am, parseSubtitles as an, parseXmlToObject as ao, resolveYamlAnchors as ap, serializeSubtitles as aq, shiftSubtitles as ar, stringifyIni as as, toCsvString as at, tomlToJson as au, validateEnvKey as av, validateIni as aw, validateNdjson as ax, validateToml as ay, validateXml as az, type ConvertedSubtitles as b, type CsvColumnType as c, data as d, type CsvColumnTypeInfo as e, type CsvColumnTypeOptions as f, type CsvColumnTypeResult as g, type CsvDuplicateGroup as h, type CsvDuplicateOptions as i, type CsvDuplicateResult as j, type CsvParseResult as k, type CsvToSqlOptions as l, type CsvToSqlResult as m, type EnvDiffResult as n, type EnvEntry as o, type EnvParseResult as p, type FrontMatterResult as q, type MarkdownTaskListProgressResult as r, type MarkdownTaskListSectionSummary as s, type MarkdownTocEntry as t, type MarkdownTocOptions as u, type MarkdownTocResult as v, type NdjsonValidateResult as w, type ParsedIni as x, type ParsedSubtitles as y, type SubtitleFormat as z };
605
+ export { findCsvDuplicateRows as $, type SubtitleFormat as A, type XmlToJsonOptions as B, type CaptionSpeedCue as C, type YamlAnchorResolveError as D, type EnvDiffError as E, type FrontMatterError as F, type YamlAnchorResolveResult as G, analyzeCaptionSpeed as H, type IniSection as I, calculateMarkdownTaskListProgress as J, convertSubtitles as K, csvToJson as L, type MarkdownTaskListItem as M, type NdjsonDedupeResult as N, csvToSqlInsert as O, type ParseOptions as P, dedupeNdjson as Q, detectDelimiter as R, type SubtitleCue as S, type TomlFormatResult as T, detectSubtitleFormat as U, type ValidationResult as V, diffEnvExample as W, type XmlFormatResult as X, type YamlAnchorInfo as Y, envToExport as Z, envToJson as _, type CaptionSpeedResult as a, formatNdjson as a0, formatSubtitleTimestamp as a1, formatToml as a2, formatXml as a3, generateMarkdownToc as a4, getSection as a5, getSectionNames as a6, getTomlStats as a7, getXmlStats as a8, getYamlStats as a9, validateToml as aA, validateXml as aB, validateYaml as aC, xmlToJson as aD, inferCsvColumnTypes as aa, iniToJson as ab, jsonArrayToNdjson as ac, jsonToIni as ad, jsonToToml as ae, jsonToXml as af, minifyNdjson as ag, minifyToml as ah, minifyXml as ai, ndjsonToJsonArray as aj, parseCsv as ak, parseEnv as al, parseFrontMatter as am, parseIni as an, parseSubtitleTimestamp as ao, parseSubtitles as ap, parseXmlToObject as aq, resolveYamlAnchors as ar, serializeSubtitles as as, shiftSubtitles as at, stringifyIni as au, toCsvString as av, tomlToJson as aw, validateEnvKey as ax, validateIni as ay, validateNdjson as az, type ConvertedSubtitles as b, type CsvColumnType as c, data as d, type CsvColumnTypeInfo as e, type CsvColumnTypeOptions as f, type CsvColumnTypeResult as g, type CsvDuplicateGroup as h, type CsvDuplicateOptions as i, type CsvDuplicateResult as j, type CsvParseResult as k, type CsvToSqlOptions as l, type CsvToSqlResult as m, type EnvDiffResult as n, type EnvEntry as o, type EnvParseResult as p, type FrontMatterResult as q, type MarkdownTaskListProgressResult as r, type MarkdownTaskListSectionSummary as s, type MarkdownTocEntry as t, type MarkdownTocOptions as u, type MarkdownTocResult as v, type NdjsonLineResult as w, type NdjsonValidateResult as x, type ParsedIni as y, type ParsedSubtitles as z };
@@ -12,6 +12,7 @@
12
12
  * - env-parser : parseEnv, envToJson, envToExport, validateEnvKey
13
13
  * - env-diff-checker: diffEnvExample
14
14
  * - ndjson-formatter: validateNdjson, minifyNdjson, formatNdjson, ndjsonToJsonArray, jsonArrayToNdjson
15
+ * - ndjson-dedup: dedupeNdjson
15
16
  * - subtitle-converter: parseSubtitleTimestamp, formatSubtitleTimestamp, detectSubtitleFormat, parseSubtitles, serializeSubtitles, convertSubtitles, shiftSubtitles
16
17
  * - markdown-front-matter-parser: parseFrontMatter
17
18
  * - markdown-toc-generator: generateMarkdownToc
@@ -216,6 +217,23 @@ declare function ndjsonToJsonArray(input: string): string | {
216
217
  declare function jsonArrayToNdjson(input: string): string | {
217
218
  error: string;
218
219
  };
220
+ interface NdjsonDedupeResult {
221
+ totalLines: number;
222
+ uniqueLines: number;
223
+ duplicatesRemoved: number;
224
+ output: string;
225
+ removedLines: number[];
226
+ }
227
+ /**
228
+ * Remove duplicate records from NDJSON input. With no `keyField`, two lines
229
+ * are duplicates when their parsed JSON values are structurally identical
230
+ * (compared via a canonical JSON.stringify, so key order matters). With a
231
+ * `keyField`, two lines are duplicates when that field's value matches; the
232
+ * first occurrence of each key is kept, later ones are dropped.
233
+ */
234
+ declare function dedupeNdjson(input: string, keyField?: string): NdjsonDedupeResult | {
235
+ error: string;
236
+ };
219
237
  type SubtitleFormat = "srt" | "vtt";
220
238
  interface SubtitleCue {
221
239
  index: number;
@@ -513,6 +531,7 @@ type data_MarkdownTaskListSectionSummary = MarkdownTaskListSectionSummary;
513
531
  type data_MarkdownTocEntry = MarkdownTocEntry;
514
532
  type data_MarkdownTocOptions = MarkdownTocOptions;
515
533
  type data_MarkdownTocResult = MarkdownTocResult;
534
+ type data_NdjsonDedupeResult = NdjsonDedupeResult;
516
535
  type data_NdjsonLineResult = NdjsonLineResult;
517
536
  type data_NdjsonValidateResult = NdjsonValidateResult;
518
537
  type data_ParseOptions = ParseOptions;
@@ -532,6 +551,7 @@ declare const data_calculateMarkdownTaskListProgress: typeof calculateMarkdownTa
532
551
  declare const data_convertSubtitles: typeof convertSubtitles;
533
552
  declare const data_csvToJson: typeof csvToJson;
534
553
  declare const data_csvToSqlInsert: typeof csvToSqlInsert;
554
+ declare const data_dedupeNdjson: typeof dedupeNdjson;
535
555
  declare const data_detectDelimiter: typeof detectDelimiter;
536
556
  declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
537
557
  declare const data_diffEnvExample: typeof diffEnvExample;
@@ -579,7 +599,7 @@ declare const data_validateXml: typeof validateXml;
579
599
  declare const data_validateYaml: typeof validateYaml;
580
600
  declare const data_xmlToJson: typeof xmlToJson;
581
601
  declare namespace data {
582
- export { type data_CaptionSpeedCue as CaptionSpeedCue, type data_CaptionSpeedResult as CaptionSpeedResult, type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvColumnType as CsvColumnType, type data_CsvColumnTypeInfo as CsvColumnTypeInfo, type data_CsvColumnTypeOptions as CsvColumnTypeOptions, type data_CsvColumnTypeResult as CsvColumnTypeResult, 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_MarkdownTaskListItem as MarkdownTaskListItem, type data_MarkdownTaskListProgressResult as MarkdownTaskListProgressResult, type data_MarkdownTaskListSectionSummary as MarkdownTaskListSectionSummary, 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, type data_YamlAnchorInfo as YamlAnchorInfo, type data_YamlAnchorResolveError as YamlAnchorResolveError, type data_YamlAnchorResolveResult as YamlAnchorResolveResult, data_analyzeCaptionSpeed as analyzeCaptionSpeed, data_calculateMarkdownTaskListProgress as calculateMarkdownTaskListProgress, 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_inferCsvColumnTypes as inferCsvColumnTypes, 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_resolveYamlAnchors as resolveYamlAnchors, 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 };
602
+ export { type data_CaptionSpeedCue as CaptionSpeedCue, type data_CaptionSpeedResult as CaptionSpeedResult, type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvColumnType as CsvColumnType, type data_CsvColumnTypeInfo as CsvColumnTypeInfo, type data_CsvColumnTypeOptions as CsvColumnTypeOptions, type data_CsvColumnTypeResult as CsvColumnTypeResult, 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_MarkdownTaskListItem as MarkdownTaskListItem, type data_MarkdownTaskListProgressResult as MarkdownTaskListProgressResult, type data_MarkdownTaskListSectionSummary as MarkdownTaskListSectionSummary, type data_MarkdownTocEntry as MarkdownTocEntry, type data_MarkdownTocOptions as MarkdownTocOptions, type data_MarkdownTocResult as MarkdownTocResult, type data_NdjsonDedupeResult as NdjsonDedupeResult, 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, type data_YamlAnchorInfo as YamlAnchorInfo, type data_YamlAnchorResolveError as YamlAnchorResolveError, type data_YamlAnchorResolveResult as YamlAnchorResolveResult, data_analyzeCaptionSpeed as analyzeCaptionSpeed, data_calculateMarkdownTaskListProgress as calculateMarkdownTaskListProgress, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_csvToSqlInsert as csvToSqlInsert, data_dedupeNdjson as dedupeNdjson, 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_inferCsvColumnTypes as inferCsvColumnTypes, 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_resolveYamlAnchors as resolveYamlAnchors, 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 };
583
603
  }
584
604
 
585
- export { formatSubtitleTimestamp as $, type XmlToJsonOptions as A, type YamlAnchorResolveError as B, type CaptionSpeedCue as C, type YamlAnchorResolveResult as D, type EnvDiffError as E, type FrontMatterError as F, analyzeCaptionSpeed as G, calculateMarkdownTaskListProgress as H, type IniSection as I, convertSubtitles as J, csvToJson as K, csvToSqlInsert as L, type MarkdownTaskListItem as M, type NdjsonLineResult as N, detectDelimiter as O, type ParseOptions as P, detectSubtitleFormat as Q, diffEnvExample as R, type SubtitleCue as S, type TomlFormatResult as T, envToExport as U, type ValidationResult as V, envToJson as W, type XmlFormatResult as X, type YamlAnchorInfo as Y, findCsvDuplicateRows as Z, formatNdjson as _, type CaptionSpeedResult as a, formatToml as a0, formatXml as a1, generateMarkdownToc as a2, getSection as a3, getSectionNames as a4, getTomlStats as a5, getXmlStats as a6, getYamlStats as a7, inferCsvColumnTypes as a8, iniToJson as a9, validateYaml as aA, xmlToJson as aB, jsonArrayToNdjson as aa, jsonToIni as ab, jsonToToml as ac, jsonToXml as ad, minifyNdjson as ae, minifyToml as af, minifyXml as ag, ndjsonToJsonArray as ah, parseCsv as ai, parseEnv as aj, parseFrontMatter as ak, parseIni as al, parseSubtitleTimestamp as am, parseSubtitles as an, parseXmlToObject as ao, resolveYamlAnchors as ap, serializeSubtitles as aq, shiftSubtitles as ar, stringifyIni as as, toCsvString as at, tomlToJson as au, validateEnvKey as av, validateIni as aw, validateNdjson as ax, validateToml as ay, validateXml as az, type ConvertedSubtitles as b, type CsvColumnType as c, data as d, type CsvColumnTypeInfo as e, type CsvColumnTypeOptions as f, type CsvColumnTypeResult as g, type CsvDuplicateGroup as h, type CsvDuplicateOptions as i, type CsvDuplicateResult as j, type CsvParseResult as k, type CsvToSqlOptions as l, type CsvToSqlResult as m, type EnvDiffResult as n, type EnvEntry as o, type EnvParseResult as p, type FrontMatterResult as q, type MarkdownTaskListProgressResult as r, type MarkdownTaskListSectionSummary as s, type MarkdownTocEntry as t, type MarkdownTocOptions as u, type MarkdownTocResult as v, type NdjsonValidateResult as w, type ParsedIni as x, type ParsedSubtitles as y, type SubtitleFormat as z };
605
+ export { findCsvDuplicateRows as $, type SubtitleFormat as A, type XmlToJsonOptions as B, type CaptionSpeedCue as C, type YamlAnchorResolveError as D, type EnvDiffError as E, type FrontMatterError as F, type YamlAnchorResolveResult as G, analyzeCaptionSpeed as H, type IniSection as I, calculateMarkdownTaskListProgress as J, convertSubtitles as K, csvToJson as L, type MarkdownTaskListItem as M, type NdjsonDedupeResult as N, csvToSqlInsert as O, type ParseOptions as P, dedupeNdjson as Q, detectDelimiter as R, type SubtitleCue as S, type TomlFormatResult as T, detectSubtitleFormat as U, type ValidationResult as V, diffEnvExample as W, type XmlFormatResult as X, type YamlAnchorInfo as Y, envToExport as Z, envToJson as _, type CaptionSpeedResult as a, formatNdjson as a0, formatSubtitleTimestamp as a1, formatToml as a2, formatXml as a3, generateMarkdownToc as a4, getSection as a5, getSectionNames as a6, getTomlStats as a7, getXmlStats as a8, getYamlStats as a9, validateToml as aA, validateXml as aB, validateYaml as aC, xmlToJson as aD, inferCsvColumnTypes as aa, iniToJson as ab, jsonArrayToNdjson as ac, jsonToIni as ad, jsonToToml as ae, jsonToXml as af, minifyNdjson as ag, minifyToml as ah, minifyXml as ai, ndjsonToJsonArray as aj, parseCsv as ak, parseEnv as al, parseFrontMatter as am, parseIni as an, parseSubtitleTimestamp as ao, parseSubtitles as ap, parseXmlToObject as aq, resolveYamlAnchors as ar, serializeSubtitles as as, shiftSubtitles as at, stringifyIni as au, toCsvString as av, tomlToJson as aw, validateEnvKey as ax, validateIni as ay, validateNdjson as az, type ConvertedSubtitles as b, type CsvColumnType as c, data as d, type CsvColumnTypeInfo as e, type CsvColumnTypeOptions as f, type CsvColumnTypeResult as g, type CsvDuplicateGroup as h, type CsvDuplicateOptions as i, type CsvDuplicateResult as j, type CsvParseResult as k, type CsvToSqlOptions as l, type CsvToSqlResult as m, type EnvDiffResult as n, type EnvEntry as o, type EnvParseResult as p, type FrontMatterResult as q, type MarkdownTaskListProgressResult as r, type MarkdownTaskListSectionSummary as s, type MarkdownTocEntry as t, type MarkdownTocOptions as u, type MarkdownTocResult as v, type NdjsonLineResult as w, type NdjsonValidateResult as x, type ParsedIni as y, type ParsedSubtitles as z };