@utilix-tech/sdk 0.4.0 → 0.5.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/dist/index.cjs CHANGED
@@ -8894,6 +8894,7 @@ __export(api_exports, {
8894
8894
  parseCspHeader: () => parseCspHeader,
8895
8895
  parseCurl: () => parseCurl,
8896
8896
  parseCurlCommand: () => parseCurlCommand,
8897
+ parseJwks: () => parseJwks,
8897
8898
  parseMetaTags: () => parseMetaTags,
8898
8899
  parseUrl: () => parseUrl,
8899
8900
  removeParam: () => removeParam,
@@ -9343,6 +9344,55 @@ function decodeJwt(token) {
9343
9344
  function formatJwtJson(obj) {
9344
9345
  return JSON.stringify(obj, null, 2);
9345
9346
  }
9347
+ function base64urlByteLength(s) {
9348
+ const clean = s.replace(/=+$/, "");
9349
+ return Math.floor(clean.length * 6 / 8);
9350
+ }
9351
+ function parseJwks(input) {
9352
+ const trimmed = input.trim();
9353
+ if (!trimmed) return { ok: false, error: "No input provided" };
9354
+ let data;
9355
+ try {
9356
+ data = JSON.parse(trimmed);
9357
+ } catch (e) {
9358
+ return { ok: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` };
9359
+ }
9360
+ let rawKeys;
9361
+ if (data && typeof data === "object" && Array.isArray(data.keys)) {
9362
+ rawKeys = data.keys;
9363
+ } else if (data && typeof data === "object" && typeof data.kty === "string") {
9364
+ rawKeys = [data];
9365
+ } else {
9366
+ return { ok: false, error: 'Input must be a JWKS object with a "keys" array, or a single JWK object with a "kty" field' };
9367
+ }
9368
+ const keys = rawKeys.map((k) => {
9369
+ const key = k ?? {};
9370
+ const kty = typeof key.kty === "string" ? key.kty : "unknown";
9371
+ let keySize = null;
9372
+ let curve;
9373
+ if (kty === "RSA" && typeof key.n === "string") {
9374
+ keySize = base64urlByteLength(key.n) * 8;
9375
+ } else if ((kty === "EC" || kty === "OKP") && typeof key.crv === "string") {
9376
+ curve = key.crv;
9377
+ } else if (kty === "oct" && typeof key.k === "string") {
9378
+ keySize = base64urlByteLength(key.k) * 8;
9379
+ }
9380
+ return {
9381
+ kty,
9382
+ use: typeof key.use === "string" ? key.use : void 0,
9383
+ keyOps: Array.isArray(key.key_ops) ? key.key_ops.filter((x) => typeof x === "string") : void 0,
9384
+ alg: typeof key.alg === "string" ? key.alg : void 0,
9385
+ kid: typeof key.kid === "string" ? key.kid : void 0,
9386
+ keySize,
9387
+ curve,
9388
+ raw: key
9389
+ };
9390
+ });
9391
+ const kidCounts = /* @__PURE__ */ new Map();
9392
+ for (const k of keys) if (k.kid) kidCounts.set(k.kid, (kidCounts.get(k.kid) ?? 0) + 1);
9393
+ const duplicateKids = [...kidCounts.entries()].filter(([, c]) => c > 1).map(([kid]) => kid);
9394
+ return { ok: true, keys, count: keys.length, duplicateKids };
9395
+ }
9346
9396
  var ALG_TO_NODE_HASH = {
9347
9397
  HS256: "sha256",
9348
9398
  HS384: "sha384",
@@ -16884,7 +16934,8 @@ function summarizeForLlm(text, maxTokens = 200, strategy = "extractive") {
16884
16934
  // src/tools/media.ts
16885
16935
  var media_exports = {};
16886
16936
  __export(media_exports, {
16887
- readImageInfo: () => readImageInfo
16937
+ readImageInfo: () => readImageInfo,
16938
+ readPdfMetadata: () => readPdfMetadata
16888
16939
  });
16889
16940
  function readU32BE(bytes, offset) {
16890
16941
  return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
@@ -17011,6 +17062,148 @@ function readImageInfo(bytes) {
17011
17062
  }
17012
17063
  return { error: "Unrecognized image format. Supported formats: PNG, JPEG, GIF, WebP, BMP" };
17013
17064
  }
17065
+ function bytesToLatin1(bytes) {
17066
+ let out = "";
17067
+ const CHUNK = 32768;
17068
+ for (let i = 0; i < bytes.length; i += CHUNK) {
17069
+ out += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
17070
+ }
17071
+ return out;
17072
+ }
17073
+ function decodePdfString(raw, isHex) {
17074
+ const bytes = [];
17075
+ if (isHex) {
17076
+ const hex = raw.replace(/\s+/g, "");
17077
+ const padded = hex.length % 2 === 0 ? hex : hex + "0";
17078
+ for (let i = 0; i < padded.length; i += 2) bytes.push(parseInt(padded.slice(i, i + 2), 16));
17079
+ } else {
17080
+ const s = raw.replace(/\\\r\n|\\\r|\\\n/g, "");
17081
+ for (let i = 0; i < s.length; i++) {
17082
+ const ch = s[i];
17083
+ if (ch === "\\" && i + 1 < s.length) {
17084
+ const next = s[i + 1];
17085
+ if (next === "n") {
17086
+ bytes.push(10);
17087
+ i++;
17088
+ } else if (next === "r") {
17089
+ bytes.push(13);
17090
+ i++;
17091
+ } else if (next === "t") {
17092
+ bytes.push(9);
17093
+ i++;
17094
+ } else if (next === "b") {
17095
+ bytes.push(8);
17096
+ i++;
17097
+ } else if (next === "f") {
17098
+ bytes.push(12);
17099
+ i++;
17100
+ } else if (next === "(" || next === ")" || next === "\\") {
17101
+ bytes.push(next.charCodeAt(0));
17102
+ i++;
17103
+ } else if (/[0-7]/.test(next)) {
17104
+ let oct = next;
17105
+ i++;
17106
+ for (let k = 0; k < 2 && i + 1 < s.length && /[0-7]/.test(s[i + 1]); k++) {
17107
+ i++;
17108
+ oct += s[i];
17109
+ }
17110
+ bytes.push(parseInt(oct, 8) & 255);
17111
+ } else {
17112
+ bytes.push(next.charCodeAt(0));
17113
+ i++;
17114
+ }
17115
+ } else {
17116
+ bytes.push(ch.charCodeAt(0) & 255);
17117
+ }
17118
+ }
17119
+ }
17120
+ if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
17121
+ let out = "";
17122
+ for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode(bytes[i] << 8 | bytes[i + 1]);
17123
+ return out;
17124
+ }
17125
+ return bytes.map((b) => String.fromCharCode(b)).join("");
17126
+ }
17127
+ function extractDictField(dict, key) {
17128
+ const litMatch = dict.match(new RegExp(`/${key}\\s*\\(((?:\\\\.|[^\\\\()])*)\\)`));
17129
+ if (litMatch) return decodePdfString(litMatch[1], false);
17130
+ const hexMatch = dict.match(new RegExp(`/${key}\\s*<([0-9A-Fa-f\\s]*)>`));
17131
+ if (hexMatch) return decodePdfString(hexMatch[1], true);
17132
+ return null;
17133
+ }
17134
+ function findObject(text, objNum, gen) {
17135
+ const m = text.match(new RegExp(`(?:^|[^0-9])${objNum}\\s+${gen}\\s+obj([\\s\\S]*?)endobj`));
17136
+ return m ? m[1] : null;
17137
+ }
17138
+ function parsePdfDate(raw) {
17139
+ const m = raw.match(/^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([Z+-])?(\d{2})?'?(\d{2})?'?/);
17140
+ if (!m) return null;
17141
+ const [, y, mo = "01", d = "01", h = "00", mi = "00", s = "00", tz, tzh, tzm] = m;
17142
+ let iso = `${y}-${mo}-${d}T${h}:${mi}:${s}`;
17143
+ if (tz === "Z") iso += "Z";
17144
+ else if (tz === "+" || tz === "-") iso += `${tz}${tzh ?? "00"}:${tzm ?? "00"}`;
17145
+ return iso;
17146
+ }
17147
+ function readPdfMetadata(bytes) {
17148
+ if (bytes.length < 8) return { error: "File too small to be a PDF" };
17149
+ const header = bytesToLatin1(bytes.subarray(0, 16));
17150
+ const versionMatch = header.match(/%PDF-(\d\.\d)/);
17151
+ if (!versionMatch) return { error: "Not a valid PDF file (missing %PDF header)" };
17152
+ const version2 = versionMatch[1];
17153
+ const text = bytesToLatin1(bytes);
17154
+ const trailerMatches = [...text.matchAll(/trailer\s*<<([\s\S]*?)>>/g)];
17155
+ let infoDict = null;
17156
+ let encrypted = false;
17157
+ if (trailerMatches.length > 0) {
17158
+ const trailer = trailerMatches[trailerMatches.length - 1][1];
17159
+ encrypted = /\/Encrypt\s+\d+\s+\d+\s+R/.test(trailer);
17160
+ const infoRef = trailer.match(/\/Info\s+(\d+)\s+(\d+)\s+R/);
17161
+ if (infoRef) infoDict = findObject(text, Number(infoRef[1]), Number(infoRef[2]));
17162
+ }
17163
+ const objBlocks = [...text.matchAll(/(\d+)\s+(\d+)\s+obj([\s\S]*?)endobj/g)];
17164
+ if (!infoDict) {
17165
+ for (const b of objBlocks) {
17166
+ if (/\/(Title|Author|Producer|Creator|CreationDate)\b/.test(b[3]) && !/\/Type\s*\/(Page|Pages|Catalog|Font)\b/.test(b[3])) {
17167
+ infoDict = b[3];
17168
+ break;
17169
+ }
17170
+ }
17171
+ }
17172
+ const title = infoDict ? extractDictField(infoDict, "Title") : null;
17173
+ const author = infoDict ? extractDictField(infoDict, "Author") : null;
17174
+ const subject = infoDict ? extractDictField(infoDict, "Subject") : null;
17175
+ const keywords = infoDict ? extractDictField(infoDict, "Keywords") : null;
17176
+ const creator = infoDict ? extractDictField(infoDict, "Creator") : null;
17177
+ const producer = infoDict ? extractDictField(infoDict, "Producer") : null;
17178
+ const creationDateRaw = infoDict ? extractDictField(infoDict, "CreationDate") : null;
17179
+ const modDateRaw = infoDict ? extractDictField(infoDict, "ModDate") : null;
17180
+ let pageCount = null;
17181
+ for (const b of objBlocks) {
17182
+ if (!/\/Type\s*\/Pages\b/.test(b[3])) continue;
17183
+ const countMatch = b[3].match(/\/Count\s+(\d+)/);
17184
+ if (countMatch) {
17185
+ const c = Number(countMatch[1]);
17186
+ if (pageCount === null || c > pageCount) pageCount = c;
17187
+ }
17188
+ }
17189
+ if (pageCount === null) {
17190
+ const pageMatches = text.match(/\/Type\s*\/Page(?!s)\b/g);
17191
+ if (pageMatches) pageCount = pageMatches.length;
17192
+ }
17193
+ return {
17194
+ version: version2,
17195
+ pageCount,
17196
+ title,
17197
+ author,
17198
+ subject,
17199
+ keywords,
17200
+ creator,
17201
+ producer,
17202
+ creationDate: creationDateRaw ? parsePdfDate(creationDateRaw) ?? creationDateRaw : null,
17203
+ modDate: modDateRaw ? parsePdfDate(modDateRaw) ?? modDateRaw : null,
17204
+ encrypted
17205
+ };
17206
+ }
17014
17207
 
17015
17208
  // src/index.ts
17016
17209
  var version = "0.1.0";
package/dist/index.d.cts CHANGED
@@ -7,13 +7,13 @@ export { g as generators } from './generators-BGtRGpJZ.cjs';
7
7
  export { t as time } from './time-DbT8fjaF.cjs';
8
8
  export { u as units } from './units-6lwDYBvX.cjs';
9
9
  export { n as network } from './network-DwyAjwJS.cjs';
10
- export { a as api } from './api-8aZtWhSj.cjs';
10
+ export { a as api } from './api-Xl7OzIgq.cjs';
11
11
  export { c as code } from './code-BUqyaofO.cjs';
12
12
  export { c as color } from './color-tPwZCr9H.cjs';
13
13
  export { c as css } from './css-Cf7AMGM-.cjs';
14
14
  export { m as misc } from './misc-CA3N198T.cjs';
15
15
  export { a as ai_agent } from './ai_agent-CcpkV2DR.cjs';
16
- export { m as media } from './media-DF2cx3pK.cjs';
16
+ export { m as media } from './media-D-K5aZnq.cjs';
17
17
 
18
18
  declare const version = "0.1.0";
19
19
 
package/dist/index.d.ts CHANGED
@@ -7,13 +7,13 @@ export { g as generators } from './generators-BGtRGpJZ.js';
7
7
  export { t as time } from './time-DbT8fjaF.js';
8
8
  export { u as units } from './units-6lwDYBvX.js';
9
9
  export { n as network } from './network-DwyAjwJS.js';
10
- export { a as api } from './api-8aZtWhSj.js';
10
+ export { a as api } from './api-Xl7OzIgq.js';
11
11
  export { c as code } from './code-BUqyaofO.js';
12
12
  export { c as color } from './color-tPwZCr9H.js';
13
13
  export { c as css } from './css-Cf7AMGM-.js';
14
14
  export { m as misc } from './misc-CA3N198T.js';
15
15
  export { a as ai_agent } from './ai_agent-CcpkV2DR.js';
16
- export { m as media } from './media-DF2cx3pK.js';
16
+ export { m as media } from './media-D-K5aZnq.js';
17
17
 
18
18
  declare const version = "0.1.0";
19
19
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { media_exports as media } from './chunk-N7C52YGL.js';
1
+ export { media_exports as media } from './chunk-WWKPLYEP.js';
2
2
  export { units_exports as units } from './chunk-QJE743LY.js';
3
3
  export { network_exports as network } from './chunk-V5JKVDBA.js';
4
- export { api_exports as api } from './chunk-HFRTZE7T.js';
4
+ export { api_exports as api } from './chunk-Q3B3YWOA.js';
5
5
  export { code_exports as code } from './chunk-V5S6OJ4A.js';
6
6
  export { color_exports as color } from './chunk-BPVAB4P2.js';
7
7
  export { css_exports as css } from './chunk-6YPV2AB5.js';
@@ -0,0 +1,53 @@
1
+ type ImageFormat = 'png' | 'jpeg' | 'gif' | 'webp' | 'bmp';
2
+ interface ImageInfo {
3
+ format: ImageFormat;
4
+ width: number;
5
+ height: number;
6
+ bitDepth?: number;
7
+ colorType?: string;
8
+ hasAlpha?: boolean;
9
+ }
10
+ interface ImageInfoError {
11
+ error: string;
12
+ }
13
+ /**
14
+ * Read image format, dimensions, and basic color info straight from file
15
+ * bytes — no decoding, no image library, no canvas. Supports PNG, JPEG,
16
+ * GIF, WebP, and BMP.
17
+ */
18
+ declare function readImageInfo(bytes: Uint8Array): ImageInfo | ImageInfoError;
19
+ interface PdfMetadata {
20
+ version: string | null;
21
+ pageCount: number | null;
22
+ title: string | null;
23
+ author: string | null;
24
+ subject: string | null;
25
+ keywords: string | null;
26
+ creator: string | null;
27
+ producer: string | null;
28
+ creationDate: string | null;
29
+ modDate: string | null;
30
+ encrypted: boolean;
31
+ }
32
+ interface PdfMetadataError {
33
+ error: string;
34
+ }
35
+ /**
36
+ * Read PDF metadata (title, author, dates, page count, encryption flag)
37
+ * straight from the file's trailer/Info dictionary and page tree — no
38
+ * pdf.js or other PDF rendering library required.
39
+ */
40
+ declare function readPdfMetadata(bytes: Uint8Array): PdfMetadata | PdfMetadataError;
41
+
42
+ type media_ImageFormat = ImageFormat;
43
+ type media_ImageInfo = ImageInfo;
44
+ type media_ImageInfoError = ImageInfoError;
45
+ type media_PdfMetadata = PdfMetadata;
46
+ type media_PdfMetadataError = PdfMetadataError;
47
+ declare const media_readImageInfo: typeof readImageInfo;
48
+ declare const media_readPdfMetadata: typeof readPdfMetadata;
49
+ declare namespace media {
50
+ export { type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, media_readImageInfo as readImageInfo, media_readPdfMetadata as readPdfMetadata };
51
+ }
52
+
53
+ export { type ImageFormat as I, type PdfMetadata as P, type ImageInfo as a, type ImageInfoError as b, type PdfMetadataError as c, readPdfMetadata as d, media as m, readImageInfo as r };
@@ -0,0 +1,53 @@
1
+ type ImageFormat = 'png' | 'jpeg' | 'gif' | 'webp' | 'bmp';
2
+ interface ImageInfo {
3
+ format: ImageFormat;
4
+ width: number;
5
+ height: number;
6
+ bitDepth?: number;
7
+ colorType?: string;
8
+ hasAlpha?: boolean;
9
+ }
10
+ interface ImageInfoError {
11
+ error: string;
12
+ }
13
+ /**
14
+ * Read image format, dimensions, and basic color info straight from file
15
+ * bytes — no decoding, no image library, no canvas. Supports PNG, JPEG,
16
+ * GIF, WebP, and BMP.
17
+ */
18
+ declare function readImageInfo(bytes: Uint8Array): ImageInfo | ImageInfoError;
19
+ interface PdfMetadata {
20
+ version: string | null;
21
+ pageCount: number | null;
22
+ title: string | null;
23
+ author: string | null;
24
+ subject: string | null;
25
+ keywords: string | null;
26
+ creator: string | null;
27
+ producer: string | null;
28
+ creationDate: string | null;
29
+ modDate: string | null;
30
+ encrypted: boolean;
31
+ }
32
+ interface PdfMetadataError {
33
+ error: string;
34
+ }
35
+ /**
36
+ * Read PDF metadata (title, author, dates, page count, encryption flag)
37
+ * straight from the file's trailer/Info dictionary and page tree — no
38
+ * pdf.js or other PDF rendering library required.
39
+ */
40
+ declare function readPdfMetadata(bytes: Uint8Array): PdfMetadata | PdfMetadataError;
41
+
42
+ type media_ImageFormat = ImageFormat;
43
+ type media_ImageInfo = ImageInfo;
44
+ type media_ImageInfoError = ImageInfoError;
45
+ type media_PdfMetadata = PdfMetadata;
46
+ type media_PdfMetadataError = PdfMetadataError;
47
+ declare const media_readImageInfo: typeof readImageInfo;
48
+ declare const media_readPdfMetadata: typeof readPdfMetadata;
49
+ declare namespace media {
50
+ export { type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, media_readImageInfo as readImageInfo, media_readPdfMetadata as readPdfMetadata };
51
+ }
52
+
53
+ export { type ImageFormat as I, type PdfMetadata as P, type ImageInfo as a, type ImageInfoError as b, type PdfMetadataError as c, readPdfMetadata as d, media as m, readImageInfo as r };
@@ -444,6 +444,55 @@ function decodeJwt(token) {
444
444
  function formatJwtJson(obj) {
445
445
  return JSON.stringify(obj, null, 2);
446
446
  }
447
+ function base64urlByteLength(s) {
448
+ const clean = s.replace(/=+$/, "");
449
+ return Math.floor(clean.length * 6 / 8);
450
+ }
451
+ function parseJwks(input) {
452
+ const trimmed = input.trim();
453
+ if (!trimmed) return { ok: false, error: "No input provided" };
454
+ let data;
455
+ try {
456
+ data = JSON.parse(trimmed);
457
+ } catch (e) {
458
+ return { ok: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` };
459
+ }
460
+ let rawKeys;
461
+ if (data && typeof data === "object" && Array.isArray(data.keys)) {
462
+ rawKeys = data.keys;
463
+ } else if (data && typeof data === "object" && typeof data.kty === "string") {
464
+ rawKeys = [data];
465
+ } else {
466
+ return { ok: false, error: 'Input must be a JWKS object with a "keys" array, or a single JWK object with a "kty" field' };
467
+ }
468
+ const keys = rawKeys.map((k) => {
469
+ const key = k ?? {};
470
+ const kty = typeof key.kty === "string" ? key.kty : "unknown";
471
+ let keySize = null;
472
+ let curve;
473
+ if (kty === "RSA" && typeof key.n === "string") {
474
+ keySize = base64urlByteLength(key.n) * 8;
475
+ } else if ((kty === "EC" || kty === "OKP") && typeof key.crv === "string") {
476
+ curve = key.crv;
477
+ } else if (kty === "oct" && typeof key.k === "string") {
478
+ keySize = base64urlByteLength(key.k) * 8;
479
+ }
480
+ return {
481
+ kty,
482
+ use: typeof key.use === "string" ? key.use : void 0,
483
+ keyOps: Array.isArray(key.key_ops) ? key.key_ops.filter((x) => typeof x === "string") : void 0,
484
+ alg: typeof key.alg === "string" ? key.alg : void 0,
485
+ kid: typeof key.kid === "string" ? key.kid : void 0,
486
+ keySize,
487
+ curve,
488
+ raw: key
489
+ };
490
+ });
491
+ const kidCounts = /* @__PURE__ */ new Map();
492
+ for (const k of keys) if (k.kid) kidCounts.set(k.kid, (kidCounts.get(k.kid) ?? 0) + 1);
493
+ const duplicateKids = [...kidCounts.entries()].filter(([, c]) => c > 1).map(([kid]) => kid);
494
+ return { ok: true, keys, count: keys.length, duplicateKids };
495
+ }
447
496
  var ALG_TO_NODE_HASH = {
448
497
  HS256: "sha256",
449
498
  HS384: "sha384",
@@ -1207,6 +1256,7 @@ exports.normalizeUrl = normalizeUrl;
1207
1256
  exports.parseCspHeader = parseCspHeader;
1208
1257
  exports.parseCurl = parseCurl;
1209
1258
  exports.parseCurlCommand = parseCurlCommand;
1259
+ exports.parseJwks = parseJwks;
1210
1260
  exports.parseMetaTags = parseMetaTags;
1211
1261
  exports.parseUrl = parseUrl;
1212
1262
  exports.removeParam = removeParam;
@@ -1 +1 @@
1
- export { C as COMMON_HEADERS, b as COMMON_PRESETS, c as CORS_COMMON_HEADERS, d as CorsConfig, e as CorsOutput, f as CspConfig, g as CspDirective, h as CspSource, i as CurlCommand, j as CurlConfig, D as DEFAULT_CORS_CONFIG, k as DIRECTIVE_DESCRIPTIONS, l as DirectiveConfig, F as FormatResult, H as HTTP_METHODS, m as Header, n as HttpMethod, J as JwtAlgorithm, o as JwtDecodeError, p as JwtDecodeResult, q as JwtExpiry, r as JwtPart, s as JwtResult, t as JwtSignOptions, M as MetaTags, O as OgTags, P as ParsedUrl, R as ResponseFormat, S as SUPPORTED_LANGUAGES, T as TargetLanguage, u as TwitterTags, v as addParam, w as base64urlDecode, x as base64urlEncode, y as buildCurl, z as buildCurlMultiline, A as buildUrl, B as convertToCode, E as decodeJwt, G as decodeUrlComponent, I as detectFormat, K as encodeUrlComponent, L as extractJsonPaths, N as formatJwtJson, Q as formatResponse, U as generateCors, V as generateCspHeader, W as generateFullHeader, X as generateMetaHtml, Y as getParam, Z as getStats, _ as isValidUrl, $ as normalizeUrl, a0 as parseCspHeader, a1 as parseCurl, a2 as parseCurlCommand, a3 as parseMetaTags, a4 as parseUrl, a5 as removeParam, a6 as signJwt, a7 as validateCorsConfig, a8 as validateCspConfig, a9 as validateOgTags } from '../api-8aZtWhSj.cjs';
1
+ export { C as COMMON_HEADERS, b as COMMON_PRESETS, c as CORS_COMMON_HEADERS, d as CorsConfig, e as CorsOutput, f as CspConfig, g as CspDirective, h as CspSource, i as CurlCommand, j as CurlConfig, D as DEFAULT_CORS_CONFIG, k as DIRECTIVE_DESCRIPTIONS, l as DirectiveConfig, F as FormatResult, H as HTTP_METHODS, m as Header, n as HttpMethod, J as JwkSummary, o as JwksError, p as JwksParseResult, q as JwksResult, r as JwtAlgorithm, s as JwtDecodeError, t as JwtDecodeResult, u as JwtExpiry, v as JwtPart, w as JwtResult, x as JwtSignOptions, M as MetaTags, O as OgTags, P as ParsedUrl, R as ResponseFormat, S as SUPPORTED_LANGUAGES, T as TargetLanguage, y as TwitterTags, z as addParam, A as base64urlDecode, B as base64urlEncode, E as buildCurl, G as buildCurlMultiline, I as buildUrl, K as convertToCode, L as decodeJwt, N as decodeUrlComponent, Q as detectFormat, U as encodeUrlComponent, V as extractJsonPaths, W as formatJwtJson, X as formatResponse, Y as generateCors, Z as generateCspHeader, _ as generateFullHeader, $ as generateMetaHtml, a0 as getParam, a1 as getStats, a2 as isValidUrl, a3 as normalizeUrl, a4 as parseCspHeader, a5 as parseCurl, a6 as parseCurlCommand, a7 as parseJwks, a8 as parseMetaTags, a9 as parseUrl, aa as removeParam, ab as signJwt, ac as validateCorsConfig, ad as validateCspConfig, ae as validateOgTags } from '../api-Xl7OzIgq.cjs';
@@ -1 +1 @@
1
- export { C as COMMON_HEADERS, b as COMMON_PRESETS, c as CORS_COMMON_HEADERS, d as CorsConfig, e as CorsOutput, f as CspConfig, g as CspDirective, h as CspSource, i as CurlCommand, j as CurlConfig, D as DEFAULT_CORS_CONFIG, k as DIRECTIVE_DESCRIPTIONS, l as DirectiveConfig, F as FormatResult, H as HTTP_METHODS, m as Header, n as HttpMethod, J as JwtAlgorithm, o as JwtDecodeError, p as JwtDecodeResult, q as JwtExpiry, r as JwtPart, s as JwtResult, t as JwtSignOptions, M as MetaTags, O as OgTags, P as ParsedUrl, R as ResponseFormat, S as SUPPORTED_LANGUAGES, T as TargetLanguage, u as TwitterTags, v as addParam, w as base64urlDecode, x as base64urlEncode, y as buildCurl, z as buildCurlMultiline, A as buildUrl, B as convertToCode, E as decodeJwt, G as decodeUrlComponent, I as detectFormat, K as encodeUrlComponent, L as extractJsonPaths, N as formatJwtJson, Q as formatResponse, U as generateCors, V as generateCspHeader, W as generateFullHeader, X as generateMetaHtml, Y as getParam, Z as getStats, _ as isValidUrl, $ as normalizeUrl, a0 as parseCspHeader, a1 as parseCurl, a2 as parseCurlCommand, a3 as parseMetaTags, a4 as parseUrl, a5 as removeParam, a6 as signJwt, a7 as validateCorsConfig, a8 as validateCspConfig, a9 as validateOgTags } from '../api-8aZtWhSj.js';
1
+ export { C as COMMON_HEADERS, b as COMMON_PRESETS, c as CORS_COMMON_HEADERS, d as CorsConfig, e as CorsOutput, f as CspConfig, g as CspDirective, h as CspSource, i as CurlCommand, j as CurlConfig, D as DEFAULT_CORS_CONFIG, k as DIRECTIVE_DESCRIPTIONS, l as DirectiveConfig, F as FormatResult, H as HTTP_METHODS, m as Header, n as HttpMethod, J as JwkSummary, o as JwksError, p as JwksParseResult, q as JwksResult, r as JwtAlgorithm, s as JwtDecodeError, t as JwtDecodeResult, u as JwtExpiry, v as JwtPart, w as JwtResult, x as JwtSignOptions, M as MetaTags, O as OgTags, P as ParsedUrl, R as ResponseFormat, S as SUPPORTED_LANGUAGES, T as TargetLanguage, y as TwitterTags, z as addParam, A as base64urlDecode, B as base64urlEncode, E as buildCurl, G as buildCurlMultiline, I as buildUrl, K as convertToCode, L as decodeJwt, N as decodeUrlComponent, Q as detectFormat, U as encodeUrlComponent, V as extractJsonPaths, W as formatJwtJson, X as formatResponse, Y as generateCors, Z as generateCspHeader, _ as generateFullHeader, $ as generateMetaHtml, a0 as getParam, a1 as getStats, a2 as isValidUrl, a3 as normalizeUrl, a4 as parseCspHeader, a5 as parseCurl, a6 as parseCurlCommand, a7 as parseJwks, a8 as parseMetaTags, a9 as parseUrl, aa as removeParam, ab as signJwt, ac as validateCorsConfig, ad as validateCspConfig, ae as validateOgTags } from '../api-Xl7OzIgq.js';
package/dist/tools/api.js CHANGED
@@ -1,2 +1,2 @@
1
- export { COMMON_HEADERS, COMMON_PRESETS, CORS_COMMON_HEADERS, DEFAULT_CORS_CONFIG, DIRECTIVE_DESCRIPTIONS, HTTP_METHODS, SUPPORTED_LANGUAGES, addParam, base64urlDecode, base64urlEncode, buildCurl, buildCurlMultiline, buildUrl, convertToCode, decodeJwt, decodeUrlComponent, detectFormat, encodeUrlComponent, extractJsonPaths, formatJwtJson, formatResponse, generateCors, generateCspHeader, generateFullHeader, generateMetaHtml, getParam, getStats, isValidUrl, normalizeUrl, parseCspHeader, parseCurl, parseCurlCommand, parseMetaTags, parseUrl, removeParam, signJwt, validateCorsConfig, validateCspConfig, validateOgTags } from '../chunk-HFRTZE7T.js';
1
+ export { COMMON_HEADERS, COMMON_PRESETS, CORS_COMMON_HEADERS, DEFAULT_CORS_CONFIG, DIRECTIVE_DESCRIPTIONS, HTTP_METHODS, SUPPORTED_LANGUAGES, addParam, 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 } from '../chunk-Q3B3YWOA.js';
2
2
  import '../chunk-MLKGABMK.js';
@@ -126,5 +126,148 @@ function readImageInfo(bytes) {
126
126
  }
127
127
  return { error: "Unrecognized image format. Supported formats: PNG, JPEG, GIF, WebP, BMP" };
128
128
  }
129
+ function bytesToLatin1(bytes) {
130
+ let out = "";
131
+ const CHUNK = 32768;
132
+ for (let i = 0; i < bytes.length; i += CHUNK) {
133
+ out += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
134
+ }
135
+ return out;
136
+ }
137
+ function decodePdfString(raw, isHex) {
138
+ const bytes = [];
139
+ if (isHex) {
140
+ const hex = raw.replace(/\s+/g, "");
141
+ const padded = hex.length % 2 === 0 ? hex : hex + "0";
142
+ for (let i = 0; i < padded.length; i += 2) bytes.push(parseInt(padded.slice(i, i + 2), 16));
143
+ } else {
144
+ const s = raw.replace(/\\\r\n|\\\r|\\\n/g, "");
145
+ for (let i = 0; i < s.length; i++) {
146
+ const ch = s[i];
147
+ if (ch === "\\" && i + 1 < s.length) {
148
+ const next = s[i + 1];
149
+ if (next === "n") {
150
+ bytes.push(10);
151
+ i++;
152
+ } else if (next === "r") {
153
+ bytes.push(13);
154
+ i++;
155
+ } else if (next === "t") {
156
+ bytes.push(9);
157
+ i++;
158
+ } else if (next === "b") {
159
+ bytes.push(8);
160
+ i++;
161
+ } else if (next === "f") {
162
+ bytes.push(12);
163
+ i++;
164
+ } else if (next === "(" || next === ")" || next === "\\") {
165
+ bytes.push(next.charCodeAt(0));
166
+ i++;
167
+ } else if (/[0-7]/.test(next)) {
168
+ let oct = next;
169
+ i++;
170
+ for (let k = 0; k < 2 && i + 1 < s.length && /[0-7]/.test(s[i + 1]); k++) {
171
+ i++;
172
+ oct += s[i];
173
+ }
174
+ bytes.push(parseInt(oct, 8) & 255);
175
+ } else {
176
+ bytes.push(next.charCodeAt(0));
177
+ i++;
178
+ }
179
+ } else {
180
+ bytes.push(ch.charCodeAt(0) & 255);
181
+ }
182
+ }
183
+ }
184
+ if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
185
+ let out = "";
186
+ for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode(bytes[i] << 8 | bytes[i + 1]);
187
+ return out;
188
+ }
189
+ return bytes.map((b) => String.fromCharCode(b)).join("");
190
+ }
191
+ function extractDictField(dict, key) {
192
+ const litMatch = dict.match(new RegExp(`/${key}\\s*\\(((?:\\\\.|[^\\\\()])*)\\)`));
193
+ if (litMatch) return decodePdfString(litMatch[1], false);
194
+ const hexMatch = dict.match(new RegExp(`/${key}\\s*<([0-9A-Fa-f\\s]*)>`));
195
+ if (hexMatch) return decodePdfString(hexMatch[1], true);
196
+ return null;
197
+ }
198
+ function findObject(text, objNum, gen) {
199
+ const m = text.match(new RegExp(`(?:^|[^0-9])${objNum}\\s+${gen}\\s+obj([\\s\\S]*?)endobj`));
200
+ return m ? m[1] : null;
201
+ }
202
+ function parsePdfDate(raw) {
203
+ const m = raw.match(/^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([Z+-])?(\d{2})?'?(\d{2})?'?/);
204
+ if (!m) return null;
205
+ const [, y, mo = "01", d = "01", h = "00", mi = "00", s = "00", tz, tzh, tzm] = m;
206
+ let iso = `${y}-${mo}-${d}T${h}:${mi}:${s}`;
207
+ if (tz === "Z") iso += "Z";
208
+ else if (tz === "+" || tz === "-") iso += `${tz}${tzh ?? "00"}:${tzm ?? "00"}`;
209
+ return iso;
210
+ }
211
+ function readPdfMetadata(bytes) {
212
+ if (bytes.length < 8) return { error: "File too small to be a PDF" };
213
+ const header = bytesToLatin1(bytes.subarray(0, 16));
214
+ const versionMatch = header.match(/%PDF-(\d\.\d)/);
215
+ if (!versionMatch) return { error: "Not a valid PDF file (missing %PDF header)" };
216
+ const version = versionMatch[1];
217
+ const text = bytesToLatin1(bytes);
218
+ const trailerMatches = [...text.matchAll(/trailer\s*<<([\s\S]*?)>>/g)];
219
+ let infoDict = null;
220
+ let encrypted = false;
221
+ if (trailerMatches.length > 0) {
222
+ const trailer = trailerMatches[trailerMatches.length - 1][1];
223
+ encrypted = /\/Encrypt\s+\d+\s+\d+\s+R/.test(trailer);
224
+ const infoRef = trailer.match(/\/Info\s+(\d+)\s+(\d+)\s+R/);
225
+ if (infoRef) infoDict = findObject(text, Number(infoRef[1]), Number(infoRef[2]));
226
+ }
227
+ const objBlocks = [...text.matchAll(/(\d+)\s+(\d+)\s+obj([\s\S]*?)endobj/g)];
228
+ if (!infoDict) {
229
+ for (const b of objBlocks) {
230
+ if (/\/(Title|Author|Producer|Creator|CreationDate)\b/.test(b[3]) && !/\/Type\s*\/(Page|Pages|Catalog|Font)\b/.test(b[3])) {
231
+ infoDict = b[3];
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ const title = infoDict ? extractDictField(infoDict, "Title") : null;
237
+ const author = infoDict ? extractDictField(infoDict, "Author") : null;
238
+ const subject = infoDict ? extractDictField(infoDict, "Subject") : null;
239
+ const keywords = infoDict ? extractDictField(infoDict, "Keywords") : null;
240
+ const creator = infoDict ? extractDictField(infoDict, "Creator") : null;
241
+ const producer = infoDict ? extractDictField(infoDict, "Producer") : null;
242
+ const creationDateRaw = infoDict ? extractDictField(infoDict, "CreationDate") : null;
243
+ const modDateRaw = infoDict ? extractDictField(infoDict, "ModDate") : null;
244
+ let pageCount = null;
245
+ for (const b of objBlocks) {
246
+ if (!/\/Type\s*\/Pages\b/.test(b[3])) continue;
247
+ const countMatch = b[3].match(/\/Count\s+(\d+)/);
248
+ if (countMatch) {
249
+ const c = Number(countMatch[1]);
250
+ if (pageCount === null || c > pageCount) pageCount = c;
251
+ }
252
+ }
253
+ if (pageCount === null) {
254
+ const pageMatches = text.match(/\/Type\s*\/Page(?!s)\b/g);
255
+ if (pageMatches) pageCount = pageMatches.length;
256
+ }
257
+ return {
258
+ version,
259
+ pageCount,
260
+ title,
261
+ author,
262
+ subject,
263
+ keywords,
264
+ creator,
265
+ producer,
266
+ creationDate: creationDateRaw ? parsePdfDate(creationDateRaw) ?? creationDateRaw : null,
267
+ modDate: modDateRaw ? parsePdfDate(modDateRaw) ?? modDateRaw : null,
268
+ encrypted
269
+ };
270
+ }
129
271
 
130
272
  exports.readImageInfo = readImageInfo;
273
+ exports.readPdfMetadata = readPdfMetadata;
@@ -1 +1 @@
1
- export { I as ImageFormat, a as ImageInfo, b as ImageInfoError, r as readImageInfo } from '../media-DF2cx3pK.cjs';
1
+ export { I as ImageFormat, a as ImageInfo, b as ImageInfoError, P as PdfMetadata, c as PdfMetadataError, r as readImageInfo, d as readPdfMetadata } from '../media-D-K5aZnq.cjs';
@@ -1 +1 @@
1
- export { I as ImageFormat, a as ImageInfo, b as ImageInfoError, r as readImageInfo } from '../media-DF2cx3pK.js';
1
+ export { I as ImageFormat, a as ImageInfo, b as ImageInfoError, P as PdfMetadata, c as PdfMetadataError, r as readImageInfo, d as readPdfMetadata } from '../media-D-K5aZnq.js';
@@ -1,2 +1,2 @@
1
- export { readImageInfo } from '../chunk-N7C52YGL.js';
1
+ export { readImageInfo, readPdfMetadata } from '../chunk-WWKPLYEP.js';
2
2
  import '../chunk-MLKGABMK.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilix-tech/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "140+ developer utility tools for Node.js: JSON, encoding, hashing, color, CSS, network and more. Runs locally, no API key required.",
5
5
  "author": "Utilix <hello@utilix.tech>",
6
6
  "license": "MIT",