@putkoff/abstract-utilities 0.1.189 → 0.1.191

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/cjs/index.js CHANGED
@@ -322,6 +322,240 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
322
322
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
323
323
  };
324
324
 
325
+ // src/functions/read_utils/src/read_utils.ts
326
+ /**
327
+ * Reads a JSON file, either via Node’s fs (if available)
328
+ * or via window.fetch in the browser. Never throws — returns
329
+ * the parsed object or null on any error.
330
+ */
331
+ function readJsonFile(relativeOrAbsolutePath) {
332
+ return __awaiter(this, void 0, void 0, function* () {
333
+ // 1) Try Node.js fs
334
+ if (typeof process !== 'undefined' &&
335
+ process.versions != null &&
336
+ process.versions.node) {
337
+ try {
338
+ const fs = yield import('fs');
339
+ const path = yield import('path');
340
+ const filePath = path.isAbsolute(relativeOrAbsolutePath)
341
+ ? relativeOrAbsolutePath
342
+ : path.resolve(process.cwd(), relativeOrAbsolutePath);
343
+ const text = yield fs.promises.readFile(filePath, 'utf8');
344
+ return JSON.parse(text);
345
+ }
346
+ catch (_a) {
347
+ // swallow and fall back to browser
348
+ }
349
+ }
350
+ // 2) Try browser fetch
351
+ const fetchFn = safeGlobalProp('fetch');
352
+ if (typeof fetchFn !== 'function') {
353
+ return null;
354
+ }
355
+ // Resolve URL against document.baseURI if possible
356
+ let url = relativeOrAbsolutePath;
357
+ const baseURI = safeGlobalProp('document', 'baseURI');
358
+ if (baseURI) {
359
+ try {
360
+ url = new URL(relativeOrAbsolutePath, baseURI).href;
361
+ }
362
+ catch (_b) {
363
+ // keep url as-is
364
+ }
365
+ }
366
+ try {
367
+ const res = yield fetchFn(url);
368
+ if (!res.ok)
369
+ return null;
370
+ return (yield res.json());
371
+ }
372
+ catch (_c) {
373
+ return null;
374
+ }
375
+ });
376
+ }
377
+ function getConfigContent() {
378
+ return __awaiter(this, void 0, void 0, function* () {
379
+ try {
380
+ // `readJsonFile` should throw if the file isn’t there or isn’t valid JSON
381
+ const cfg = yield readJsonFile('./config.json');
382
+ return cfg;
383
+ }
384
+ catch (_a) {
385
+ // swallow errors & return null so callers can detect “no config”
386
+ return null;
387
+ }
388
+ });
389
+ }
390
+ // 2) Pull a single key out of that object
391
+ function getConfigVar() {
392
+ return __awaiter(this, arguments, void 0, function* (key = null) {
393
+ const cfg = yield getConfigContent();
394
+ if (cfg && typeof cfg === 'object' && key in cfg) {
395
+ return cfg[key];
396
+ }
397
+ return undefined;
398
+ });
399
+ }
400
+
401
+ /**
402
+ * Unwraps nested { result } fields until you hit a non-object or no more "result" keys.
403
+ */
404
+ function getResult(obj) {
405
+ let current = obj;
406
+ while (current &&
407
+ typeof current === "object" &&
408
+ Object.prototype.hasOwnProperty.call(current, "result")) {
409
+ current = current.result;
410
+ }
411
+ return current;
412
+ }
413
+ // Determines HTTP method, defaults to GET or POST based on body
414
+ function getMethod(method = null, body = null) {
415
+ const validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'PULL'];
416
+ method = (method || '').toUpperCase();
417
+ if (!validMethods.includes(method)) {
418
+ method = body ? 'POST' : 'GET';
419
+ }
420
+ return method;
421
+ }
422
+ // Gets headers, skips JSON headers when body is FormData
423
+ function getHeaders(headers = {}, method = null, body = null) {
424
+ const result = Object.assign({}, headers);
425
+ // inject auth if missing
426
+ if (!result.Authorization) {
427
+ const token = getToken();
428
+ Object.assign(result, getAuthorizationHeader(result, token));
429
+ }
430
+ method = getMethod(method, body);
431
+ // if it’s a multipart FormData, let the browser set the boundary header
432
+ if (body instanceof FormData) {
433
+ return result;
434
+ }
435
+ // otherwise for POST/PUT/PATCH default to JSON
436
+ if (['POST', 'PUT', 'PATCH'].includes(method) && !result['Content-Type']) {
437
+ result['Content-Type'] = 'application/json';
438
+ }
439
+ return result;
440
+ }
441
+ // Prepares request body, serializes to JSON for non-GET requests
442
+ function getBody(body = null, method = null) {
443
+ method = getMethod(method, body);
444
+ if (method === 'GET') {
445
+ return undefined;
446
+ }
447
+ if (body) {
448
+ try {
449
+ return JSON.stringify(body);
450
+ }
451
+ catch (err) {
452
+ return body;
453
+ }
454
+ }
455
+ return undefined;
456
+ }
457
+ // Prepares fetch variables, passes FormData intact
458
+ function getFetchVars(headers = null, method = null, body = null) {
459
+ method = getMethod(method, body);
460
+ headers = getHeaders(headers || {}, method, body);
461
+ // only JSON-stringify non-FormData bodies
462
+ if (!(body instanceof FormData)) {
463
+ body = getBody(body, method);
464
+ }
465
+ return { method, headers, body };
466
+ }
467
+ /*
468
+ * parseResult no longer needs to worry about JSON vs HTML redirect errors;
469
+ * all 401/403 have already been handled above.
470
+ */
471
+ function parseResult(res) {
472
+ return __awaiter(this, void 0, void 0, function* () {
473
+ // runs checkResponse first, will throw if session is expired
474
+ res = checkResponse(res);
475
+ if (!res.ok) {
476
+ // for any other non-401 errors, you can still surface them
477
+ const errorText = yield res.text();
478
+ throw new Error(errorText || res.statusText);
479
+ }
480
+ // now safely parse JSON
481
+ return res.json();
482
+ });
483
+ }
484
+ /**
485
+ * Intercept 401/403 and force a clean redirect to login
486
+ * without ever showing an alert.
487
+ */
488
+ function checkResponse(res) {
489
+ if (res.status === 401 || res.status === 403) {
490
+ // 1) clear out the stale token
491
+ localStorage.removeItem("token");
492
+ // 2) replace history so "back" doesn’t re-trigger the protected route
493
+ window.history.replaceState({}, "", "/secure-files");
494
+ // 3) short-circuit all further fetch logic
495
+ throw new Error("SessionExpired");
496
+ }
497
+ return res;
498
+ }
499
+
500
+ function fetchIt(endpoint_1) {
501
+ return __awaiter(this, arguments, void 0, function* (endpoint, body = null, method = null, headers = null, blob = false, configUrl = false, withCredentials = true, returnJson = true, returnReult = true) {
502
+ method = (method || "GET").toUpperCase();
503
+ // 2) choose the URL
504
+ let url = endpoint;
505
+ // 3) prepare headers & body
506
+ headers = Object.assign(Object.assign(Object.assign({}, (body instanceof FormData ? {} : { "Content-Type": "application/json" })), getAuthorizationHeader()), headers);
507
+ const opts = {
508
+ method,
509
+ credentials: withCredentials ? "include" : "same-origin",
510
+ headers,
511
+ body: body instanceof FormData
512
+ ? body
513
+ : body != null && method !== "GET"
514
+ ? JSON.stringify(body)
515
+ : undefined,
516
+ };
517
+ console.debug("➡️ secureFetchIt →", url, opts);
518
+ const res = yield fetch(url, opts);
519
+ if (!res.ok) {
520
+ const err = yield res.text();
521
+ throw new Error(`HTTP ${res.status}: ${err}`);
522
+ }
523
+ if (blob)
524
+ return res.blob();
525
+ if (returnReult)
526
+ return getResult(res.json());
527
+ if (returnJson)
528
+ return res.json();
529
+ return res;
530
+ });
531
+ }
532
+ // Constructs HTML directory path
533
+ function getHtmlDirectory(directory, filename) {
534
+ return `${directory}/${filename}.html`;
535
+ }
536
+ // Fetches HTML content
537
+ function fetchIndexHtml(filename_1) {
538
+ return __awaiter(this, arguments, void 0, function* (filename, directory = 'sf_index', base = 'html') {
539
+ const url = `/${base}/${directory}/${filename}.html`;
540
+ const response = yield fetch(url);
541
+ return yield response.text();
542
+ });
543
+ }
544
+ // Fetches and injects HTML content into container
545
+ function fetchIndexHtmlContainer(filename_1) {
546
+ return __awaiter(this, arguments, void 0, function* (filename, doc = document, directory = 'html') {
547
+ const container = `${filename}-container`;
548
+ const html = yield fetchIndexHtml(filename, directory);
549
+ const el = doc.getElementById(container);
550
+ if (el) {
551
+ el.innerHTML = html;
552
+ }
553
+ else {
554
+ console.warn(`⚠️ No container found for: #${container}`);
555
+ }
556
+ });
557
+ }
558
+
325
559
  function assertPath(path) {
326
560
  if (typeof path !== 'string') {
327
561
  throw new TypeError('Path must be a string. Received ' + JSON.stringify(path));
@@ -1154,57 +1388,39 @@ function alertit(obj = null) {
1154
1388
  alert(msg);
1155
1389
  }
1156
1390
 
1157
- // src/functions/read_utils/src/read_utils.ts
1158
- /**
1159
- * Reads a JSON file, either via Node’s fs (if available)
1160
- * or via window.fetch in the browser. Never throws — returns
1161
- * the parsed object or null on any error.
1162
- */
1163
- function readJsonFile(relativeOrAbsolutePath) {
1164
- return __awaiter(this, void 0, void 0, function* () {
1165
- // 1) Try Node.js fs
1166
- if (typeof process !== 'undefined' &&
1167
- process.versions != null &&
1168
- process.versions.node) {
1169
- try {
1170
- const fs = yield import('fs');
1171
- const path = yield import('path');
1172
- const filePath = path.isAbsolute(relativeOrAbsolutePath)
1173
- ? relativeOrAbsolutePath
1174
- : path.resolve(process.cwd(), relativeOrAbsolutePath);
1175
- const text = yield fs.promises.readFile(filePath, 'utf8');
1176
- return JSON.parse(text);
1177
- }
1178
- catch (_a) {
1179
- // swallow and fall back to browser
1180
- }
1181
- }
1182
- // 2) Try browser fetch
1183
- const fetchFn = safeGlobalProp('fetch');
1184
- if (typeof fetchFn !== 'function') {
1185
- return null;
1186
- }
1187
- // Resolve URL against document.baseURI if possible
1188
- let url = relativeOrAbsolutePath;
1189
- const baseURI = safeGlobalProp('document', 'baseURI');
1190
- if (baseURI) {
1191
- try {
1192
- url = new URL(relativeOrAbsolutePath, baseURI).href;
1193
- }
1194
- catch (_b) {
1195
- // keep url as-is
1196
- }
1197
- }
1198
- try {
1199
- const res = yield fetchFn(url);
1200
- if (!res.ok)
1201
- return null;
1202
- return (yield res.json());
1203
- }
1204
- catch (_c) {
1205
- return null;
1206
- }
1207
- });
1391
+ function Button(_a) {
1392
+ var { children, color = 'gray', variant = 'default', className = '' } = _a, rest = __rest(_a, ["children", "color", "variant", "className"]);
1393
+ const base = 'rounded px-3 py-1 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-150';
1394
+ const variantStyles = {
1395
+ default: '',
1396
+ icon: 'p-1 bg-transparent hover:bg-gray-100',
1397
+ primary: 'text-white',
1398
+ secondary: '',
1399
+ };
1400
+ const palette = {
1401
+ gray: variant === 'primary'
1402
+ ? 'bg-gray-600 hover:bg-gray-700 focus:ring-gray-500'
1403
+ : 'bg-gray-200 hover:bg-gray-300 focus:ring-gray-400',
1404
+ green: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500',
1405
+ blue: variant === 'primary'
1406
+ ? 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-500'
1407
+ : 'bg-blue-200 hover:bg-blue-300 focus:ring-blue-400',
1408
+ };
1409
+ return (jsxRuntime.jsx("button", Object.assign({ className: `${base} ${variantStyles[variant]} ${palette[color]} ${className}` }, rest, { children: children })));
1410
+ }
1411
+
1412
+ function Checkbox(_a) {
1413
+ var { label } = _a, rest = __rest(_a, ["label"]);
1414
+ return (jsxRuntime.jsxs("label", { className: 'flex items-center gap-2 mb-4', children: [jsxRuntime.jsx("input", Object.assign({ type: 'checkbox' }, rest)), jsxRuntime.jsx("span", { children: label })] }));
1415
+ }
1416
+
1417
+ function Input(_a) {
1418
+ var { label, trailing } = _a, rest = __rest(_a, ["label", "trailing"]);
1419
+ return (jsxRuntime.jsxs("label", { className: 'mb-4 block', children: [jsxRuntime.jsx("span", { className: 'block text-sm font-medium mb-1', children: label }), jsxRuntime.jsxs("div", { className: 'flex gap-2', children: [jsxRuntime.jsx("input", Object.assign({ className: 'flex-1 rounded border px-2 py-1 disabled:bg-gray-100' }, rest)), trailing] })] }));
1420
+ }
1421
+
1422
+ function Spinner() {
1423
+ return (jsxRuntime.jsx("p", { className: 'animate-pulse', children: "Loading\u2026" }));
1208
1424
  }
1209
1425
 
1210
1426
  // src/functions/config_utils/src/config_utils.ts
@@ -1278,222 +1494,6 @@ function getConfig(key) {
1278
1494
  return key != null ? cfg[key] : cfg;
1279
1495
  });
1280
1496
  }
1281
- function getConfigContent() {
1282
- return __awaiter(this, void 0, void 0, function* () {
1283
- try {
1284
- // `readJsonFile` should throw if the file isn’t there or isn’t valid JSON
1285
- const cfg = yield readJsonFile('./config.json');
1286
- return cfg;
1287
- }
1288
- catch (_a) {
1289
- // swallow errors & return null so callers can detect “no config”
1290
- return null;
1291
- }
1292
- });
1293
- }
1294
- // 2) Pull a single key out of that object
1295
- function getConfigVar() {
1296
- return __awaiter(this, arguments, void 0, function* (key = null) {
1297
- const cfg = yield getConfigContent();
1298
- if (cfg && typeof cfg === 'object' && key in cfg) {
1299
- return cfg[key];
1300
- }
1301
- return undefined;
1302
- });
1303
- }
1304
-
1305
- // Constructs API URL from endpoint
1306
- /**
1307
- * Unwraps nested { result } fields until you hit a non-object or no more "result" keys.
1308
- */
1309
- function getResult(obj) {
1310
- let current = obj;
1311
- while (current &&
1312
- typeof current === "object" &&
1313
- Object.prototype.hasOwnProperty.call(current, "result")) {
1314
- current = current.result;
1315
- }
1316
- return current;
1317
- }
1318
- // Determines HTTP method, defaults to GET or POST based on body
1319
- function getMethod(method = null, body = null) {
1320
- const validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'PULL'];
1321
- method = (method || '').toUpperCase();
1322
- if (!validMethods.includes(method)) {
1323
- method = body ? 'POST' : 'GET';
1324
- }
1325
- return method;
1326
- }
1327
- // Gets headers, skips JSON headers when body is FormData
1328
- function getHeaders(headers = {}, method = null, body = null) {
1329
- const result = Object.assign({}, headers);
1330
- // inject auth if missing
1331
- if (!result.Authorization) {
1332
- const token = getToken();
1333
- Object.assign(result, getAuthorizationHeader(result, token));
1334
- }
1335
- method = getMethod(method, body);
1336
- // if it’s a multipart FormData, let the browser set the boundary header
1337
- if (body instanceof FormData) {
1338
- return result;
1339
- }
1340
- // otherwise for POST/PUT/PATCH default to JSON
1341
- if (['POST', 'PUT', 'PATCH'].includes(method) && !result['Content-Type']) {
1342
- result['Content-Type'] = 'application/json';
1343
- }
1344
- return result;
1345
- }
1346
- // Prepares request body, serializes to JSON for non-GET requests
1347
- function getBody(body = null, method = null) {
1348
- method = getMethod(method, body);
1349
- if (method === 'GET') {
1350
- return undefined;
1351
- }
1352
- if (body) {
1353
- try {
1354
- return JSON.stringify(body);
1355
- }
1356
- catch (err) {
1357
- return body;
1358
- }
1359
- }
1360
- return undefined;
1361
- }
1362
- // Prepares fetch variables, passes FormData intact
1363
- function getFetchVars(headers = null, method = null, body = null) {
1364
- method = getMethod(method, body);
1365
- headers = getHeaders(headers || {}, method, body);
1366
- // only JSON-stringify non-FormData bodies
1367
- if (!(body instanceof FormData)) {
1368
- body = getBody(body, method);
1369
- }
1370
- return { method, headers, body };
1371
- }
1372
- /*
1373
- * parseResult no longer needs to worry about JSON vs HTML redirect errors;
1374
- * all 401/403 have already been handled above.
1375
- */
1376
- function parseResult(res) {
1377
- return __awaiter(this, void 0, void 0, function* () {
1378
- // runs checkResponse first, will throw if session is expired
1379
- res = checkResponse(res);
1380
- if (!res.ok) {
1381
- // for any other non-401 errors, you can still surface them
1382
- const errorText = yield res.text();
1383
- throw new Error(errorText || res.statusText);
1384
- }
1385
- // now safely parse JSON
1386
- return res.json();
1387
- });
1388
- }
1389
- /**
1390
- * Intercept 401/403 and force a clean redirect to login
1391
- * without ever showing an alert.
1392
- */
1393
- function checkResponse(res) {
1394
- if (res.status === 401 || res.status === 403) {
1395
- // 1) clear out the stale token
1396
- localStorage.removeItem("token");
1397
- // 2) replace history so "back" doesn’t re-trigger the protected route
1398
- window.history.replaceState({}, "", "/secure-files");
1399
- // 3) short-circuit all further fetch logic
1400
- throw new Error("SessionExpired");
1401
- }
1402
- return res;
1403
- }
1404
- function fetchIt(endpoint_1) {
1405
- return __awaiter(this, arguments, void 0, function* (endpoint, body = null, method = null, headers = null, blob = false, configUrl = false, withCredentials = true, returnJson = true, returnReult = true) {
1406
- method = (method || "GET").toUpperCase();
1407
- // 2) choose the URL
1408
- let url = endpoint;
1409
- // 3) prepare headers & body
1410
- headers = Object.assign(Object.assign(Object.assign({}, (body instanceof FormData ? {} : { "Content-Type": "application/json" })), getAuthorizationHeader()), headers);
1411
- const opts = {
1412
- method,
1413
- credentials: withCredentials ? "include" : "same-origin",
1414
- headers,
1415
- body: body instanceof FormData
1416
- ? body
1417
- : body != null && method !== "GET"
1418
- ? JSON.stringify(body)
1419
- : undefined,
1420
- };
1421
- console.debug("➡️ secureFetchIt →", url, opts);
1422
- const res = yield fetch(url, opts);
1423
- if (!res.ok) {
1424
- const err = yield res.text();
1425
- throw new Error(`HTTP ${res.status}: ${err}`);
1426
- }
1427
- if (blob)
1428
- return res.blob();
1429
- if (returnReult)
1430
- return getResult(res.json());
1431
- if (returnJson)
1432
- return res.json();
1433
- return res;
1434
- });
1435
- }
1436
- // Constructs HTML directory path
1437
- function getHtmlDirectory(directory, filename) {
1438
- return `${directory}/${filename}.html`;
1439
- }
1440
- // Fetches HTML content
1441
- function fetchIndexHtml(filename_1) {
1442
- return __awaiter(this, arguments, void 0, function* (filename, directory = 'sf_index', base = 'html') {
1443
- const url = `/${base}/${directory}/${filename}.html`;
1444
- const response = yield fetch(url);
1445
- return yield response.text();
1446
- });
1447
- }
1448
- // Fetches and injects HTML content into container
1449
- function fetchIndexHtmlContainer(filename_1) {
1450
- return __awaiter(this, arguments, void 0, function* (filename, doc = document, directory = 'html') {
1451
- const container = `${filename}-container`;
1452
- const html = yield fetchIndexHtml(filename, directory);
1453
- const el = doc.getElementById(container);
1454
- if (el) {
1455
- el.innerHTML = html;
1456
- }
1457
- else {
1458
- console.warn(`⚠️ No container found for: #${container}`);
1459
- }
1460
- });
1461
- }
1462
-
1463
- function Button(_a) {
1464
- var { children, color = 'gray', variant = 'default', className = '' } = _a, rest = __rest(_a, ["children", "color", "variant", "className"]);
1465
- const base = 'rounded px-3 py-1 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-150';
1466
- const variantStyles = {
1467
- default: '',
1468
- icon: 'p-1 bg-transparent hover:bg-gray-100',
1469
- primary: 'text-white',
1470
- secondary: '',
1471
- };
1472
- const palette = {
1473
- gray: variant === 'primary'
1474
- ? 'bg-gray-600 hover:bg-gray-700 focus:ring-gray-500'
1475
- : 'bg-gray-200 hover:bg-gray-300 focus:ring-gray-400',
1476
- green: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500',
1477
- blue: variant === 'primary'
1478
- ? 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-500'
1479
- : 'bg-blue-200 hover:bg-blue-300 focus:ring-blue-400',
1480
- };
1481
- return (jsxRuntime.jsx("button", Object.assign({ className: `${base} ${variantStyles[variant]} ${palette[color]} ${className}` }, rest, { children: children })));
1482
- }
1483
-
1484
- function Checkbox(_a) {
1485
- var { label } = _a, rest = __rest(_a, ["label"]);
1486
- return (jsxRuntime.jsxs("label", { className: 'flex items-center gap-2 mb-4', children: [jsxRuntime.jsx("input", Object.assign({ type: 'checkbox' }, rest)), jsxRuntime.jsx("span", { children: label })] }));
1487
- }
1488
-
1489
- function Input(_a) {
1490
- var { label, trailing } = _a, rest = __rest(_a, ["label", "trailing"]);
1491
- return (jsxRuntime.jsxs("label", { className: 'mb-4 block', children: [jsxRuntime.jsx("span", { className: 'block text-sm font-medium mb-1', children: label }), jsxRuntime.jsxs("div", { className: 'flex gap-2', children: [jsxRuntime.jsx("input", Object.assign({ className: 'flex-1 rounded border px-2 py-1 disabled:bg-gray-100' }, rest)), trailing] })] }));
1492
- }
1493
-
1494
- function Spinner() {
1495
- return (jsxRuntime.jsx("p", { className: 'animate-pulse', children: "Loading\u2026" }));
1496
- }
1497
1497
 
1498
1498
  Object.defineProperty(exports, "useCallback", {
1499
1499
  enumerable: true,