@nestledjs/data-browser 0.1.6 → 0.1.8

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.
@@ -1,145 +1,214 @@
1
- const c = "mi-admin-config", a = "1.0";
2
- const s = (e) => typeof e != "string" ? "" : e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/javascript:/gi, "").replace(/on\w+\s*=/gi, "").trim().substring(0, 1e3), f = (e) => Array.isArray(e) ? e.slice(0, 100).map((o) => s(o)).filter((o) => o.length > 0) : [], g = (e) => {
3
- if (!e || typeof e != "object") return null;
4
- const o = e, n = s(o.orderBy), t = s(o.orderDirection);
5
- return !["asc", "desc"].includes(t) || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(n) ? null : { orderBy: n, orderDirection: t };
6
- }, d = (e) => {
7
- if (!e || typeof e != "object") return !1;
8
- const o = e;
9
- if (o.version !== a || !o.models || typeof o.models != "object") return !1;
10
- const n = o.models;
11
- for (const [t, r] of Object.entries(n)) {
12
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t)) return !1;
13
- if (r && typeof r == "object") {
14
- const l = r;
15
- if (l.visibleColumns !== void 0 && !Array.isArray(l.visibleColumns)) return !1;
16
- if (l.sortPreference !== void 0) {
17
- const u = g(l.sortPreference);
18
- if (l.sortPreference !== null && u === null) return !1;
1
+ const ADMIN_CONFIG_KEY = "mi-admin-config";
2
+ const ADMIN_CONFIG_VERSION = "1.0";
3
+ const MAX_CONFIG_SIZE = 5e4;
4
+ const sanitizeString = (value) => {
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);
7
+ };
8
+ const sanitizeArray = (value) => {
9
+ if (!Array.isArray(value)) return [];
10
+ return value.slice(0, 100).map((item) => sanitizeString(item)).filter((item) => item.length > 0);
11
+ };
12
+ const sanitizeSortPreference = (value) => {
13
+ if (!value || typeof value !== "object") return null;
14
+ const obj = value;
15
+ const orderBy = sanitizeString(obj.orderBy);
16
+ const orderDirection = sanitizeString(obj.orderDirection);
17
+ if (!["asc", "desc"].includes(orderDirection)) return null;
18
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(orderBy)) return null;
19
+ return { orderBy, orderDirection };
20
+ };
21
+ const validateAdminConfig = (config) => {
22
+ if (!config || typeof config !== "object") return false;
23
+ const configObj = config;
24
+ if (configObj.version !== ADMIN_CONFIG_VERSION) return false;
25
+ if (!configObj.models || typeof configObj.models !== "object") return false;
26
+ const models = configObj.models;
27
+ 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;
19
35
  }
20
- if (l.searchFields !== void 0 && !Array.isArray(l.searchFields)) return !1;
36
+ if (config2.searchFields !== void 0 && !Array.isArray(config2.searchFields)) return false;
21
37
  }
22
38
  }
