@utilix-tech/sdk 0.19.0 → 0.21.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 +22 -5
- package/dist/{chunk-F57WMKZO.js → chunk-2I4O5YCT.js} +100 -1
- package/dist/{chunk-U34B5XPK.js → chunk-TFNCFD3E.js} +25 -1
- package/dist/{chunk-TSAGO3XP.js → chunk-WO3LJMVV.js} +54 -1
- package/dist/{chunk-3QMUUTBH.js → chunk-Y5OOP6WN.js} +94 -1
- package/dist/index.cjs +269 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/{json-BjSoIS1h.d.cts → json-DEVFIpsC.d.cts} +26 -2
- package/dist/{json-BjSoIS1h.d.ts → json-DEVFIpsC.d.ts} +26 -2
- package/dist/{network-DZugNyAQ.d.cts → network-BJOjJnmY.d.cts} +45 -2
- package/dist/{network-DZugNyAQ.d.ts → network-BJOjJnmY.d.ts} +45 -2
- package/dist/{text-Bbx-tZ43.d.cts → text-DS70aMY5.d.cts} +33 -2
- package/dist/{text-Bbx-tZ43.d.ts → text-DS70aMY5.d.ts} +33 -2
- package/dist/tools/json.cjs +53 -0
- package/dist/tools/json.d.cts +1 -1
- package/dist/tools/json.d.ts +1 -1
- package/dist/tools/json.js +1 -1
- package/dist/tools/network.cjs +93 -0
- package/dist/tools/network.d.cts +1 -1
- package/dist/tools/network.d.ts +1 -1
- package/dist/tools/network.js +1 -1
- package/dist/tools/text.cjs +99 -0
- package/dist/tools/text.d.cts +1 -1
- package/dist/tools/text.d.ts +1 -1
- package/dist/tools/text.js +1 -1
- package/dist/tools/units.cjs +24 -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-BFTQwpBu.d.cts → units-pIRkHnsU.d.cts} +24 -2
- package/dist/{units-BFTQwpBu.d.ts → units-pIRkHnsU.d.ts} +24 -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.21.0
|
|
6
|
+
- Add `calculatePercentageChange` (percentage-change-calculator): percentage change between two numbers, with absolute change, direction, and multiplier.
|
|
7
|
+
- Add `resolveJsonPointer` (json-pointer-resolver): resolve an RFC 6901 JSON Pointer against a document, with ~0/~1 escaping and clear not-found reasons.
|
|
8
|
+
|
|
9
|
+
## 0.20.0
|
|
10
|
+
- Add `buildCacheControl` (cache-control-builder): assemble and explain a `Cache-Control` response header from directives, with warnings for combinations that cancel out.
|
|
11
|
+
- Add `detectFillerWords` (filler-word-detector): scan text for filler words, hedge phrases, and clichés with per-phrase counts and a density score.
|
|
12
|
+
|
|
5
13
|
## 0.19.0
|
|
6
14
|
- Add `detectRedos` (regex-redos-detector): statically flag catastrophic-backtracking risk (nested quantifiers, overlapping alternation, adjacent overlapping quantifiers).
|
|
7
15
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**558 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)
|
|
@@ -66,7 +66,7 @@ const { formatJson } = require("@utilix-tech/sdk/json");
|
|
|
66
66
|
|
|
67
67
|
```ts
|
|
68
68
|
import { formatJson, minifyJson, diffJson, jsonToTypescript, validateJson,
|
|
69
|
-
evaluateJsonPath, jsonToCsv, yamlToJson, jsonToYaml, getJsonStats } from "@utilix-tech/sdk/json";
|
|
69
|
+
evaluateJsonPath, resolveJsonPointer, jsonToCsv, yamlToJson, jsonToYaml, getJsonStats } from "@utilix-tech/sdk/json";
|
|
70
70
|
|
|
71
71
|
// Format with 2-space indent
|
|
72
72
|
formatJson('{"name":"alice","age":30}', { indent: 2 });
|
|
@@ -80,6 +80,10 @@ jsonToTypescript('{"id":1,"name":"Alice"}', { rootName: "User" });
|
|
|
80
80
|
// Query with JSONPath
|
|
81
81
|
evaluateJsonPath(obj, "$.users[*].name");
|
|
82
82
|
|
|
83
|
+
// Resolve an RFC 6901 JSON Pointer
|
|
84
|
+
resolveJsonPointer('{"users":[{"name":"Alice"}]}', "/users/0/name");
|
|
85
|
+
// { found: true, value: "Alice", tokens: ["users", "0", "name"] }
|
|
86
|
+
|
|
83
87
|
// Convert JSON to CSV
|
|
84
88
|
jsonToCsv('[{"a":1,"b":2}]');
|
|
85
89
|
|
|
@@ -132,7 +136,8 @@ generateHtpasswdFile([{ username: "alice", password: "secret" }]);
|
|
|
132
136
|
|
|
133
137
|
```ts
|
|
134
138
|
import { convertCase, slugify, countWords, generateWords, generateParagraphs,
|
|
135
|
-
escapeString, htmlToMarkdown, applyOps, detectPassiveVoice, scoreReadability
|
|
139
|
+
escapeString, htmlToMarkdown, applyOps, detectPassiveVoice, scoreReadability,
|
|
140
|
+
detectFillerWords } from "@utilix-tech/sdk/text";
|
|
136
141
|
|
|
137
142
|
convertCase("hello world", "camelCase"); // "helloWorld"
|
|
138
143
|
convertCase("hello world", "PascalCase"); // "HelloWorld"
|
|
@@ -157,6 +162,10 @@ detectPassiveVoice("The report was reviewed by the committee. They approved it."
|
|
|
157
162
|
// Flesch Reading Ease, Flesch-Kincaid Grade Level, and Gunning Fog Index
|
|
158
163
|
scoreReadability("The cat sat on the mat. It was a sunny day.");
|
|
159
164
|
// { fleschReadingEase: 109, fleschKincaidGrade: -0.6, gunningFog: 2.2, readingLevel: "Very easy (5th grade)", ... }
|
|
165
|
+
|
|
166
|
+
// Flag filler words, hedge phrases, and clichés with counts and density
|
|
167
|
+
detectFillerWords("At the end of the day, I just really think we should circle back.");
|
|
168
|
+
// { totalMatches: 4, fillerCount: 2, clicheCount: 2, density: 28.6, matches: [...] }
|
|
160
169
|
```
|
|
161
170
|
|
|
162
171
|
---
|
|
@@ -247,7 +256,7 @@ getNextRuns("*/5 * * * *", 5); // next 5 run timestamps
|
|
|
247
256
|
### `/units`: Conversions
|
|
248
257
|
|
|
249
258
|
```ts
|
|
250
|
-
import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue } from "@utilix-tech/sdk/units";
|
|
259
|
+
import { convertBytes, pxToAll, convert, formatValue, validateCardNumber, validateIban, calculateLoan, validateBarcode, calculateTax, calculateCompoundInterest, validateIsbn, calculateTipSplit, calculateSimpleInterest, calculateTimeValue, calculatePercentageChange } from "@utilix-tech/sdk/units";
|
|
251
260
|
|
|
252
261
|
// File sizes
|
|
253
262
|
convertBytes(1073741824, "GB"); // 1
|
|
@@ -305,6 +314,10 @@ calculateSimpleInterest(1000, 5, 2);
|
|
|
305
314
|
// Present / future value (time value of money), either direction
|
|
306
315
|
calculateTimeValue(1000, 5, 10, "future");
|
|
307
316
|
// { presentValue: 1000, futureValue: 1628.89, growthFactor: 1.628895, ... }
|
|
317
|
+
|
|
318
|
+
// Percentage change between two numbers
|
|
319
|
+
calculatePercentageChange(200, 250);
|
|
320
|
+
// { percentChange: 25, absoluteChange: 50, direction: "increase", multiplier: 1.25 }
|
|
308
321
|
```
|
|
309
322
|
|
|
310
323
|
---
|
|
@@ -315,7 +328,7 @@ calculateTimeValue(1000, 5, 10, "future");
|
|
|
315
328
|
import { getStatusCode, isValidIp, isValidIpv4, isValidIpv6,
|
|
316
329
|
searchStatusCodes, buildGeoUrl, countryCodeToFlag,
|
|
317
330
|
parseHar, validateSitemap, validateRobotsTxt,
|
|
318
|
-
parseUserAgent } from "@utilix-tech/sdk/network";
|
|
331
|
+
parseUserAgent, buildCacheControl } from "@utilix-tech/sdk/network";
|
|
319
332
|
|
|
320
333
|
getStatusCode(404);
|
|
321
334
|
// { code: 404, text: "Not Found", description: "...", category: "Client Error" }
|
|
@@ -345,6 +358,10 @@ validateSitemap('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><ur
|
|
|
345
358
|
// Validate a robots.txt file: User-agent groups, rules, and syntax mistakes
|
|
346
359
|
validateRobotsTxt("User-agent: *\nDisallow: /admin\nSitemap: https://example.com/sitemap.xml");
|
|
347
360
|
// { groups: [...], sitemaps: [...], issues: [...], valid: true }
|
|
361
|
+
|
|
362
|
+
// Build and explain a Cache-Control response header from directives
|
|
363
|
+
buildCacheControl({ visibility: "public", maxAge: 3600, mustRevalidate: true });
|
|
364
|
+
// { header: "public, max-age=3600, must-revalidate", directives: [...], warnings: [] }
|
|
348
365
|
```
|
|
349
366
|
|
|
350
367
|
---
|
|
@@ -20,6 +20,7 @@ __export(text_exports, {
|
|
|
20
20
|
csvToHtml: () => csvToHtml,
|
|
21
21
|
csvToMarkdown: () => csvToMarkdown,
|
|
22
22
|
detectDelimiter: () => detectDelimiter,
|
|
23
|
+
detectFillerWords: () => detectFillerWords,
|
|
23
24
|
detectPassiveVoice: () => detectPassiveVoice,
|
|
24
25
|
diffLines: () => diffLines,
|
|
25
26
|
diffLinesSimple: () => diffLinesSimple,
|
|
@@ -2457,5 +2458,103 @@ function scoreReadability(text) {
|
|
|
2457
2458
|
readingLevel: readabilityGradeLabel(fleschReadingEase)
|
|
2458
2459
|
};
|
|
2459
2460
|
}
|
|
2461
|
+
var FILLER_WORDS = [
|
|
2462
|
+
"very",
|
|
2463
|
+
"really",
|
|
2464
|
+
"just",
|
|
2465
|
+
"actually",
|
|
2466
|
+
"basically",
|
|
2467
|
+
"literally",
|
|
2468
|
+
"simply",
|
|
2469
|
+
"totally",
|
|
2470
|
+
"definitely",
|
|
2471
|
+
"certainly",
|
|
2472
|
+
"essentially",
|
|
2473
|
+
"absolutely",
|
|
2474
|
+
"completely",
|
|
2475
|
+
"extremely",
|
|
2476
|
+
"truly",
|
|
2477
|
+
"honestly",
|
|
2478
|
+
"seriously",
|
|
2479
|
+
"obviously",
|
|
2480
|
+
"clearly",
|
|
2481
|
+
"quite",
|
|
2482
|
+
"rather",
|
|
2483
|
+
"somewhat",
|
|
2484
|
+
"fairly",
|
|
2485
|
+
"virtually"
|
|
2486
|
+
];
|
|
2487
|
+
var HEDGE_PHRASES = [
|
|
2488
|
+
"maybe",
|
|
2489
|
+
"perhaps",
|
|
2490
|
+
"probably",
|
|
2491
|
+
"possibly",
|
|
2492
|
+
"arguably",
|
|
2493
|
+
"kind of",
|
|
2494
|
+
"sort of",
|
|
2495
|
+
"a bit",
|
|
2496
|
+
"a little",
|
|
2497
|
+
"more or less",
|
|
2498
|
+
"in my opinion",
|
|
2499
|
+
"i think",
|
|
2500
|
+
"i believe",
|
|
2501
|
+
"to some extent"
|
|
2502
|
+
];
|
|
2503
|
+
var CLICHE_PHRASES = [
|
|
2504
|
+
"at the end of the day",
|
|
2505
|
+
"think outside the box",
|
|
2506
|
+
"low-hanging fruit",
|
|
2507
|
+
"move the needle",
|
|
2508
|
+
"circle back",
|
|
2509
|
+
"touch base",
|
|
2510
|
+
"on the same page",
|
|
2511
|
+
"hit the ground running",
|
|
2512
|
+
"paradigm shift",
|
|
2513
|
+
"win-win",
|
|
2514
|
+
"game changer",
|
|
2515
|
+
"best of both worlds",
|
|
2516
|
+
"only time will tell",
|
|
2517
|
+
"back to the drawing board",
|
|
2518
|
+
"the tip of the iceberg",
|
|
2519
|
+
"a perfect storm",
|
|
2520
|
+
"needle in a haystack",
|
|
2521
|
+
"raise the bar",
|
|
2522
|
+
"push the envelope",
|
|
2523
|
+
"the elephant in the room",
|
|
2524
|
+
"boil the ocean",
|
|
2525
|
+
"when it rains it pours"
|
|
2526
|
+
];
|
|
2527
|
+
var FILLER_DICTIONARY = [
|
|
2528
|
+
...FILLER_WORDS.map((phrase) => ({ phrase, category: "filler" })),
|
|
2529
|
+
...HEDGE_PHRASES.map((phrase) => ({ phrase, category: "hedge" })),
|
|
2530
|
+
...CLICHE_PHRASES.map((phrase) => ({ phrase, category: "cliche" }))
|
|
2531
|
+
];
|
|
2532
|
+
function _fillerRegex(phrase) {
|
|
2533
|
+
const tokens = phrase.split(/\s+/).map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
2534
|
+
return new RegExp("\\b" + tokens.join("\\s+") + "\\b", "gi");
|
|
2535
|
+
}
|
|
2536
|
+
function detectFillerWords(text) {
|
|
2537
|
+
const totalWords = (text.match(/[A-Za-z0-9']+/g) ?? []).length;
|
|
2538
|
+
const matches = [];
|
|
2539
|
+
for (const entry of FILLER_DICTIONARY) {
|
|
2540
|
+
const re = _fillerRegex(entry.phrase);
|
|
2541
|
+
const indices = [];
|
|
2542
|
+
let m;
|
|
2543
|
+
while ((m = re.exec(text)) !== null) {
|
|
2544
|
+
indices.push(m.index);
|
|
2545
|
+
if (m.index === re.lastIndex) re.lastIndex++;
|
|
2546
|
+
}
|
|
2547
|
+
if (indices.length > 0) {
|
|
2548
|
+
matches.push({ phrase: entry.phrase, category: entry.category, count: indices.length, indices });
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
matches.sort((a, b) => b.count - a.count || a.category.localeCompare(b.category) || a.phrase.localeCompare(b.phrase));
|
|
2552
|
+
const fillerCount = matches.filter((x) => x.category === "filler").reduce((s, x) => s + x.count, 0);
|
|
2553
|
+
const hedgeCount = matches.filter((x) => x.category === "hedge").reduce((s, x) => s + x.count, 0);
|
|
2554
|
+
const clicheCount = matches.filter((x) => x.category === "cliche").reduce((s, x) => s + x.count, 0);
|
|
2555
|
+
const totalMatches = fillerCount + hedgeCount + clicheCount;
|
|
2556
|
+
const density = totalWords > 0 ? Math.round(totalMatches / totalWords * 1e3) / 10 : 0;
|
|
2557
|
+
return { totalWords, totalMatches, fillerCount, hedgeCount, clicheCount, density, matches };
|
|
2558
|
+
}
|
|
2460
2559
|
|
|
2461
|
-
export { CASE_FORMATS, DEFAULT_CONVERT_OPTIONS, ESCAPE_MODES, LINE_OPS, MORSE_CODE, addBorder, analyzeText, applyOp, applyOps, centerText, convertAll, convertCase, countWords, csvToHtml, csvToMarkdown, detectDelimiter, detectPassiveVoice, diffLines, diffLinesSimple, diffStats, escapeAll, escapeString, extractHeadings, formatReadingTime, generate, generateDiff, generateHtmlTable, generateMarkdownTable, generateParagraphs, generateSentences, generateWords, getAvailableFonts, getMorseForChar, getMorseTiming, htmlToMarkdown, isValidMorse, markdownToHtml, morseToText, numberToOrdinal, numberToRoman, numberToWords, ordinalWords, parseDiff, parseTableData, readabilityScore, renderMarkdown, romanToNumber, scoreReadability, sentenceCount, slugify, slugifyAll, syllableCount, textToAscii, textToMorse, text_exports, unescapeString, wordFrequency };
|
|
2560
|
+
export { CASE_FORMATS, DEFAULT_CONVERT_OPTIONS, ESCAPE_MODES, LINE_OPS, MORSE_CODE, addBorder, analyzeText, applyOp, applyOps, centerText, convertAll, convertCase, countWords, csvToHtml, csvToMarkdown, detectDelimiter, detectFillerWords, detectPassiveVoice, diffLines, diffLinesSimple, diffStats, escapeAll, escapeString, extractHeadings, formatReadingTime, generate, generateDiff, generateHtmlTable, generateMarkdownTable, generateParagraphs, generateSentences, generateWords, getAvailableFonts, getMorseForChar, getMorseTiming, htmlToMarkdown, isValidMorse, markdownToHtml, morseToText, numberToOrdinal, numberToRoman, numberToWords, ordinalWords, parseDiff, parseTableData, readabilityScore, renderMarkdown, romanToNumber, scoreReadability, sentenceCount, slugify, slugifyAll, syllableCount, textToAscii, textToMorse, text_exports, unescapeString, wordFrequency };
|
|
@@ -20,6 +20,7 @@ __export(units_exports, {
|
|
|
20
20
|
calcSubnet: () => calcSubnet,
|
|
21
21
|
calculateCompoundInterest: () => calculateCompoundInterest,
|
|
22
22
|
calculateLoan: () => calculateLoan,
|
|
23
|
+
calculatePercentageChange: () => calculatePercentageChange,
|
|
23
24
|
calculateSimpleInterest: () => calculateSimpleInterest,
|
|
24
25
|
calculateTax: () => calculateTax,
|
|
25
26
|
calculateTimeValue: () => calculateTimeValue,
|
|
@@ -1337,5 +1338,28 @@ function calculateTimeValue(amount, annualRatePercent, years, direction, compoun
|
|
|
1337
1338
|
interest: round22(futureValue - presentValue)
|
|
1338
1339
|
};
|
|
1339
1340
|
}
|
|
1341
|
+
function calculatePercentageChange(oldValue, newValue) {
|
|
1342
|
+
if (!Number.isFinite(oldValue)) return { error: "Original value must be a finite number" };
|
|
1343
|
+
if (!Number.isFinite(newValue)) return { error: "New value must be a finite number" };
|
|
1344
|
+
if (oldValue === 0 && newValue !== 0) {
|
|
1345
|
+
return { error: "Percentage change from zero is undefined (division by zero); use a nonzero original value" };
|
|
1346
|
+
}
|
|
1347
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
1348
|
+
const absoluteChange = newValue - oldValue;
|
|
1349
|
+
const percentChange = oldValue === 0 ? 0 : absoluteChange / Math.abs(oldValue) * 100;
|
|
1350
|
+
const multiplier = oldValue === 0 ? 1 : newValue / oldValue;
|
|
1351
|
+
let direction;
|
|
1352
|
+
if (absoluteChange > 0) direction = "increase";
|
|
1353
|
+
else if (absoluteChange < 0) direction = "decrease";
|
|
1354
|
+
else direction = "no change";
|
|
1355
|
+
return {
|
|
1356
|
+
oldValue,
|
|
1357
|
+
newValue,
|
|
1358
|
+
absoluteChange: round4(absoluteChange),
|
|
1359
|
+
percentChange: round4(percentChange),
|
|
1360
|
+
direction,
|
|
1361
|
+
multiplier: round4(multiplier)
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1340
1364
|
|
|
1341
|
-
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, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateCompoundInterest, calculateLoan, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, compareSemver, convert, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, 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 };
|
|
1365
|
+
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, autoFormat, bumpVersion, calcAspectRatio, calcSubnet, calculateCompoundInterest, calculateLoan, calculatePercentageChange, calculateSimpleInterest, calculateTax, calculateTimeValue, calculateTipSplit, compareSemver, convert, convertBytes, convertIp, coverBox, decimalToIp, decimalToIpCidr, describePermissions, fitIntoBox, formatNumber, formatValue, formatVwValue, formatWithSeparator, gcd, ipFromBinary, ipFromDecimal, ipFromHex, ipToBinary, ipToDecimal, ipToDecimalCidr, ipToHex, ipToOctal, isValid, isValidCidr, isValidIp, isValidIpv4, 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 };
|
|
@@ -24,6 +24,7 @@ __export(json_exports, {
|
|
|
24
24
|
minifyJson: () => minifyJson,
|
|
25
25
|
parseJson: () => parseJson,
|
|
26
26
|
parseStructs: () => parseStructs,
|
|
27
|
+
resolveJsonPointer: () => resolveJsonPointer,
|
|
27
28
|
toPascalCase: () => toPascalCase,
|
|
28
29
|
toSnakeCase: () => toSnakeCase,
|
|
29
30
|
validateJson: () => validateJson,
|
|
@@ -1249,5 +1250,57 @@ function jsonToZod(json, options) {
|
|
|
1249
1250
|
|
|
1250
1251
|
export const ${rootName}Schema = ${schema}`;
|
|
1251
1252
|
}
|
|
1253
|
+
function _unescapePointerToken(token) {
|
|
1254
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1255
|
+
}
|
|
1256
|
+
function resolveJsonPointer(json, pointer) {
|
|
1257
|
+
let parsed;
|
|
1258
|
+
try {
|
|
1259
|
+
parsed = JSON.parse(json);
|
|
1260
|
+
} catch {
|
|
1261
|
+
return { pointer, found: false, tokens: [], error: "Invalid JSON input" };
|
|
1262
|
+
}
|
|
1263
|
+
let ptr = pointer;
|
|
1264
|
+
if (ptr.startsWith("#")) {
|
|
1265
|
+
ptr = ptr.slice(1);
|
|
1266
|
+
try {
|
|
1267
|
+
ptr = decodeURIComponent(ptr);
|
|
1268
|
+
} catch {
|
|
1269
|
+
return { pointer, found: false, tokens: [], error: "Invalid percent-encoding in URI-fragment pointer" };
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (ptr === "") {
|
|
1273
|
+
return { pointer, found: true, value: parsed, tokens: [] };
|
|
1274
|
+
}
|
|
1275
|
+
if (!ptr.startsWith("/")) {
|
|
1276
|
+
return { pointer, found: false, tokens: [], error: "Pointer must be empty or start with '/' (RFC 6901)" };
|
|
1277
|
+
}
|
|
1278
|
+
const tokens = ptr.split("/").slice(1).map(_unescapePointerToken);
|
|
1279
|
+
let current = parsed;
|
|
1280
|
+
for (const token of tokens) {
|
|
1281
|
+
if (Array.isArray(current)) {
|
|
1282
|
+
if (token === "-") {
|
|
1283
|
+
return { pointer, found: false, tokens, failedAt: token, reason: '"-" refers to the nonexistent element after the end of the array' };
|
|
1284
|
+
}
|
|
1285
|
+
if (!/^(0|[1-9][0-9]*)$/.test(token)) {
|
|
1286
|
+
return { pointer, found: false, tokens, failedAt: token, reason: `"${token}" is not a valid array index` };
|
|
1287
|
+
}
|
|
1288
|
+
const idx = Number(token);
|
|
1289
|
+
if (idx >= current.length) {
|
|
1290
|
+
return { pointer, found: false, tokens, failedAt: token, reason: `array index ${idx} is out of bounds (length ${current.length})` };
|
|
1291
|
+
}
|
|
1292
|
+
current = current[idx];
|
|
1293
|
+
} else if (current !== null && typeof current === "object") {
|
|
1294
|
+
const obj = current;
|
|
1295
|
+
if (!Object.prototype.hasOwnProperty.call(obj, token)) {
|
|
1296
|
+
return { pointer, found: false, tokens, failedAt: token, reason: `key "${token}" not found` };
|
|
1297
|
+
}
|
|
1298
|
+
current = obj[token];
|
|
1299
|
+
} else {
|
|
1300
|
+
return { pointer, found: false, tokens, failedAt: token, reason: `cannot descend into ${current === null ? "null" : typeof current} at "${token}"` };
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return { pointer, found: true, value: current, tokens };
|
|
1304
|
+
}
|
|
1252
1305
|
|
|
1253
|
-
export { csvToJson, detectFormat, diffJson, evaluateJsonPath, formatDiffLine, formatJson, generateExample, getJsonStats, inferGoType, inferPythonType, inferType, jsonToCsv, jsonToGo, jsonToPython, jsonToTypescript, jsonToYaml, jsonToZod, json_exports, minifyJson, parseJson, parseStructs, toPascalCase, toSnakeCase, validateJson, validateJsonPath, validateJsonString, yamlToJson };
|
|
1306
|
+
export { csvToJson, detectFormat, diffJson, evaluateJsonPath, formatDiffLine, formatJson, generateExample, getJsonStats, inferGoType, inferPythonType, inferType, jsonToCsv, jsonToGo, jsonToPython, jsonToTypescript, jsonToYaml, jsonToZod, json_exports, minifyJson, parseJson, parseStructs, resolveJsonPointer, toPascalCase, toSnakeCase, validateJson, validateJsonPath, validateJsonString, yamlToJson };
|
|
@@ -6,6 +6,7 @@ __export(network_exports, {
|
|
|
6
6
|
DNS_RECORD_TYPES: () => DNS_RECORD_TYPES,
|
|
7
7
|
HTTP_HEADERS: () => HTTP_HEADERS,
|
|
8
8
|
STATUS_CODES: () => STATUS_CODES,
|
|
9
|
+
buildCacheControl: () => buildCacheControl,
|
|
9
10
|
buildDnsQueryUrl: () => buildDnsQueryUrl,
|
|
10
11
|
buildGeoUrl: () => buildGeoUrl,
|
|
11
12
|
buildMapUrl: () => buildMapUrl,
|
|
@@ -1721,5 +1722,97 @@ function parseUserAgent(ua) {
|
|
|
1721
1722
|
const device = { type: _detectUaDeviceType(s) };
|
|
1722
1723
|
return { browser, engine, os, device, raw: s };
|
|
1723
1724
|
}
|
|
1725
|
+
var _CACHE_NUM_FIELDS = ["maxAge", "sMaxAge", "staleWhileRevalidate", "staleIfError"];
|
|
1726
|
+
function _humanizeSeconds(s) {
|
|
1727
|
+
if (s === 0) return "0 seconds";
|
|
1728
|
+
const units = [
|
|
1729
|
+
[86400, "day"],
|
|
1730
|
+
[3600, "hour"],
|
|
1731
|
+
[60, "minute"],
|
|
1732
|
+
[1, "second"]
|
|
1733
|
+
];
|
|
1734
|
+
const parts = [];
|
|
1735
|
+
let rem = s;
|
|
1736
|
+
for (const [size, label] of units) {
|
|
1737
|
+
if (rem >= size) {
|
|
1738
|
+
const n = Math.floor(rem / size);
|
|
1739
|
+
rem -= n * size;
|
|
1740
|
+
parts.push(`${n} ${label}${n === 1 ? "" : "s"}`);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
return parts.slice(0, 2).join(" ");
|
|
1744
|
+
}
|
|
1745
|
+
function buildCacheControl(options = {}) {
|
|
1746
|
+
const nums = {
|
|
1747
|
+
maxAge: null,
|
|
1748
|
+
sMaxAge: null,
|
|
1749
|
+
staleWhileRevalidate: null,
|
|
1750
|
+
staleIfError: null
|
|
1751
|
+
};
|
|
1752
|
+
for (const field of _CACHE_NUM_FIELDS) {
|
|
1753
|
+
const v = options[field];
|
|
1754
|
+
if (v === void 0 || v === null) continue;
|
|
1755
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v) || v < 0) {
|
|
1756
|
+
return { error: `${field} must be a non-negative integer number of seconds` };
|
|
1757
|
+
}
|
|
1758
|
+
nums[field] = v;
|
|
1759
|
+
}
|
|
1760
|
+
const directives = [];
|
|
1761
|
+
const warnings = [];
|
|
1762
|
+
if (options.visibility === "public") {
|
|
1763
|
+
directives.push({ name: "public", value: null, explanation: "Any cache, including shared caches and CDNs, may store this response." });
|
|
1764
|
+
}
|
|
1765
|
+
if (options.visibility === "private") {
|
|
1766
|
+
directives.push({ name: "private", value: null, explanation: "Only the user's browser may cache this response; shared caches and CDNs must not." });
|
|
1767
|
+
}
|
|
1768
|
+
if (options.noCache) {
|
|
1769
|
+
directives.push({ name: "no-cache", value: null, explanation: "A cache may store the response but must revalidate it with the origin before every reuse." });
|
|
1770
|
+
}
|
|
1771
|
+
if (options.noStore) {
|
|
1772
|
+
directives.push({ name: "no-store", value: null, explanation: "No cache may store any part of this request or response." });
|
|
1773
|
+
}
|
|
1774
|
+
if (nums.maxAge !== null) {
|
|
1775
|
+
directives.push({ name: "max-age", value: String(nums.maxAge), explanation: `Considered fresh for ${_humanizeSeconds(nums.maxAge)} (${nums.maxAge}s), after which it becomes stale.` });
|
|
1776
|
+
}
|
|
1777
|
+
if (nums.sMaxAge !== null) {
|
|
1778
|
+
directives.push({ name: "s-maxage", value: String(nums.sMaxAge), explanation: `For shared caches (CDNs, proxies) it is fresh for ${_humanizeSeconds(nums.sMaxAge)} (${nums.sMaxAge}s), overriding max-age there.` });
|
|
1779
|
+
}
|
|
1780
|
+
if (options.mustRevalidate) {
|
|
1781
|
+
directives.push({ name: "must-revalidate", value: null, explanation: "Once stale, a cache must revalidate with the origin and never serve the stale copy." });
|
|
1782
|
+
}
|
|
1783
|
+
if (options.proxyRevalidate) {
|
|
1784
|
+
directives.push({ name: "proxy-revalidate", value: null, explanation: "Like must-revalidate, but the requirement applies only to shared caches." });
|
|
1785
|
+
}
|
|
1786
|
+
if (options.immutable) {
|
|
1787
|
+
directives.push({ name: "immutable", value: null, explanation: "The body will not change while fresh, so browsers should not revalidate it on reload." });
|
|
1788
|
+
}
|
|
1789
|
+
if (options.noTransform) {
|
|
1790
|
+
directives.push({ name: "no-transform", value: null, explanation: "Intermediaries must not modify the response body (for example, recompressing images)." });
|
|
1791
|
+
}
|
|
1792
|
+
if (nums.staleWhileRevalidate !== null) {
|
|
1793
|
+
directives.push({ name: "stale-while-revalidate", value: String(nums.staleWhileRevalidate), explanation: `A cache may serve the stale response for up to ${_humanizeSeconds(nums.staleWhileRevalidate)} (${nums.staleWhileRevalidate}s) while it revalidates in the background.` });
|
|
1794
|
+
}
|
|
1795
|
+
if (nums.staleIfError !== null) {
|
|
1796
|
+
directives.push({ name: "stale-if-error", value: String(nums.staleIfError), explanation: `A cache may serve the stale response for up to ${_humanizeSeconds(nums.staleIfError)} (${nums.staleIfError}s) if the origin returns an error.` });
|
|
1797
|
+
}
|
|
1798
|
+
if (options.mustUnderstand) {
|
|
1799
|
+
directives.push({ name: "must-understand", value: null, explanation: "A cache should store the response only if it understands the caching requirements of the response's status code." });
|
|
1800
|
+
}
|
|
1801
|
+
if (options.noStore && directives.length > 1) {
|
|
1802
|
+
warnings.push("no-store means nothing is cached, so the other directives have no effect.");
|
|
1803
|
+
}
|
|
1804
|
+
if (options.noCache && nums.maxAge !== null) {
|
|
1805
|
+
warnings.push("no-cache forces revalidation on every use, so max-age has little practical effect alongside it.");
|
|
1806
|
+
}
|
|
1807
|
+
if (options.immutable && nums.maxAge === null) {
|
|
1808
|
+
warnings.push("immutable only applies while the response is fresh \u2014 add a max-age so it has a freshness lifetime.");
|
|
1809
|
+
}
|
|
1810
|
+
if (directives.length === 0) {
|
|
1811
|
+
warnings.push("No directives set \u2014 an empty Cache-Control header has no effect.");
|
|
1812
|
+
}
|
|
1813
|
+
const header = directives.map((d) => d.value === null ? d.name : `${d.name}=${d.value}`).join(", ");
|
|
1814
|
+
const snippet = `Cache-Control: ${header}`;
|
|
1815
|
+
return { header, headerName: "Cache-Control", snippet, directives, warnings };
|
|
1816
|
+
}
|
|
1724
1817
|
|
|
1725
|
-
export { DNS_RECORD_TYPES, HTTP_HEADERS, STATUS_CODES, buildDnsQueryUrl, buildGeoUrl, buildMapUrl, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseDnsResponse, parseGeoResponse, parseHar, parseUserAgent, searchHeaders, searchStatusCodes, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateRobotsTxt, validateSitemap };
|
|
1818
|
+
export { DNS_RECORD_TYPES, HTTP_HEADERS, STATUS_CODES, buildCacheControl, buildDnsQueryUrl, buildGeoUrl, buildMapUrl, countryCodeToFlag, formatCoordinates, formatTtl, getByCategory, getHeader, getHeadersByCategory, getStatusByCategory, getStatusCode, isClientError, isServerError, isSuccess, isValidIp, isValidIpv4, isValidIpv6, network_exports, parseDnsResponse, parseGeoResponse, parseHar, parseUserAgent, searchHeaders, searchStatusCodes, statusCodeToText, typeNumberToLabel, validateDomain, validateHeaderName, validateHeaderValue, validateRobotsTxt, validateSitemap };
|