@nestledjs/data-browser 1.0.15 → 1.0.16

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.
@@ -3,7 +3,7 @@ const ADMIN_CONFIG_VERSION = "1.0";
3
3
  const MAX_CONFIG_SIZE = 5e4;
4
4
  const sanitizeString = (value) => {
5
5
  if (typeof value !== "string") return "";
6
- return value.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/javascript:/gi, "").replace(/on\w+\s*=/gi, "").trim().substring(0, 1e3);
6
+ return value.replaceAll(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replaceAll(/javascript:/gi, "").replaceAll(/on\w+\s*=/gi, "").trim().substring(0, 1e3);
7
7
  };
8
8
  const sanitizeArray = (value) => {
9
9
  if (!Array.isArray(value)) return [];
@@ -15,9 +15,21 @@ const sanitizeSortPreference = (value) => {
15
15
  const orderBy = sanitizeString(obj.orderBy);
16
16
  const orderDirection = sanitizeString(obj.orderDirection);
17
17
  if (!["asc", "desc"].includes(orderDirection)) return null;
18
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(orderBy)) return null;
18
+ if (!/^[a-zA-Z_]\w*$/.test(orderBy)) return null;
19
19
  return { orderBy, orderDirection };
20
20
  };
21
+ const isValidModelName = (name) => /^[a-zA-Z_]\w*$/.test(name);
22
+ const isValidModelConfig = (modelConfig) => {
23
+ if (!modelConfig || typeof modelConfig !== "object") return true;
24
+ const cfg = modelConfig;
25
+ if (cfg.visibleColumns !== void 0 && !Array.isArray(cfg.visibleColumns)) return false;
26
+ if (cfg.sortPreference !== void 0) {
27
+ const sortPref = sanitizeSortPreference(cfg.sortPreference);
28
+ if (cfg.sortPreference !== null && sortPref === null) return false;
29
+ }
30
+ if (cfg.searchFields !== void 0 && !Array.isArray(cfg.searchFields)) return false;
31
+ return true;
32
+ };
21
33
  const validateAdminConfig = (config) => {
22
34
  if (!config || typeof config !== "object") return false;
23
35
  const configObj = config;
@@ -25,16 +37,8 @@ const validateAdminConfig = (config) => {
25
37
  if (!configObj.models || typeof configObj.models !== "object") return false;
26
38
  const models = configObj.models;
27
39
  for (const [modelName, modelConfig] of Object.entries(models)) {
28
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(modelName)) return false;
29
- if (modelConfig && typeof modelConfig === "object") {
30
- const config2 = modelConfig;
31
- if (config2.visibleColumns !== void 0 && !Array.isArray(config2.visibleColumns)) return false;
32
- if (config2.sortPreference !== void 0) {
33
- const sortPref = sanitizeSortPreference(config2.sortPreference);
34
- if (config2.sortPreference !== null && sortPref === null) return false;
35
- }
36
- if (config2.searchFields !== void 0 && !Array.isArray(config2.searchFields)) return false;
37
- }
40
+ if (!isValidModelName(modelName)) return false;
41
+ if (!isValidModelConfig(modelConfig)) return false;
38
42
  }
39
43
  return true;
40
44
  };
@@ -56,10 +60,11 @@ const SecureAdminLocalStorage = {
56
60
  return { version: ADMIN_CONFIG_VERSION, models: {} };
57
61
  }
58
62
  return parsed;
59
- } catch (error) {
63
+ } catch {
60
64
  try {
61
65
  localStorage.removeItem(ADMIN_CONFIG_KEY);
62
- } catch {
66
+ } catch (cleanupError) {
67
+ console.error("Unexpected error:", cleanupError);
63
68
  }
64
69
  return { version: ADMIN_CONFIG_VERSION, models: {} };
65
70
  }
@@ -77,6 +82,7 @@ const SecureAdminLocalStorage = {
77
82
  localStorage.setItem(ADMIN_CONFIG_KEY, serialized);
78
83
  return true;
79
84
  } catch (error) {
85
+ console.error("Unexpected error:", error);
80
86
  return false;
81
87
  }
82
88
  },
@@ -149,6 +155,7 @@ const SecureAdminLocalStorage = {
149
155
  }
