@utilix-tech/sdk 0.69.0 → 0.71.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 +14 -6
- package/dist/{api-DmTNE4c1.d.cts → api-C8rImfka.d.cts} +55 -2
- package/dist/{api-DmTNE4c1.d.ts → api-C8rImfka.d.ts} +55 -2
- package/dist/{chunk-52ZDKSIX.js → chunk-4S63T7Y4.js} +72 -1
- package/dist/{chunk-4HNHEOJL.js → chunk-653HDWXW.js} +41 -1
- package/dist/{chunk-226EH3ZI.js → chunk-PT4N4UBL.js} +132 -1
- package/dist/index.cjs +242 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{media-BUvwm4wU.d.cts → media-56zrIBw4.d.cts} +47 -2
- package/dist/{media-BUvwm4wU.d.ts → media-56zrIBw4.d.ts} +47 -2
- package/dist/tools/api.cjs +71 -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 +131 -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 +40 -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-DPefgzmH.d.ts → units-BT8P27G0.d.cts} +29 -2
- package/dist/{units-DPefgzmH.d.cts → units-BT8P27G0.d.ts} +29 -2
- package/package.json +1 -1
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.71.0
|
|
6
|
+
- Add `readMidiInfo` (midi-file-info): parse a Standard MIDI File's header and every track chunk to report format, tick division, initial tempo, time signature, and per-track event/note-on counts, plus total duration in seconds accounting for tempo changes.
|
|
7
|
+
|
|
8
|
+
## 0.70.0
|
|
9
|
+
- Add `generateHotp`, `generateTotp` (totp-hotp-generator): generate an HOTP code (RFC 4226) for a counter value, or the current TOTP code (RFC 6238) plus seconds until rotation, from a base32-encoded shared secret, supporting SHA-1/SHA-256/SHA-512 and 6-8 digit codes.
|
|
10
|
+
- Add `calculateTaxEquivalentYield` (tax-equivalent-yield-calculator): convert between a tax-free municipal bond yield and its taxable equivalent given a marginal federal (plus optional state) tax rate, in both directions.
|
|
11
|
+
|
|
5
12
|
## 0.69.0
|
|
6
13
|
- Add `parseCommitTrailers` (git-commit-trailer-parser): parse a commit message's trailer block (Co-authored-by, Signed-off-by, Fixes, Reviewed-by, etc.) per git's RFC 822-style trailer convention, returning each trailer's key and value plus the body with the trailer block removed.
|
|
7
14
|
- Add `extractMarkdownFootnotes` (markdown-footnote-extractor): extract every Markdown-Extra-style footnote reference and definition from a document, flagging references with no matching definition and definitions that are never referenced.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**692 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)
|
|
@@ -876,10 +876,10 @@ analyzeString("café"); // { length: 4, codePoints: [...], ... }
|
|
|
876
876
|
|
|
877
877
|
---
|
|
878
878
|
|
|
879
|
-
### `/media`: Image Header Parsing, EXIF, PDF Metadata & Page Dimensions, WAV, ID3, ICO, FLAC, Video &
|
|
879
|
+
### `/media`: Image Header Parsing, EXIF, PDF Metadata & Page Dimensions, WAV, ID3, ICO, FLAC, Video, GIF & MIDI Metadata
|
|
880
880
|
|
|
881
881
|
```ts
|
|
882
|
-
import { readImageInfo, readExifData, readPdfMetadata, readPdfPageDimensions, readWavInfo, readId3Tags, readIcoInfo, readFlacInfo, readVideoInfo, inspectGifFrames } from "@utilix-tech/sdk/media";
|
|
882
|
+
import { readImageInfo, readExifData, readPdfMetadata, readPdfPageDimensions, readWavInfo, readId3Tags, readIcoInfo, readFlacInfo, readVideoInfo, inspectGifFrames, readMidiInfo } from "@utilix-tech/sdk/media";
|
|
883
883
|
|
|
884
884
|
// Reads format, dimensions, bit depth, and alpha channel straight from
|
|
885
885
|
// file bytes: no decoding, no canvas, no image library.
|
|
@@ -945,9 +945,17 @@ readVideoInfo(new Uint8Array(videoBytes));
|
|
|
945
945
|
const gifBytes = await fs.promises.readFile("animation.gif");
|
|
946
946
|
inspectGifFrames(new Uint8Array(gifBytes));
|
|
947
947
|
// { version: "89a", width: 480, height: 270, frameCount: 24, animated: true, loopCount: 0, totalDurationMs: 2400, ... }
|
|
948
|
+
|
|
949
|
+
// Parses a Standard MIDI File's header and every track chunk to report
|
|
950
|
+
// format, tick division, initial tempo, time signature, and per-track
|
|
951
|
+
// event/note-on counts, plus total duration in seconds: no MIDI playback
|
|
952
|
+
// or synthesis.
|
|
953
|
+
const midiBytes = await fs.promises.readFile("song.mid");
|
|
954
|
+
readMidiInfo(new Uint8Array(midiBytes));
|
|
955
|
+
// { format: 1, trackCount: 2, initialTempoBpm: 120, timeSignature: { numerator: 4, denominator: 4 }, durationSeconds: 4, tracks: [...] }
|
|
948
956
|
```
|
|
949
957
|
|
|
950
|
-
`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. `readVideoInfo` supports MP4 (ISO BMFF) and WebM (EBML/Matroska) containers only. `inspectGifFrames` reads the GIF87a/89a block structure only; it never decodes pixel data.
|
|
958
|
+
`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. `readVideoInfo` supports MP4 (ISO BMFF) and WebM (EBML/Matroska) containers only. `inspectGifFrames` reads the GIF87a/89a block structure only; it never decodes pixel data. `readMidiInfo` reads Standard MIDI Files (format 0/1/2) only.
|
|
951
959
|
|
|
952
960
|
---
|
|
953
961
|
|
|
@@ -962,9 +970,9 @@ inspectGifFrames(new Uint8Array(gifBytes));
|
|
|
962
970
|
| `@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 |
|
|
963
971
|
| `@utilix-tech/sdk/generators` | UUID v4/v7, ULID, passwords, strength check, entropy estimator, fake data, CSP nonce generator |
|
|
964
972
|
| `@utilix-tech/sdk/time` | Date diff, timezone convert, cron parse, relative time |
|
|
965
|
-
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, EMI calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator, Rule of 72 calculator, savings goal calculator, ROI calculator, cash denomination/till calculator, payback period calculator, CD maturity calculator, debt-to-income ratio calculator, freelancer hourly rate calculator, debt snowball/avalanche payoff planner |
|
|
973
|
+
| `@utilix-tech/sdk/units` | Bytes, CSS px/rem, number bases, currency format, credit card/IBAN/barcode/ISBN validation, loan calculator, EMI calculator, compound/simple interest calculators, VAT/sales tax calculator, tip/bill split calculator, break-even point calculator, Rule of 72 calculator, savings goal calculator, ROI calculator, cash denomination/till calculator, payback period calculator, CD maturity calculator, debt-to-income ratio calculator, freelancer hourly rate calculator, debt snowball/avalanche payoff planner, tax-equivalent yield calculator |
|
|
966
974
|
| `@utilix-tech/sdk/network` | HTTP status codes, IP validation, country flags, geolocation URLs, HAR file parsing, User-Agent parsing, IPv6 address compress/expand, Permissions-Policy header builder, Client Hints header builder/parser, Content-Disposition header builder/parser, X-Content-Type-Options header builder/risk assessor, sitemap.xml validator, sitemap index splitter, Prefer header builder/parser (RFC 7240), Retry-After header builder/parser (RFC 9110), 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 |
|
|
967
|
-
| `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header |
|
|
975
|
+
| `@utilix-tech/sdk/api` | cURL build/parse, JWT decode/sign, JWKS parse, CORS headers, CSP header, TOTP/HOTP code generator |
|
|
968
976
|
| `@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 |
|
|
969
977
|
| `@utilix-tech/sdk/color` | Hex/RGB/HSL/HSV/CMYK convert, palette generation, contrast check |
|
|
970
978
|
| `@utilix-tech/sdk/css` | CSS gradient, box-shadow, border-radius, keyframe animation generators |
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - url-parser : parseUrl, buildUrl, addParam, removeParam, getParam,
|
|
14
14
|
* encodeUrlComponent, decodeUrlComponent, isValidUrl, normalizeUrl
|
|
15
15
|
* - og-meta : parseMetaTags, validateOgTags, generateMetaHtml
|
|
16
|
+
* - totp-hotp-generator : generateHotp, generateTotp
|
|
16
17
|
*
|
|
17
18
|
* Node 18+ only. All crypto uses node:crypto; no browser-only APIs.
|
|
18
19
|
*/
|
|
@@ -287,6 +288,51 @@ declare function validateOgTags(tags: OgTags): {
|
|
|
287
288
|
missing: string[];
|
|
288
289
|
};
|
|
289
290
|
declare function generateMetaHtml(tags: Partial<OgTags & TwitterTags>): string;
|
|
291
|
+
type OtpAlgorithm = 'SHA1' | 'SHA256' | 'SHA512';
|
|
292
|
+
interface OtpOptions {
|
|
293
|
+
digits?: number;
|
|
294
|
+
algorithm?: OtpAlgorithm;
|
|
295
|
+
}
|
|
296
|
+
interface HotpResult {
|
|
297
|
+
code: string;
|
|
298
|
+
counter: number;
|
|
299
|
+
digits: number;
|
|
300
|
+
algorithm: OtpAlgorithm;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Generates an HOTP code (RFC 4226) for a given counter value from a
|
|
304
|
+
* base32-encoded shared secret. Supports 6-8 digit codes and
|
|
305
|
+
* SHA-1/SHA-256/SHA-512 (SHA-1 is the default per the RFC and what most
|
|
306
|
+
* authenticator apps expect). The secret is never stored, only used for
|
|
307
|
+
* this one HMAC computation.
|
|
308
|
+
*/
|
|
309
|
+
declare function generateHotp(secret: string, counter: number, options?: OtpOptions): HotpResult | {
|
|
310
|
+
error: string;
|
|
311
|
+
};
|
|
312
|
+
interface TotpOptions extends OtpOptions {
|
|
313
|
+
periodSeconds?: number;
|
|
314
|
+
/** Unix timestamp in seconds. Defaults to the current time; pass an
|
|
315
|
+
* explicit value for deterministic/testable output. */
|
|
316
|
+
timestamp?: number;
|
|
317
|
+
}
|
|
318
|
+
interface TotpResult {
|
|
319
|
+
code: string;
|
|
320
|
+
counter: number;
|
|
321
|
+
digits: number;
|
|
322
|
+
algorithm: OtpAlgorithm;
|
|
323
|
+
periodSeconds: number;
|
|
324
|
+
secondsRemaining: number;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Generates the current TOTP code (RFC 6238) from a base32-encoded shared
|
|
328
|
+
* secret, plus how many seconds remain before it rotates. TOTP is HOTP
|
|
329
|
+
* where the counter is the number of `periodSeconds` windows elapsed since
|
|
330
|
+
* the Unix epoch. Defaults (30s period, 6 digits, SHA-1) match what
|
|
331
|
+
* Google Authenticator and most other authenticator apps use.
|
|
332
|
+
*/
|
|
333
|
+
declare function generateTotp(secret: string, options?: TotpOptions): TotpResult | {
|
|
334
|
+
error: string;
|
|
335
|
+
};
|
|
290
336
|
|
|
291
337
|
declare const api_COMMON_HEADERS: typeof COMMON_HEADERS;
|
|
292
338
|
declare const api_COMMON_PRESETS: typeof COMMON_PRESETS;
|
|
@@ -304,6 +350,7 @@ type api_DirectiveConfig = DirectiveConfig;
|
|
|
304
350
|
type api_FormatResult = FormatResult;
|
|
305
351
|
declare const api_HTTP_METHODS: typeof HTTP_METHODS;
|
|
306
352
|
type api_Header = Header;
|
|
353
|
+
type api_HotpResult = HotpResult;
|
|
307
354
|
type api_HttpMethod = HttpMethod;
|
|
308
355
|
type api_JwkSummary = JwkSummary;
|
|
309
356
|
type api_JwkThumbprintErrorResult = JwkThumbprintErrorResult;
|
|
@@ -322,10 +369,14 @@ type api_JwtResult = JwtResult;
|
|
|
322
369
|
type api_JwtSignOptions = JwtSignOptions;
|
|
323
370
|
type api_MetaTags = MetaTags;
|
|
324
371
|
type api_OgTags = OgTags;
|
|
372
|
+
type api_OtpAlgorithm = OtpAlgorithm;
|
|
373
|
+
type api_OtpOptions = OtpOptions;
|
|
325
374
|
type api_ParsedUrl = ParsedUrl;
|
|
326
375
|
type api_ResponseFormat = ResponseFormat;
|
|
327
376
|
declare const api_SUPPORTED_LANGUAGES: typeof SUPPORTED_LANGUAGES;
|
|
328
377
|
type api_TargetLanguage = TargetLanguage;
|
|
378
|
+
type api_TotpOptions = TotpOptions;
|
|
379
|
+
type api_TotpResult = TotpResult;
|
|
329
380
|
type api_TwitterTags = TwitterTags;
|
|
330
381
|
declare const api_addParam: typeof addParam;
|
|
331
382
|
declare const api_base64urlDecode: typeof base64urlDecode;
|
|
@@ -345,7 +396,9 @@ declare const api_formatResponse: typeof formatResponse;
|
|
|
345
396
|
declare const api_generateCors: typeof generateCors;
|
|
346
397
|
declare const api_generateCspHeader: typeof generateCspHeader;
|
|
347
398
|
declare const api_generateFullHeader: typeof generateFullHeader;
|
|
399
|
+
declare const api_generateHotp: typeof generateHotp;
|
|
348
400
|
declare const api_generateMetaHtml: typeof generateMetaHtml;
|
|
401
|
+
declare const api_generateTotp: typeof generateTotp;
|
|
349
402
|
declare const api_getParam: typeof getParam;
|
|
350
403
|
declare const api_getStats: typeof getStats;
|
|
351
404
|
declare const api_isValidUrl: typeof isValidUrl;
|
|
@@ -362,7 +415,7 @@ declare const api_validateCorsConfig: typeof validateCorsConfig;
|
|
|
362
415
|
declare const api_validateCspConfig: typeof validateCspConfig;
|
|
363
416
|
declare const api_validateOgTags: typeof validateOgTags;
|
|
364
417
|
declare namespace api {
|
|
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 };
|
|
418
|
+
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_HotpResult as HotpResult, 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_OtpAlgorithm as OtpAlgorithm, type api_OtpOptions as OtpOptions, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TotpOptions as TotpOptions, type api_TotpResult as TotpResult, 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_generateHotp as generateHotp, api_generateMetaHtml as generateMetaHtml, api_generateTotp as generateTotp, 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 };
|
|
366
419
|
}
|
|
367
420
|
|
|
368
|
-
export {
|
|
421
|
+
export { decodeJwt as $, type JwtPart as A, type JwtResult as B, COMMON_HEADERS as C, DEFAULT_CORS_CONFIG as D, type JwtSignOptions as E, type FormatResult as F, type OtpAlgorithm as G, HTTP_METHODS as H, type OtpOptions as I, type JwkSummary as J, type TotpOptions as K, type TotpResult as L, type MetaTags as M, type TwitterTags as N, type OgTags as O, type ParsedUrl as P, addParam as Q, type ResponseFormat as R, SUPPORTED_LANGUAGES as S, type TargetLanguage as T, base64urlDecode as U, base64urlEncode as V, buildCurl as W, buildCurlMultiline as X, buildUrl as Y, computeJwkThumbprint as Z, convertToCode as _, api as a, decodeUrlComponent as a0, detectFormat as a1, encodeUrlComponent as a2, extractJsonPaths as a3, formatJwtJson as a4, formatResponse as a5, generateCors as a6, generateCspHeader as a7, generateFullHeader as a8, generateHotp as a9, generateMetaHtml as aa, generateTotp as ab, getParam as ac, getStats as ad, isValidUrl as ae, normalizeUrl as af, parseCspHeader as ag, parseCurl as ah, parseCurlCommand as ai, parseJwks as aj, parseMetaTags as ak, parseUrl as al, removeParam as am, signJwt as an, validateCorsConfig as ao, validateCspConfig as ap, validateOgTags as aq, 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 HotpResult as n, type HttpMethod as o, type JwkThumbprintErrorResult as p, type JwkThumbprintHash as q, type JwkThumbprintOutcome as r, type JwkThumbprintResult as s, type JwksError as t, type JwksParseResult as u, type JwksResult as v, type JwtAlgorithm as w, type JwtDecodeError as x, type JwtDecodeResult as y, type JwtExpiry as z };
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - url-parser : parseUrl, buildUrl, addParam, removeParam, getParam,
|
|
14
14
|
* encodeUrlComponent, decodeUrlComponent, isValidUrl, normalizeUrl
|
|
15
15
|
* - og-meta : parseMetaTags, validateOgTags, generateMetaHtml
|
|
16
|
+
* - totp-hotp-generator : generateHotp, generateTotp
|
|
16
17
|
*
|
|
17
18
|
* Node 18+ only. All crypto uses node:crypto; no browser-only APIs.
|
|
18
19
|
*/
|
|
@@ -287,6 +288,51 @@ declare function validateOgTags(tags: OgTags): {
|
|
|
287
288
|
missing: string[];
|
|
288
289
|
};
|
|
289
290
|
declare function generateMetaHtml(tags: Partial<OgTags & TwitterTags>): string;
|
|
291
|
+
type OtpAlgorithm = 'SHA1' | 'SHA256' | 'SHA512';
|
|
292
|
+
interface OtpOptions {
|
|
293
|
+
digits?: number;
|
|
294
|
+
algorithm?: OtpAlgorithm;
|
|
295
|
+
}
|
|
296
|
+
interface HotpResult {
|
|
297
|
+
code: string;
|
|
298
|
+
counter: number;
|
|
299
|
+
digits: number;
|
|
300
|
+
algorithm: OtpAlgorithm;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Generates an HOTP code (RFC 4226) for a given counter value from a
|
|
304
|
+
* base32-encoded shared secret. Supports 6-8 digit codes and
|
|
305
|
+
* SHA-1/SHA-256/SHA-512 (SHA-1 is the default per the RFC and what most
|
|
306
|
+
* authenticator apps expect). The secret is never stored, only used for
|
|
307
|
+
* this one HMAC computation.
|
|
308
|
+
*/
|
|
309
|
+
declare function generateHotp(secret: string, counter: number, options?: OtpOptions): HotpResult | {
|
|
310
|
+
error: string;
|
|
311
|
+
};
|
|
312
|
+
interface TotpOptions extends OtpOptions {
|
|
313
|
+
periodSeconds?: number;
|
|
314
|
+
/** Unix timestamp in seconds. Defaults to the current time; pass an
|
|
315
|
+
* explicit value for deterministic/testable output. */
|
|
316
|
+
timestamp?: number;
|
|
317
|
+
}
|
|
318
|
+
interface TotpResult {
|
|
319
|
+
code: string;
|
|
320
|
+
counter: number;
|
|
321
|
+
digits: number;
|
|
322
|
+
algorithm: OtpAlgorithm;
|
|
323
|
+
periodSeconds: number;
|
|
324
|
+
secondsRemaining: number;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Generates the current TOTP code (RFC 6238) from a base32-encoded shared
|
|
328
|
+
* secret, plus how many seconds remain before it rotates. TOTP is HOTP
|
|
329
|
+
* where the counter is the number of `periodSeconds` windows elapsed since
|
|
330
|
+
* the Unix epoch. Defaults (30s period, 6 digits, SHA-1) match what
|
|
331
|
+
* Google Authenticator and most other authenticator apps use.
|
|
332
|
+
*/
|
|
333
|
+
declare function generateTotp(secret: string, options?: TotpOptions): TotpResult | {
|
|
334
|
+
error: string;
|
|
335
|
+
};
|
|
290
336
|
|
|
291
337
|
declare const api_COMMON_HEADERS: typeof COMMON_HEADERS;
|
|
292
338
|
declare const api_COMMON_PRESETS: typeof COMMON_PRESETS;
|
|
@@ -304,6 +350,7 @@ type api_DirectiveConfig = DirectiveConfig;
|
|
|
304
350
|
type api_FormatResult = FormatResult;
|
|
305
351
|
declare const api_HTTP_METHODS: typeof HTTP_METHODS;
|
|
306
352
|
type api_Header = Header;
|
|
353
|
+
type api_HotpResult = HotpResult;
|
|
307
354
|
type api_HttpMethod = HttpMethod;
|
|
308
355
|
type api_JwkSummary = JwkSummary;
|
|
309
356
|
type api_JwkThumbprintErrorResult = JwkThumbprintErrorResult;
|
|
@@ -322,10 +369,14 @@ type api_JwtResult = JwtResult;
|
|
|
322
369
|
type api_JwtSignOptions = JwtSignOptions;
|
|
323
370
|
type api_MetaTags = MetaTags;
|
|
324
371
|
type api_OgTags = OgTags;
|
|
372
|
+
type api_OtpAlgorithm = OtpAlgorithm;
|
|
373
|
+
type api_OtpOptions = OtpOptions;
|
|
325
374
|
type api_ParsedUrl = ParsedUrl;
|
|
326
375
|
type api_ResponseFormat = ResponseFormat;
|
|
327
376
|
declare const api_SUPPORTED_LANGUAGES: typeof SUPPORTED_LANGUAGES;
|
|
328
377
|
type api_TargetLanguage = TargetLanguage;
|
|
378
|
+
type api_TotpOptions = TotpOptions;
|
|
379
|
+
type api_TotpResult = TotpResult;
|
|
329
380
|
type api_TwitterTags = TwitterTags;
|
|
330
381
|
declare const api_addParam: typeof addParam;
|
|
331
382
|
declare const api_base64urlDecode: typeof base64urlDecode;
|
|
@@ -345,7 +396,9 @@ declare const api_formatResponse: typeof formatResponse;
|
|
|
345
396
|
declare const api_generateCors: typeof generateCors;
|
|
346
397
|
declare const api_generateCspHeader: typeof generateCspHeader;
|
|
347
398
|
declare const api_generateFullHeader: typeof generateFullHeader;
|
|
399
|
+
declare const api_generateHotp: typeof generateHotp;
|
|
348
400
|
declare const api_generateMetaHtml: typeof generateMetaHtml;
|
|
401
|
+
declare const api_generateTotp: typeof generateTotp;
|
|
349
402
|
declare const api_getParam: typeof getParam;
|
|
350
403
|
declare const api_getStats: typeof getStats;
|
|
351
404
|
declare const api_isValidUrl: typeof isValidUrl;
|
|
@@ -362,7 +415,7 @@ declare const api_validateCorsConfig: typeof validateCorsConfig;
|
|
|
362
415
|
declare const api_validateCspConfig: typeof validateCspConfig;
|
|
363
416
|
declare const api_validateOgTags: typeof validateOgTags;
|
|
364
417
|
declare namespace api {
|
|
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 };
|
|
418
|
+
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_HotpResult as HotpResult, 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_OtpAlgorithm as OtpAlgorithm, type api_OtpOptions as OtpOptions, type api_ParsedUrl as ParsedUrl, type api_ResponseFormat as ResponseFormat, api_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES, type api_TargetLanguage as TargetLanguage, type api_TotpOptions as TotpOptions, type api_TotpResult as TotpResult, 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_generateHotp as generateHotp, api_generateMetaHtml as generateMetaHtml, api_generateTotp as generateTotp, 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 };
|
|
366
419
|
}
|
|
367
420
|
|
|
368
|
-
export {
|
|
421
|
+
export { decodeJwt as $, type JwtPart as A, type JwtResult as B, COMMON_HEADERS as C, DEFAULT_CORS_CONFIG as D, type JwtSignOptions as E, type FormatResult as F, type OtpAlgorithm as G, HTTP_METHODS as H, type OtpOptions as I, type JwkSummary as J, type TotpOptions as K, type TotpResult as L, type MetaTags as M, type TwitterTags as N, type OgTags as O, type ParsedUrl as P, addParam as Q, type ResponseFormat as R, SUPPORTED_LANGUAGES as S, type TargetLanguage as T, base64urlDecode as U, base64urlEncode as V, buildCurl as W, buildCurlMultiline as X, buildUrl as Y, computeJwkThumbprint as Z, convertToCode as _, api as a, decodeUrlComponent as a0, detectFormat as a1, encodeUrlComponent as a2, extractJsonPaths as a3, formatJwtJson as a4, formatResponse as a5, generateCors as a6, generateCspHeader as a7, generateFullHeader as a8, generateHotp as a9, generateMetaHtml as aa, generateTotp as ab, getParam as ac, getStats as ad, isValidUrl as ae, normalizeUrl as af, parseCspHeader as ag, parseCurl as ah, parseCurlCommand as ai, parseJwks as aj, parseMetaTags as ak, parseUrl as al, removeParam as am, signJwt as an, validateCorsConfig as ao, validateCspConfig as ap, validateOgTags as aq, 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 HotpResult as n, type HttpMethod as o, type JwkThumbprintErrorResult as p, type JwkThumbprintHash as q, type JwkThumbprintOutcome as r, type JwkThumbprintResult as s, type JwksError as t, type JwksParseResult as u, type JwksResult as v, type JwtAlgorithm as w, type JwtDecodeError as x, type JwtDecodeResult as y, type JwtExpiry as z };
|
|
@@ -29,7 +29,9 @@ __export(api_exports, {
|
|
|
29
29
|
generateCors: () => generateCors,
|
|
30
30
|
generateCspHeader: () => generateCspHeader,
|
|
31
31
|
generateFullHeader: () => generateFullHeader,
|
|
32
|
+
generateHotp: () => generateHotp,
|
|
32
33
|
generateMetaHtml: () => generateMetaHtml,
|
|
34
|
+
generateTotp: () => generateTotp,
|
|
33
35
|
getParam: () => getParam,
|
|
34
36
|
getStats: () => getStats,
|
|
35
37
|
isValidUrl: () => isValidUrl,
|
|
@@ -1308,5 +1310,74 @@ function generateMetaHtml(tags) {
|
|
|
1308
1310
|
}
|
|
1309
1311
|
return lines.join("\n");
|
|
1310
1312
|
}
|
|
1313
|
+
var OTP_ALG_TO_NODE_HASH = {
|
|
1314
|
+
SHA1: "sha1",
|
|
1315
|
+
SHA256: "sha256",
|
|
1316
|
+
SHA512: "sha512"
|
|
1317
|
+
};
|
|
1318
|
+
function decodeBase32Secret(secret) {
|
|
1319
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
1320
|
+
const cleaned = secret.toUpperCase().replace(/[\s=]/g, "");
|
|
1321
|
+
if (!cleaned) {
|
|
1322
|
+
return { error: "Secret must not be empty" };
|
|
1323
|
+
}
|
|
1324
|
+
const bytes = [];
|
|
1325
|
+
let buffer = 0;
|
|
1326
|
+
let bitsLeft = 0;
|
|
1327
|
+
for (const char of cleaned) {
|
|
1328
|
+
const val = alphabet.indexOf(char);
|
|
1329
|
+
if (val === -1) {
|
|
1330
|
+
return { error: `Invalid base32 character '${char}' in secret` };
|
|
1331
|
+
}
|
|
1332
|
+
buffer = buffer << 5 | val;
|
|
1333
|
+
bitsLeft += 5;
|
|
1334
|
+
if (bitsLeft >= 8) {
|
|
1335
|
+
bitsLeft -= 8;
|
|
1336
|
+
bytes.push(buffer >> bitsLeft & 255);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return Buffer.from(bytes);
|
|
1340
|
+
}
|
|
1341
|
+
function hotpFromCounter(keyBytes, counter, digits, algorithm) {
|
|
1342
|
+
const counterBuffer = Buffer.alloc(8);
|
|
1343
|
+
counterBuffer.writeUInt32BE(Math.floor(counter / 4294967296), 0);
|
|
1344
|
+
counterBuffer.writeUInt32BE(counter % 4294967296, 4);
|
|
1345
|
+
const hmac = createHmac(OTP_ALG_TO_NODE_HASH[algorithm], keyBytes).update(counterBuffer).digest();
|
|
1346
|
+
const offset = hmac[hmac.length - 1] & 15;
|
|
1347
|
+
const binCode = (hmac[offset] & 127) << 24 | (hmac[offset + 1] & 255) << 16 | (hmac[offset + 2] & 255) << 8 | hmac[offset + 3] & 255;
|
|
1348
|
+
const code = (binCode % 10 ** digits).toString().padStart(digits, "0");
|
|
1349
|
+
return code;
|
|
1350
|
+
}
|
|
1351
|
+
function generateHotp(secret, counter, options = {}) {
|
|
1352
|
+
const digits = options.digits ?? 6;
|
|
1353
|
+
const algorithm = options.algorithm ?? "SHA1";
|
|
1354
|
+
if (!Number.isInteger(counter) || counter < 0) {
|
|
1355
|
+
return { error: "Counter must be a non-negative whole number" };
|
|
1356
|
+
}
|
|
1357
|
+
if (!Number.isInteger(digits) || digits < 6 || digits > 8) {
|
|
1358
|
+
return { error: "Digits must be an integer between 6 and 8" };
|
|
1359
|
+
}
|
|
1360
|
+
if (!(algorithm in OTP_ALG_TO_NODE_HASH)) {
|
|
1361
|
+
return { error: "Algorithm must be one of SHA1, SHA256, SHA512" };
|
|
1362
|
+
}
|
|
1363
|
+
const keyBytes = decodeBase32Secret(secret);
|
|
1364
|
+
if ("error" in keyBytes) return keyBytes;
|
|
1365
|
+
return { code: hotpFromCounter(keyBytes, counter, digits, algorithm), counter, digits, algorithm };
|
|
1366
|
+
}
|
|
1367
|
+
function generateTotp(secret, options = {}) {
|
|
1368
|
+
const periodSeconds = options.periodSeconds ?? 30;
|
|
1369
|
+
const timestamp = options.timestamp ?? Math.floor(Date.now() / 1e3);
|
|
1370
|
+
if (!Number.isFinite(periodSeconds) || periodSeconds <= 0) {
|
|
1371
|
+
return { error: "Period must be a positive number of seconds" };
|
|
1372
|
+
}
|
|
1373
|
+
if (!Number.isFinite(timestamp) || timestamp < 0) {
|
|
1374
|
+
return { error: "Timestamp must be a non-negative number of seconds" };
|
|
1375
|
+
}
|
|
1376
|
+
const counter = Math.floor(timestamp / periodSeconds);
|
|
1377
|
+
const hotp = generateHotp(secret, counter, options);
|
|
1378
|
+
if ("error" in hotp) return hotp;
|
|
1379
|
+
const secondsRemaining = periodSeconds - timestamp % periodSeconds;
|
|
1380
|
+
return { ...hotp, periodSeconds, secondsRemaining };
|
|
1381
|
+
}
|
|
1311
1382
|
|
|
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 };
|
|
1383
|
+
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, generateHotp, generateMetaHtml, generateTotp, getParam, getStats, isValidUrl, normalizeUrl, parseCspHeader, parseCurl, parseCurlCommand, parseJwks, parseMetaTags, parseUrl, removeParam, signJwt, validateCorsConfig, validateCspConfig, validateOgTags };
|
|
@@ -47,6 +47,7 @@ __export(units_exports, {
|
|
|
47
47
|
calculateSavingsGoal: () => calculateSavingsGoal,
|
|
48
48
|
calculateSimpleInterest: () => calculateSimpleInterest,
|
|
49
49
|
calculateTax: () => calculateTax,
|
|
50
|
+
calculateTaxEquivalentYield: () => calculateTaxEquivalentYield,
|
|
50
51
|
calculateTimeValue: () => calculateTimeValue,
|
|
51
52
|
calculateTipSplit: () => calculateTipSplit,
|
|
52
53
|
calculateVideoBitrate: () => calculateVideoBitrate,
|
|
@@ -2503,5 +2504,44 @@ function calculateLoanRefinance(currentBalance, currentRatePercent, currentRemai
|
|
|
2503
2504
|
totalInterestSavings: round2(current.totalInterest - refinanced.totalInterest)
|
|
2504
2505
|
};
|
|
2505
2506
|
}
|
|
2507
|
+
function calculateTaxEquivalentYield(inputYieldPercent, federalTaxRatePercent, stateTaxRatePercent = 0, direction) {
|
|
2508
|
+
if (!Number.isFinite(inputYieldPercent) || inputYieldPercent < 0) {
|
|
2509
|
+
return { error: "Yield must be zero or a positive number" };
|
|
2510
|
+
}
|
|
2511
|
+
if (!Number.isFinite(federalTaxRatePercent) || federalTaxRatePercent < 0 || federalTaxRatePercent >= 100) {
|
|
2512
|
+
return { error: "Federal tax rate must be a percentage between 0 and 100 (exclusive of 100)" };
|
|
2513
|
+
}
|
|
2514
|
+
if (!Number.isFinite(stateTaxRatePercent) || stateTaxRatePercent < 0 || stateTaxRatePercent >= 100) {
|
|
2515
|
+
return { error: "State tax rate must be a percentage between 0 and 100 (exclusive of 100)" };
|
|
2516
|
+
}
|
|
2517
|
+
if (direction !== "tax-free-to-taxable" && direction !== "taxable-to-tax-free") {
|
|
2518
|
+
return { error: 'Direction must be "tax-free-to-taxable" or "taxable-to-tax-free"' };
|
|
2519
|
+
}
|
|
2520
|
+
const combinedTaxRatePercent = federalTaxRatePercent + stateTaxRatePercent;
|
|
2521
|
+
if (combinedTaxRatePercent >= 100) {
|
|
2522
|
+
return { error: "Combined federal and state tax rate must be less than 100%" };
|
|
2523
|
+
}
|
|
2524
|
+
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
2525
|
+
const combinedRateDecimal = combinedTaxRatePercent / 100;
|
|
2526
|
+
let taxFreeYield;
|
|
2527
|
+
let taxableYield;
|
|
2528
|
+
if (direction === "tax-free-to-taxable") {
|
|
2529
|
+
taxFreeYield = inputYieldPercent;
|
|
2530
|
+
taxableYield = taxFreeYield / (1 - combinedRateDecimal);
|
|
2531
|
+
} else {
|
|
2532
|
+
taxableYield = inputYieldPercent;
|
|
2533
|
+
taxFreeYield = taxableYield * (1 - combinedRateDecimal);
|
|
2534
|
+
}
|
|
2535
|
+
return {
|
|
2536
|
+
direction,
|
|
2537
|
+
inputYield: round6(inputYieldPercent),
|
|
2538
|
+
federalTaxRatePercent: round6(federalTaxRatePercent),
|
|
2539
|
+
stateTaxRatePercent: round6(stateTaxRatePercent),
|
|
2540
|
+
combinedTaxRatePercent: round6(combinedTaxRatePercent),
|
|
2541
|
+
taxFreeYield: round6(taxFreeYield),
|
|
2542
|
+
taxableYield: round6(taxableYield),
|
|
2543
|
+
yieldDifference: round6(taxableYield - taxFreeYield)
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2506
2546
|
|
|
2507
|
-
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, GPA_GRADE_POINTS, USD_DENOMINATIONS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateExtraPaymentPayoff, calculateFreelancerRate, calculateGpa, calculateLoan, calculateLoanRefinance, calculateMarginMarkup, calculateMovingAverages, calculatePaybackPeriod, calculatePercentageChange, calculateRoi, calculateRuleOf72, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, planDebtPayoff, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, viewportToPx };
|
|
2547
|
+
export { BASE_LABELS, BASE_PREFIXES, BYTE_UNITS, COMMON_CURRENCIES, COMMON_LOCALES, COMMON_PERMISSIONS, COMMON_RATIOS, COMMON_SIZES, COMMON_TAX_RATES, COMMON_TIP_PERCENTAGES, COMMON_VIEWPORTS, GPA_GRADE_POINTS, USD_DENOMINATIONS, applySuccessiveDiscounts, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateBlendedLoanRate, calculateBreakEven, calculateCashBreakdown, calculateCashRounding, calculateCdMaturity, calculateChange, calculateCompoundInterest, calculateCreditUtilization, calculateDebtToIncomeRatio, calculateDepreciationSchedule, calculateDiscount, calculateEmi, calculateExtraPaymentPayoff, calculateFreelancerRate, calculateGpa, calculateLoan, calculateLoanRefinance, calculateMarginMarkup, calculateMovingAverages, calculatePaybackPeriod, calculatePercentageChange, calculateRoi, calculateRuleOf72, calculateSavingsGoal, calculateSimpleInterest, calculateTax, calculateTaxEquivalentYield, calculateTimeValue, calculateTipSplit, calculateVideoBitrate, compareSemver, compareUnitPrices, convert, convertAprApy, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, marginToMarkup, markupToMargin, octalToPermissions, parseFileSize, parseInput, parseNumberString, parseSemver, permissionsToChmod, permissionsToOctal, permissionsToSymbolic, planDebtPayoff, ptToPx, pxToAll, pxToViewport, remToPx, satisfiesRange, scaleByHeight, scaleByWidth, sortVersions, splitSubnet, symbolicToPermissions, units_exports, validateBarcode, validateCardNumber, validateIban, validateIsbn, viewportToPx };
|
|
@@ -9,6 +9,7 @@ __export(media_exports, {
|
|
|
9
9
|
readIcoInfo: () => readIcoInfo,
|
|
10
10
|
readId3Tags: () => readId3Tags,
|
|
11
11
|
readImageInfo: () => readImageInfo,
|
|
12
|
+
readMidiInfo: () => readMidiInfo,
|
|
12
13
|
readPdfMetadata: () => readPdfMetadata,
|
|
13
14
|
readPdfPageDimensions: () => readPdfPageDimensions,
|
|
14
15
|
readVideoInfo: () => readVideoInfo,
|
|
@@ -1306,5 +1307,135 @@ function inspectGifFrames(bytes) {
|
|
|
1306
1307
|
hasTransparency
|
|
1307
1308
|
};
|
|
1308
1309
|
}
|
|
1310
|
+
function readU16BE(bytes, offset) {
|
|
1311
|
+
return bytes[offset] << 8 | bytes[offset + 1];
|
|
1312
|
+
}
|
|
1313
|
+
function readMidiVarLen(bytes, pos) {
|
|
1314
|
+
let value = 0;
|
|
1315
|
+
let i = pos;
|
|
1316
|
+
for (let n = 0; n < 4; n++) {
|
|
1317
|
+
if (i >= bytes.length) return null;
|
|
1318
|
+
const b = bytes[i];
|
|
1319
|
+
value = value << 7 | b & 127;
|
|
1320
|
+
i++;
|
|
1321
|
+
if ((b & 128) === 0) return { value, next: i };
|
|
1322
|
+
}
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
function readMidiInfo(bytes) {
|
|
1326
|
+
if (bytes.length < 14) return { error: "File is too small to be a valid MIDI file" };
|
|
1327
|
+
if (Buffer.from(bytes.subarray(0, 4)).toString("ascii") !== "MThd") {
|
|
1328
|
+
return { error: 'Not a valid MIDI file (missing "MThd" header chunk)' };
|
|
1329
|
+
}
|
|
1330
|
+
const headerLength = readU32BE(bytes, 4);
|
|
1331
|
+
if (headerLength < 6 || 8 + headerLength > bytes.length) return { error: "Truncated or invalid MThd header chunk" };
|
|
1332
|
+
const format = readU16BE(bytes, 8);
|
|
1333
|
+
if (format !== 0 && format !== 1 && format !== 2) return { error: `Unsupported MIDI format: ${format} (expected 0, 1, or 2)` };
|
|
1334
|
+
const trackCount = readU16BE(bytes, 10);
|
|
1335
|
+
if (trackCount < 1) return { error: "MIDI file declares zero tracks" };
|
|
1336
|
+
const divisionRaw = readU16BE(bytes, 12);
|
|
1337
|
+
const division = (divisionRaw & 32768) !== 0 ? { type: "smpte", framesPerSecond: -(divisionRaw >> 8 << 24 >> 24), ticksPerFrame: divisionRaw & 255 } : { type: "ppq", ticksPerQuarterNote: divisionRaw & 32767 };
|
|
1338
|
+
const tracks = [];
|
|
1339
|
+
const tempoChanges = [];
|
|
1340
|
+
let timeSignature = null;
|
|
1341
|
+
let maxEndTick = 0;
|
|
1342
|
+
let offset = 8 + headerLength;
|
|
1343
|
+
let tracksParsed = 0;
|
|
1344
|
+
while (tracksParsed < trackCount && offset + 8 <= bytes.length) {
|
|
1345
|
+
const chunkId = Buffer.from(bytes.subarray(offset, offset + 4)).toString("ascii");
|
|
1346
|
+
const chunkLength = readU32BE(bytes, offset + 4);
|
|
1347
|
+
const dataStart = offset + 8;
|
|
1348
|
+
const dataEnd = Math.min(dataStart + chunkLength, bytes.length);
|
|
1349
|
+
if (chunkId !== "MTrk") {
|
|
1350
|
+
offset = dataEnd;
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
let pos = dataStart;
|
|
1354
|
+
let runningStatus = 0;
|
|
1355
|
+
let absoluteTick = 0;
|
|
1356
|
+
let eventCount = 0;
|
|
1357
|
+
let noteOnCount = 0;
|
|
1358
|
+
let name = null;
|
|
1359
|
+
while (pos < dataEnd) {
|
|
1360
|
+
const delta = readMidiVarLen(bytes, pos);
|
|
1361
|
+
if (!delta) break;
|
|
1362
|
+
pos = delta.next;
|
|
1363
|
+
absoluteTick += delta.value;
|
|
1364
|
+
if (pos >= dataEnd) break;
|
|
1365
|
+
let statusByte = bytes[pos];
|
|
1366
|
+
if (statusByte & 128) {
|
|
1367
|
+
runningStatus = statusByte;
|
|
1368
|
+
pos++;
|
|
1369
|
+
}
|
|
1370
|
+
statusByte = runningStatus;
|
|
1371
|
+
eventCount++;
|
|
1372
|
+
if (statusByte === 255) {
|
|
1373
|
+
const metaType = bytes[pos];
|
|
1374
|
+
pos++;
|
|
1375
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1376
|
+
if (!len) break;
|
|
1377
|
+
pos = len.next;
|
|
1378
|
+
const data = bytes.subarray(pos, Math.min(pos + len.value, dataEnd));
|
|
1379
|
+
pos += len.value;
|
|
1380
|
+
if (metaType === 81 && data.length >= 3) {
|
|
1381
|
+
tempoChanges.push({ tick: absoluteTick, usPerQuarter: data[0] << 16 | data[1] << 8 | data[2] });
|
|
1382
|
+
} else if (metaType === 88 && data.length >= 2) {
|
|
1383
|
+
if (!timeSignature) timeSignature = { numerator: data[0], denominator: 2 ** data[1] };
|
|
1384
|
+
} else if (metaType === 3 && name === null) {
|
|
1385
|
+
name = Buffer.from(data).toString("utf8").trim() || null;
|
|
1386
|
+
} else if (metaType === 47) {
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
} else if (statusByte === 240 || statusByte === 247) {
|
|
1390
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1391
|
+
if (!len) break;
|
|
1392
|
+
pos = len.next + len.value;
|
|
1393
|
+
} else if (statusByte >= 128) {
|
|
1394
|
+
const type = statusByte & 240;
|
|
1395
|
+
if (type === 192 || type === 208) {
|
|
1396
|
+
pos += 1;
|
|
1397
|
+
} else {
|
|
1398
|
+
if (type === 144 && (bytes[pos + 1] ?? 0) > 0) noteOnCount++;
|
|
1399
|
+
pos += 2;
|
|
1400
|
+
}
|
|
1401
|
+
} else {
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
tracks.push({ index: tracksParsed, name, eventCount, noteOnCount });
|
|
1406
|
+
maxEndTick = Math.max(maxEndTick, absoluteTick);
|
|
1407
|
+
offset = dataEnd;
|
|
1408
|
+
tracksParsed++;
|
|
1409
|
+
}
|
|
1410
|
+
if (tracks.length === 0) return { error: "No MTrk track chunks found in this MIDI file" };
|
|
1411
|
+
const DEFAULT_US_PER_QUARTER = 5e5;
|
|
1412
|
+
const initialTempoBpm = Math.round(6e7 / (tempoChanges[0]?.usPerQuarter ?? DEFAULT_US_PER_QUARTER) * 100) / 100;
|
|
1413
|
+
let durationSeconds = null;
|
|
1414
|
+
if (division.type === "smpte") {
|
|
1415
|
+
const ticksPerSecond = division.framesPerSecond * division.ticksPerFrame;
|
|
1416
|
+
durationSeconds = ticksPerSecond > 0 ? Math.round(maxEndTick / ticksPerSecond * 1e3) / 1e3 : null;
|
|
1417
|
+
} else {
|
|
1418
|
+
const sorted = [...tempoChanges].sort((a, b) => a.tick - b.tick);
|
|
1419
|
+
let seconds = 0;
|
|
1420
|
+
let prevTick = 0;
|
|
1421
|
+
let currentUsPerQuarter = DEFAULT_US_PER_QUARTER;
|
|
1422
|
+
for (const change of sorted) {
|
|
1423
|
+
seconds += (change.tick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1424
|
+
prevTick = change.tick;
|
|
1425
|
+
currentUsPerQuarter = change.usPerQuarter;
|
|
1426
|
+
}
|
|
1427
|
+
seconds += (maxEndTick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1428
|
+
durationSeconds = Math.round(seconds * 1e3) / 1e3;
|
|
1429
|
+
}
|
|
1430
|
+
return {
|
|
1431
|
+
format,
|
|
1432
|
+
trackCount: tracks.length,
|
|
1433
|
+
division,
|
|
1434
|
+
initialTempoBpm,
|
|
1435
|
+
timeSignature,
|
|
1436
|
+
durationSeconds,
|
|
1437
|
+
tracks
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1309
1440
|
|
|
1310
|
-
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo };
|
|
1441
|
+
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readMidiInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo };
|