@builttocreate/engine-utils 2.10.1 → 2.11.0-beta.1629.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.
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.isTargetOptionDeleted = exports.isFieldEmpty = exports.handleMultiSelectOptionToggle = exports.getTemplateWithLookups = exports.getMultiSelectFieldValue = exports.getImportFieldValues = exports.getDefaultOption = exports.getDefaultFieldValuesFromTemplate = exports.getChartFieldValue = exports["default"] = void 0;
7
+ exports.isTargetOptionDeleted = exports.isFieldEmpty = exports.handleMultiSelectOptionToggle = exports.getTemplateWithLookups = exports.getMultiSelectFieldValue = exports.getImportFieldValues = exports.getDefaultOption = exports.getDefaultFieldValuesFromTemplate = exports.getChartFieldValue = exports.flattenSchemaTree = exports["default"] = void 0;
8
8
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
@@ -298,6 +298,45 @@ var isTargetOptionDeleted = exports.isTargetOptionDeleted = function isTargetOpt
298
298
  var result = !(undeletedOptions.length > 0);
299
299
  return result;
300
300
  };
301
+
302
+ /**
303
+ *
304
+ * Flattens a nested schema tree into an array of objects and adds a depth property to each object.
305
+ *
306
+ * @param {Object} schemaObj
307
+ * @returns {Array} // An array of objects with the schema's id, depth, and other properties.
308
+ */
309
+ var flattenSchemaTree = exports.flattenSchemaTree = function flattenSchemaTree(schemaObj) {
310
+ var flattened = [];
311
+
312
+ // Recursive function to traverse the tree.
313
+ function traverse(schemaId, depth, parentId) {
314
+ var node = schemaObj[schemaId];
315
+ if (!node) return;
316
+
317
+ // Create a new object that includes the schema's id and its depth.
318
+ flattened.push(_objectSpread({
319
+ id: schemaId,
320
+ parentId: parentId,
321
+ depth: depth
322
+ }, node));
323
+
324
+ // If the node has children, traverse them with increased depth.
325
+ if (node.children && node.children.length > 0) {
326
+ node.children.forEach(function (childId) {
327
+ return traverse(childId, depth + 1, schemaId);
328
+ });
329
+ }
330
+ }
331
+
332
+ // Find and traverse each root node.
333
+ Object.keys(schemaObj).forEach(function (schemaId) {
334
+ if (schemaObj[schemaId].root) {
335
+ traverse(schemaId, 0);
336
+ }
337
+ });
338
+ return flattened;
339
+ };
301
340
  var _default = exports["default"] = {
302
341
  getDefaultOption: getDefaultOption,
303
342
  getChartFieldValue: getChartFieldValue,
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.generateReadableId = generateReadableId;
7
+ exports.generateReadableTableColumnId = generateReadableTableColumnId;
8
+ exports.getFieldPositionsCount = getFieldPositionsCount;
9
+ exports.getFieldsCount = getFieldsCount;
10
+ exports.getViewsFieldPositionsCount = getViewsFieldPositionsCount;
11
+ /**
12
+ * Counts fields of a specific type in an array of fields
13
+ *
14
+ * @param {Array} fields - Array of fields that have a .type property
15
+ * @param {String} type - The field type to count
16
+ * @returns {Number} - Count of fields matching the type
17
+ */
18
+ function countFieldsByType(fields, type) {
19
+ if (!fields || !Array.isArray(fields)) return 0;
20
+ return fields.filter(function (field) {
21
+ return field && field.type === type;
22
+ }).length;
23
+ }
24
+
25
+ /**
26
+ * Counts field positions of a specific type in document fields
27
+ *
28
+ * @param {Array} fields - Array of fields
29
+ * @param {String} type - Field type to count
30
+ * @returns {Number} - Count of fields matching the type
31
+ */
32
+ function getFieldsCount(fields, type) {
33
+ if (!fields || !Array.isArray(fields)) {
34
+ console.warn('getFieldsCount: fields parameter is missing or not an array');
35
+ return 0;
36
+ }
37
+ return countFieldsByType(fields, type);
38
+ }
39
+
40
+ /**
41
+ * Counts field positions of a specific type in an array of pages
42
+ *
43
+ * @param {Array} pages - Array of pages with fieldPositions
44
+ * @param {String} type - Field type to count
45
+ * @returns {Number} - Count of fieldPositions matching the type
46
+ */
47
+ function getFieldPositionsCount(pages, type) {
48
+ if (!pages || !Array.isArray(pages)) {
49
+ return 0;
50
+ }
51
+ if (!pages.length) return 0;
52
+ var count = 0;
53
+ pages.forEach(function (page) {
54
+ if (page && page.fieldPositions) {
55
+ count += countFieldsByType(page.fieldPositions, type);
56
+ }
57
+ });
58
+ return count;
59
+ }
60
+
61
+ /**
62
+ * Counts field positions in views
63
+ *
64
+ * @param {Array} views - Array of views with pages with fieldPositions
65
+ * @param {String} type - Field type to count
66
+ * @returns {Number} - Count of fieldPositions in views matching the type
67
+ */
68
+ function getViewsFieldPositionsCount(views, type) {
69
+ if (!views || !Array.isArray(views)) {
70
+ return 0;
71
+ }
72
+ if (!views.length) return 0;
73
+ var count = 0;
74
+ views.forEach(function (view) {
75
+ if (view && view.pages && view.pages.length) {
76
+ count += getFieldPositionsCount(view.pages, type);
77
+ }
78
+ });
79
+ return count;
80
+ }
81
+
82
+ /**
83
+ * Gets the next field ID for a specific type by counting existing fields
84
+ * and incrementing. It is built to support any combination of the parameters.
85
+ *
86
+ * But it is recommended to use it with all 3 parts if possible.
87
+ *
88
+ * @param {Object} parts - Object containing fields, pages, and views
89
+ * @param {Array} parts.fields - Array of fields
90
+ * @param {Array} parts.pages - Array of pages with fieldPositions
91
+ * @param {Array} parts.views - Array of views with pages
92
+ * @param {String} type - The field type to count
93
+ * @param {String} prefix - An optional prefix to add to the ID
94
+ * @returns {String} - An ID in the format "prefix{type}{count+1}" (e.g. "text3")
95
+ */
96
+ function generateReadableId(parts, type) {
97
+ var splitterId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
98
+ var fields = parts === null || parts === void 0 ? void 0 : parts.fields;
99
+ var pages = parts === null || parts === void 0 ? void 0 : parts.pages;
100
+ var views = parts === null || parts === void 0 ? void 0 : parts.views;
101
+ if (!type) throw new Error('generateReadableId: type is required');
102
+
103
+ // Check if all three parameters are missing
104
+ if (!fields && !pages && !views) {
105
+ throw new Error('generateReadableId: at least one of fields, pages, or views is required');
106
+ }
107
+ if (!fields) console.warn('generateReadableId: fields parameter is missing');
108
+
109
+ // Note: leaving for now until we allow targeting fieldPositions
110
+ // if (pages) count += getFieldPositionsCount(pages, type);
111
+ // if (views && views.length > 1) count += getViewsFieldPositionsCount(views, type);
112
+
113
+ var count = fields.length ? getFieldsCount(fields, type) : 0;
114
+ var readableId = "".concat(type).concat(splitterId).concat(count + 1);
115
+ while (hasDuplicateId(fields, readableId)) {
116
+ count++;
117
+ readableId = "".concat(type).concat(splitterId).concat(count + 1);
118
+ }
119
+ return readableId;
120
+ }
121
+ function hasDuplicateId(items, targetId) {
122
+ return items.some(function (item) {
123
+ return item._id === targetId;
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Generates a readable table column ID by counting existing columns
129
+ *
130
+ * @param {String} tableId - The ID of the table (used as prefix)
131
+ * @param {Array} tableColumns - Array of table column objects
132
+ * @returns {String} - An ID in the format "{tableId}Column{count+1}" (e.g. "table1Column1")
133
+ */
134
+ function generateReadableTableColumnId(tableId, tableColumns, columnId) {
135
+ //Add logic that ensures that created Id is not a duplicate
136
+ if (!tableId) throw new Error('generateReadableTableColumnId: tableId is required');
137
+ if (!columnId) throw new Error('generateReadableTableColumnId: columnId is required');
138
+ if (!tableColumns || !Array.isArray(tableColumns)) {
139
+ console.warn('generateReadableTableColumnId: tableColumns parameter is missing or not an array');
140
+ return "".concat(tableId, "Column1");
141
+ }
142
+ var counts = {};
143
+ var generatedId = null;
144
+
145
+ //Filter out undefined and null values from tableColumns
146
+ var validColumns = tableColumns.filter(function (column) {
147
+ return column !== undefined && column !== null;
148
+ });
149
+ validColumns.forEach(function (col) {
150
+ var type = (col.type || 'unknown').toLowerCase();
151
+ counts[type] = (counts[type] || 0) + 1;
152
+ var readableId = "".concat(type).concat(counts[type]);
153
+ if (col._id === columnId) {
154
+ /**
155
+ * If the generatedReadableId is already present in the tableColumns,
156
+ * we need to increment the count until we find a unique ID.
157
+ * This ensures that the new column ID is unique.
158
+ */
159
+ while (hasDuplicateId(validColumns, readableId)) {
160
+ counts[type]++;
161
+ readableId = "".concat(type).concat(counts[type]);
162
+ }
163
+ generatedId = readableId;
164
+ }
165
+ });
166
+ return generatedId;
167
+ }
package/dist/index.js CHANGED
@@ -31,6 +31,18 @@ Object.defineProperty(exports, "generateObjectId", {
31
31
  return _generateObjectId["default"];
32
32
  }
33
33
  });
34
+ Object.defineProperty(exports, "generateReadableId", {
35
+ enumerable: true,
36
+ get: function get() {
37
+ return _generateReadableId.generateReadableId;
38
+ }
39
+ });
40
+ Object.defineProperty(exports, "generateReadableTableColumnId", {
41
+ enumerable: true,
42
+ get: function get() {
43
+ return _generateReadableId.generateReadableTableColumnId;
44
+ }
45
+ });
34
46
  exports.paginationHelper = exports.joyDocHelper = void 0;
35
47
  Object.defineProperty(exports, "reduxApiMiddleware", {
36
48
  enumerable: true,
@@ -45,6 +57,12 @@ Object.defineProperty(exports, "validateObjectId", {
45
57
  return _validateObjectId["default"];
46
58
  }
47
59
  });
60
+ Object.defineProperty(exports, "validateReadableId", {
61
+ enumerable: true,
62
+ get: function get() {
63
+ return _validateReadableId["default"];
64
+ }
65
+ });
48
66
  var tableHelper = _interopRequireWildcard(require("./tableHelper"));
49
67
  exports.tableHelper = tableHelper;
50
68
  var chartHelper = _interopRequireWildcard(require("./chartHelper"));
@@ -66,7 +84,10 @@ var _changedKeys = _interopRequireDefault(require("./changedKeys"));
66
84
  var _Roles = _interopRequireDefault(require("./constants/Roles"));
67
85
  var _generateObjectId = _interopRequireDefault(require("./generateObjectId"));
68
86
  var _validateObjectId = _interopRequireDefault(require("./validateObjectId"));
69
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
87
+ var _generateReadableId = require("./generateReadableId");
88
+ var _validateReadableId = _interopRequireDefault(require("./validateReadableId"));
89
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
90
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) { if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } } return n["default"] = e, t && t.set(e, n), n; }
70
91
  var _default = exports["default"] = {
71
92
  fileHelper: fileHelper,
72
93
  tableHelper: tableHelper,
@@ -81,5 +102,8 @@ var _default = exports["default"] = {
81
102
  roleHelper: roleHelper,
82
103
  Roles: _Roles["default"],
83
104
  generateObjectId: _generateObjectId["default"],
84
- validateObjectId: _validateObjectId["default"]
105
+ validateObjectId: _validateObjectId["default"],
106
+ generateReadableId: _generateReadableId.generateReadableId,
107
+ generateReadableTableColumnId: _generateReadableId.generateReadableTableColumnId,
108
+ validateReadableId: _validateReadableId["default"]
85
109
  };
@@ -9,6 +9,7 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _generateObjectId = _interopRequireDefault(require("./generateObjectId"));
11
11
  var _validateObjectId = _interopRequireDefault(require("./validateObjectId"));
12
+ var _generateReadableId = require("./generateReadableId");
12
13
  var _PageLayoutModes = _interopRequireDefault(require("./constants/PageLayoutModes"));
13
14
  var _FieldPresentationModes = _interopRequireDefault(require("./constants/FieldPresentationModes"));
14
15
  var _DocumentTypes = _interopRequireDefault(require("./constants/DocumentTypes"));
@@ -18,6 +19,7 @@ var _FieldDisplayTypes = _interopRequireDefault(require("./constants/FieldDispla
18
19
  var _FileViews = _interopRequireDefault(require("./constants/FileViews"));
19
20
  var _FormStyleOptions = _interopRequireDefault(require("./constants/FormStyleOptions"));
20
21
  var _tableHelper = require("./tableHelper");
22
+ var _validateReadableId = _interopRequireDefault(require("./validateReadableId"));
21
23
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
22
24
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
23
25
  /**
@@ -85,6 +87,43 @@ var getDefaultJoyDoc = exports.getDefaultJoyDoc = function getDefaultJoyDoc() {
85
87
  }, defaults);
86
88
  };
87
89
 
90
+ /**
91
+ * If existing _id is a valid ObjectId, keep it
92
+ * Otherwise, generate a readable ID using the field type
93
+ * with as much data as we are given to determine the proper count.
94
+ *
95
+ * @param {String} existingId - The existing ID to check
96
+ * @param {String} type - The field type for generating readable IDs
97
+ * @param {Object} doc - The document context for readable ID generation
98
+ * @param {Object} generatedIdCounts - Object to track generated ID counts within this operation
99
+ * @returns {String} - Either the existing ObjectId or a new readable ID
100
+ */
101
+ var getFieldId = function getFieldId(existingId, type, doc) {
102
+ // If existing ID is a valid ObjectId or readableId, maintain it
103
+ //Note: readableId validation is already done when it's being generated
104
+
105
+ if (existingId && (0, _validateObjectId["default"])(existingId)) {
106
+ return existingId;
107
+ }
108
+ if (existingId) return existingId;
109
+
110
+ // Otherwise, generate a readable ID
111
+ if (type) {
112
+ // Get the base count from existing fields
113
+ var baseCount = 0;
114
+ if (doc.fields) baseCount += (0, _generateReadableId.getFieldsCount)(doc.fields, type);
115
+
116
+ // Note: leaving for now until we allow targeting fieldPositions
117
+ // if (doc.pages) baseCount += getFieldPositionsCount(getAllPages(doc), type);
118
+ // if (doc.views && doc.views.length > 1) baseCount += getViewsFieldPositionsCount(getAllViews(doc), type);
119
+
120
+ return "".concat(type).concat(baseCount + 1);
121
+ }
122
+
123
+ // Fallback to ObjectId if we can't generate readable ID
124
+ return (0, _generateObjectId["default"])();
125
+ };
126
+
88
127
  /**
89
128
  * Get Clean JoyDoc Format
90
129
  *
@@ -105,32 +144,52 @@ var getCleanedJoyDoc = exports.getCleanedJoyDoc = function getCleanedJoyDoc(doc)
105
144
  nextDoc = _removeOrphanedFields.nextDoc;
106
145
 
107
146
  /**
108
- * Step 1.1: Replace doc._id if it is not valid
147
+ * Step 1.1: Replace doc._id if it is not valid (always use objectId for documents)
109
148
  */
110
149
  if (nextDoc._id && !(0, _validateObjectId["default"])(nextDoc._id)) nextDoc._id = (0, _generateObjectId["default"])();
150
+
151
+ /**
152
+ * Step 1.2: Clean field IDs using simplified approach
153
+ */
154
+ if (nextDoc.fields && nextDoc.fields.length > 0) {
155
+ nextDoc.fields = nextDoc.fields.map(function (field) {
156
+ var nextField = _objectSpread({}, field);
157
+
158
+ // Clean field _id using simplified approach
159
+ nextField._id = getFieldId(field._id, field.type, nextDoc);
160
+
161
+ // Clean table field rowOrder
162
+ if (field.type === _FieldTypes["default"].table) {
163
+ var cleanedRoworder = (0, _tableHelper.getCleanedRowOrder)(field.rowOrder, field.value);
164
+ nextField.rowOrder = cleanedRoworder;
165
+ }
166
+ return nextField;
167
+ });
168
+ }
111
169
  if (nextDoc.files && nextDoc.files.length > 0) {
112
170
  nextDoc.files = nextDoc.files.map(function (file) {
113
171
  var _nextFile$views;
114
172
  var nextFile = _objectSpread({}, file);
115
173
 
116
174
  /**
117
- * Step 1.2: Replace files[x]._id if it is not valid
175
+ * Step 1.3: Replace files[x]._id if it is not a valid ObjectId or valid readable ID
118
176
  */
119
- if (nextFile._id && !(0, _validateObjectId["default"])(nextFile._id)) nextFile._id = (0, _generateObjectId["default"])();
177
+
178
+ if (nextFile._id && !(0, _validateObjectId["default"])(nextFile._id) && !(0, _validateReadableId["default"])(nextFile._id)) nextFile._id = (0, _generateObjectId["default"])();
120
179
 
121
180
  /**
122
- * Step 1.3: Replace page ids and field ids if they are not valid
181
+ * Step 1.4: Replace page ids and field ids if they are not valid
123
182
  */
124
- nextFile.pages = getCleanedJoyDocPages(nextFile.pages);
183
+ nextFile.pages = getCleanedJoyDocPages(nextFile.pages, nextDoc);
125
184
  nextFile.pageOrder = cleanPageOrder(getPageOrder(nextFile.pageOrder, nextFile.pages), nextFile.pages);
126
185
 
127
186
  /**
128
- * Step 1.4: Replace view page ids and field ids if they are not valid
187
+ * Step 1.5: Replace view page ids and field ids if they are not valid
129
188
  */
130
- if ((nextFile === null || nextFile === void 0 || (_nextFile$views = nextFile.views) === null || _nextFile$views === void 0 ? void 0 : _nextFile$views.length) > 0) {
189
+ if ((nextFile === null || nextFile === void 0 ? void 0 : (_nextFile$views = nextFile.views) === null || _nextFile$views === void 0 ? void 0 : _nextFile$views.length) > 0) {
131
190
  nextFile.views = nextFile.views.map(function (view) {
132
191
  var nextView = _objectSpread({}, view);
133
- nextView.pages = getCleanedJoyDocPages(nextView.pages);
192
+ nextView.pages = getCleanedJoyDocPages(nextView.pages, nextDoc);
134
193
  nextView.pageOrder = cleanPageOrder(getPageOrder(nextView.pageOrder, nextView.pages), nextView.pages);
135
194
  return nextView;
136
195
  });
@@ -140,21 +199,6 @@ var getCleanedJoyDoc = exports.getCleanedJoyDoc = function getCleanedJoyDoc(doc)
140
199
  } else {
141
200
  nextDoc.files = [getDefaultJoyDocFile()];
142
201
  }
143
-
144
- /**
145
- * Logic below is used to clean the table field rowOrder by removing the
146
- * deletedRowIds / duplicateRowIds from the rowOrder.
147
- **/
148
- if (nextDoc.fields && nextDoc.fields.length > 0) {
149
- nextDoc.fields = nextDoc.fields.map(function (field) {
150
- var nextField = _objectSpread({}, field);
151
- if (field.type === _FieldTypes["default"].table) {
152
- var cleanedRoworder = (0, _tableHelper.getCleanedRowOrder)(field.rowOrder, field.value);
153
- nextField.rowOrder = cleanedRoworder;
154
- }
155
- return nextField;
156
- });
157
- }
158
202
  return nextDoc;
159
203
  };
160
204
 
@@ -167,13 +211,28 @@ var removeOrphanedFieldsFromJoydoc = exports.removeOrphanedFieldsFromJoydoc = fu
167
211
  if (!doc) return getDefaultJoyDoc();
168
212
  var nextDoc = _objectSpread({}, doc);
169
213
  var associatedFieldIdLookup = {};
170
- nextDoc === null || nextDoc === void 0 || (_nextDoc$files = nextDoc.files) === null || _nextDoc$files === void 0 || _nextDoc$files.forEach(function (file) {
171
- var _file$views;
214
+ nextDoc === null || nextDoc === void 0 ? void 0 : (_nextDoc$files = nextDoc.files) === null || _nextDoc$files === void 0 ? void 0 : _nextDoc$files.forEach(function (file) {
215
+ var _file$header, _file$header$fieldPos, _file$footer, _file$footer$fieldPos, _file$pages, _file$views;
216
+ /**
217
+ * Header
218
+ */
219
+ if (file.header) (_file$header = file.header) === null || _file$header === void 0 ? void 0 : (_file$header$fieldPos = _file$header.fieldPositions) === null || _file$header$fieldPos === void 0 ? void 0 : _file$header$fieldPos.forEach(function (fieldPosition) {
220
+ return associatedFieldIdLookup[fieldPosition.field] = true;
221
+ });
222
+
223
+ /**
224
+ * Footer
225
+ */
226
+ if (file.footer) (_file$footer = file.footer) === null || _file$footer === void 0 ? void 0 : (_file$footer$fieldPos = _file$footer.fieldPositions) === null || _file$footer$fieldPos === void 0 ? void 0 : _file$footer$fieldPos.forEach(function (fieldPosition) {
227
+ return associatedFieldIdLookup[fieldPosition.field] = true;
228
+ });
229
+
172
230
  /**
173
231
  * Primary view
174
232
  */
175
- file.pages.forEach(function (page) {
176
- page === null || page === void 0 || page.fieldPositions.forEach(function (fieldPosition) {
233
+ (_file$pages = file.pages) === null || _file$pages === void 0 ? void 0 : _file$pages.forEach(function (page) {
234
+ var _page$fieldPositions;
235
+ page === null || page === void 0 ? void 0 : (_page$fieldPositions = page.fieldPositions) === null || _page$fieldPositions === void 0 ? void 0 : _page$fieldPositions.forEach(function (fieldPosition) {
177
236
  return associatedFieldIdLookup[fieldPosition.field] = true;
178
237
  });
179
238
  });
@@ -181,10 +240,11 @@ var removeOrphanedFieldsFromJoydoc = exports.removeOrphanedFieldsFromJoydoc = fu
181
240
  /**
182
241
  * Alternative view
183
242
  */
184
- (_file$views = file.views) === null || _file$views === void 0 || _file$views.forEach(function (view) {
243
+ (_file$views = file.views) === null || _file$views === void 0 ? void 0 : _file$views.forEach(function (view) {
185
244
  var _view$pages;
186
- view === null || view === void 0 || (_view$pages = view.pages) === null || _view$pages === void 0 || _view$pages.forEach(function (page) {
187
- page === null || page === void 0 || page.fieldPositions.forEach(function (fieldPosition) {
245
+ view === null || view === void 0 ? void 0 : (_view$pages = view.pages) === null || _view$pages === void 0 ? void 0 : _view$pages.forEach(function (page) {
246
+ var _page$fieldPositions2;
247
+ page === null || page === void 0 ? void 0 : (_page$fieldPositions2 = page.fieldPositions) === null || _page$fieldPositions2 === void 0 ? void 0 : _page$fieldPositions2.forEach(function (fieldPosition) {
188
248
  return associatedFieldIdLookup[fieldPosition.field] = true;
189
249
  });
190
250
  });
@@ -215,25 +275,31 @@ var removeOrphanedFieldsFromJoydoc = exports.removeOrphanedFieldsFromJoydoc = fu
215
275
  * 1. Valid page inside document file.pages array.
216
276
  * 2. All id's in pages and fieldPositions are valid format
217
277
  *
218
- * @param {Object} doc
278
+ * @param {Array} pages
279
+ * @param {Object} doc - The full document (needed for readableId generation)
219
280
  * @returns {Object}
220
281
  */
221
- var getCleanedJoyDocPages = exports.getCleanedJoyDocPages = function getCleanedJoyDocPages(pages) {
282
+ var getCleanedJoyDocPages = exports.getCleanedJoyDocPages = function getCleanedJoyDocPages(pages, doc) {
222
283
  if ((pages === null || pages === void 0 ? void 0 : pages.length) > 0) {
223
284
  return pages.map(function (page) {
224
285
  /**
225
- * Step 1: Replace pages[x]._id if it is not valid
286
+ * Step 1: Replace pages[x]._id if it is not valid (always use objectId for pages)
226
287
  */
227
288
  var nextPage = _objectSpread({}, page);
228
- if (nextPage._id && !(0, _validateObjectId["default"])(nextPage._id)) nextPage._id = (0, _generateObjectId["default"])();
289
+ if (nextPage._id && !(0, _validateObjectId["default"])(nextPage._id) && !(0, _validateReadableId["default"])(nextPage._id)) nextPage._id = (0, _generateObjectId["default"])();
229
290
  if (nextPage.fieldPositions && nextPage.fieldPositions.length > 0) {
230
291
  nextPage.fieldPositions = nextPage.fieldPositions.map(function (fieldPosition) {
231
292
  /**
232
293
  * Step 1.2: Replace fieldPosition _id and field reference if they are not valid
294
+ * Use simplified approach: respect ObjectIds, otherwise generate readable IDs
233
295
  */
234
296
  var nextFieldPosition = _objectSpread({}, fieldPosition);
235
- if (nextFieldPosition._id && !(0, _validateObjectId["default"])(nextFieldPosition._id)) nextFieldPosition._id = (0, _generateObjectId["default"])();
236
- if (nextFieldPosition.field && !(0, _validateObjectId["default"])(nextFieldPosition.field)) nextFieldPosition.field = (0, _generateObjectId["default"])();
297
+
298
+ // Clean fieldPosition _id
299
+ nextFieldPosition._id = nextFieldPosition._id && ((0, _validateObjectId["default"])(nextFieldPosition._id) || (0, _validateReadableId["default"])(nextFieldPosition._id)) ? nextFieldPosition._id : (0, _generateObjectId["default"])();
300
+
301
+ // Clean fieldPosition.field reference
302
+ nextFieldPosition.field = getFieldId(nextFieldPosition.field, fieldPosition.type, doc);
237
303
  return nextFieldPosition;
238
304
  });
239
305
  } else {
@@ -387,6 +453,7 @@ var getDocumentFromTemplate = exports.getDocumentFromTemplate = function getDocu
387
453
  var duplicateDocumentPage = exports.duplicateDocumentPage = function duplicateDocumentPage(doc, fileId, pageId) {
388
454
  var _nextDoc$fields;
389
455
  var pageDefaults = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
456
+ console.log('duplicateDocumentPage', doc, fileId, pageId, pageDefaults);
390
457
  var changelogInfo = {
391
458
  primaryPages: {
392
459
  targetIndex: 0,
@@ -401,8 +468,8 @@ var duplicateDocumentPage = exports.duplicateDocumentPage = function duplicateDo
401
468
  });
402
469
  var newPageId = (0, _generateObjectId["default"])();
403
470
  var fieldLookup = {};
404
- (_nextDoc$fields = nextDoc.fields) === null || _nextDoc$fields === void 0 || _nextDoc$fields.forEach(function (field) {
405
- if (field.file === fileId) fieldLookup[field._id] = field;
471
+ (_nextDoc$fields = nextDoc.fields) === null || _nextDoc$fields === void 0 ? void 0 : _nextDoc$fields.forEach(function (field) {
472
+ fieldLookup[field._id] = field;
406
473
  });
407
474
 
408
475
  /**
@@ -434,6 +501,7 @@ var duplicateDocumentPage = exports.duplicateDocumentPage = function duplicateDo
434
501
  var newFieldsLookupByOldId = {};
435
502
  var duplicatedFields = [];
436
503
  fieldIdsToDuplicate.forEach(function (fieldId) {
504
+ var _nextDoc$files$fileIn, _nextDoc$files$fileIn2;
437
505
  /**
438
506
  * Why do we check if a new field has already been created?
439
507
  *
@@ -444,9 +512,21 @@ var duplicateDocumentPage = exports.duplicateDocumentPage = function duplicateDo
444
512
  */
445
513
  if (newFieldsLookupByOldId[fieldId]) return;
446
514
  var field = fieldLookup[fieldId];
515
+
516
+ // Skip if field not found in lookup
517
+ if (!field) return;
518
+
519
+ // Generate readable ID using current fields plus already duplicated fields
520
+ var allFields = [].concat((0, _toConsumableArray2["default"])(nextDoc.fields), (0, _toConsumableArray2["default"])(duplicatedFields));
447
521
  var duplicateField = _objectSpread(_objectSpread({}, field), {}, {
448
- _id: (0, _generateObjectId["default"])()
522
+ _id: (0, _generateReadableId.generateReadableId)({
523
+ fields: allFields,
524
+ pages: (_nextDoc$files$fileIn = nextDoc.files[fileIndex]) === null || _nextDoc$files$fileIn === void 0 ? void 0 : _nextDoc$files$fileIn.pages,
525
+ views: (_nextDoc$files$fileIn2 = nextDoc.files[fileIndex]) === null || _nextDoc$files$fileIn2 === void 0 ? void 0 : _nextDoc$files$fileIn2.views
526
+ }, field.type)
449
527
  });
528
+ // Remove formulas property when duplicating
529
+ delete duplicateField.formulas;
450
530
  duplicatedFields.push(duplicateField);
451
531
 
452
532
  /**
@@ -596,7 +676,7 @@ var duplicateDocumentPage = exports.duplicateDocumentPage = function duplicateDo
596
676
  */
597
677
  var getMobileViewFromFile = exports.getMobileViewFromFile = function getMobileViewFromFile(file) {
598
678
  var _file$views2;
599
- var mobileViewIndex = file !== null && file !== void 0 && file.views && (file === null || file === void 0 || (_file$views2 = file.views) === null || _file$views2 === void 0 ? void 0 : _file$views2.length) > 0 ? file.views.findIndex(function (view) {
679
+ var mobileViewIndex = file !== null && file !== void 0 && file.views && (file === null || file === void 0 ? void 0 : (_file$views2 = file.views) === null || _file$views2 === void 0 ? void 0 : _file$views2.length) > 0 ? file.views.findIndex(function (view) {
600
680
  return view.type === _FileViews["default"].mobile;
601
681
  }) : -1;
602
682
  if (mobileViewIndex !== -1) {
@@ -704,10 +784,10 @@ var generateMobilePageFieldPositions = exports.generateMobilePageFieldPositions
704
784
  */
705
785
  var mergeAssoicatedFieldPositionsForMobilePage = exports.mergeAssoicatedFieldPositionsForMobilePage = function mergeAssoicatedFieldPositionsForMobilePage(fieldPositions) {
706
786
  var filteredFieldPositions = [];
707
- fieldPositions === null || fieldPositions === void 0 || fieldPositions.forEach(function (fieldPosition) {
787
+ fieldPositions === null || fieldPositions === void 0 ? void 0 : fieldPositions.forEach(function (fieldPosition) {
708
788
  var fieldPositionIndex = filteredFieldPositions.findIndex(function (filtered) {
709
789
  var _filtered$field, _fieldPosition$field;
710
- return (filtered === null || filtered === void 0 || (_filtered$field = filtered.field) === null || _filtered$field === void 0 ? void 0 : _filtered$field.toString()) === (fieldPosition === null || fieldPosition === void 0 || (_fieldPosition$field = fieldPosition.field) === null || _fieldPosition$field === void 0 ? void 0 : _fieldPosition$field.toString());
790
+ return (filtered === null || filtered === void 0 ? void 0 : (_filtered$field = filtered.field) === null || _filtered$field === void 0 ? void 0 : _filtered$field.toString()) === (fieldPosition === null || fieldPosition === void 0 ? void 0 : (_fieldPosition$field = fieldPosition.field) === null || _fieldPosition$field === void 0 ? void 0 : _fieldPosition$field.toString());
711
791
  });
712
792
 
713
793
  //Only add field to the list if it hasn't already been added
@@ -15,7 +15,8 @@ var _Table = _interopRequireWildcard(require("./constants/Table"));
15
15
  var _FieldTypes = _interopRequireDefault(require("./constants/FieldTypes"));
16
16
  var _FieldTableColumnTypes = _interopRequireDefault(require("./constants/FieldTableColumnTypes"));
17
17
  var _FieldTableCustomColumnIds = _interopRequireDefault(require("./constants/FieldTableCustomColumnIds"));
18
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
18
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
19
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) { if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } } return n["default"] = e, t && t.set(e, n), n; }
19
20
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
20
21
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
21
22
  var parser = new _hotFormulaParser.Parser();
@@ -256,14 +257,10 @@ var handleMoveColumnRight = exports.handleMoveColumnRight = function handleMoveC
256
257
  * @return {Object}
257
258
  */
258
259
  var getDefaultColumnOption = exports.getDefaultColumnOption = function getDefaultColumnOption(value) {
259
- var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
260
260
  return {
261
261
  _id: (0, _generateObjectId["default"])(),
262
262
  value: value || 'Option',
263
- deleted: false,
264
- styles: {
265
- backgroundColor: color
266
- }
263
+ deleted: false
267
264
  };
268
265
  };
269
266
 
@@ -422,7 +419,7 @@ var getDefaultDropdownColumn = exports.getDefaultDropdownColumn = function getDe
422
419
  */
423
420
  var getDefaultMultiSelectColumn = exports.getDefaultMultiSelectColumn = function getDefaultMultiSelectColumn() {
424
421
  var defaults = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
425
- var options = defaults.options || [getDefaultColumnOption('Option 1', '#f0f0f0'), getDefaultColumnOption('Option 2', '#f0f0f0'), getDefaultColumnOption('Option 3', '#f0f0f0')];
422
+ var options = defaults.options || [getDefaultColumnOption('Option 1'), getDefaultColumnOption('Option 2'), getDefaultColumnOption('Option 3')];
426
423
  return _objectSpread({
427
424
  _id: (0, _generateObjectId["default"])(),
428
425
  type: _FieldTableColumnTypes["default"].multiSelect,
@@ -761,6 +758,7 @@ var generateTableLookup = exports.generateTableLookup = function generateTableLo
761
758
  if (!(row !== null && row !== void 0 && row.cells)) console.warn("Missing 'cells' in row: ".concat(row._id));
762
759
  tableColumns.forEach(function (column) {
763
760
  if (!column.operand) return;
761
+ // eslint-disable-next-line no-console
764
762
  if (!row) console.warn("Row via column ".concat(column === null || column === void 0 ? void 0 : column._id, " is undefined."));
765
763
  var cellKey = "".concat(column.operand).concat(rowIndex + 1);
766
764
  var cellRawValue = row && row !== null && row !== void 0 && row.cells && row.cells[column._id] ? row.cells[column._id] : undefined;
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ var _fieldHelper = require("./fieldHelper");
11
+ var _FieldTypes = _interopRequireDefault(require("./constants/FieldTypes"));
12
+ // Check if value follows a valid readableId pattern for use in JavaScript formulas
13
+ // Note to test this please run npm test src/validateReadableId.test.js
14
+
15
+ // JavaScript reserved words and future reserved words
16
+ var JS_RESERVED_WORDS = new Set([
17
+ // ES6 Keywords
18
+ 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'export', 'extends', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield',
19
+ // ES6 Future Reserved Words
20
+ 'enum', 'implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', 'await', 'async',
21
+ // Literals
22
+ 'null', 'true', 'false', 'undefined', 'NaN', 'Infinity']);
23
+
24
+ // Common Excel function names (subset of most commonly used)
25
+ var EXCEL_FUNCTIONS = new Set([
26
+ // Math functions
27
+ 'sum', 'average', 'count', 'max', 'min', 'round', 'roundup', 'rounddown', 'ceiling', 'floor', 'abs', 'sign', 'sqrt', 'power', 'exp', 'ln', 'log', 'log10', 'mod', 'quotient', 'product', 'sumif', 'countif', 'averageif', 'sumifs', 'countifs', 'averageifs', 'maxifs', 'minifs', 'subtotal', 'aggregate', 'rand', 'randbetween', 'pi', 'degrees', 'radians', 'fact', 'combin', 'permut', 'gcd', 'lcm', 'mround', 'multinomial', 'seriessum', 'sqrtpi', 'sumproduct', 'sumsq', 'sumx2my2', 'sumx2py2', 'sumxmy2',
28
+ // Text functions
29
+ 'concatenate', 'concat', 'left', 'right', 'mid', 'len', 'lower', 'upper', 'proper', 'trim', 'substitute', 'replace', 'find', 'search', 'text', 'value', 'clean', 'code', 'char', 'exact', 'rept', 'fixed', 'dollar', 'textjoin', 'textbefore', 'textafter', 'textsplit', 'unichar', 'unicode',
30
+ // Date functions
31
+ 'date', 'today', 'now', 'year', 'month', 'day', 'hour', 'minute', 'second', 'weekday', 'weeknum', 'datedif', 'datevalue', 'timevalue', 'days', 'days360', 'edate', 'eomonth', 'networkdays', 'workday', 'isoweeknum', 'yearfrac',
32
+ // Logical functions
33
+ 'if', 'and', 'or', 'not', 'xor', 'iferror', 'ifna', 'ifs', 'switch', 'true', 'false',
34
+ // Lookup functions
35
+ 'vlookup', 'hlookup', 'lookup', 'match', 'index', 'choose', 'offset', 'indirect', 'row', 'column', 'rows', 'columns', 'transpose', 'unique', 'filter', 'sort', 'sortby', 'xlookup', 'xmatch',
36
+ // Statistical functions
37
+ 'median', 'mode', 'stdev', 'var', 'correl', 'forecast', 'trend', 'growth', 'linest', 'logest', 'slope', 'intercept', 'pearson', 'rsq', 'steyx', 'kurt', 'skew', 'rank', 'percentile', 'quartile', 'percentrank', 'large', 'small', 'standardize', 'ztest', 'ttest', 'ftest', 'chisq', 'norm', 'lognorm', 'expon', 'gamma', 'beta', 'binom', 'poisson', 'weibull', 'hypgeom', 'negbinom', 'geomean', 'harmean', 'devsq', 'avedev', 'confidence',
38
+ // Financial functions
39
+ 'pv', 'fv', 'pmt', 'rate', 'nper', 'irr', 'npv', 'mirr', 'xirr', 'xnpv', 'sln', 'syd', 'ddb', 'vdb', 'ipmt', 'ppmt', 'cumipmt', 'cumprinc', 'price', 'yield', 'duration', 'mduration', 'disc', 'intrate', 'received', 'accrint', 'accrintm', 'coupdaybs', 'coupdays', 'coupdaysnc', 'coupncd', 'coupnum', 'couppcd', 'tbilleq', 'tbillprice', 'tbillyield',
40
+ // Information functions
41
+ 'isblank', 'iserror', 'isna', 'isnumber', 'istext', 'islogical', 'isref', 'isformula', 'cell', 'info', 'type', 'na', 'error', 'iseven', 'isodd', 'n', 'sheet', 'sheets',
42
+ // Database functions
43
+ 'dsum', 'daverage', 'dcount', 'dcounta', 'dmax', 'dmin', 'dproduct', 'dstdev', 'dstdevp', 'dvar', 'dvarp', 'dget',
44
+ // Engineering functions
45
+ 'convert', 'bin2dec', 'bin2hex', 'bin2oct', 'dec2bin', 'dec2hex', 'dec2oct', 'hex2bin', 'hex2dec', 'hex2oct', 'oct2bin', 'oct2dec', 'oct2hex', 'complex', 'imabs', 'imaginary', 'imargument', 'imconjugate', 'imcos', 'imdiv', 'imexp', 'imln', 'imlog10', 'imlog2', 'impower', 'improduct', 'imreal', 'imsin', 'imsqrt', 'imsub', 'imsum', 'delta', 'erf', 'erfc', 'gestep', 'besseli', 'besselj', 'besselk', 'bessely',
46
+ // Array functions (newer)
47
+ 'sequence', 'randarray', 'tocol', 'torow', 'wrapcols', 'wraprows', 'take', 'drop', 'expand', 'choosecols', 'chooserows', 'vstack', 'hstack']);
48
+
49
+ // JavaScript built-in object methods and properties
50
+ var JS_BUILTIN_NAMES = new Set([
51
+ // Number methods
52
+ 'parseFloat', 'parseInt', 'isFinite', 'isInteger', 'isNaN', 'isSafeInteger', 'toExponential', 'toFixed', 'toPrecision', 'toString', 'valueOf',
53
+ // String methods
54
+ 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'endsWith', 'includes', 'indexOf', 'lastIndexOf', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'split', 'startsWith', 'substring', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim', 'trimEnd', 'trimStart', 'length',
55
+ // Array methods
56
+ 'from', 'isArray', 'of', 'concat', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'unshift', 'values',
57
+ // Object methods
58
+ 'assign', 'create', 'defineProperties', 'defineProperty', 'entries', 'freeze', 'fromEntries', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'hasOwnProperty', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'preventExtensions', 'propertyIsEnumerable', 'seal', 'setPrototypeOf', 'values',
59
+ // Global properties
60
+ 'constructor', 'prototype', '__proto__']);
61
+
62
+ // Custom reserved words from the spec
63
+ var CUSTOM_RESERVED_WORDS = new Set([
64
+ // Booleans
65
+ 'checked', 'unchecked', 'enabled', 'disabled', 'on', 'off', 'yes', 'no',
66
+ // Self referencing
67
+ 'current', 'self'
68
+
69
+ // Note: 'index' is already in EXCEL_FUNCTIONS, so it's not included here to avoid duplicate warnings
70
+ ]);
71
+
72
+ // Common JoyDoc properties (based on the codebase analysis)
73
+ var JOYDOC_PROPERTIES = new Set(['_id', 'identifier', 'name', 'type', 'files', 'fields', 'pages', 'pageOrder', 'fieldPositions', 'value', 'file', 'field', 'views', 'source', 'template', 'createdOn', 'updatedOn', 'deleted', 'width', 'height', 'rowHeight', 'cols', 'layout', 'presentation', 'padding', 'styles', 'margin', 'rowOrder', 'columnOrder', 'columns', 'rows', 'options', 'required', 'readOnly', 'disabled', 'hidden', 'validation', 'formula', 'format', 'placeholder', 'defaultValue', 'minValue', 'maxValue', 'minLength', 'maxLength', 'pattern', 'title', 'description', 'displayType', 'x', 'y', 'w', 'h']);
74
+
75
+ // Field types from FieldTypes.js
76
+ // Note: These are removed from validation because generateReadableId uses them as prefixes
77
+ // e.g., "text1", "number1", etc. The bare field type names should be allowed.
78
+
79
+ /**
80
+ * Validates if a string can be used as a JavaScript identifier
81
+ * and doesn't conflict with reserved words
82
+ *
83
+ * @param {*} readableId - The ID to validate
84
+ * @returns {boolean} - True if valid, false otherwise
85
+ */
86
+ var _default = exports["default"] = function _default(readableId, fields, tableColumns, fieldId) {
87
+ // Must be a non-empty string
88
+ if (typeof readableId !== 'string' || readableId.length === 0) {
89
+ console.warn("validateReadableId: Invalid input - expected non-empty string, got ".concat(typeof readableId === 'string' ? 'empty string' : (0, _typeof2["default"])(readableId)));
90
+ return false;
91
+ }
92
+
93
+ // Must not be parseable as a number (but check for Infinity as a special case)
94
+ if (readableId !== 'Infinity' && !isNaN(readableId)) {
95
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - cannot be a numeric value"));
96
+ return false;
97
+ }
98
+
99
+ // Must match the pattern: start with letter or underscore, followed by letters, numbers, or underscores
100
+ var identifierPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
101
+ if (!identifierPattern.test(readableId)) {
102
+ if (/^[0-9]/.test(readableId)) {
103
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - identifiers cannot start with a number"));
104
+ } else if (/[^a-zA-Z0-9_]/.test(readableId)) {
105
+ var invalidChars = readableId.match(/[^a-zA-Z0-9_]/g);
106
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - contains invalid characters: ").concat((0, _toConsumableArray2["default"])(new Set(invalidChars)).join(', ')));
107
+ } else {
108
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - must start with a letter or underscore and contain only letters, numbers, and underscores"));
109
+ }
110
+ return false;
111
+ }
112
+
113
+ // Convert to lowercase for case-insensitive checking
114
+ var lowercaseId = readableId.toLowerCase();
115
+
116
+ // Check in order of precedence:
117
+
118
+ // 1. JavaScript reserved words (highest priority)
119
+ if (JS_RESERVED_WORDS.has(readableId) || JS_RESERVED_WORDS.has(lowercaseId)) {
120
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it's a JavaScript reserved word"));
121
+ return false;
122
+ }
123
+
124
+ // 2. JavaScript built-in names
125
+ if (JS_BUILTIN_NAMES.has(readableId)) {
126
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it's a JavaScript built-in method or property"));
127
+ return false;
128
+ }
129
+
130
+ // 3. JoyDoc properties
131
+ if (JOYDOC_PROPERTIES.has(readableId)) {
132
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it's a JoyDoc property name"));
133
+ return false;
134
+ }
135
+
136
+ // 4. Custom reserved words (lower priority than JoyDoc since 'index' can be more specific)
137
+ if (CUSTOM_RESERVED_WORDS.has(lowercaseId)) {
138
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it's a reserved word (boolean/self-referencing term)"));
139
+ return false;
140
+ }
141
+
142
+ // 5. Excel functions (lowest priority since it has many general terms)
143
+ if (EXCEL_FUNCTIONS.has(lowercaseId)) {
144
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it's an Excel function name"));
145
+ return false;
146
+ }
147
+
148
+ //6. Check for duplicates in readableIds
149
+
150
+ if (Array.isArray(fields)) {
151
+ //DuplicateId check for fieldIds
152
+ var duplicateField = fields.find(function (field) {
153
+ return field._id === readableId;
154
+ });
155
+ if (duplicateField) {
156
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it already exists in fields"));
157
+ return false;
158
+ }
159
+
160
+ /**
161
+ * DuplicateId check for nested tables of collection fields
162
+ * When readableIds for nestedTables are passed in, we need the fieldId to retrieve the collection fields that houses the nestedTable
163
+ */
164
+ if (fieldId) {
165
+ var targetField = fields.find(function (field) {
166
+ return field._id === fieldId;
167
+ });
168
+ if ((targetField === null || targetField === void 0 ? void 0 : targetField.type) === _FieldTypes["default"].collection) {
169
+ var flattenedSchema = (0, _fieldHelper.flattenSchemaTree)(targetField.schema);
170
+ var readableIdExists = flattenedSchema.some(function (schema) {
171
+ return schema.id === readableId;
172
+ });
173
+ if (readableIdExists) {
174
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it already exists in fields"));
175
+ return false;
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ //DuplicateId check for columnIds
182
+ if (tableColumns && Array.isArray(tableColumns)) {
183
+ var duplicateColumnId = tableColumns.find(function (column) {
184
+ return column._id === readableId;
185
+ });
186
+ if (duplicateColumnId) {
187
+ console.warn("validateReadableId: \"".concat(readableId, "\" is invalid - it already exists in fields"));
188
+ return false;
189
+ }
190
+ }
191
+ return true;
192
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builttocreate/engine-utils",
3
- "version": "2.10.1",
3
+ "version": "2.11.0-beta.1629.0",
4
4
  "description": "Utility library for common logic shared across web and mobile",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "homepage": "https://github.com/zionlabs/engine-utils#readme",
32
32
  "devDependencies": {
33
- "@babel/cli": "^7.12.10",
33
+ "@babel/cli": "^7.27.2",
34
34
  "@babel/core": "^7.12.10",
35
35
  "@babel/plugin-proposal-class-properties": "^7.12.1",
36
36
  "@babel/plugin-transform-regenerator": "^7.12.1",