150
156
  return JSON.stringify(config, null, 2);
151
157
  } catch (error) {
158
+ console.error("Unexpected error:", error);
152
159
  return null;
153
160
  }
154
161
  },
@@ -178,6 +185,7 @@ const SecureAdminLocalStorage = {
178
185
  }
179
186
  return SecureAdminLocalStorage.setConfig(config);
180
187
  } catch (error) {
188
+ console.error("Unexpected error:", error);
181
189
  return false;
182
190
  }
183
191
  },
@@ -187,6 +195,7 @@ const SecureAdminLocalStorage = {
187
195
  localStorage.removeItem(ADMIN_CONFIG_KEY);
188
196
  return true;
189
197
  } catch (error) {
198
+ console.error("Unexpected error:", error);
190
199
  return false;
191
200
  }
192
201
  }
@@ -4,6 +4,15 @@ function kebabCase(name) {
4
4
  function spacedWords(name) {
5
5
  return name.replaceAll(/([a-z])([A-Z])/g, "$1 $2").replaceAll(/([A-Z])([A-Z][a-z])/g, "$1 $2");
6
6
  }
7
+ function isUppercaseLetter(char) {
8
+ return char >= "A" && char <= "Z";
9
+ }
10
+ function isLowercaseLetter(char) {
11
+ return char !== void 0 && char >= "a" && char <= "z";
12
+ }
13
+ function titleCaseAcronym(acronym) {
14
+ return acronym.charAt(0).toUpperCase() + acronym.slice(1).toLowerCase();
15
+ }
7
16
  function formatFieldName(fieldName) {
8
17
  if (fieldName.includes(".")) {
9
18
  return fieldName.split(".").map((part) => {
@@ -22,15 +31,29 @@ function formatFieldName(fieldName) {
22
31
  return fieldName.replaceAll(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (str) => str.toUpperCase());
23
32
  }
24
33
  function normalizeModelNameForDocument(modelName) {
25
- return modelName.replace(/([A-Z]{2,})([A-Z][a-z])/g, (_, acronym, rest) => {
26
- if (acronym.length >= 2) {
27
- return acronym.charAt(0).toUpperCase() + acronym.slice(1).toLowerCase() + rest;
34
+ let normalized = "";
35
+ let index = 0;
36
+ while (index < modelName.length) {
37
+ if (!isUppercaseLetter(modelName[index])) {
38
+ normalized += modelName[index];
39
+ index += 1;
40
+ continue;
28
41
  }
29
- return acronym + rest;
30
- }).replace(
31
- /[A-Z]{3,}$/g,
32
- (match) => match.charAt(0).toUpperCase() + match.slice(1).toLowerCase()
33
- );
42
+ const runStart = index;
43
+ while (index < modelName.length && isUppercaseLetter(modelName[index])) {
44
+ index += 1;
45
+ }
46
+ const run = modelName.slice(runStart, index);
47
+ const nextIsLowercase = isLowercaseLetter(modelName[index]);
48
+ if (run.length >= 4 && nextIsLowercase) {
49
+ normalized += titleCaseAcronym(run.slice(0, -1)) + run[run.length - 1];
50
+ } else if (run.length >= 3 && index === modelName.length) {
51
+ normalized += titleCaseAcronym(run);
52
+ } else {
53
+ normalized += run;
54
+ }
55
+ }
56
+ return normalized;
34
57
  }
35
58
  function getItemDisplayName(item) {
36
59
  if (item.name) return item.name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestledjs/data-browser",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Universal admin data browser for Nestled framework projects with full CRUD operations",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -37,7 +37,7 @@
37
37
  "peerDependencies": {
38
38
  "@apollo/client": "^4.0.0",
39
39
  "@heroicons/react": "^2.0.0",
40
- "@nestledjs/forms": "^0.6.3",
40
+ "@nestledjs/forms": "0.7.7",
41
41
  "@nestledjs/shared-components": "^1.0.14",
42
42
  "react": "^19.0.0",
43
43
  "react-router": "^7.0.0"
@@ -53,4 +53,4 @@
53
53
  "registry": "https://registry.npmjs.org/"
54
54
  },
55
55
  "type": "module"
56
- }
56
+ }