@utilix-tech/sdk 0.94.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 CHANGED
@@ -2,6 +2,10 @@
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
+
5
9
  ## 0.94.0
6
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.
7
11
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/sdk
2
2
 
3
- **740 developer utility functions for Node.js: runs locally, no API key required.**
3
+ **742 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)
@@ -772,6 +772,16 @@ summarizeGitDiffStat(" src/foo.ts | 12 +++++-------\n 1 file changed, 5 insertio
772
772
  import { buildAltSvcHeader, parseAltSvcHeader } from "@utilix-tech/sdk/network";
773
773
  buildAltSvcHeader([{ protocolId: "h3", port: 443, maxAge: 86400 }]);
774
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 }
775
785
  ```
776
786
 
777
787
  ---
@@ -1090,7 +1100,7 @@ readMidiInfo(new Uint8Array(midiBytes));
1090
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 |
1091
1101
  | `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, entropy estimator, fake data, CSP nonce generator |
1092
1102
  | `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
1093
- | `@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 |
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 |
1094
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) |
1095
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 |
1096
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 |
@@ -1098,7 +1108,7 @@ readMidiInfo(new Uint8Array(midiBytes));
1098
1108
  | `@utilix-tech/sdk/css` | CSS gradient, box-shadow, border-radius, keyframe animation generators |
1099
1109
  | `@utilix-tech/sdk/misc` | SVG optimize/sanitize, file size format, Unicode inspector |
