@utilix-tech/sdk 0.93.0 → 0.95.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 +7 -0
- package/README.md +19 -4
- package/dist/{chunk-XAPHAUPI.js → chunk-G5NUC5MV.js} +52 -1
- package/dist/{chunk-K3B4JTHN.js → chunk-K242OOF4.js} +111 -1
- package/dist/{chunk-ZWFZRDQ4.js → chunk-YSS4VVZU.js} +112 -1
- package/dist/index.cjs +272 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{media-DF8BS-iA.d.ts → media-C8Y67lUI.d.cts} +62 -2
- package/dist/{media-DF8BS-iA.d.cts → media-C8Y67lUI.d.ts} +62 -2
- package/dist/{network-9j5aKpoR.d.cts → network-B6fQrsCc.d.cts} +47 -2
- package/dist/{network-9j5aKpoR.d.ts → network-B6fQrsCc.d.ts} +47 -2
- package/dist/tools/media.cjs +111 -0
- package/dist/tools/media.d.cts +1 -1
- package/dist/tools/media.d.ts +1 -1
- package/dist/tools/media.js +1 -1
- package/dist/tools/network.cjs +110 -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 +51 -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-DnWrOTSV.d.ts → units-BBzv0VIt.d.cts} +51 -2
- package/dist/{units-DnWrOTSV.d.cts → units-BBzv0VIt.d.ts} +51 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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.95.0
|
|
6
|
+
- Add `readPngChunks` (png-chunk-inspector): walk a PNG's chunk stream directly from its bytes to report every chunk's type, offset, size, and CRC-32 validity, plus the decoded IHDR header and common ancillary chunks (tEXt, pHYs, gAMA, tIME, acTL).
|
|
7
|
+
- Add `estimateAudioFileSize` (audio-file-size-estimator): estimate an audio file's size before recording or exporting it, uncompressed PCM size from sample rate/bit depth/channels or compressed size from a target bitrate.
|
|
8
|
+
|
|
9
|
+
## 0.94.0
|
|
10
|
+
- Add `buildAltSvcHeader`/`parseAltSvcHeader` (alt-svc-header-builder): build an `Alt-Svc` response header (RFC 7838) from one or more alternative services with a plain-English note per protocol-id, flagging outdated HTTP/3 draft protocol-ids, or parse an existing header back into its alternatives, including the special `clear` value.
|
|
11
|
+
|
|
5
12
|
## 0.93.0
|
|
6
13
|
- Add `findDuplicateLockfileVersions`/`detectLockfileFormat` (lockfile-duplicate-version-reporter): parse an npm, yarn, or pnpm lockfile and report every package installed at more than one resolved version, with a count of install locations per version.
|
|
7
14
|
- Add `summarizeGitDiffStat` (git-diff-stat-summarizer): parse `git diff --stat` / `git log --stat` output into structured per-file insertion/deletion counts plus overall totals, flagging a scaled-down bar's split as approximate.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**742 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)
|
|
@@ -767,6 +767,21 @@ findDuplicateLockfileVersions('{"lockfileVersion":3,"packages":{"node_modules/lo
|
|
|
767
767
|
import { summarizeGitDiffStat } from "@utilix-tech/sdk/code";
|
|
768
768
|
summarizeGitDiffStat(" src/foo.ts | 12 +++++-------\n 1 file changed, 5 insertions(+), 7 deletions(-)\n");
|
|
769
769
|
// { files: [{ file: "src/foo.ts", changes: 12, insertions: 5, deletions: 7, binary: false, approximate: false }], filesChanged: 1, totalInsertions: 5, totalDeletions: 7 }
|
|
770
|
+
|
|
771
|
+
// Build an Alt-Svc header (RFC 7838) advertising HTTP/3, or parse an existing one
|
|
772
|
+
import { buildAltSvcHeader, parseAltSvcHeader } from "@utilix-tech/sdk/network";
|
|
773
|
+
buildAltSvcHeader([{ protocolId: "h3", port: 443, maxAge: 86400 }]);
|
|
774
|
+
// { header: 'Alt-Svc: h3=":443"; ma=86400', headerValue: 'h3=":443"; ma=86400', entries: [{ protocolId: "h3", authority: ":443", maxAge: 86400, persist: false, note: "..." }], cleared: false, warnings: [] }
|
|
775
|
+
|
|
776
|
+
// Walk a PNG's chunk stream directly from its bytes: type, offset, size, CRC validity
|
|
777
|
+
import { readPngChunks } from "@utilix-tech/sdk/media";
|
|
778
|
+
readPngChunks(pngBytes);
|
|
779
|
+
// { valid: true, fileSize: 1024, chunkCount: 5, chunks: [{ type: "IHDR", offset: 8, dataLength: 13, crcValid: true, critical: true }, ...], header: { width: 64, height: 64, bitDepth: 8, colorType: "rgba", hasAlpha: true, interlace: false }, textChunks: [], physicalDimensions: null, gamma: null, lastModified: null, isAnimated: false }
|
|
780
|
+
|
|
781
|
+
// Estimate an audio file's size before recording/exporting it
|
|
782
|
+
import { estimateAudioFileSize } from "@utilix-tech/sdk/units";
|
|
783
|
+
estimateAudioFileSize({ mode: "pcm", durationSeconds: 60, sampleRateHz: 44100, bitDepth: 16, channels: 2 });
|
|
784
|
+
// { mode: "pcm", durationSeconds: 60, sampleRateHz: 44100, bitDepth: 16, channels: 2, dataBytes: 10584000, fileSizeBytes: 10584044, fileSizeMB: 10.58, fileSizeMiB: 10.09 }
|
|
770
785
|
```
|
|
771
786
|
|
|
772
787
|
---
|
|
@@ -1085,15 +1100,15 @@ readMidiInfo(new Uint8Array(midiBytes));
|
|
|
1085
1100
|
| `@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, Markdown table column aligner, Markdown heading hierarchy validator, Markdown internal link checker |
|
|
1086
1101
|
| `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, entropy estimator, fake data, CSP nonce generator |
|
|
1087
1102
|
| `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
|
|
1088
|
-
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN/VIN/ISIN 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, debt snowball/avalanche payoff planner, tax-equivalent yield calculator, mortgage discount points break-even calculator, loan prepayment penalty calculator, data transfer time / required bandwidth estimator, real rate of return (Fisher equation) calculator, sales commission calculator (flat or tiered), annuity payout calculator, semver range intersection checker |
|
|
1089
|
-
| `@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, robots.txt validator, robots.txt generator, Prefer header builder/parser (RFC 7240), Retry-After header builder/parser (RFC 9110), Priority header builder/parser (RFC 9218), WebSocket close code reference/lookup, Strict-Transport-Security (HSTS) header builder/parser, X-Robots-Tag header builder/parser, WebSocket frame opcode reference/lookup, robots meta tag builder/parser, Cookie header byte-size budget calculator |
|
|
1103
|
+
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN/VIN/ISIN 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, debt snowball/avalanche payoff planner, tax-equivalent yield calculator, mortgage discount points break-even calculator, loan prepayment penalty calculator, data transfer time / required bandwidth estimator, real rate of return (Fisher equation) calculator, sales commission calculator (flat or tiered), annuity payout calculator, semver range intersection checker, audio file size estimator |
|
|
1104
|
+
| `@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, robots.txt validator, robots.txt generator, Prefer header builder/parser (RFC 7240), Retry-After header builder/parser (RFC 9110), Priority header builder/parser (RFC 9218), WebSocket close code reference/lookup, Strict-Transport-Security (HSTS) header builder/parser, X-Robots-Tag header builder/parser, WebSocket frame opcode reference/lookup, robots meta tag builder/parser, Cookie header byte-size budget calculator, Alt-Svc header builder/parser (RFC 7838) |
|
|
1090
1105
|
| `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header, TOTP/HOTP code generator, webhook HMAC signature verifier |
|
|
1091
1106
|
| `@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, package.json dependency range auditor, regex capture group/backreference visualizer, SVG sprite symbol extractor, ARIA role & attribute validator, database connection string parser, Markdown code fence language tag validator, SQL query parameter placeholder counter, Markdown link reference extractor, regex character class builder, Dockerfile linter, Mermaid diagram syntax validator |
|
|
1092
1107
|
| `@utilix-tech/sdk/color` | Hex/RGB/HSL/HSV/CMYK convert, palette generation, contrast check |
|
|
1093
1108
|
| `@utilix-tech/sdk/css` | CSS gradient, box-shadow, border-radius, keyframe animation generators |
|
|
1094
1109
|
| `@utilix-tech/sdk/misc` | SVG optimize/sanitize, file size format, Unicode inspector |
|
|
1095
1110
|
| `@utilix-tech/sdk/ai_agent` | Token estimate/trim, chunk text, extract URLs/JSON/keywords, sanitize HTML, flatten/merge JSON, dedupe lines, validate schema, PII/secret/injection detect, vector similarity, few-shot prompt formatter, tool-definition schema linter, retry/backoff schedule calculator, prompt template variable extractor |
|
|
1096
|
-
| `@utilix-tech/sdk/media` | Image format & dimension reading from raw bytes (PNG, JPEG, GIF, WebP, BMP); PDF metadata reading; PDF page dimension/orientation listing; WAV audio info; ID3 tags; ICO favicon inspection; FLAC metadata reading; MP4/WebM video info; GIF frame/loop inspection |
|
|
1111
|
+
| `@utilix-tech/sdk/media` | Image format & dimension reading from raw bytes (PNG, JPEG, GIF, WebP, BMP); PDF metadata reading; PDF page dimension/orientation listing; WAV audio info; ID3 tags; ICO favicon inspection; FLAC metadata reading; MP4/WebM video info; GIF frame/loop inspection; PNG chunk stream inspection with CRC validation |
|
|
1097
1112
|
|
|
1098
1113
|
---
|
|
1099
1114
|
|
|
@@ -70,6 +70,7 @@ __export(units_exports, {
|
|
|
70
70
|
decimalToIpCidr: () => decimalToIpCidr,
|
|
71
71
|
describePermissions: () => describePermissions,
|
|
72
72
|
detectNegativeAmortization: () => detectNegativeAmortization,
|
|
73
|
+
estimateAudioFileSize: () => estimateAudioFileSize,
|
|
73
74
|
fitIntoBox: () => fitIntoBox,
|
|
74
75
|
formatNumber: () => formatNumber,
|
|
75
76
|
formatValue: () => formatValue,
|
|
@@ -3166,5 +3167,55 @@ function detectNegativeAmortization(input) {
|
|
|
3166
3167
|
capCrossedMonth
|
|
3167
3168
|
};
|
|
3168
3169
|
}
|
|
3170
|
+
var AUDIO_FILE_SIZE_WAV_HEADER_BYTES = 44;
|
|
3171
|
+
function estimateAudioFileSize(input) {
|
|
3172
|
+
const { mode, durationSeconds } = input;
|
|
3173
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
|
|
3174
|
+
return { error: "durationSeconds must be a positive number" };
|
|
3175
|
+
}
|
|
3176
|
+
const round22 = (n) => Math.round(n * 100) / 100;
|
|
3177
|
+
if (mode === "pcm") {
|
|
3178
|
+
const { sampleRateHz, bitDepth, channels } = input;
|
|
3179
|
+
if (!Number.isFinite(sampleRateHz) || sampleRateHz <= 0) {
|
|
3180
|
+
return { error: 'sampleRateHz must be a positive number for mode "pcm"' };
|
|
3181
|
+
}
|
|
3182
|
+
if (!Number.isFinite(bitDepth) || bitDepth <= 0) {
|
|
3183
|
+
return { error: 'bitDepth must be a positive number for mode "pcm"' };
|
|
3184
|
+
}
|
|
3185
|
+
if (!Number.isInteger(channels) || channels <= 0) {
|
|
3186
|
+
return { error: 'channels must be a positive whole number for mode "pcm"' };
|
|
3187
|
+
}
|
|
3188
|
+
const dataBytes = sampleRateHz * (bitDepth / 8) * channels * durationSeconds;
|
|
3189
|
+
const fileSizeBytes = dataBytes + AUDIO_FILE_SIZE_WAV_HEADER_BYTES;
|
|
3190
|
+
return {
|
|
3191
|
+
mode,
|
|
3192
|
+
durationSeconds,
|
|
3193
|
+
sampleRateHz,
|
|
3194
|
+
bitDepth,
|
|
3195
|
+
channels,
|
|
3196
|
+
dataBytes: Math.round(dataBytes),
|
|
3197
|
+
fileSizeBytes: Math.round(fileSizeBytes),
|
|
3198
|
+
fileSizeMB: round22(fileSizeBytes / 1e6),
|
|
3199
|
+
fileSizeMiB: round22(fileSizeBytes / 1048576)
|
|
3200
|
+
};
|
|
3201
|
+
}
|
|
3202
|
+
if (mode === "compressed") {
|
|
3203
|
+
const { bitrateKbps } = input;
|
|
3204
|
+
if (!Number.isFinite(bitrateKbps) || bitrateKbps <= 0) {
|
|
3205
|
+
return { error: 'bitrateKbps must be a positive number for mode "compressed"' };
|
|
3206
|
+
}
|
|
3207
|
+
const dataBytes = bitrateKbps * 1e3 * durationSeconds / 8;
|
|
3208
|
+
return {
|
|
3209
|
+
mode,
|
|
3210
|
+
durationSeconds,
|
|
3211
|
+
bitrateKbps,
|
|
3212
|
+
dataBytes: Math.round(dataBytes),
|
|
3213
|
+
fileSizeBytes: Math.round(dataBytes),
|
|
3214
|
+
fileSizeMB: round22(dataBytes / 1e6),
|
|
3215
|
+
fileSizeMiB: round22(dataBytes / 1048576)
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
return { error: 'mode must be "pcm" or "compressed"' };
|
|
3219
|
+
}
|
|
3169
3220
|
|
|
3170
|
-
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, calculateAnnuityPayout, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditCardMinimumPayment, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateExtraPaymentPayoff, calculateFreelancerRate, calculateGpa, calculateLoan, calculateLoanPrepaymentPenalty, calculateLoanRefinance, calculateMarginMarkup, calculateMortgagePoints, calculateMovingAverages, calculatePaybackPeriod, calculatePercentageChange, calculateRealRateOfReturn, calculateRequiredBandwidth, calculateRoi, calculateRuleOf72, calculateSalesCommission, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTaxEquivalentYield, calculateTimeValue, calculateTipSplit, calculateTransferTime, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, detectNegativeAmortization, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, intersectSemverRanges, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, planDebtPayoff, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, validateIsin, validateVin, viewportToPx };
|
|
3221
|
+
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, calculateAnnuityPayout, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditCardMinimumPayment, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateExtraPaymentPayoff, calculateFreelancerRate, calculateGpa, calculateLoan, calculateLoanPrepaymentPenalty, calculateLoanRefinance, calculateMarginMarkup, calculateMortgagePoints, calculateMovingAverages, calculatePaybackPeriod, calculatePercentageChange, calculateRealRateOfReturn, calculateRequiredBandwidth, calculateRoi, calculateRuleOf72, calculateSalesCommission, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTaxEquivalentYield, calculateTimeValue, calculateTipSplit, calculateTransferTime, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, detectNegativeAmortization, estimateAudioFileSize, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, intersectSemverRanges, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, planDebtPayoff, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, validateIsin, validateVin, viewportToPx };
|
|
@@ -14,6 +14,7 @@ __export(network_exports, {
|
|
|
14
14
|
analyzeEmailHeaders: () => analyzeEmailHeaders,
|
|
15
15
|
assessContentTypeRisk: () => assessContentTypeRisk,
|
|
16
16
|
buildAcceptLanguage: () => buildAcceptLanguage,
|
|
17
|
+
buildAltSvcHeader: () => buildAltSvcHeader,
|
|
17
18
|
buildCacheControl: () => buildCacheControl,
|
|
18
19
|
buildClearSiteData: () => buildClearSiteData,
|
|
19
20
|
buildClientHints: () => buildClientHints,
|
|
@@ -56,6 +57,7 @@ __export(network_exports, {
|
|
|
56
57
|
lookupCloseCode: () => lookupCloseCode,
|
|
57
58
|
lookupOpcode: () => lookupOpcode,
|
|
58
59
|
parseAcceptLanguage: () => parseAcceptLanguage,
|
|
60
|
+
parseAltSvcHeader: () => parseAltSvcHeader,
|
|
59
61
|
parseClearSiteData: () => parseClearSiteData,
|
|
60
62
|
parseClientHintHeaders: () => parseClientHintHeaders,
|
|
61
63
|
parseContentDisposition: () => parseContentDisposition,
|
|
@@ -3711,5 +3713,113 @@ function parseClearSiteData(raw) {
|
|
|
3711
3713
|
}
|
|
3712
3714
|
return buildClearSiteData(directives);
|
|
3713
3715
|
}
|
|
3716
|
+
var ALT_SVC_DEFAULT_MAX_AGE = 86400;
|
|
3717
|
+
var ALT_SVC_DEPRECATED_H3_DRAFTS = /* @__PURE__ */ new Set(["h3-29", "h3-27", "h3-25", "h3-24", "h3-23", "h3-22"]);
|
|
3718
|
+
var ALT_SVC_PROTOCOL_NOTES = {
|
|
3719
|
+
"h3": "HTTP/3 over QUIC, the current protocol ID clients should be offered.",
|
|
3720
|
+
"h3-29": 'An outdated HTTP/3 draft version (draft-29); modern clients only match "h3".',
|
|
3721
|
+
"h3-27": 'An outdated HTTP/3 draft version (draft-27); modern clients only match "h3".',
|
|
3722
|
+
"h3-25": 'An outdated HTTP/3 draft version (draft-25); modern clients only match "h3".',
|
|
3723
|
+
"h2": "HTTP/2 over TLS.",
|
|
3724
|
+
"h2c": "HTTP/2 over cleartext TCP; rarely advertised via Alt-Svc since it needs an out-of-band upgrade rather than ALPN negotiation.",
|
|
3725
|
+
"http/1.1": "Plain HTTP/1.1; advertising it via Alt-Svc is unusual since it is already the universal fallback."
|
|
3726
|
+
};
|
|
3727
|
+
function buildAltSvcHeader(alternatives) {
|
|
3728
|
+
if (!Array.isArray(alternatives) || alternatives.length === 0) {
|
|
3729
|
+
return { error: "At least one alternative service is required" };
|
|
3730
|
+
}
|
|
3731
|
+
const warnings = [];
|
|
3732
|
+
const entries = [];
|
|
3733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3734
|
+
for (const alt of alternatives) {
|
|
3735
|
+
const protocolId = typeof alt.protocolId === "string" ? alt.protocolId.trim() : "";
|
|
3736
|
+
if (!protocolId) return { error: 'Each alternative needs a protocol-id (e.g. "h3")' };
|
|
3737
|
+
if (!Number.isInteger(alt.port) || alt.port < 1 || alt.port > 65535) {
|
|
3738
|
+
return { error: `Each alternative needs a valid port (1-65535), got ${JSON.stringify(alt.port)}` };
|
|
3739
|
+
}
|
|
3740
|
+
const host = alt.host?.trim();
|
|
3741
|
+
const authority = `${host ?? ""}:${alt.port}`;
|
|
3742
|
+
const maxAge = alt.maxAge !== void 0 ? alt.maxAge : ALT_SVC_DEFAULT_MAX_AGE;
|
|
3743
|
+
if (!Number.isFinite(maxAge) || maxAge < 0) return { error: "maxAge must be a non-negative number of seconds" };
|
|
3744
|
+
const persist = alt.persist === true;
|
|
3745
|
+
const dedupeKey = `${protocolId.toLowerCase()}|${authority.toLowerCase()}`;
|
|
3746
|
+
if (seen.has(dedupeKey)) {
|
|
3747
|
+
warnings.push(`duplicate alternative "${protocolId}=${JSON.stringify(authority)}" was removed`);
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
seen.add(dedupeKey);
|
|
3751
|
+
if (maxAge === 0) warnings.push(`"${protocolId}" has ma=0, so clients should treat it as already expired`);
|
|
3752
|
+
if (persist && maxAge === 0) warnings.push(`"${protocolId}" sets persist=1 with ma=0, which is contradictory: the entry expires immediately`);
|
|
3753
|
+
if (ALT_SVC_DEPRECATED_H3_DRAFTS.has(protocolId.toLowerCase())) {
|
|
3754
|
+
warnings.push(`"${protocolId}" is an outdated HTTP/3 draft protocol-id; modern clients only match "h3"`);
|
|
3755
|
+
}
|
|
3756
|
+
const note = ALT_SVC_PROTOCOL_NOTES[protocolId.toLowerCase()] ?? `Advertises "${protocolId}" as an alternative protocol clients may switch to for this authority.`;
|
|
3757
|
+
entries.push({ protocolId, authority, maxAge, persist, note });
|
|
3758
|
+
}
|
|
3759
|
+
if (entries.length === 0) return { error: "At least one alternative service is required" };
|
|
3760
|
+
const headerValue = entries.map((e) => {
|
|
3761
|
+
let s = `${e.protocolId}="${e.authority}"; ma=${e.maxAge}`;
|
|
3762
|
+
if (e.persist) s += "; persist=1";
|
|
3763
|
+
return s;
|
|
3764
|
+
}).join(", ");
|
|
3765
|
+
return { header: `Alt-Svc: ${headerValue}`, headerValue, entries, cleared: false, warnings };
|
|
3766
|
+
}
|
|
3767
|
+
function altSvcSplitTopLevel(s, sep) {
|
|
3768
|
+
const out = [];
|
|
3769
|
+
let inQuotes = false;
|
|
3770
|
+
let current = "";
|
|
3771
|
+
for (const ch of s) {
|
|
3772
|
+
if (ch === '"') inQuotes = !inQuotes;
|
|
3773
|
+
if (ch === sep && !inQuotes) {
|
|
3774
|
+
out.push(current);
|
|
3775
|
+
current = "";
|
|
3776
|
+
} else {
|
|
3777
|
+
current += ch;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
out.push(current);
|
|
3781
|
+
return out;
|
|
3782
|
+
}
|
|
3783
|
+
function parseAltSvcHeader(raw) {
|
|
3784
|
+
if (!raw || raw.trim() === "") return { error: "Input is empty" };
|
|
3785
|
+
const withoutLabel = raw.trim().replace(/^alt-svc:\s*/i, "");
|
|
3786
|
+
if (withoutLabel.toLowerCase() === "clear") {
|
|
3787
|
+
return { header: "Alt-Svc: clear", headerValue: "clear", entries: [], cleared: true, warnings: [] };
|
|
3788
|
+
}
|
|
3789
|
+
const parts = altSvcSplitTopLevel(withoutLabel, ",");
|
|
3790
|
+
const alternatives = [];
|
|
3791
|
+
for (const part of parts) {
|
|
3792
|
+
const trimmedPart = part.trim();
|
|
3793
|
+
if (!trimmedPart) continue;
|
|
3794
|
+
const eq = trimmedPart.indexOf("=");
|
|
3795
|
+
if (eq === -1) return { error: `Could not parse alternative: "${trimmedPart}"` };
|
|
3796
|
+
const protocolId = trimmedPart.slice(0, eq).trim();
|
|
3797
|
+
const rest = trimmedPart.slice(eq + 1).trim();
|
|
3798
|
+
const segments = altSvcSplitTopLevel(rest, ";").map((s) => s.trim());
|
|
3799
|
+
const authorityMatch = segments[0]?.match(/^"(.*)"$/);
|
|
3800
|
+
if (!authorityMatch) return { error: `Alternative "${protocolId}" is missing a quoted authority (e.g. ":443")` };
|
|
3801
|
+
const authority = authorityMatch[1];
|
|
3802
|
+
const colonIdx = authority.lastIndexOf(":");
|
|
3803
|
+
if (colonIdx === -1) return { error: `Alternative "${protocolId}" has an authority with no port: "${authority}"` };
|
|
3804
|
+
const host = authority.slice(0, colonIdx) || void 0;
|
|
3805
|
+
const portStr = authority.slice(colonIdx + 1);
|
|
3806
|
+
const port = Number(portStr);
|
|
3807
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return { error: `Alternative "${protocolId}" has an invalid port: "${portStr}"` };
|
|
3808
|
+
let maxAge;
|
|
3809
|
+
let persist = false;
|
|
3810
|
+
for (const seg of segments.slice(1)) {
|
|
3811
|
+
const sepIdx = seg.indexOf("=");
|
|
3812
|
+
if (sepIdx === -1) continue;
|
|
3813
|
+
const key = seg.slice(0, sepIdx).trim().toLowerCase();
|
|
3814
|
+
const value = seg.slice(sepIdx + 1).trim();
|
|
3815
|
+
if (key === "ma") maxAge = Number(value);
|
|
3816
|
+
if (key === "persist") persist = value === "1";
|
|
3817
|
+
}
|
|
3818
|
+
if (maxAge !== void 0 && !Number.isFinite(maxAge)) return { error: `Alternative "${protocolId}" has a non-numeric ma value` };
|
|
3819
|
+
alternatives.push({ protocolId, host, port, maxAge, persist });
|
|
3820
|
+
}
|
|
3821
|
+
if (alternatives.length === 0) return { error: "No alternative services found" };
|
|
3822
|
+
return buildAltSvcHeader(alternatives);
|
|
3823
|
+
}
|
|
3714
3824
|
|
|
3715
|
-
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, buildClearSiteData, buildClientHints, buildContentDisposition, buildDnsQueryUrl, buildGeoUrl, buildHsts, buildLinkHeader, buildMapUrl, buildPermissionsPolicy, buildPreferHeader, buildPriorityHeader, buildRangeHeader, buildReferrerPolicy, buildRetryAfterHeader, buildRobotsMetaTag, buildVaryHeader, buildXContentTypeOptionsHeader, buildXRobotsTag, calculateCookieSizeBudget, checkSetCookieHeader, countryCodeToFlag, extractMultipartBoundary, formatCoordinates, formatTtl, generateRobotsTxt, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, listCloseCodes, listOpcodes, lookupCloseCode, lookupOpcode, network_exports, parseAcceptLanguage, parseClearSiteData, parseClientHintHeaders, parseContentDisposition, parseContentRangeHeader, parseDnsResponse, parseGeoResponse, parseHar, parseHsts, parseIpv6Address, parseMultipartFormData, parsePermissionsPolicy, parsePreferHeader, parsePriorityHeader, parseRetryAfterHeader, parseRobotsMetaTag, parseUserAgent, parseVaryHeader, parseXContentTypeOptionsHeader, parseXRobotsTag, scoreSecurityHeaders, searchHeaders, searchStatusCodes, splitSitemap, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateManifest, validateRobotsTxt, validateSitemap };
|
|
3825
|
+
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, buildAltSvcHeader, buildCacheControl, buildClearSiteData, buildClientHints, buildContentDisposition, buildDnsQueryUrl, buildGeoUrl, buildHsts, buildLinkHeader, buildMapUrl, buildPermissionsPolicy, buildPreferHeader, buildPriorityHeader, buildRangeHeader, buildReferrerPolicy, buildRetryAfterHeader, buildRobotsMetaTag, buildVaryHeader, buildXContentTypeOptionsHeader, buildXRobotsTag, calculateCookieSizeBudget, checkSetCookieHeader, countryCodeToFlag, extractMultipartBoundary, formatCoordinates, formatTtl, generateRobotsTxt, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, listCloseCodes, listOpcodes, lookupCloseCode, lookupOpcode, network_exports, parseAcceptLanguage, parseAltSvcHeader, parseClearSiteData, parseClientHintHeaders, parseContentDisposition, parseContentRangeHeader, parseDnsResponse, parseGeoResponse, parseHar, parseHsts, parseIpv6Address, parseMultipartFormData, parsePermissionsPolicy, parsePreferHeader, parsePriorityHeader, parseRetryAfterHeader, parseRobotsMetaTag, parseUserAgent, parseVaryHeader, parseXContentTypeOptionsHeader, parseXRobotsTag, scoreSecurityHeaders, searchHeaders, searchStatusCodes, splitSitemap, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateManifest, validateRobotsTxt, validateSitemap };
|
|
@@ -13,6 +13,7 @@ __export(media_exports, {
|
|
|
13
13
|
readPdfMetadata: () => readPdfMetadata,
|
|
14
14
|
readPdfOutline: () => readPdfOutline,
|
|
15
15
|
readPdfPageDimensions: () => readPdfPageDimensions,
|
|
16
|
+
readPngChunks: () => readPngChunks,
|
|
16
17
|
readVideoInfo: () => readVideoInfo,
|
|
17
18
|
readWavInfo: () => readWavInfo
|
|
18
19
|
});
|
|
@@ -1506,5 +1507,115 @@ function readMidiInfo(bytes) {
|
|
|
1506
1507
|
tracks
|
|
1507
1508
|
};
|
|
1508
1509
|
}
|
|
1510
|
+
var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
1511
|
+
var PNG_CRC_TABLE = null;
|
|
1512
|
+
function getPngCrcTable() {
|
|
1513
|
+
if (PNG_CRC_TABLE) return PNG_CRC_TABLE;
|
|
1514
|
+
const table = new Uint32Array(256);
|
|
1515
|
+
for (let n = 0; n < 256; n++) {
|
|
1516
|
+
let c = n;
|
|
1517
|
+
for (let k = 0; k < 8; k++) {
|
|
1518
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1519
|
+
}
|
|
1520
|
+
table[n] = c;
|
|
1521
|
+
}
|
|
1522
|
+
PNG_CRC_TABLE = table;
|
|
1523
|
+
return table;
|
|
1524
|
+
}
|
|
1525
|
+
function pngCrc32(bytes) {
|
|
1526
|
+
const table = getPngCrcTable();
|
|
1527
|
+
let crc = 4294967295;
|
|
1528
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1529
|
+
crc = table[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
|
|
1530
|
+
}
|
|
1531
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1532
|
+
}
|
|
1533
|
+
function pad2(n) {
|
|
1534
|
+
return n < 10 ? `0${n}` : `${n}`;
|
|
1535
|
+
}
|
|
1536
|
+
function readPngChunks(bytes) {
|
|
1537
|
+
if (bytes.length < 8) return { error: "File too small to be a valid PNG file" };
|
|
1538
|
+
for (let i = 0; i < 8; i++) {
|
|
1539
|
+
if (bytes[i] !== PNG_SIGNATURE[i]) return { error: "Not a valid PNG file (bad 8-byte signature)" };
|
|
1540
|
+
}
|
|
1541
|
+
const chunks = [];
|
|
1542
|
+
const textChunks = [];
|
|
1543
|
+
let header = null;
|
|
1544
|
+
let physicalDimensions = null;
|
|
1545
|
+
let gamma = null;
|
|
1546
|
+
let lastModified = null;
|
|
1547
|
+
let isAnimated = false;
|
|
1548
|
+
let offset = 8;
|
|
1549
|
+
while (offset + 8 <= bytes.length) {
|
|
1550
|
+
const dataLength = readU32BE(bytes, offset);
|
|
1551
|
+
const type = String.fromCharCode(bytes[offset + 4], bytes[offset + 5], bytes[offset + 6], bytes[offset + 7]);
|
|
1552
|
+
const dataStart = offset + 8;
|
|
1553
|
+
const dataEnd = dataStart + dataLength;
|
|
1554
|
+
const crcOffset = dataEnd;
|
|
1555
|
+
if (crcOffset + 4 > bytes.length) {
|
|
1556
|
+
return { error: `Truncated chunk "${type}" at offset ${offset}` };
|
|
1557
|
+
}
|
|
1558
|
+
const data = bytes.subarray(dataStart, dataEnd);
|
|
1559
|
+
const expectedCrc = readU32BE(bytes, crcOffset);
|
|
1560
|
+
const actualCrc = pngCrc32(bytes.subarray(offset + 4, dataEnd));
|
|
1561
|
+
const firstChar = type.charCodeAt(0);
|
|
1562
|
+
const critical = firstChar >= 65 && firstChar <= 90;
|
|
1563
|
+
chunks.push({ type, offset, dataLength, crcValid: actualCrc === expectedCrc, critical });
|
|
1564
|
+
if (type === "IHDR" && data.length >= 13) {
|
|
1565
|
+
const colorTypeNum = data[9];
|
|
1566
|
+
header = {
|
|
1567
|
+
width: readU32BE(data, 0),
|
|
1568
|
+
height: readU32BE(data, 4),
|
|
1569
|
+
bitDepth: data[8],
|
|
1570
|
+
colorType: PNG_COLOR_TYPES[colorTypeNum] ?? "unknown",
|
|
1571
|
+
hasAlpha: colorTypeNum === 4 || colorTypeNum === 6,
|
|
1572
|
+
interlace: data[12] === 1
|
|
1573
|
+
};
|
|
1574
|
+
} else if (type === "tEXt") {
|
|
1575
|
+
const nullIdx = data.indexOf(0);
|
|
1576
|
+
if (nullIdx !== -1) {
|
|
1577
|
+
textChunks.push({
|
|
1578
|
+
keyword: Buffer.from(data.subarray(0, nullIdx)).toString("latin1"),
|
|
1579
|
+
text: Buffer.from(data.subarray(nullIdx + 1)).toString("latin1"),
|
|
1580
|
+
compressed: false
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
} else if (type === "zTXt" || type === "iTXt") {
|
|
1584
|
+
const nullIdx = data.indexOf(0);
|
|
1585
|
+
if (nullIdx !== -1) {
|
|
1586
|
+
textChunks.push({ keyword: Buffer.from(data.subarray(0, nullIdx)).toString("latin1"), text: null, compressed: true });
|
|
1587
|
+
}
|
|
1588
|
+
} else if (type === "pHYs" && data.length >= 9) {
|
|
1589
|
+
physicalDimensions = {
|
|
1590
|
+
pixelsPerUnitX: readU32BE(data, 0),
|
|
1591
|
+
pixelsPerUnitY: readU32BE(data, 4),
|
|
1592
|
+
unit: data[8] === 1 ? "meter" : "unknown"
|
|
1593
|
+
};
|
|
1594
|
+
} else if (type === "gAMA" && data.length >= 4) {
|
|
1595
|
+
gamma = readU32BE(data, 0) / 1e5;
|
|
1596
|
+
} else if (type === "tIME" && data.length >= 7) {
|
|
1597
|
+
const year = data[0] << 8 | data[1];
|
|
1598
|
+
lastModified = `${year}-${pad2(data[2])}-${pad2(data[3])}T${pad2(data[4])}:${pad2(data[5])}:${pad2(data[6])}Z`;
|
|
1599
|
+
} else if (type === "acTL") {
|
|
1600
|
+
isAnimated = true;
|
|
1601
|
+
}
|
|
1602
|
+
offset = crcOffset + 4;
|
|
1603
|
+
if (type === "IEND") break;
|
|
1604
|
+
}
|
|
1605
|
+
if (chunks.length === 0) return { error: "No chunks found after the PNG signature" };
|
|
1606
|
+
if (!header) return { error: "Missing required IHDR chunk" };
|
|
1607
|
+
return {
|
|
1608
|
+
valid: true,
|
|
1609
|
+
fileSize: bytes.length,
|
|
1610
|
+
chunkCount: chunks.length,
|
|
1611
|
+
chunks,
|
|
1612
|
+
header,
|
|
1613
|
+
textChunks,
|
|
1614
|
+
physicalDimensions,
|
|
1615
|
+
gamma,
|
|
1616
|
+
lastModified,
|
|
1617
|
+
isAnimated
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1509
1620
|
|
|
1510
|
-
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readMidiInfo, readPdfMetadata, readPdfOutline, readPdfPageDimensions, readVideoInfo, readWavInfo };
|
|
1621
|
+
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readMidiInfo, readPdfMetadata, readPdfOutline, readPdfPageDimensions, readPngChunks, readVideoInfo, readWavInfo };
|