23
- return !0;
24
- }, i = {
39
+ return true;
40
+ };
41
+ const SecureAdminLocalStorage = {
25
42
  // Get the full admin config with validation
26
43
  getConfig: () => {
27
44
  try {
28
- const e = localStorage.getItem(c);
29
- if (!e)
30
- return { version: a, models: {} };
31
- if (e.length > 5e4)
32
- return console.warn("[AdminLocalStorage] Config exceeds size limit, resetting"), localStorage.removeItem(c), { version: a, models: {} };
33
- const o = JSON.parse(e);
34
- return d(o) ? o : (console.warn("[AdminLocalStorage] Invalid config detected, resetting"), localStorage.removeItem(c), { version: a, models: {} });
35
- } catch (e) {
36
- console.warn("[AdminLocalStorage] Failed to load config:", e);
45
+ const stored = localStorage.getItem(ADMIN_CONFIG_KEY);
46
+ if (!stored) {
47
+ return { version: ADMIN_CONFIG_VERSION, models: {} };
48
+ }
49
+ if (stored.length > MAX_CONFIG_SIZE) {
50
+ console.warn("[AdminLocalStorage] Config exceeds size limit, resetting");
51
+ localStorage.removeItem(ADMIN_CONFIG_KEY);
52
+ return { version: ADMIN_CONFIG_VERSION, models: {} };
53
+ }
54
+ const parsed = JSON.parse(stored);
55
+ if (!validateAdminConfig(parsed)) {
56
+ console.warn("[AdminLocalStorage] Invalid config detected, resetting");
57
+ localStorage.removeItem(ADMIN_CONFIG_KEY);
58
+ return { version: ADMIN_CONFIG_VERSION, models: {} };
59
+ }
60
+ return parsed;
61
+ } catch (error) {
62
+ console.warn("[AdminLocalStorage] Failed to load config:", error);
37
63
  try {
38
- localStorage.removeItem(c);
64
+ localStorage.removeItem(ADMIN_CONFIG_KEY);
39
65
  } catch {
40
66
  }
41
- return { version: a, models: {} };
67
+ return { version: ADMIN_CONFIG_VERSION, models: {} };
42
68
  }
43
69
  },
44
70
  // Save the full admin config with validation
45
- setConfig: (e) => {
71
+ setConfig: (config) => {
46
72
  try {
47
- if (!d(e))
48
- return console.warn("[AdminLocalStorage] Invalid config provided"), !1;
49
- const o = JSON.stringify(e);
50
- return o.length > 5e4 ? (console.warn("[AdminLocalStorage] Config too large to store"), !1) : (localStorage.setItem(c, o), !0);
51
- } catch (o) {
52
- return console.warn("[AdminLocalStorage] Failed to save config:", o), !1;
73
+ if (!validateAdminConfig(config)) {
74
+ console.warn("[AdminLocalStorage] Invalid config provided");
75
+ return false;
76
+ }
77
+ const serialized = JSON.stringify(config);
78
+ if (serialized.length > MAX_CONFIG_SIZE) {
79
+ console.warn("[AdminLocalStorage] Config too large to store");
80
+ return false;
81
+ }
82
+ localStorage.setItem(ADMIN_CONFIG_KEY, serialized);
83
+ return true;
84
+ } catch (error) {
85
+ console.warn("[AdminLocalStorage] Failed to save config:", error);
86
+ return false;
53
87
  }
54
88
  },
55
89
  // Get visible columns for a specific model with sanitization
56
- getColumnVisibility: (e) => {
57
- var r;
58
- const o = s(e);
59
- if (!o) return null;
60
- const t = (r = i.getConfig().models[o]) == null ? void 0 : r.visibleColumns;
61
- return t ? f(t) : null;
90
+ getColumnVisibility: (modelName) => {
91
+ var _a;
92
+ const sanitizedModelName = sanitizeString(modelName);
93
+ if (!sanitizedModelName) return null;
94
+ const config = SecureAdminLocalStorage.getConfig();
95
+ const columns = (_a = config.models[sanitizedModelName]) == null ? void 0 : _a.visibleColumns;
96
+ return columns ? sanitizeArray(columns) : null;
62
97
  },
63
98
  // Set visible columns for a specific model with validation
64
- setColumnVisibility: (e, o) => {
65
- const n = s(e), t = f(o);
66
- if (!n) return !1;
67
- const r = i.getConfig();
68
- return r.models[n] || (r.models[n] = {}), r.models[n].visibleColumns = t, i.setConfig(r);
99
+ setColumnVisibility: (modelName, visibleColumns) => {
100
+ const sanitizedModelName = sanitizeString(modelName);
101
+ const sanitizedColumns = sanitizeArray(visibleColumns);
102
+ if (!sanitizedModelName) return false;
103
+ const config = SecureAdminLocalStorage.getConfig();
104
+ if (!config.models[sanitizedModelName]) {
105
+ config.models[sanitizedModelName] = {};
106
+ }
107
+ config.models[sanitizedModelName].visibleColumns = sanitizedColumns;
108
+ return SecureAdminLocalStorage.setConfig(config);
69
109
  },
70
110
  // Get sort preference for a specific model with validation
71
- getSortPreference: (e) => {
72
- var r;
73
- const o = s(e);
74
- if (!o) return null;
75
- const t = (r = i.getConfig().models[o]) == null ? void 0 : r.sortPreference;
76
- return t ? g(t) : null;
111
+ getSortPreference: (modelName) => {
112
+ var _a;
113
+ const sanitizedModelName = sanitizeString(modelName);
114
+ if (!sanitizedModelName) return null;
115
+ const config = SecureAdminLocalStorage.getConfig();
116
+ const sortPref = (_a = config.models[sanitizedModelName]) == null ? void 0 : _a.sortPreference;
117
+ return sortPref ? sanitizeSortPreference(sortPref) : null;
77
118
  },
78
119
  // Set sort preference for a specific model with validation
79
- setSortPreference: (e, o) => {
80
- const n = s(e), t = g(o);
81
- if (!n || !t) return !1;
82
- const r = i.getConfig();
83
- return r.models[n] || (r.models[n] = {}), r.models[n].sortPreference = t, i.setConfig(r);
120
+ setSortPreference: (modelName, sortPreference) => {
121
+ const sanitizedModelName = sanitizeString(modelName);
122
+ const sanitizedSortPref = sanitizeSortPreference(sortPreference);
123
+ if (!sanitizedModelName || !sanitizedSortPref) return false;
124
+ const config = SecureAdminLocalStorage.getConfig();
125
+ if (!config.models[sanitizedModelName]) {
126
+ config.models[sanitizedModelName] = {};
127
+ }
128
+ config.models[sanitizedModelName].sortPreference = sanitizedSortPref;
129
+ return SecureAdminLocalStorage.setConfig(config);
84
130
  },
85
131
  // Get search fields for a specific model with sanitization
86
- getSearchFields: (e) => {
87
- var r;
88
- const o = s(e);
89
- if (!o) return null;
90
- const t = (r = i.getConfig().models[o]) == null ? void 0 : r.searchFields;
91
- return t ? f(t) : null;
132
+ getSearchFields: (modelName) => {
133
+ var _a;
134
+ const sanitizedModelName = sanitizeString(modelName);
135
+ if (!sanitizedModelName) return null;
136
+ const config = SecureAdminLocalStorage.getConfig();
137
+ const searchFields = (_a = config.models[sanitizedModelName]) == null ? void 0 : _a.searchFields;
138
+ return searchFields ? sanitizeArray(searchFields) : null;
92
139
  },
93
140
  // Set search fields for a specific model with validation
94
- setSearchFields: (e, o) => {
95
- const n = s(e), t = f(o);
96
- if (!n) return !1;
97
- const r = i.getConfig();
98
- return r.models[n] || (r.models[n] = {}), r.models[n].searchFields = t, i.setConfig(r);
141
+ setSearchFields: (modelName, searchFields) => {
142
+ const sanitizedModelName = sanitizeString(modelName);
143
+ const sanitizedFields = sanitizeArray(searchFields);
144
+ if (!sanitizedModelName) return false;
145
+ const config = SecureAdminLocalStorage.getConfig();
146
+ if (!config.models[sanitizedModelName]) {
147
+ config.models[sanitizedModelName] = {};
148
+ }
149
+ config.models[sanitizedModelName].searchFields = sanitizedFields;
150
+ return SecureAdminLocalStorage.setConfig(config);
99
151
  },
100
152
  // Export config as JSON string with validation
101
153
  exportConfig: () => {
102
154
  try {
103
- const e = i.getConfig();
104
- return d(e) ? JSON.stringify(e, null, 2) : (console.warn("[AdminLocalStorage] Cannot export invalid config"), null);
105
- } catch (e) {
106
- return console.warn("[AdminLocalStorage] Failed to export config:", e), null;
155
+ const config = SecureAdminLocalStorage.getConfig();
156
+ if (!validateAdminConfig(config)) {
157
+ console.warn("[AdminLocalStorage] Cannot export invalid config");
158
+ return null;
159
+ }
160
+ return JSON.stringify(config, null, 2);
161
+ } catch (error) {
162
+ console.warn("[AdminLocalStorage] Failed to export config:", error);
163
+ return null;
107
164
  }
108
165
  },
109
166
  // Import config from JSON string with comprehensive security validation
110
- importConfig: (e) => {
167
+ importConfig: (configJson) => {
111
168
  try {
112
- const o = s(e);
113
- if (!o || o.length > 5e4)
114
- return console.warn("[AdminLocalStorage] Invalid or oversized config JSON"), !1;
115
- const n = JSON.parse(o);
116
- if (!d(n))
117
- return console.warn("[AdminLocalStorage] Failed config validation during import"), !1;
118
- const t = JSON.stringify(n), r = [
169
+ const sanitizedJson = sanitizeString(configJson);
170
+ if (!sanitizedJson || sanitizedJson.length > MAX_CONFIG_SIZE) {
171
+ console.warn("[AdminLocalStorage] Invalid or oversized config JSON");
172
+ return false;
173
+ }
174
+ const config = JSON.parse(sanitizedJson);
175
+ if (!validateAdminConfig(config)) {
176
+ console.warn("[AdminLocalStorage] Failed config validation during import");
177
+ return false;
178
+ }
179
+ const serialized = JSON.stringify(config);
180
+ const suspiciousPatterns = [
119
181
  /<script/i,
120
182
  /javascript:/i,
121
183
  /on\w+\s*=/i,
122
184
  /eval\s*\(/i,
123
185
  /function\s*\(/i
124
186
  ];
125
- for (const l of r)
126
- if (l.test(t))
127
- return console.warn("[AdminLocalStorage] Suspicious content detected in config"), !1;
128
- return i.setConfig(n);
129
- } catch (o) {
130
- return console.warn("[AdminLocalStorage] Failed to import config:", o), !1;
187
+ for (const pattern of suspiciousPatterns) {
188
+ if (pattern.test(serialized)) {
189
+ console.warn("[AdminLocalStorage] Suspicious content detected in config");
190
+ return false;
191
+ }
192
+ }
193
+ return SecureAdminLocalStorage.setConfig(config);
194
+ } catch (error) {
195
+ console.warn("[AdminLocalStorage] Failed to import config:", error);
196
+ return false;
131
197
  }
132
198
  },
133
199
  // Clear all stored data (for security/privacy)
134
200
  clearConfig: () => {
135
201
  try {
136
- return localStorage.removeItem(c), !0;
137
- } catch (e) {
138
- return console.warn("[AdminLocalStorage] Failed to clear config:", e), !1;
202
+ localStorage.removeItem(ADMIN_CONFIG_KEY);
203
+ return true;
204
+ } catch (error) {
205
+ console.warn("[AdminLocalStorage] Failed to clear config:", error);
206
+ return false;
139
207
  }
140
208
  }
141
- }, m = i;
209
+ };
210
+ const AdminLocalStorage = SecureAdminLocalStorage;
142
211
  export {
143
- m as AdminLocalStorage,
144
- i as SecureAdminLocalStorage
212
+ AdminLocalStorage,
213
+ SecureAdminLocalStorage
145
214
  };
@@ -1,27 +1,39 @@
1
- function n(r) {
2
- return r.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
1
+ function kebabCase(name) {
2
+ return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
3
3
  }
4
- function s(r) {
5
- return r.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
4
+ function spacedWords(name) {
5
+ return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
6
6
  }
7
- function c(r) {
8
- return r.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (e) => e.toUpperCase());
7
+ function formatFieldName(fieldName) {
8
+ return fieldName.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (str) => str.toUpperCase());
9
9
  }
10
- function i(r) {
11
- return r === r.toUpperCase() && r.length > 1 ? r.charAt(0).toUpperCase() + r.slice(1).toLowerCase() : r;
10
+ function normalizeModelNameForDocument(modelName) {
11
+ if (modelName === modelName.toUpperCase() && modelName.length > 1) {
12
+ return modelName.charAt(0).toUpperCase() + modelName.slice(1).toLowerCase();
13
+ }
14
+ return modelName;
12
15
  }
13
- function u(r) {
14
- return r.name ? r.name : r.title ? r.title : r.firstName && r.lastName ? `${r.firstName} ${r.lastName}` : r.firstName ? r.firstName : r.email ? r.email : r.id;
16
+ function getItemDisplayName(item) {
17
+ if (item.name) return item.name;
18
+ if (item.title) return item.title;
19
+ if (item.firstName && item.lastName) return `${item.firstName} ${item.lastName}`;
20
+ if (item.firstName) return item.firstName;
21
+ if (item.email) return item.email;
22
+ return item.id;
15
23
  }
16
- function f(r) {
17
- const t = ["name", "title", "email", "firstName", "lastName", "subject"].filter((a) => r.includes(a));
18
- return t.length >= 1 ? t.slice(0, 2) : r.slice(0, Math.min(2, r.length));
24
+ function getSmartSearchFields(availableFields) {
25
+ const primaryFields = ["name", "title", "email", "firstName", "lastName", "subject"];
26
+ const primaryMatches = primaryFields.filter((field) => availableFields.includes(field));
27
+ if (primaryMatches.length >= 1) {
28
+ return primaryMatches.slice(0, 2);
29
+ }
30
+ return availableFields.slice(0, Math.min(2, availableFields.length));
19
31
  }
20
32
  export {
21
- c as formatFieldName,
22
- u as getItemDisplayName,
23
- f as getSmartSearchFields,
24
- n as kebabCase,
25
- i as normalizeModelNameForDocument,
26
- s as spacedWords
33
+ formatFieldName,
34
+ getItemDisplayName,
35
+ getSmartSearchFields,
36
+ kebabCase,
37
+ normalizeModelNameForDocument,
38
+ spacedWords
27
39
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestledjs/data-browser",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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",
@@ -39,7 +39,7 @@
39
39
  "@heroicons/react": "^2.0.0",
40
40
  "@nestledjs/forms": "^0.5.0",
41
41
  "@nestledjs/helpers": "^0.1.0",
42
- "@nestledjs/shared-components": "^0.1.6",
42
+ "@nestledjs/shared-components": "^0.1.8",
43
43
  "react": "^19.0.0",
44
44
  "react-router": "^7.0.0"
45
45
  },