1100
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 |
1101
- | `@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 |
1102
1112
 
1103
1113
  ---
1104
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 };
@@ -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 };
package/dist/index.cjs CHANGED
@@ -9372,6 +9372,7 @@ __export(units_exports, {
9372
9372
  decimalToIpCidr: () => decimalToIpCidr,
9373
9373
  describePermissions: () => describePermissions,
9374
9374
  detectNegativeAmortization: () => detectNegativeAmortization,
9375
+ estimateAudioFileSize: () => estimateAudioFileSize,
9375
9376
  fitIntoBox: () => fitIntoBox,
9376
9377
  formatNumber: () => formatNumber,
9377
9378
  formatValue: () => formatValue,
@@ -12468,6 +12469,56 @@ function detectNegativeAmortization(input) {
12468
12469
  capCrossedMonth
12469
12470
  };
12470
12471
  }
12472
+ var AUDIO_FILE_SIZE_WAV_HEADER_BYTES = 44;
12473
+ function estimateAudioFileSize(input) {
12474
+ const { mode, durationSeconds } = input;
12475
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
12476
+ return { error: "durationSeconds must be a positive number" };
12477
+ }
12478
+ const round23 = (n) => Math.round(n * 100) / 100;
12479
+ if (mode === "pcm") {
12480
+ const { sampleRateHz, bitDepth, channels } = input;
12481
+ if (!Number.isFinite(sampleRateHz) || sampleRateHz <= 0) {
12482
+ return { error: 'sampleRateHz must be a positive number for mode "pcm"' };
12483
+ }
12484
+ if (!Number.isFinite(bitDepth) || bitDepth <= 0) {
12485
+ return { error: 'bitDepth must be a positive number for mode "pcm"' };
12486
+ }
12487
+ if (!Number.isInteger(channels) || channels <= 0) {
12488
+ return { error: 'channels must be a positive whole number for mode "pcm"' };
12489
+ }
12490
+ const dataBytes = sampleRateHz * (bitDepth / 8) * channels * durationSeconds;
12491
+ const fileSizeBytes = dataBytes + AUDIO_FILE_SIZE_WAV_HEADER_BYTES;
12492
+ return {
12493
+ mode,
12494
+ durationSeconds,
12495
+ sampleRateHz,
12496
+ bitDepth,
12497
+ channels,
12498
+ dataBytes: Math.round(dataBytes),
12499
+ fileSizeBytes: Math.round(fileSizeBytes),
12500
+ fileSizeMB: round23(fileSizeBytes / 1e6),
12501
+ fileSizeMiB: round23(fileSizeBytes / 1048576)
12502
+ };
12503
+ }
12504
+ if (mode === "compressed") {
12505
+ const { bitrateKbps } = input;
12506
+ if (!Number.isFinite(bitrateKbps) || bitrateKbps <= 0) {
12507
+ return { error: 'bitrateKbps must be a positive number for mode "compressed"' };
12508
+ }
12509
+ const dataBytes = bitrateKbps * 1e3 * durationSeconds / 8;
12510
+ return {
12511
+ mode,
12512
+ durationSeconds,
12513
+ bitrateKbps,
12514
+ dataBytes: Math.round(dataBytes),
12515
+ fileSizeBytes: Math.round(dataBytes),
12516
+ fileSizeMB: round23(dataBytes / 1e6),
12517
+ fileSizeMiB: round23(dataBytes / 1048576)
12518
+ };
12519
+ }
12520
+ return { error: 'mode must be "pcm" or "compressed"' };
12521
+ }
12471
12522
 
12472
12523
  // src/tools/network.ts
12473
12524
  var network_exports = {};
@@ -27334,6 +27385,7 @@ __export(media_exports, {
27334
27385
  readPdfMetadata: () => readPdfMetadata,
27335
27386
  readPdfOutline: () => readPdfOutline,
27336
27387
  readPdfPageDimensions: () => readPdfPageDimensions,
27388
+ readPngChunks: () => readPngChunks,
27337
27389
  readVideoInfo: () => readVideoInfo,
27338
27390
  readWavInfo: () => readWavInfo
27339
27391
  });
@@ -28827,6 +28879,116 @@ function readMidiInfo(bytes) {
28827
28879
  tracks
28828
28880
  };
28829
28881
  }
28882
+ var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
28883
+ var PNG_CRC_TABLE = null;
28884
+ function getPngCrcTable() {
28885
+ if (PNG_CRC_TABLE) return PNG_CRC_TABLE;
28886
+ const table = new Uint32Array(256);
28887
+ for (let n = 0; n < 256; n++) {
28888
+ let c = n;
28889
+ for (let k = 0; k < 8; k++) {
28890
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
28891
+ }
28892
+ table[n] = c;
28893
+ }
28894
+ PNG_CRC_TABLE = table;
28895
+ return table;
28896
+ }
28897
+ function pngCrc32(bytes) {
28898
+ const table = getPngCrcTable();
28899
+ let crc = 4294967295;
28900
+ for (let i = 0; i < bytes.length; i++) {
28901
+ crc = table[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
28902
+ }
28903
+ return (crc ^ 4294967295) >>> 0;
28904
+ }
28905
+ function pad2(n) {
28906
+ return n < 10 ? `0${n}` : `${n}`;
28907
+ }
28908
+ function readPngChunks(bytes) {
28909
+ if (bytes.length < 8) return { error: "File too small to be a valid PNG file" };
28910
+ for (let i = 0; i < 8; i++) {
28911
+ if (bytes[i] !== PNG_SIGNATURE[i]) return { error: "Not a valid PNG file (bad 8-byte signature)" };
28912
+ }
28913
+ const chunks = [];
28914
+ const textChunks = [];
28915
+ let header = null;
28916
+ let physicalDimensions = null;
28917
+ let gamma = null;
28918
+ let lastModified = null;
28919
+ let isAnimated = false;
28920
+ let offset = 8;
28921
+ while (offset + 8 <= bytes.length) {
28922
+ const dataLength = readU32BE(bytes, offset);
28923
+ const type = String.fromCharCode(bytes[offset + 4], bytes[offset + 5], bytes[offset + 6], bytes[offset + 7]);
28924
+ const dataStart = offset + 8;
28925
+ const dataEnd = dataStart + dataLength;
28926
+ const crcOffset = dataEnd;
28927
+ if (crcOffset + 4 > bytes.length) {
28928
+ return { error: `Truncated chunk "${type}" at offset ${offset}` };
28929
+ }
28930
+ const data = bytes.subarray(dataStart, dataEnd);
28931
+ const expectedCrc = readU32BE(bytes, crcOffset);
28932
+ const actualCrc = pngCrc32(bytes.subarray(offset + 4, dataEnd));
28933
+ const firstChar = type.charCodeAt(0);
28934
+ const critical = firstChar >= 65 && firstChar <= 90;
28935
+ chunks.push({ type, offset, dataLength, crcValid: actualCrc === expectedCrc, critical });
28936
+ if (type === "IHDR" && data.length >= 13) {
28937
+ const colorTypeNum = data[9];
28938
+ header = {
28939
+ width: readU32BE(data, 0),
28940
+ height: readU32BE(data, 4),
28941
+ bitDepth: data[8],
28942
+ colorType: PNG_COLOR_TYPES[colorTypeNum] ?? "unknown",
28943
+ hasAlpha: colorTypeNum === 4 || colorTypeNum === 6,
28944
+ interlace: data[12] === 1
28945
+ };
28946
+ } else if (type === "tEXt") {
28947
+ const nullIdx = data.indexOf(0);
28948
+ if (nullIdx !== -1) {
28949
+ textChunks.push({
28950
+ keyword: Buffer.from(data.subarray(0, nullIdx)).toString("latin1"),
28951
+ text: Buffer.from(data.subarray(nullIdx + 1)).toString("latin1"),
28952
+ compressed: false
28953
+ });
28954
+ }
28955
+ } else if (type === "zTXt" || type === "iTXt") {
28956
+ const nullIdx = data.indexOf(0);
28957
+ if (nullIdx !== -1) {
28958
+ textChunks.push({ keyword: Buffer.from(data.subarray(0, nullIdx)).toString("latin1"), text: null, compressed: true });
28959
+ }
28960
+ } else if (type === "pHYs" && data.length >= 9) {
28961
+ physicalDimensions = {
28962
+ pixelsPerUnitX: readU32BE(data, 0),
28963
+ pixelsPerUnitY: readU32BE(data, 4),
28964
+ unit: data[8] === 1 ? "meter" : "unknown"
28965
+ };
28966
+ } else if (type === "gAMA" && data.length >= 4) {
28967
+ gamma = readU32BE(data, 0) / 1e5;
28968
+ } else if (type === "tIME" && data.length >= 7) {
28969
+ const year = data[0] << 8 | data[1];
28970
+ lastModified = `${year}-${pad2(data[2])}-${pad2(data[3])}T${pad2(data[4])}:${pad2(data[5])}:${pad2(data[6])}Z`;
28971
+ } else if (type === "acTL") {
28972
+ isAnimated = true;
28973
+ }
28974
+ offset = crcOffset + 4;
28975
+ if (type === "IEND") break;
28976
+ }
28977
+ if (chunks.length === 0) return { error: "No chunks found after the PNG signature" };
28978
+ if (!header) return { error: "Missing required IHDR chunk" };
28979
+ return {
28980
+ valid: true,
28981
+ fileSize: bytes.length,
28982
+ chunkCount: chunks.length,
28983
+ chunks,
28984
+ header,
28985
+ textChunks,
28986
+ physicalDimensions,
28987
+ gamma,
28988
+ lastModified,
28989
+ isAnimated
28990
+ };
28991
+ }
28830
28992
 
28831
28993
  // src/index.ts
28832
28994
  var version = "0.1.0";
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ export { t as text } from './text-DOopHoK3.cjs';
5
5
  export { d as data } from './data-3qMdFIF5.cjs';
6
6
  export { g as generators } from './generators-DAXXrMvG.cjs';
7
7
  export { t as time } from './time-VMAy_SrV.cjs';
8
- export { u as units } from './units-DnWrOTSV.cjs';
8
+ export { u as units } from './units-BBzv0VIt.cjs';
9
9
  export { n as network } from './network-B6fQrsCc.cjs';
10
10
  export { a as api } from './api-CQnIUrVB.cjs';
11
11
  export { c as code } from './code-CZHElFHu.cjs';
@@ -13,7 +13,7 @@ export { c as color } from './color-tPwZCr9H.cjs';
13
13
  export { c as css } from './css-Cf7AMGM-.cjs';
14
14
  export { m as misc } from './misc-CA3N198T.cjs';
15
15
  export { a as ai_agent } from './ai_agent-B9wWLBDf.cjs';
16
- export { m as media } from './media-DF8BS-iA.cjs';
16
+ export { m as media } from './media-C8Y67lUI.cjs';
17
17
 
18
18
  declare const version = "0.1.0";
19
19
 
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { t as text } from './text-DOopHoK3.js';
5
5
  export { d as data } from './data-3qMdFIF5.js';
6
6
  export { g as generators } from './generators-DAXXrMvG.js';
7
7
  export { t as time } from './time-VMAy_SrV.js';
8
- export { u as units } from './units-DnWrOTSV.js';
8
+ export { u as units } from './units-BBzv0VIt.js';
9
9
  export { n as network } from './network-B6fQrsCc.js';
10
10
  export { a as api } from './api-CQnIUrVB.js';
11
11
  export { c as code } from './code-CZHElFHu.js';
@@ -13,7 +13,7 @@ export { c as color } from './color-tPwZCr9H.js';
13
13
  export { c as css } from './css-Cf7AMGM-.js';
14
14
  export { m as misc } from './misc-CA3N198T.js';
15
15
  export { a as ai_agent } from './ai_agent-B9wWLBDf.js';
16
- export { m as media } from './media-DF8BS-iA.js';
16
+ export { m as media } from './media-C8Y67lUI.js';
17
17
 
18
18
  declare const version = "0.1.0";
19
19
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { media_exports as media } from './chunk-ZWFZRDQ4.js';
2
- export { units_exports as units } from './chunk-XAPHAUPI.js';
1
+ export { media_exports as media } from './chunk-YSS4VVZU.js';
2
+ export { units_exports as units } from './chunk-G5NUC5MV.js';
3
3
  export { network_exports as network } from './chunk-K242OOF4.js';
4
4
  export { api_exports as api } from './chunk-U2HREMZU.js';
5
5
  export { code_exports as code } from './chunk-KRPIOHJ4.js';
@@ -289,6 +289,59 @@ interface MidiInfoError {
289
289
  * frames-per-second/ticks-per-frame rate instead.
290
290
  */
291
291
  declare function readMidiInfo(bytes: Uint8Array): MidiInfo | MidiInfoError;
292
+ interface PngChunkEntry {
293
+ type: string;
294
+ offset: number;
295
+ dataLength: number;
296
+ crcValid: boolean;
297
+ /** A chunk type is "critical" (decoders must understand it) when its
298
+ * first letter is uppercase, e.g. IHDR vs. an ancillary tEXt. */
299
+ critical: boolean;
300
+ }
301
+ interface PngHeaderInfo {
302
+ width: number;
303
+ height: number;
304
+ bitDepth: number;
305
+ colorType: string;
306
+ hasAlpha: boolean;
307
+ interlace: boolean;
308
+ }
309
+ interface PngTextChunk {
310
+ keyword: string;
311
+ text: string | null;
312
+ /** True for zTXt/iTXt, whose text this function does not decode. */
313
+ compressed: boolean;
314
+ }
315
+ interface PngPhysicalDimensions {
316
+ pixelsPerUnitX: number;
317
+ pixelsPerUnitY: number;
318
+ unit: 'meter' | 'unknown';
319
+ }
320
+ interface PngChunkInfo {
321
+ valid: true;
322
+ fileSize: number;
323
+ chunkCount: number;
324
+ chunks: PngChunkEntry[];
325
+ header: PngHeaderInfo | null;
326
+ textChunks: PngTextChunk[];
327
+ physicalDimensions: PngPhysicalDimensions | null;
328
+ gamma: number | null;
329
+ lastModified: string | null;
330
+ isAnimated: boolean;
331
+ }
332
+ interface PngChunkInfoError {
333
+ error: string;
334
+ }
335
+ /**
336
+ * Walk a PNG file's full chunk stream directly from its bytes: every
337
+ * chunk's type, byte offset, data length, and whether its stored CRC-32
338
+ * matches the type+data it covers, plus the decoded IHDR header and a
339
+ * handful of common ancillary chunks (tEXt, pHYs, gAMA, tIME, and acTL for
340
+ * APNG animation). No image library or pixel decoding is involved, and
341
+ * zTXt/iTXt text is listed but not zlib-inflated, so the result is
342
+ * identical in a browser tool and on a server.
343
+ */
344
+ declare function readPngChunks(bytes: Uint8Array): PngChunkInfo | PngChunkInfoError;
292
345
 
293
346
  type media_ExifData = ExifData;
294
347
  type media_ExifError = ExifError;
@@ -318,6 +371,12 @@ type media_PdfOutlineResult = PdfOutlineResult;
318
371
  type media_PdfPageDimension = PdfPageDimension;
319
372
  type media_PdfPageDimensionsResult = PdfPageDimensionsResult;
320
373
  type media_PdfPageOrientation = PdfPageOrientation;
374
+ type media_PngChunkEntry = PngChunkEntry;
375
+ type media_PngChunkInfo = PngChunkInfo;
376
+ type media_PngChunkInfoError = PngChunkInfoError;
377
+ type media_PngHeaderInfo = PngHeaderInfo;
378
+ type media_PngPhysicalDimensions = PngPhysicalDimensions;
379
+ type media_PngTextChunk = PngTextChunk;
321
380
  type media_VideoInfo = VideoInfo;
322
381
  type media_VideoInfoError = VideoInfoError;
323
382
  type media_WavInfo = WavInfo;
@@ -332,10 +391,11 @@ declare const media_readMidiInfo: typeof readMidiInfo;
332
391
  declare const media_readPdfMetadata: typeof readPdfMetadata;
333
392
  declare const media_readPdfOutline: typeof readPdfOutline;
334
393
  declare const media_readPdfPageDimensions: typeof readPdfPageDimensions;
394
+ declare const media_readPngChunks: typeof readPngChunks;
335
395
  declare const media_readVideoInfo: typeof readVideoInfo;
336
396
  declare const media_readWavInfo: typeof readWavInfo;
337
397
  declare namespace media {
338
- export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_MidiDivision as MidiDivision, type media_MidiInfo as MidiInfo, type media_MidiInfoError as MidiInfoError, type media_MidiTimeSignature as MidiTimeSignature, type media_MidiTrackInfo as MidiTrackInfo, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfOutlineDestType as PdfOutlineDestType, type media_PdfOutlineError as PdfOutlineError, type media_PdfOutlineItem as PdfOutlineItem, type media_PdfOutlineResult as PdfOutlineResult, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readMidiInfo as readMidiInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfOutline as readPdfOutline, media_readPdfPageDimensions as readPdfPageDimensions, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
398
+ export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_MidiDivision as MidiDivision, type media_MidiInfo as MidiInfo, type media_MidiInfoError as MidiInfoError, type media_MidiTimeSignature as MidiTimeSignature, type media_MidiTrackInfo as MidiTrackInfo, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfOutlineDestType as PdfOutlineDestType, type media_PdfOutlineError as PdfOutlineError, type media_PdfOutlineItem as PdfOutlineItem, type media_PdfOutlineResult as PdfOutlineResult, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_PngChunkEntry as PngChunkEntry, type media_PngChunkInfo as PngChunkInfo, type media_PngChunkInfoError as PngChunkInfoError, type media_PngHeaderInfo as PngHeaderInfo, type media_PngPhysicalDimensions as PngPhysicalDimensions, type media_PngTextChunk as PngTextChunk, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readMidiInfo as readMidiInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfOutline as readPdfOutline, media_readPdfPageDimensions as readPdfPageDimensions, media_readPngChunks as readPngChunks, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
339
399
  }
340
400
 
341
- export { readExifData as A, readFlacInfo as B, readIcoInfo as C, readId3Tags as D, type ExifData as E, type FlacInfo as F, type GifInfo as G, readImageInfo as H, type IcoImageEntry as I, readMidiInfo as J, readPdfMetadata as K, readPdfOutline as L, type MidiDivision as M, readPdfPageDimensions as N, readVideoInfo as O, type PdfMetadata as P, readWavInfo as Q, type VideoInfo as V, type WavInfo as W, type ExifError as a, type FlacInfoError as b, type GifInfoError as c, type IcoInfo as d, type IcoInfoError as e, type Id3Tags as f, type Id3TagsError as g, type ImageFormat as h, type ImageInfo as i, type ImageInfoError as j, type MidiInfo as k, type MidiInfoError as l, media as m, type MidiTimeSignature as n, type MidiTrackInfo as o, type PdfMetadataError as p, type PdfOutlineDestType as q, type PdfOutlineError as r, type PdfOutlineItem as s, type PdfOutlineResult as t, type PdfPageDimension as u, type PdfPageDimensionsResult as v, type PdfPageOrientation as w, type VideoInfoError as x, type WavInfoError as y, inspectGifFrames as z };
401
+ export { type PngHeaderInfo as A, type PngPhysicalDimensions as B, type PngTextChunk as C, type VideoInfoError as D, type ExifData as E, type FlacInfo as F, type GifInfo as G, type WavInfoError as H, type IcoImageEntry as I, inspectGifFrames as J, readExifData as K, readFlacInfo as L, type MidiDivision as M, readIcoInfo as N, readId3Tags as O, type PdfMetadata as P, readImageInfo as Q, readMidiInfo as R, readPdfMetadata as S, readPdfOutline as T, readPdfPageDimensions as U, type VideoInfo as V, type WavInfo as W, readPngChunks as X, readVideoInfo as Y, readWavInfo as Z, type ExifError as a, type FlacInfoError as b, type GifInfoError as c, type IcoInfo as d, type IcoInfoError as e, type Id3Tags as f, type Id3TagsError as g, type ImageFormat as h, type ImageInfo as i, type ImageInfoError as j, type MidiInfo as k, type MidiInfoError as l, media as m, type MidiTimeSignature as n, type MidiTrackInfo as o, type PdfMetadataError as p, type PdfOutlineDestType as q, type PdfOutlineError as r, type PdfOutlineItem as s, type PdfOutlineResult as t, type PdfPageDimension as u, type PdfPageDimensionsResult as v, type PdfPageOrientation as w, type PngChunkEntry as x, type PngChunkInfo as y, type PngChunkInfoError as z };