@utilix-tech/sdk 0.25.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +27 -8
- package/dist/{api-Xl7OzIgq.d.cts → api-DmTNE4c1.d.cts} +29 -2
- package/dist/{api-Xl7OzIgq.d.ts → api-DmTNE4c1.d.ts} +29 -2
- package/dist/{chunk-Q3B3YWOA.js → chunk-52ZDKSIX.js} +45 -2
- package/dist/{chunk-FVKNR3PV.js → chunk-I63BCOAG.js} +88 -1
- package/dist/{chunk-XMKJYVKB.js → chunk-Q3M23WNJ.js} +97 -1
- package/dist/index.cjs +226 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{media-9HEpTPGA.d.cts → media-CN17a3MM.d.cts} +34 -2
- package/dist/{media-9HEpTPGA.d.ts → media-CN17a3MM.d.ts} +34 -2
- package/dist/tools/api.cjs +43 -0
- package/dist/tools/api.d.cts +1 -1
- package/dist/tools/api.d.ts +1 -1
- package/dist/tools/api.js +1 -1
- package/dist/tools/media.cjs +96 -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/units.cjs +87 -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-DUqjk24x.d.ts → units-COt5uI20.d.cts} +64 -2
- package/dist/{units-DUqjk24x.d.cts → units-COt5uI20.d.ts} +64 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Full release notes for all Utilix surfaces (browser tools, REST API, SDKs, MCP server) live at [utilix.tech/changelog](https://utilix.tech/changelog). This file tracks just this package.
|
|
4
4
|
|
|
5
|
+
## 0.27.0
|
|
6
|
+
- Add `calculateRuleOf72` (rule-of-72-calculator): estimate years to double an investment from a rate, or the rate needed to double in a given number of years, alongside the exact logarithmic result.
|
|
7
|
+
- Add `readFlacInfo` (flac-metadata-reader): sample rate, channels, bit depth, total samples, and duration from a FLAC file's STREAMINFO block, plus Vorbis comment tags when present.
|
|
8
|
+
|
|
9
|
+
## 0.26.0
|
|
10
|
+
- Add `computeJwkThumbprint` (jwk-thumbprint): RFC 7638 canonical thumbprint of a single JWK, hashed with SHA-256/384/512.
|
|
11
|
+
- Add `calculateVideoBitrate` (video-bitrate-calculator): resulting file size from a video/audio bitrate and duration, or the required bitrate to hit a target file size.
|
|
12
|
+
|
|
5
13
|
## 0.25.0
|
|
6
14
|
- Add `calculateBreakEven` (break-even-calculator): break-even unit count and revenue from fixed costs, variable cost per unit, and price per unit, plus profit and margin of safety at an optional sales volume.
|
|
7
15
|
- Add `calculateStringDistance` (string-distance): Levenshtein edit distance and normalized similarity between two strings, with an optional Damerau (transposition) mode.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**578 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)
|
|
@@ -272,7 +272,7 @@ formatIso8601Duration(90061); // { iso: "P1DT1H1M1S", ... }
|
|
|
272
272
|
### `/units`: Conversions
|
|
273
273
|
|
|
274
274
|
```ts
|
|
275
|
-
import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue, calculatePercentageChange, convertAprApy, calculateMarginMarkup, calculateDiscount, applySuccessiveDiscounts, calculateBreakEven } from "@utilix-tech/sdk/units";
|
|
275
|
+
import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue, calculatePercentageChange, convertAprApy, calculateMarginMarkup, calculateDiscount, applySuccessiveDiscounts, calculateBreakEven, calculateVideoBitrate, calculateRuleOf72 } from "@utilix-tech/sdk/units";
|
|
276
276
|
|
|
277
277
|
// File sizes
|
|
278
278
|
convertBytes(1073741824, "GB"); // 1
|
|
@@ -354,6 +354,14 @@ applySuccessiveDiscounts(100, [20, 10]);
|
|
|
354
354
|
// Break-even point: fixed costs, variable cost/unit, price/unit
|
|
355
355
|
calculateBreakEven({ fixedCosts: 10000, variableCostPerUnit: 20, pricePerUnit: 50 });
|
|
356
356
|
// { breakEvenUnitsRounded: 334, breakEvenRevenue: 16666.6667, contributionMarginPerUnit: 30, ... }
|
|
357
|
+
|
|
358
|
+
// Video file size from a bitrate + duration, or the bitrate to hit a target size
|
|
359
|
+
calculateVideoBitrate({ mode: "size-from-bitrate", durationSeconds: 600, videoBitrateKbps: 5000, audioBitrateKbps: 192 });
|
|
360
|
+
// { totalBitrateKbps: 5192, fileSizeBytes: 389400000, fileSizeMB: 389.4, fileSizeMiB: 371.36, ... }
|
|
361
|
+
|
|
362
|
+
// Rule of 72: years to double from a rate, or the rate needed to double in N years
|
|
363
|
+
calculateRuleOf72(8, "rate-to-years");
|
|
364
|
+
// { ruleOf72Estimate: 9, exactValue: 9.0065, errorPercent: -0.0718, ... }
|
|
357
365
|
```
|
|
358
366
|
|
|
359
367
|
---
|
|
@@ -405,7 +413,7 @@ buildCacheControl({ visibility: "public", maxAge: 3600, mustRevalidate: true });
|
|
|
405
413
|
### `/api`: cURL, JWT, CORS, HTTP
|
|
406
414
|
|
|
407
415
|
```ts
|
|
408
|
-
import { buildCurl, parseCurlCommand, decodeJwt, signJwt, parseJwks,
|
|
416
|
+
import { buildCurl, parseCurlCommand, decodeJwt, signJwt, parseJwks, computeJwkThumbprint,
|
|
409
417
|
generateCors, generateCspHeader } from "@utilix-tech/sdk/api";
|
|
410
418
|
|
|
411
419
|
buildCurl({
|
|
@@ -429,6 +437,10 @@ decodeJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
|
|
|
429
437
|
parseJwks('{"keys":[{"kty":"RSA","use":"sig","kid":"1","n":"...","e":"AQAB"}]}');
|
|
430
438
|
// { ok: true, keys: [{ kty: "RSA", kid: "1", keySize: 2048, ... }], count: 1, duplicateKids: [] }
|
|
431
439
|
|
|
440
|
+
// JWK thumbprint (RFC 7638): canonical fingerprint of a single JWK
|
|
441
|
+
computeJwkThumbprint('{"kty":"oct","k":"c2VjcmV0"}');
|
|
442
|
+
// { ok: true, kty: "oct", hashAlg: "SHA-256", thumbprint: "DWBh0SEIAPYh1x5uvot4z3AhaikHkxNJa3Ada2fT-Cg", ... }
|
|
443
|
+
|
|
432
444
|
// Generate CORS headers
|
|
433
445
|
generateCors({ origins: ["https://myapp.com"], methods: ["GET", "POST"] });
|
|
434
446
|
|
|
@@ -549,10 +561,10 @@ analyzeString("café"); // { length: 4, codePoints: [...], ... }
|
|
|
549
561
|
|
|
550
562
|
---
|
|
551
563
|
|
|
552
|
-
### `/media`: Image Header Parsing, EXIF, PDF, WAV, ID3 &
|
|
564
|
+
### `/media`: Image Header Parsing, EXIF, PDF, WAV, ID3, ICO & FLAC Metadata
|
|
553
565
|
|
|
554
566
|
```ts
|
|
555
|
-
import { readImageInfo, readExifData, readPdfMetadata, readWavInfo, readId3Tags, readIcoInfo } from "@utilix-tech/sdk/media";
|
|
567
|
+
import { readImageInfo, readExifData, readPdfMetadata, readWavInfo, readId3Tags, readIcoInfo, readFlacInfo } from "@utilix-tech/sdk/media";
|
|
556
568
|
|
|
557
569
|
// Reads format, dimensions, bit depth, and alpha channel straight from
|
|
558
570
|
// file bytes: no decoding, no canvas, no image library.
|
|
@@ -590,9 +602,16 @@ readId3Tags(new Uint8Array(mp3Bytes));
|
|
|
590
602
|
const icoBytes = await fs.promises.readFile("favicon.ico");
|
|
591
603
|
readIcoInfo(new Uint8Array(icoBytes));
|
|
592
604
|
// { imageCount: 3, images: [{ width: 16, height: 16, bitCount: 32, bytesInRes: 1128, ... }, ...] }
|
|
605
|
+
|
|
606
|
+
// Reads sample rate, channels, bit depth, total samples, and duration from a
|
|
607
|
+
// FLAC file's STREAMINFO block, plus Vorbis comment tags (artist, title,
|
|
608
|
+
// album, ...) from its VORBIS_COMMENT block if present.
|
|
609
|
+
const flacBytes = await fs.promises.readFile("track.flac");
|
|
610
|
+
readFlacInfo(new Uint8Array(flacBytes));
|
|
611
|
+
// { sampleRate: 44100, channels: 2, bitsPerSample: 16, durationSeconds: 214.7, artist: "Artist Name", ... }
|
|
593
612
|
```
|
|
594
613
|
|
|
595
|
-
`readImageInfo` supports PNG, JPEG, GIF, WebP, and BMP. `readExifData` supports JPEG only. `readWavInfo` supports the canonical RIFF/WAVE container only. `readId3Tags` decodes ID3v2.3/2.4 and ID3v1/1.1 only (ID3v2.2 is detected but not decoded). `readIcoInfo` reads .ico icon files only and rejects .cur cursor files.
|
|
614
|
+
`readImageInfo` supports PNG, JPEG, GIF, WebP, and BMP. `readExifData` supports JPEG only. `readWavInfo` supports the canonical RIFF/WAVE container only. `readId3Tags` decodes ID3v2.3/2.4 and ID3v1/1.1 only (ID3v2.2 is detected but not decoded). `readIcoInfo` reads .ico icon files only and rejects .cur cursor files. `readFlacInfo` reads the native FLAC container only, not FLAC-in-Ogg.
|
|
596
615
|
|
|
597
616
|
---
|
|
598
617
|
|
|
@@ -607,7 +626,7 @@ readIcoInfo(new Uint8Array(icoBytes));
|
|
|
607
626
|
| `@utilix-tech/sdk/data` | CSV, YAML, TOML, XML, INI, NDJSON parse and convert, SRT/WebVTT subtitles |
|
|
608
627
|
| `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, fake data |
|
|
609
628
|
| `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
|
|
610
|
-
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator |
|
|
629
|
+
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator, Rule of 72 calculator |
|
|
611
630
|
| `@utilix-tech/sdk/network` | HTTP status codes, IP validation, country flags, geolocation URLs, HAR file parsing, User-Agent parsing |
|
|
612
631
|
| `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header |
|
|
613
632
|
| `@utilix-tech/sdk/code` | SQL format/minify, HTML format/minify, regex tester, GraphQL format, MIME type lookup, GitHub URL parsing |
|
|
@@ -615,7 +634,7 @@ readIcoInfo(new Uint8Array(icoBytes));
|
|
|
615
634
|
| `@utilix-tech/sdk/css` | CSS gradient, box-shadow, border-radius, keyframe animation generators |
|
|
616
635
|
| `@utilix-tech/sdk/misc` | SVG optimize/sanitize, file size format, Unicode inspector |
|
|
617
636
|
| `@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 |
|
|
618
|
-
| `@utilix-tech/sdk/media` | Image format & dimension reading from raw bytes (PNG, JPEG, GIF, WebP, BMP); PDF metadata reading; WAV audio info; ID3 tags; ICO favicon inspection |
|
|
637
|
+
| `@utilix-tech/sdk/media` | Image format & dimension reading from raw bytes (PNG, JPEG, GIF, WebP, BMP); PDF metadata reading; WAV audio info; ID3 tags; ICO favicon inspection; FLAC metadata reading |
|
|
619
638
|
|
|
620
639
|
---
|
|
621
640
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - curl-to-code : parseCurlCommand, convertToCode
|
|
6
6
|
* - jwt-decoder : decodeJwt, formatJwtJson
|
|
7
7
|
* - jwk-viewer : parseJwks
|
|
8
|
+
* - jwk-thumbprint : computeJwkThumbprint
|
|
8
9
|
* - jwt-signer : base64urlEncode, base64urlDecode, signJwt
|
|
9
10
|
* - cors-builder : validateCorsConfig, generateCors
|
|
10
11
|
* - csp-builder : generateCspHeader, generateFullHeader, parseCspHeader, validateCspConfig
|
|
@@ -106,6 +107,27 @@ type JwksParseResult = JwksResult | JwksError;
|
|
|
106
107
|
* use the keys cryptographically: this is inspection only.
|
|
107
108
|
*/
|
|
108
109
|
declare function parseJwks(input: string): JwksParseResult;
|
|
110
|
+
type JwkThumbprintHash = 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
111
|
+
interface JwkThumbprintResult {
|
|
112
|
+
ok: true;
|
|
113
|
+
kty: string;
|
|
114
|
+
hashAlg: JwkThumbprintHash;
|
|
115
|
+
members: string[];
|
|
116
|
+
canonicalJson: string;
|
|
117
|
+
thumbprint: string;
|
|
118
|
+
}
|
|
119
|
+
interface JwkThumbprintErrorResult {
|
|
120
|
+
ok: false;
|
|
121
|
+
error: string;
|
|
122
|
+
}
|
|
123
|
+
type JwkThumbprintOutcome = JwkThumbprintResult | JwkThumbprintErrorResult;
|
|
124
|
+
/**
|
|
125
|
+
* Compute the RFC 7638 thumbprint of a single JWK: build the canonical JSON
|
|
126
|
+
* of its required members (lexicographic key order, no whitespace) and hash
|
|
127
|
+
* it. This is a fingerprint for identifying/matching a key, not a signature
|
|
128
|
+
* or verification, and it only accepts a bare JWK, not a JWKS set.
|
|
129
|
+
*/
|
|
130
|
+
declare function computeJwkThumbprint(input: string, hashAlg?: JwkThumbprintHash): JwkThumbprintOutcome;
|
|
109
131
|
type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
|
|
110
132
|
interface JwtSignOptions {
|
|
111
133
|
algorithm?: JwtAlgorithm;
|
|
@@ -284,6 +306,10 @@ declare const api_HTTP_METHODS: typeof HTTP_METHODS;
|
|
|
284
306
|
type api_Header = Header;
|
|
285
307
|
type api_HttpMethod = HttpMethod;
|
|
286
308
|
type api_JwkSummary = JwkSummary;
|
|
309
|
+
type api_JwkThumbprintErrorResult = JwkThumbprintErrorResult;
|
|
310
|
+
type api_JwkThumbprintHash = JwkThumbprintHash;
|
|
311
|
+
type api_JwkThumbprintOutcome = JwkThumbprintOutcome;
|
|
312
|
+
type api_JwkThumbprintResult = JwkThumbprintResult;
|
|
287
313
|
type api_JwksError = JwksError;
|
|
288
314
|
type api_JwksParseResult = JwksParseResult;
|
|
289
315
|
type api_JwksResult = JwksResult;
|
|
@@ -307,6 +333,7 @@ declare const api_base64urlEncode: typeof base64urlEncode;
|
|
|
307
333
|
declare const api_buildCurl: typeof buildCurl;
|
|
308
334
|
declare const api_buildCurlMultiline: typeof buildCurlMultiline;
|
|
309
335
|
declare const api_buildUrl: typeof buildUrl;
|
|
336
|
+
declare const api_computeJwkThumbprint: typeof computeJwkThumbprint;
|
|
310
337
|
declare const api_convertToCode: typeof convertToCode;
|
|
311
338
|
declare const api_decodeJwt: typeof decodeJwt;
|
|
312
339
|
declare const api_decodeUrlComponent: typeof decodeUrlComponent;
|
|
@@ -335,7 +362,7 @@ declare const api_validateCorsConfig: typeof validateCorsConfig;
|
|
|
335
362
|
declare const api_validateCspConfig: typeof validateCspConfig;
|
|
336
363
|
declare const api_validateOgTags: typeof validateOgTags;
|
|
337
364
|
declare namespace api {
|
|
338
|
-
export { api_COMMON_HEADERS as COMMON_HEADERS, api_COMMON_PRESETS as COMMON_PRESETS, api_CORS_COMMON_HEADERS as CORS_COMMON_HEADERS, type api_CorsConfig as CorsConfig, type api_CorsOutput as CorsOutput, type api_CspConfig as CspConfig, type api_CspDirective as CspDirective, type api_CspSource as CspSource, type api_CurlCommand as CurlCommand, type api_CurlConfig as CurlConfig, api_DEFAULT_CORS_CONFIG as DEFAULT_CORS_CONFIG, api_DIRECTIVE_DESCRIPTIONS as DIRECTIVE_DESCRIPTIONS, type api_DirectiveConfig as DirectiveConfig, type api_FormatResult as FormatResult, api_HTTP_METHODS as HTTP_METHODS, type api_Header as Header, type api_HttpMethod as HttpMethod, type api_JwkSummary as JwkSummary, type api_JwksError as JwksError, type api_JwksParseResult as JwksParseResult, type api_JwksResult as JwksResult, type api_JwtAlgorithm as JwtAlgorithm, type api_JwtDecodeError as JwtDecodeError, type api_JwtDecodeResult as JwtDecodeResult, type api_JwtExpiry as JwtExpiry, type api_JwtPart as JwtPart, type api_JwtResult as JwtResult, type api_JwtSignOptions as JwtSignOptions, type api_MetaTags as MetaTags, type api_OgTags as OgTags, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TwitterTags as TwitterTags, api_addParam as addParam, api_base64urlDecode as base64urlDecode, api_base64urlEncode as base64urlEncode, api_buildCurl as buildCurl, api_buildCurlMultiline as buildCurlMultiline, api_buildUrl as buildUrl, api_convertToCode as convertToCode, api_decodeJwt as decodeJwt, api_decodeUrlComponent as decodeUrlComponent, api_detectFormat as detectFormat, api_encodeUrlComponent as encodeUrlComponent, api_extractJsonPaths as extractJsonPaths, api_formatJwtJson as formatJwtJson, api_formatResponse as formatResponse, api_generateCors as generateCors, api_generateCspHeader as generateCspHeader, api_generateFullHeader as generateFullHeader, api_generateMetaHtml as generateMetaHtml, api_getParam as getParam, api_getStats as getStats, api_isValidUrl as isValidUrl, api_normalizeUrl as normalizeUrl, api_parseCspHeader as parseCspHeader, api_parseCurl as parseCurl, api_parseCurlCommand as parseCurlCommand, api_parseJwks as parseJwks, api_parseMetaTags as parseMetaTags, api_parseUrl as parseUrl, api_removeParam as removeParam, api_signJwt as signJwt, api_validateCorsConfig as validateCorsConfig, api_validateCspConfig as validateCspConfig, api_validateOgTags as validateOgTags };
|
|
365
|
+
export { api_COMMON_HEADERS as COMMON_HEADERS, api_COMMON_PRESETS as COMMON_PRESETS, api_CORS_COMMON_HEADERS as CORS_COMMON_HEADERS, type api_CorsConfig as CorsConfig, type api_CorsOutput as CorsOutput, type api_CspConfig as CspConfig, type api_CspDirective as CspDirective, type api_CspSource as CspSource, type api_CurlCommand as CurlCommand, type api_CurlConfig as CurlConfig, api_DEFAULT_CORS_CONFIG as DEFAULT_CORS_CONFIG, api_DIRECTIVE_DESCRIPTIONS as DIRECTIVE_DESCRIPTIONS, type api_DirectiveConfig as DirectiveConfig, type api_FormatResult as FormatResult, api_HTTP_METHODS as HTTP_METHODS, type api_Header as Header, type api_HttpMethod as HttpMethod, type api_JwkSummary as JwkSummary, type api_JwkThumbprintErrorResult as JwkThumbprintErrorResult, type api_JwkThumbprintHash as JwkThumbprintHash, type api_JwkThumbprintOutcome as JwkThumbprintOutcome, type api_JwkThumbprintResult as JwkThumbprintResult, type api_JwksError as JwksError, type api_JwksParseResult as JwksParseResult, type api_JwksResult as JwksResult, type api_JwtAlgorithm as JwtAlgorithm, type api_JwtDecodeError as JwtDecodeError, type api_JwtDecodeResult as JwtDecodeResult, type api_JwtExpiry as JwtExpiry, type api_JwtPart as JwtPart, type api_JwtResult as JwtResult, type api_JwtSignOptions as JwtSignOptions, type api_MetaTags as MetaTags, type api_OgTags as OgTags, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TwitterTags as TwitterTags, api_addParam as addParam, api_base64urlDecode as base64urlDecode, api_base64urlEncode as base64urlEncode, api_buildCurl as buildCurl, api_buildCurlMultiline as buildCurlMultiline, api_buildUrl as buildUrl, api_computeJwkThumbprint as computeJwkThumbprint, api_convertToCode as convertToCode, api_decodeJwt as decodeJwt, api_decodeUrlComponent as decodeUrlComponent, api_detectFormat as detectFormat, api_encodeUrlComponent as encodeUrlComponent, api_extractJsonPaths as extractJsonPaths, api_formatJwtJson as formatJwtJson, api_formatResponse as formatResponse, api_generateCors as generateCors, api_generateCspHeader as generateCspHeader, api_generateFullHeader as generateFullHeader, api_generateMetaHtml as generateMetaHtml, api_getParam as getParam, api_getStats as getStats, api_isValidUrl as isValidUrl, api_normalizeUrl as normalizeUrl, api_parseCspHeader as parseCspHeader, api_parseCurl as parseCurl, api_parseCurlCommand as parseCurlCommand, api_parseJwks as parseJwks, api_parseMetaTags as parseMetaTags, api_parseUrl as parseUrl, api_removeParam as removeParam, api_signJwt as signJwt, api_validateCorsConfig as validateCorsConfig, api_validateCspConfig as validateCspConfig, api_validateOgTags as validateOgTags };
|
|
339
366
|
}
|
|
340
367
|
|
|
341
|
-
export {
|
|
368
|
+
export { formatJwtJson as $, type JwtResult as A, type JwtSignOptions as B, COMMON_HEADERS as C, DEFAULT_CORS_CONFIG as D, type TwitterTags as E, type FormatResult as F, addParam as G, HTTP_METHODS as H, base64urlDecode as I, type JwkSummary as J, base64urlEncode as K, buildCurl as L, type MetaTags as M, buildCurlMultiline as N, type OgTags as O, type ParsedUrl as P, buildUrl as Q, type ResponseFormat as R, SUPPORTED_LANGUAGES as S, type TargetLanguage as T, computeJwkThumbprint as U, convertToCode as V, decodeJwt as W, decodeUrlComponent as X, detectFormat as Y, encodeUrlComponent as Z, extractJsonPaths as _, api as a, formatResponse as a0, generateCors as a1, generateCspHeader as a2, generateFullHeader as a3, generateMetaHtml as a4, getParam as a5, getStats as a6, isValidUrl as a7, normalizeUrl as a8, parseCspHeader as a9, parseCurl as aa, parseCurlCommand as ab, parseJwks as ac, parseMetaTags as ad, parseUrl as ae, removeParam as af, signJwt as ag, validateCorsConfig as ah, validateCspConfig as ai, validateOgTags as aj, COMMON_PRESETS as b, CORS_COMMON_HEADERS as c, type CorsConfig as d, type CorsOutput as e, type CspConfig as f, type CspDirective as g, type CspSource as h, type CurlCommand as i, type CurlConfig as j, DIRECTIVE_DESCRIPTIONS as k, type DirectiveConfig as l, type Header as m, type HttpMethod as n, type JwkThumbprintErrorResult as o, type JwkThumbprintHash as p, type JwkThumbprintOutcome as q, type JwkThumbprintResult as r, type JwksError as s, type JwksParseResult as t, type JwksResult as u, type JwtAlgorithm as v, type JwtDecodeError as w, type JwtDecodeResult as x, type JwtExpiry as y, type JwtPart as z };
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - curl-to-code : parseCurlCommand, convertToCode
|
|
6
6
|
* - jwt-decoder : decodeJwt, formatJwtJson
|
|
7
7
|
* - jwk-viewer : parseJwks
|
|
8
|
+
* - jwk-thumbprint : computeJwkThumbprint
|
|
8
9
|
* - jwt-signer : base64urlEncode, base64urlDecode, signJwt
|
|
9
10
|
* - cors-builder : validateCorsConfig, generateCors
|
|
10
11
|
* - csp-builder : generateCspHeader, generateFullHeader, parseCspHeader, validateCspConfig
|
|
@@ -106,6 +107,27 @@ type JwksParseResult = JwksResult | JwksError;
|
|
|
106
107
|
* use the keys cryptographically: this is inspection only.
|
|
107
108
|
*/
|
|
108
109
|
declare function parseJwks(input: string): JwksParseResult;
|
|
110
|
+
type JwkThumbprintHash = 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
111
|
+
interface JwkThumbprintResult {
|
|
112
|
+
ok: true;
|
|
113
|
+
kty: string;
|
|
114
|
+
hashAlg: JwkThumbprintHash;
|
|
115
|
+
members: string[];
|
|
116
|
+
canonicalJson: string;
|
|
117
|
+
thumbprint: string;
|
|
118
|
+
}
|
|
119
|
+
interface JwkThumbprintErrorResult {
|
|
120
|
+
ok: false;
|
|
121
|
+
error: string;
|
|
122
|
+
}
|
|
123
|
+
type JwkThumbprintOutcome = JwkThumbprintResult | JwkThumbprintErrorResult;
|
|
124
|
+
/**
|
|
125
|
+
* Compute the RFC 7638 thumbprint of a single JWK: build the canonical JSON
|
|
126
|
+
* of its required members (lexicographic key order, no whitespace) and hash
|
|
127
|
+
* it. This is a fingerprint for identifying/matching a key, not a signature
|
|
128
|
+
* or verification, and it only accepts a bare JWK, not a JWKS set.
|
|
129
|
+
*/
|
|
130
|
+
declare function computeJwkThumbprint(input: string, hashAlg?: JwkThumbprintHash): JwkThumbprintOutcome;
|
|
109
131
|
type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
|
|
110
132
|
interface JwtSignOptions {
|
|
111
133
|
algorithm?: JwtAlgorithm;
|
|
@@ -284,6 +306,10 @@ declare const api_HTTP_METHODS: typeof HTTP_METHODS;
|
|
|
284
306
|
type api_Header = Header;
|
|
285
307
|
type api_HttpMethod = HttpMethod;
|
|
286
308
|
type api_JwkSummary = JwkSummary;
|
|
309
|
+
type api_JwkThumbprintErrorResult = JwkThumbprintErrorResult;
|
|
310
|
+
type api_JwkThumbprintHash = JwkThumbprintHash;
|
|
311
|
+
type api_JwkThumbprintOutcome = JwkThumbprintOutcome;
|
|
312
|
+
type api_JwkThumbprintResult = JwkThumbprintResult;
|
|
287
313
|
type api_JwksError = JwksError;
|
|
288
314
|
type api_JwksParseResult = JwksParseResult;
|
|
289
315
|
type api_JwksResult = JwksResult;
|
|
@@ -307,6 +333,7 @@ declare const api_base64urlEncode: typeof base64urlEncode;
|
|
|
307
333
|
declare const api_buildCurl: typeof buildCurl;
|
|
308
334
|
declare const api_buildCurlMultiline: typeof buildCurlMultiline;
|
|
309
335
|
declare const api_buildUrl: typeof buildUrl;
|
|
336
|
+
declare const api_computeJwkThumbprint: typeof computeJwkThumbprint;
|
|
310
337
|
declare const api_convertToCode: typeof convertToCode;
|
|
311
338
|
declare const api_decodeJwt: typeof decodeJwt;
|
|
312
339
|
declare const api_decodeUrlComponent: typeof decodeUrlComponent;
|
|
@@ -335,7 +362,7 @@ declare const api_validateCorsConfig: typeof validateCorsConfig;
|
|
|
335
362
|
declare const api_validateCspConfig: typeof validateCspConfig;
|
|
336
363
|
declare const api_validateOgTags: typeof validateOgTags;
|
|
337
364
|
declare namespace api {
|
|
338
|
-
export { api_COMMON_HEADERS as COMMON_HEADERS, api_COMMON_PRESETS as COMMON_PRESETS, api_CORS_COMMON_HEADERS as CORS_COMMON_HEADERS, type api_CorsConfig as CorsConfig, type api_CorsOutput as CorsOutput, type api_CspConfig as CspConfig, type api_CspDirective as CspDirective, type api_CspSource as CspSource, type api_CurlCommand as CurlCommand, type api_CurlConfig as CurlConfig, api_DEFAULT_CORS_CONFIG as DEFAULT_CORS_CONFIG, api_DIRECTIVE_DESCRIPTIONS as DIRECTIVE_DESCRIPTIONS, type api_DirectiveConfig as DirectiveConfig, type api_FormatResult as FormatResult, api_HTTP_METHODS as HTTP_METHODS, type api_Header as Header, type api_HttpMethod as HttpMethod, type api_JwkSummary as JwkSummary, type api_JwksError as JwksError, type api_JwksParseResult as JwksParseResult, type api_JwksResult as JwksResult, type api_JwtAlgorithm as JwtAlgorithm, type api_JwtDecodeError as JwtDecodeError, type api_JwtDecodeResult as JwtDecodeResult, type api_JwtExpiry as JwtExpiry, type api_JwtPart as JwtPart, type api_JwtResult as JwtResult, type api_JwtSignOptions as JwtSignOptions, type api_MetaTags as MetaTags, type api_OgTags as OgTags, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TwitterTags as TwitterTags, api_addParam as addParam, api_base64urlDecode as base64urlDecode, api_base64urlEncode as base64urlEncode, api_buildCurl as buildCurl, api_buildCurlMultiline as buildCurlMultiline, api_buildUrl as buildUrl, api_convertToCode as convertToCode, api_decodeJwt as decodeJwt, api_decodeUrlComponent as decodeUrlComponent, api_detectFormat as detectFormat, api_encodeUrlComponent as encodeUrlComponent, api_extractJsonPaths as extractJsonPaths, api_formatJwtJson as formatJwtJson, api_formatResponse as formatResponse, api_generateCors as generateCors, api_generateCspHeader as generateCspHeader, api_generateFullHeader as generateFullHeader, api_generateMetaHtml as generateMetaHtml, api_getParam as getParam, api_getStats as getStats, api_isValidUrl as isValidUrl, api_normalizeUrl as normalizeUrl, api_parseCspHeader as parseCspHeader, api_parseCurl as parseCurl, api_parseCurlCommand as parseCurlCommand, api_parseJwks as parseJwks, api_parseMetaTags as parseMetaTags, api_parseUrl as parseUrl, api_removeParam as removeParam, api_signJwt as signJwt, api_validateCorsConfig as validateCorsConfig, api_validateCspConfig as validateCspConfig, api_validateOgTags as validateOgTags };
|
|
365
|
+
export { api_COMMON_HEADERS as COMMON_HEADERS, api_COMMON_PRESETS as COMMON_PRESETS, api_CORS_COMMON_HEADERS as CORS_COMMON_HEADERS, type api_CorsConfig as CorsConfig, type api_CorsOutput as CorsOutput, type api_CspConfig as CspConfig, type api_CspDirective as CspDirective, type api_CspSource as CspSource, type api_CurlCommand as CurlCommand, type api_CurlConfig as CurlConfig, api_DEFAULT_CORS_CONFIG as DEFAULT_CORS_CONFIG, api_DIRECTIVE_DESCRIPTIONS as DIRECTIVE_DESCRIPTIONS, type api_DirectiveConfig as DirectiveConfig, type api_FormatResult as FormatResult, api_HTTP_METHODS as HTTP_METHODS, type api_Header as Header, type api_HttpMethod as HttpMethod, type api_JwkSummary as JwkSummary, type api_JwkThumbprintErrorResult as JwkThumbprintErrorResult, type api_JwkThumbprintHash as JwkThumbprintHash, type api_JwkThumbprintOutcome as JwkThumbprintOutcome, type api_JwkThumbprintResult as JwkThumbprintResult, type api_JwksError as JwksError, type api_JwksParseResult as JwksParseResult, type api_JwksResult as JwksResult, type api_JwtAlgorithm as JwtAlgorithm, type api_JwtDecodeError as JwtDecodeError, type api_JwtDecodeResult as JwtDecodeResult, type api_JwtExpiry as JwtExpiry, type api_JwtPart as JwtPart, type api_JwtResult as JwtResult, type api_JwtSignOptions as JwtSignOptions, type api_MetaTags as MetaTags, type api_OgTags as OgTags, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TwitterTags as TwitterTags, api_addParam as addParam, api_base64urlDecode as base64urlDecode, api_base64urlEncode as base64urlEncode, api_buildCurl as buildCurl, api_buildCurlMultiline as buildCurlMultiline, api_buildUrl as buildUrl, api_computeJwkThumbprint as computeJwkThumbprint, api_convertToCode as convertToCode, api_decodeJwt as decodeJwt, api_decodeUrlComponent as decodeUrlComponent, api_detectFormat as detectFormat, api_encodeUrlComponent as encodeUrlComponent, api_extractJsonPaths as extractJsonPaths, api_formatJwtJson as formatJwtJson, api_formatResponse as formatResponse, api_generateCors as generateCors, api_generateCspHeader as generateCspHeader, api_generateFullHeader as generateFullHeader, api_generateMetaHtml as generateMetaHtml, api_getParam as getParam, api_getStats as getStats, api_isValidUrl as isValidUrl, api_normalizeUrl as normalizeUrl, api_parseCspHeader as parseCspHeader, api_parseCurl as parseCurl, api_parseCurlCommand as parseCurlCommand, api_parseJwks as parseJwks, api_parseMetaTags as parseMetaTags, api_parseUrl as parseUrl, api_removeParam as removeParam, api_signJwt as signJwt, api_validateCorsConfig as validateCorsConfig, api_validateCspConfig as validateCspConfig, api_validateOgTags as validateOgTags };
|
|
339
366
|
}
|
|
340
367
|
|
|
341
|
-
export {
|
|
368
|
+
export { formatJwtJson as $, type JwtResult as A, type JwtSignOptions as B, COMMON_HEADERS as C, DEFAULT_CORS_CONFIG as D, type TwitterTags as E, type FormatResult as F, addParam as G, HTTP_METHODS as H, base64urlDecode as I, type JwkSummary as J, base64urlEncode as K, buildCurl as L, type MetaTags as M, buildCurlMultiline as N, type OgTags as O, type ParsedUrl as P, buildUrl as Q, type ResponseFormat as R, SUPPORTED_LANGUAGES as S, type TargetLanguage as T, computeJwkThumbprint as U, convertToCode as V, decodeJwt as W, decodeUrlComponent as X, detectFormat as Y, encodeUrlComponent as Z, extractJsonPaths as _, api as a, formatResponse as a0, generateCors as a1, generateCspHeader as a2, generateFullHeader as a3, generateMetaHtml as a4, getParam as a5, getStats as a6, isValidUrl as a7, normalizeUrl as a8, parseCspHeader as a9, parseCurl as aa, parseCurlCommand as ab, parseJwks as ac, parseMetaTags as ad, parseUrl as ae, removeParam as af, signJwt as ag, validateCorsConfig as ah, validateCspConfig as ai, validateOgTags as aj, COMMON_PRESETS as b, CORS_COMMON_HEADERS as c, type CorsConfig as d, type CorsOutput as e, type CspConfig as f, type CspDirective as g, type CspSource as h, type CurlCommand as i, type CurlConfig as j, DIRECTIVE_DESCRIPTIONS as k, type DirectiveConfig as l, type Header as m, type HttpMethod as n, type JwkThumbprintErrorResult as o, type JwkThumbprintHash as p, type JwkThumbprintOutcome as q, type JwkThumbprintResult as r, type JwksError as s, type JwksParseResult as t, type JwksResult as u, type JwtAlgorithm as v, type JwtDecodeError as w, type JwtDecodeResult as x, type JwtExpiry as y, type JwtPart as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __export } from './chunk-MLKGABMK.js';
|
|
2
|
-
import { createHmac } from 'crypto';
|
|
2
|
+
import { createHash, createHmac } from 'crypto';
|
|
3
3
|
|
|
4
4
|
// src/tools/api.ts
|
|
5
5
|
var api_exports = {};
|
|
@@ -17,6 +17,7 @@ __export(api_exports, {
|
|
|
17
17
|
buildCurl: () => buildCurl,
|
|
18
18
|
buildCurlMultiline: () => buildCurlMultiline,
|
|
19
19
|
buildUrl: () => buildUrl,
|
|
20
|
+
computeJwkThumbprint: () => computeJwkThumbprint,
|
|
20
21
|
convertToCode: () => convertToCode,
|
|
21
22
|
decodeJwt: () => decodeJwt,
|
|
22
23
|
decodeUrlComponent: () => decodeUrlComponent,
|
|
@@ -535,6 +536,48 @@ function parseJwks(input) {
|
|
|
535
536
|
const duplicateKids = [...kidCounts.entries()].filter(([, c]) => c > 1).map(([kid]) => kid);
|
|
536
537
|
return { ok: true, keys, count: keys.length, duplicateKids };
|
|
537
538
|
}
|
|
539
|
+
var JWK_THUMBPRINT_MEMBERS = {
|
|
540
|
+
oct: ["k", "kty"],
|
|
541
|
+
RSA: ["e", "kty", "n"],
|
|
542
|
+
EC: ["crv", "kty", "x", "y"],
|
|
543
|
+
OKP: ["crv", "kty", "x"]
|
|
544
|
+
};
|
|
545
|
+
var JWK_THUMBPRINT_NODE_HASH = {
|
|
546
|
+
"SHA-256": "sha256",
|
|
547
|
+
"SHA-384": "sha384",
|
|
548
|
+
"SHA-512": "sha512"
|
|
549
|
+
};
|
|
550
|
+
function computeJwkThumbprint(input, hashAlg = "SHA-256") {
|
|
551
|
+
const trimmed = input.trim();
|
|
552
|
+
if (!trimmed) return { ok: false, error: "No input provided" };
|
|
553
|
+
const nodeHash = JWK_THUMBPRINT_NODE_HASH[hashAlg];
|
|
554
|
+
if (!nodeHash) return { ok: false, error: 'hashAlg must be one of "SHA-256", "SHA-384", "SHA-512"' };
|
|
555
|
+
let jwk;
|
|
556
|
+
try {
|
|
557
|
+
jwk = JSON.parse(trimmed);
|
|
558
|
+
} catch (e) {
|
|
559
|
+
return { ok: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
560
|
+
}
|
|
561
|
+
if (jwk === null || typeof jwk !== "object" || Array.isArray(jwk)) {
|
|
562
|
+
return { ok: false, error: "Input must be a single JWK object, not a JWKS set or array" };
|
|
563
|
+
}
|
|
564
|
+
const obj = jwk;
|
|
565
|
+
const kty = obj.kty;
|
|
566
|
+
if (typeof kty !== "string") return { ok: false, error: 'JWK must have a "kty" (key type) member' };
|
|
567
|
+
const members = JWK_THUMBPRINT_MEMBERS[kty];
|
|
568
|
+
if (!members) return { ok: false, error: `Unsupported key type "${kty}": supported types are RSA, EC, oct, OKP` };
|
|
569
|
+
const canonical = {};
|
|
570
|
+
for (const m of members) {
|
|
571
|
+
const v = obj[m];
|
|
572
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
573
|
+
return { ok: false, error: `JWK is missing required member "${m}" for key type "${kty}"` };
|
|
574
|
+
}
|
|
575
|
+
canonical[m] = v;
|
|
576
|
+
}
|
|
577
|
+
const canonicalJson = "{" + members.map((m) => `${JSON.stringify(m)}:${JSON.stringify(canonical[m])}`).join(",") + "}";
|
|
578
|
+
const thumbprint = createHash(nodeHash).update(canonicalJson, "utf8").digest("base64url");
|
|
579
|
+
return { ok: true, kty, hashAlg, members, canonicalJson, thumbprint };
|
|
580
|
+
}
|
|
538
581
|
var ALG_TO_NODE_HASH = {
|
|
539
582
|
HS256: "sha256",
|
|
540
583
|
HS384: "sha384",
|
|
@@ -1266,4 +1309,4 @@ function generateMetaHtml(tags) {
|
|
|
1266
1309
|
return lines.join("\n");
|
|
1267
1310
|
}
|
|
1268
1311
|
|
|
1269
|
-
export { COMMON_HEADERS, COMMON_PRESETS, CORS_COMMON_HEADERS, DEFAULT_CORS_CONFIG, DIRECTIVE_DESCRIPTIONS, HTTP_METHODS, SUPPORTED_LANGUAGES, addParam, api_exports, base64urlDecode, base64urlEncode, buildCurl, buildCurlMultiline, buildUrl, convertToCode, decodeJwt, decodeUrlComponent, detectFormat, encodeUrlComponent, extractJsonPaths, formatJwtJson, formatResponse, generateCors, generateCspHeader, generateFullHeader, generateMetaHtml, getParam, getStats, isValidUrl, normalizeUrl, parseCspHeader, parseCurl, parseCurlCommand, parseJwks, parseMetaTags, parseUrl, removeParam, signJwt, validateCorsConfig, validateCspConfig, validateOgTags };
|
|
1312
|
+
export { COMMON_HEADERS, COMMON_PRESETS, CORS_COMMON_HEADERS, DEFAULT_CORS_CONFIG, DIRECTIVE_DESCRIPTIONS, HTTP_METHODS, SUPPORTED_LANGUAGES, addParam, api_exports, base64urlDecode, base64urlEncode, buildCurl, buildCurlMultiline, buildUrl, computeJwkThumbprint, convertToCode, decodeJwt, decodeUrlComponent, detectFormat, encodeUrlComponent, extractJsonPaths, formatJwtJson, formatResponse, generateCors, generateCspHeader, generateFullHeader, generateMetaHtml, getParam, getStats, isValidUrl, normalizeUrl, parseCspHeader, parseCurl, parseCurlCommand, parseJwks, parseMetaTags, parseUrl, removeParam, signJwt, validateCorsConfig, validateCspConfig, validateOgTags };
|
|
@@ -25,10 +25,12 @@ __export(units_exports, {
|
|
|
25
25
|
calculateLoan: () => calculateLoan,
|
|
26
26
|
calculateMarginMarkup: () => calculateMarginMarkup,
|
|
27
27
|
calculatePercentageChange: () => calculatePercentageChange,
|
|
28
|
+
calculateRuleOf72: () => calculateRuleOf72,
|
|
28
29
|
calculateSimpleInterest: () => calculateSimpleInterest,
|
|
29
30
|
calculateTax: () => calculateTax,
|
|
30
31
|
calculateTimeValue: () => calculateTimeValue,
|
|
31
32
|
calculateTipSplit: () => calculateTipSplit,
|
|
33
|
+
calculateVideoBitrate: () => calculateVideoBitrate,
|
|
32
34
|
compareSemver: () => compareSemver,
|
|
33
35
|
convert: () => convert,
|
|
34
36
|
convertAprApy: () => convertAprApy,
|
|
@@ -1589,5 +1591,90 @@ function calculateBreakEven(input) {
|
|
|
1589
1591
|
}
|
|
1590
1592
|
return result;
|
|
1591
1593
|
}
|
|
1594
|
+
function calculateVideoBitrate(input) {
|
|
1595
|
+
const { mode, durationSeconds, videoBitrateKbps, targetSizeMB } = input;
|
|
1596
|
+
const audioBitrateKbps = input.audioBitrateKbps ?? 0;
|
|
1597
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
|
|
1598
|
+
return { error: "durationSeconds must be a positive number" };
|
|
1599
|
+
}
|
|
1600
|
+
if (!Number.isFinite(audioBitrateKbps) || audioBitrateKbps < 0) {
|
|
1601
|
+
return { error: "audioBitrateKbps must be zero or a positive number" };
|
|
1602
|
+
}
|
|
1603
|
+
const round22 = (n) => Math.round(n * 100) / 100;
|
|
1604
|
+
if (mode === "size-from-bitrate") {
|
|
1605
|
+
if (typeof videoBitrateKbps !== "number" || !Number.isFinite(videoBitrateKbps) || videoBitrateKbps <= 0) {
|
|
1606
|
+
return { error: 'videoBitrateKbps must be a positive number for mode "size-from-bitrate"' };
|
|
1607
|
+
}
|
|
1608
|
+
const totalBitrateKbps = videoBitrateKbps + audioBitrateKbps;
|
|
1609
|
+
const fileSizeBytes = totalBitrateKbps * 1e3 * durationSeconds / 8;
|
|
1610
|
+
return {
|
|
1611
|
+
mode,
|
|
1612
|
+
durationSeconds,
|
|
1613
|
+
videoBitrateKbps: round22(videoBitrateKbps),
|
|
1614
|
+
audioBitrateKbps: round22(audioBitrateKbps),
|
|
1615
|
+
totalBitrateKbps: round22(totalBitrateKbps),
|
|
1616
|
+
fileSizeBytes: Math.round(fileSizeBytes),
|
|
1617
|
+
fileSizeMB: round22(fileSizeBytes / 1e6),
|
|
1618
|
+
fileSizeMiB: round22(fileSizeBytes / 1048576)
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
if (mode === "bitrate-from-size") {
|
|
1622
|
+
if (typeof targetSizeMB !== "number" || !Number.isFinite(targetSizeMB) || targetSizeMB <= 0) {
|
|
1623
|
+
return { error: 'targetSizeMB must be a positive number for mode "bitrate-from-size"' };
|
|
1624
|
+
}
|
|
1625
|
+
const fileSizeBytes = targetSizeMB * 1e6;
|
|
1626
|
+
const totalBitrateKbps = fileSizeBytes * 8 / (1e3 * durationSeconds);
|
|
1627
|
+
const videoKbps = totalBitrateKbps - audioBitrateKbps;
|
|
1628
|
+
if (videoKbps <= 0) {
|
|
1629
|
+
return {
|
|
1630
|
+
error: "audioBitrateKbps exceeds the total bitrate available at this target size and duration; lower the audio bitrate or raise the target size"
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
mode,
|
|
1635
|
+
durationSeconds,
|
|
1636
|
+
videoBitrateKbps: round22(videoKbps),
|
|
1637
|
+
audioBitrateKbps: round22(audioBitrateKbps),
|
|
1638
|
+
totalBitrateKbps: round22(totalBitrateKbps),
|
|
1639
|
+
fileSizeBytes: Math.round(fileSizeBytes),
|
|
1640
|
+
fileSizeMB: round22(targetSizeMB),
|
|
1641
|
+
fileSizeMiB: round22(fileSizeBytes / 1048576)
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
return { error: 'mode must be "size-from-bitrate" or "bitrate-from-size"' };
|
|
1645
|
+
}
|
|
1646
|
+
function calculateRuleOf72(value, direction) {
|
|
1647
|
+
if (!Number.isFinite(value) || value <= 0) return { error: "Value must be a positive number" };
|
|
1648
|
+
if (direction !== "rate-to-years" && direction !== "years-to-rate") {
|
|
1649
|
+
return { error: 'Direction must be "rate-to-years" or "years-to-rate"' };
|
|
1650
|
+
}
|
|
1651
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
1652
|
+
if (direction === "rate-to-years") {
|
|
1653
|
+
const ratePercent = value;
|
|
1654
|
+
const ruleOf72Estimate2 = 72 / ratePercent;
|
|
1655
|
+
const exactValue2 = Math.log(2) / Math.log(1 + ratePercent / 100);
|
|
1656
|
+
const errorPercent2 = (ruleOf72Estimate2 - exactValue2) / exactValue2 * 100;
|
|
1657
|
+
return {
|
|
1658
|
+
direction,
|
|
1659
|
+
ratePercent: round4(ratePercent),
|
|
1660
|
+
years: round4(exactValue2),
|
|
1661
|
+
ruleOf72Estimate: round4(ruleOf72Estimate2),
|
|
1662
|
+
exactValue: round4(exactValue2),
|
|
1663
|
+
errorPercent: round4(errorPercent2)
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
const years = value;
|
|
1667
|
+
const ruleOf72Estimate = 72 / years;
|
|
1668
|
+
const exactValue = (Math.pow(2, 1 / years) - 1) * 100;
|
|
1669
|
+
const errorPercent = (ruleOf72Estimate - exactValue) / exactValue * 100;
|
|
1670
|
+
return {
|
|
1671
|
+
direction,
|
|
1672
|
+
ratePercent: round4(exactValue),
|
|
1673
|
+
years: round4(years),
|
|
1674
|
+
ruleOf72Estimate: round4(ruleOf72Estimate),
|
|
1675
|
+
exactValue: round4(exactValue),
|
|
1676
|
+
errorPercent: round4(errorPercent)
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1592
1679
|
|
|
1593
|
-
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBreakEven, calculateCompoundInterest, calculateDiscount, calculateLoan, calculateMarginMarkup, calculatePercentageChange, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, compareSemver, 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 };
|
|
1680
|
+
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBreakEven, calculateCompoundInterest, calculateDiscount, calculateLoan, calculateMarginMarkup, calculatePercentageChange, calculateRuleOf72, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, calculateVideoBitrate, compareSemver, 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 };
|
|
@@ -4,6 +4,7 @@ import { __export } from './chunk-MLKGABMK.js';
|
|
|
4
4
|
var media_exports = {};
|
|
5
5
|
__export(media_exports, {
|
|
6
6
|
readExifData: () => readExifData,
|
|
7
|
+
readFlacInfo: () => readFlacInfo,
|
|
7
8
|
readIcoInfo: () => readIcoInfo,
|
|
8
9
|
readId3Tags: () => readId3Tags,
|
|
9
10
|
readImageInfo: () => readImageInfo,
|
|
@@ -764,5 +765,100 @@ function readIcoInfo(bytes) {
|
|
|
764
765
|
}
|
|
765
766
|
return { imageCount: count, images };
|
|
766
767
|
}
|
|
768
|
+
var FLAC_TAG_FIELDS = {
|
|
769
|
+
TITLE: "title",
|
|
770
|
+
ARTIST: "artist",
|
|
771
|
+
ALBUM: "album",
|
|
772
|
+
DATE: "date",
|
|
773
|
+
GENRE: "genre",
|
|
774
|
+
TRACKNUMBER: "trackNumber"
|
|
775
|
+
};
|
|
776
|
+
function readU24BE(bytes, offset) {
|
|
777
|
+
return bytes[offset] << 16 | bytes[offset + 1] << 8 | bytes[offset + 2];
|
|
778
|
+
}
|
|
779
|
+
function readVorbisComment(bytes, start, end) {
|
|
780
|
+
let offset = start;
|
|
781
|
+
if (offset + 4 > end) return null;
|
|
782
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
783
|
+
const vendorLength = view.getUint32(offset, true);
|
|
784
|
+
offset += 4;
|
|
785
|
+
if (offset + vendorLength > end) return null;
|
|
786
|
+
const vendor = Buffer.from(bytes.subarray(offset, offset + vendorLength)).toString("utf8");
|
|
787
|
+
offset += vendorLength;
|
|
788
|
+
if (offset + 4 > end) return null;
|
|
789
|
+
const commentCount = view.getUint32(offset, true);
|
|
790
|
+
offset += 4;
|
|
791
|
+
const tags = {};
|
|
792
|
+
for (let i = 0; i < commentCount; i++) {
|
|
793
|
+
if (offset + 4 > end) break;
|
|
794
|
+
const commentLength = view.getUint32(offset, true);
|
|
795
|
+
offset += 4;
|
|
796
|
+
if (offset + commentLength > end) break;
|
|
797
|
+
const comment = Buffer.from(bytes.subarray(offset, offset + commentLength)).toString("utf8");
|
|
798
|
+
offset += commentLength;
|
|
799
|
+
const eq = comment.indexOf("=");
|
|
800
|
+
if (eq > 0) {
|
|
801
|
+
const key = comment.slice(0, eq).toUpperCase();
|
|
802
|
+
const value = comment.slice(eq + 1);
|
|
803
|
+
tags[key] = value;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return { vendor, tags };
|
|
807
|
+
}
|
|
808
|
+
function readFlacInfo(bytes) {
|
|
809
|
+
if (bytes.length < 4) return { error: "File is too small to be a valid FLAC file" };
|
|
810
|
+
if (readAscii4(bytes, 0) !== "fLaC") return { error: 'Not a FLAC file (missing "fLaC" marker)' };
|
|
811
|
+
let offset = 4;
|
|
812
|
+
let streamInfo = null;
|
|
813
|
+
let vorbisComment = null;
|
|
814
|
+
while (offset + 4 <= bytes.length) {
|
|
815
|
+
const header = bytes[offset];
|
|
816
|
+
const isLast = (header & 128) !== 0;
|
|
817
|
+
const blockType = header & 127;
|
|
818
|
+
const length = readU24BE(bytes, offset + 1);
|
|
819
|
+
const dataStart = offset + 4;
|
|
820
|
+
const dataEnd = dataStart + length;
|
|
821
|
+
if (dataEnd > bytes.length) return { error: "Truncated metadata block" };
|
|
822
|
+
if (blockType === 0) {
|
|
823
|
+
if (length < 34) return { error: "Truncated STREAMINFO block" };
|
|
824
|
+
const minBlockSize = bytes[dataStart] << 8 | bytes[dataStart + 1];
|
|
825
|
+
const maxBlockSize = bytes[dataStart + 2] << 8 | bytes[dataStart + 3];
|
|
826
|
+
const minFrameSize = readU24BE(bytes, dataStart + 4);
|
|
827
|
+
const maxFrameSize = readU24BE(bytes, dataStart + 7);
|
|
828
|
+
let packed = 0n;
|
|
829
|
+
for (let i = 0; i < 8; i++) packed = packed << 8n | BigInt(bytes[dataStart + 10 + i]);
|
|
830
|
+
const sampleRate = Number(packed >> 44n & 0xfffffn);
|
|
831
|
+
const channels = Number(packed >> 41n & 0x7n) + 1;
|
|
832
|
+
const bitsPerSample = Number(packed >> 36n & 0x1fn) + 1;
|
|
833
|
+
const totalSamples = Number(packed & 0xfffffffffn);
|
|
834
|
+
if (sampleRate <= 0) return { error: "Invalid sample rate in STREAMINFO block" };
|
|
835
|
+
streamInfo = {
|
|
836
|
+
sampleRate,
|
|
837
|
+
channels,
|
|
838
|
+
bitsPerSample,
|
|
839
|
+
totalSamples,
|
|
840
|
+
durationSeconds: Math.round(totalSamples / sampleRate * 1e3) / 1e3,
|
|
841
|
+
minBlockSize,
|
|
842
|
+
maxBlockSize,
|
|
843
|
+
minFrameSize,
|
|
844
|
+
maxFrameSize
|
|
845
|
+
};
|
|
846
|
+
} else if (blockType === 4) {
|
|
847
|
+
vorbisComment = readVorbisComment(bytes, dataStart, dataEnd);
|
|
848
|
+
}
|
|
849
|
+
offset = dataEnd;
|
|
850
|
+
if (isLast) break;
|
|
851
|
+
}
|
|
852
|
+
if (!streamInfo) return { error: "No STREAMINFO block found in this FLAC file" };
|
|
853
|
+
const result = { ...streamInfo };
|
|
854
|
+
if (vorbisComment) {
|
|
855
|
+
result.vendor = vorbisComment.vendor;
|
|
856
|
+
result.tags = vorbisComment.tags;
|
|
857
|
+
for (const [key, field] of Object.entries(FLAC_TAG_FIELDS)) {
|
|
858
|
+
if (vorbisComment.tags[key] !== void 0) result[field] = vorbisComment.tags[key];
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return result;
|
|
862
|
+
}
|
|
767
863
|
|
|
768
|
-
export { media_exports, readExifData, readIcoInfo, readId3Tags, readImageInfo, readPdfMetadata, readWavInfo };
|
|
864
|
+
export { media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readPdfMetadata, readWavInfo };
|