@okf/ootils 1.28.1 → 1.28.2

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.
@@ -1260,49 +1260,27 @@ declare const UI_CONTENT: {
1260
1260
  };
1261
1261
  };
1262
1262
 
1263
- interface GenCleanCamelCaseIdOptions {
1264
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1265
- stripNonAlphanumeric?: boolean;
1266
- /** Truncate result to this max length. Default: no limit */
1267
- maxLength?: number;
1268
- }
1269
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1270
-
1271
1263
  /**
1272
- * Generates a clean, deterministic contentType ID from text.
1273
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1264
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1274
1265
  *
1275
- * Idempotent: running twice produces the same output.
1266
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1267
+ * 2. Strips all non a-zA-Z0-9 from result
1268
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1269
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1270
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1271
+ * 5. Prepends 'a' if starts with digit
1272
+ * 6. Truncates to 40 chars
1276
1273
  *
1277
- * @example
1278
- * genCleanContentTypeId("My Articles!") => "myArticles"
1279
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1280
- * genCleanContentTypeId("123 Test") => "a123Test"
1281
- */
1282
- declare const genCleanContentTypeId: (text: string) => string;
1283
-
1284
- /**
1285
- * Generates a clean, short, unique ID from text input.
1286
- * Used for block valuePaths in templates.
1287
- *
1288
- * 1. Strips all characters except A-Z, a-z, 0-9
1289
- * 2. Lowercases the result
1290
- * 3. Prepends 'a' if starts with a digit
1291
- * 4. Truncates to maxLength (default 10)
1292
- * 5. Appends _{random id} (default 4 chars)
1293
- *
1294
- * Idempotent: if input already matches the output pattern
1295
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1296
- *
1297
- * @param text - The input text to convert
1298
- * @param maxLength - Max characters before the random suffix (default 10)
1299
- * @param shortIdLength - Length of the random suffix (default 4)
1274
+ * Idempotent: running twice produces the same output.
1300
1275
  *
1301
1276
  * @example
1302
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1303
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1304
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1277
+ * genCleanCamelCaseId("Author Name") => "authorName"
1278
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1279
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1280
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1281
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1282
+ * genCleanCamelCaseId("---") => "a"
1305
1283
  */
1306
- declare const genCleanValuePath: (text: string) => string;
1284
+ declare const genCleanCamelCaseId: (id: string) => string;
1307
1285
 
1308
- export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
1286
+ export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/browser.d.ts CHANGED
@@ -1260,49 +1260,27 @@ declare const UI_CONTENT: {
1260
1260
  };
1261
1261
  };
1262
1262
 
1263
- interface GenCleanCamelCaseIdOptions {
1264
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1265
- stripNonAlphanumeric?: boolean;
1266
- /** Truncate result to this max length. Default: no limit */
1267
- maxLength?: number;
1268
- }
1269
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1270
-
1271
1263
  /**
1272
- * Generates a clean, deterministic contentType ID from text.
1273
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1264
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1274
1265
  *
1275
- * Idempotent: running twice produces the same output.
1266
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1267
+ * 2. Strips all non a-zA-Z0-9 from result
1268
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1269
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1270
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1271
+ * 5. Prepends 'a' if starts with digit
1272
+ * 6. Truncates to 40 chars
1276
1273
  *
1277
- * @example
1278
- * genCleanContentTypeId("My Articles!") => "myArticles"
1279
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1280
- * genCleanContentTypeId("123 Test") => "a123Test"
1281
- */
1282
- declare const genCleanContentTypeId: (text: string) => string;
1283
-
1284
- /**
1285
- * Generates a clean, short, unique ID from text input.
1286
- * Used for block valuePaths in templates.
1287
- *
1288
- * 1. Strips all characters except A-Z, a-z, 0-9
1289
- * 2. Lowercases the result
1290
- * 3. Prepends 'a' if starts with a digit
1291
- * 4. Truncates to maxLength (default 10)
1292
- * 5. Appends _{random id} (default 4 chars)
1293
- *
1294
- * Idempotent: if input already matches the output pattern
1295
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1296
- *
1297
- * @param text - The input text to convert
1298
- * @param maxLength - Max characters before the random suffix (default 10)
1299
- * @param shortIdLength - Length of the random suffix (default 4)
1274
+ * Idempotent: running twice produces the same output.
1300
1275
  *
1301
1276
  * @example
1302
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1303
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1304
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1277
+ * genCleanCamelCaseId("Author Name") => "authorName"
1278
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1279
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1280
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1281
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1282
+ * genCleanCamelCaseId("---") => "a"
1305
1283
  */
1306
- declare const genCleanValuePath: (text: string) => string;
1284
+ declare const genCleanCamelCaseId: (id: string) => string;
1307
1285
 
1308
- export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
1286
+ export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/browser.js CHANGED
@@ -35,8 +35,6 @@ __export(browser_exports, {
35
35
  extractAllBlocksFromTpl: () => extractAllBlocksFromTpl,
36
36
  extractAndOrganizeBlocks: () => extractAndOrganizeBlocks,
37
37
  genCleanCamelCaseId: () => genCleanCamelCaseId,
38
- genCleanContentTypeId: () => genCleanContentTypeId,
39
- genCleanValuePath: () => genCleanValuePath,
40
38
  genTagId: () => genTagId,
41
39
  generateFilterKey: () => generateFilterKey,
42
40
  getFilterKeyForBlock: () => getFilterKeyForBlock,
@@ -2128,54 +2126,28 @@ var autoGenFilterConfigsFromTpl = ({
2128
2126
  };
2129
2127
 
2130
2128
  // src/utils/genCleanCamelCaseId.ts
2131
- var genCleanCamelCaseId = (id, options) => {
2129
+ var MAX_LENGTH = 40;
2130
+ function fnv1a(str, length = 8) {
2131
+ let hash = 2166136261;
2132
+ for (let i = 0; i < str.length; i++) {
2133
+ hash ^= str.charCodeAt(i);
2134
+ hash = hash * 16777619 >>> 0;
2135
+ }
2136
+ return hash.toString(36).slice(0, length);
2137
+ }
2138
+ var genCleanCamelCaseId = (id) => {
2132
2139
  if (!id || typeof id !== "string") return id;
2133
- const { stripNonAlphanumeric = false, maxLength } = options || {};
2134
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
2135
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
2136
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
2137
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
2140
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
2138
2141
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
2139
2142
  if (words.length === 0) return "a";
2140
2143
  let result = words.map((word, i) => {
2141
2144
  const lower = word.toLowerCase();
2142
2145
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
2143
2146
  }).join("");
2147
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
2148
+ result = strippedResult || fnv1a(result);
2144
2149
  if (/^\d/.test(result)) result = "a" + result;
2145
- if (stripNonAlphanumeric) {
2146
- result = result.replace(/[^a-zA-Z0-9]/g, "");
2147
- }
2148
- if (maxLength) {
2149
- result = result.slice(0, maxLength);
2150
- }
2151
- return result;
2152
- };
2153
-
2154
- // src/utils/genCleanContentTypeId.ts
2155
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
2156
-
2157
- // src/utils/genCleanValuePath.ts
2158
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
2159
- function genShortId(length = 4) {
2160
- let result = "";
2161
- for (let i = 0; i < length; i++) {
2162
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
2163
- }
2164
- return result;
2165
- }
2166
- var genCleanValuePath = (text) => {
2167
- const maxLength = 10;
2168
- const shortIdLength = 4;
2169
- if (!text || typeof text !== "string") return text;
2170
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
2171
- if (idempotencyPattern.test(text)) return text;
2172
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
2173
- if (!cleaned) {
2174
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
2175
- }
2176
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
2177
- const truncated = cleaned.slice(0, maxLength);
2178
- return `${truncated}_${genShortId(shortIdLength)}`;
2150
+ return result.slice(0, MAX_LENGTH);
2179
2151
  };
2180
2152
  // Annotate the CommonJS export names for ESM import in node:
2181
2153
  0 && (module.exports = {
@@ -2194,8 +2166,6 @@ var genCleanValuePath = (text) => {
2194
2166
  extractAllBlocksFromTpl,
2195
2167
  extractAndOrganizeBlocks,
2196
2168
  genCleanCamelCaseId,
2197
- genCleanContentTypeId,
2198
- genCleanValuePath,
2199
2169
  genTagId,
2200
2170
  generateFilterKey,
2201
2171
  getFilterKeyForBlock,
package/dist/browser.mjs CHANGED
@@ -2070,54 +2070,28 @@ var autoGenFilterConfigsFromTpl = ({
2070
2070
  };
2071
2071
 
2072
2072
  // src/utils/genCleanCamelCaseId.ts
2073
- var genCleanCamelCaseId = (id, options) => {
2073
+ var MAX_LENGTH = 40;
2074
+ function fnv1a(str, length = 8) {
2075
+ let hash = 2166136261;
2076
+ for (let i = 0; i < str.length; i++) {
2077
+ hash ^= str.charCodeAt(i);
2078
+ hash = hash * 16777619 >>> 0;
2079
+ }
2080
+ return hash.toString(36).slice(0, length);
2081
+ }
2082
+ var genCleanCamelCaseId = (id) => {
2074
2083
  if (!id || typeof id !== "string") return id;
2075
- const { stripNonAlphanumeric = false, maxLength } = options || {};
2076
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
2077
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
2078
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
2079
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
2084
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
2080
2085
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
2081
2086
  if (words.length === 0) return "a";
2082
2087
  let result = words.map((word, i) => {
2083
2088
  const lower = word.toLowerCase();
2084
2089
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
2085
2090
  }).join("");
2091
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
2092
+ result = strippedResult || fnv1a(result);
2086
2093
  if (/^\d/.test(result)) result = "a" + result;
2087
- if (stripNonAlphanumeric) {
2088
- result = result.replace(/[^a-zA-Z0-9]/g, "");
2089
- }
2090
- if (maxLength) {
2091
- result = result.slice(0, maxLength);
2092
- }
2093
- return result;
2094
- };
2095
-
2096
- // src/utils/genCleanContentTypeId.ts
2097
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
2098
-
2099
- // src/utils/genCleanValuePath.ts
2100
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
2101
- function genShortId(length = 4) {
2102
- let result = "";
2103
- for (let i = 0; i < length; i++) {
2104
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
2105
- }
2106
- return result;
2107
- }
2108
- var genCleanValuePath = (text) => {
2109
- const maxLength = 10;
2110
- const shortIdLength = 4;
2111
- if (!text || typeof text !== "string") return text;
2112
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
2113
- if (idempotencyPattern.test(text)) return text;
2114
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
2115
- if (!cleaned) {
2116
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
2117
- }
2118
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
2119
- const truncated = cleaned.slice(0, maxLength);
2120
- return `${truncated}_${genShortId(shortIdLength)}`;
2094
+ return result.slice(0, MAX_LENGTH);
2121
2095
  };
2122
2096
  export {
2123
2097
  BASE_BULLMQ_CONFIG,
@@ -2135,8 +2109,6 @@ export {
2135
2109
  extractAllBlocksFromTpl,
2136
2110
  extractAndOrganizeBlocks,
2137
2111
  genCleanCamelCaseId,
2138
- genCleanContentTypeId,
2139
- genCleanValuePath,
2140
2112
  genTagId,
2141
2113
  generateFilterKey,
2142
2114
  getFilterKeyForBlock,
package/dist/node.d.mts CHANGED
@@ -1267,50 +1267,28 @@ declare const UI_CONTENT: {
1267
1267
  };
1268
1268
  };
1269
1269
 
1270
- interface GenCleanCamelCaseIdOptions {
1271
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1272
- stripNonAlphanumeric?: boolean;
1273
- /** Truncate result to this max length. Default: no limit */
1274
- maxLength?: number;
1275
- }
1276
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1277
-
1278
1270
  /**
1279
- * Generates a clean, deterministic contentType ID from text.
1280
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1271
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1281
1272
  *
1282
- * Idempotent: running twice produces the same output.
1273
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1274
+ * 2. Strips all non a-zA-Z0-9 from result
1275
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1276
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1277
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1278
+ * 5. Prepends 'a' if starts with digit
1279
+ * 6. Truncates to 40 chars
1283
1280
  *
1284
- * @example
1285
- * genCleanContentTypeId("My Articles!") => "myArticles"
1286
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1287
- * genCleanContentTypeId("123 Test") => "a123Test"
1288
- */
1289
- declare const genCleanContentTypeId: (text: string) => string;
1290
-
1291
- /**
1292
- * Generates a clean, short, unique ID from text input.
1293
- * Used for block valuePaths in templates.
1294
- *
1295
- * 1. Strips all characters except A-Z, a-z, 0-9
1296
- * 2. Lowercases the result
1297
- * 3. Prepends 'a' if starts with a digit
1298
- * 4. Truncates to maxLength (default 10)
1299
- * 5. Appends _{random id} (default 4 chars)
1300
- *
1301
- * Idempotent: if input already matches the output pattern
1302
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1303
- *
1304
- * @param text - The input text to convert
1305
- * @param maxLength - Max characters before the random suffix (default 10)
1306
- * @param shortIdLength - Length of the random suffix (default 4)
1281
+ * Idempotent: running twice produces the same output.
1307
1282
  *
1308
1283
  * @example
1309
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1310
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1311
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1284
+ * genCleanCamelCaseId("Author Name") => "authorName"
1285
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1286
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1287
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1288
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1289
+ * genCleanCamelCaseId("---") => "a"
1312
1290
  */
1313
- declare const genCleanValuePath: (text: string) => string;
1291
+ declare const genCleanCamelCaseId: (id: string) => string;
1314
1292
 
1315
1293
  declare class MongoConnector {
1316
1294
  static getInstance(): any;
@@ -2042,4 +2020,4 @@ declare function GET_GLOBAL_BULLMQ_CONFIG({ env, redisCredentials }: {
2042
2020
  };
2043
2021
  }): Object;
2044
2022
 
2045
- export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
2023
+ export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/node.d.ts CHANGED
@@ -1267,50 +1267,28 @@ declare const UI_CONTENT: {
1267
1267
  };
1268
1268
  };
1269
1269
 
1270
- interface GenCleanCamelCaseIdOptions {
1271
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1272
- stripNonAlphanumeric?: boolean;
1273
- /** Truncate result to this max length. Default: no limit */
1274
- maxLength?: number;
1275
- }
1276
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1277
-
1278
1270
  /**
1279
- * Generates a clean, deterministic contentType ID from text.
1280
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1271
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1281
1272
  *
1282
- * Idempotent: running twice produces the same output.
1273
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1274
+ * 2. Strips all non a-zA-Z0-9 from result
1275
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1276
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1277
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1278
+ * 5. Prepends 'a' if starts with digit
1279
+ * 6. Truncates to 40 chars
1283
1280
  *
1284
- * @example
1285
- * genCleanContentTypeId("My Articles!") => "myArticles"
1286
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1287
- * genCleanContentTypeId("123 Test") => "a123Test"
1288
- */
1289
- declare const genCleanContentTypeId: (text: string) => string;
1290
-
1291
- /**
1292
- * Generates a clean, short, unique ID from text input.
1293
- * Used for block valuePaths in templates.
1294
- *
1295
- * 1. Strips all characters except A-Z, a-z, 0-9
1296
- * 2. Lowercases the result
1297
- * 3. Prepends 'a' if starts with a digit
1298
- * 4. Truncates to maxLength (default 10)
1299
- * 5. Appends _{random id} (default 4 chars)
1300
- *
1301
- * Idempotent: if input already matches the output pattern
1302
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1303
- *
1304
- * @param text - The input text to convert
1305
- * @param maxLength - Max characters before the random suffix (default 10)
1306
- * @param shortIdLength - Length of the random suffix (default 4)
1281
+ * Idempotent: running twice produces the same output.
1307
1282
  *
1308
1283
  * @example
1309
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1310
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1311
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1284
+ * genCleanCamelCaseId("Author Name") => "authorName"
1285
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1286
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1287
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1288
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1289
+ * genCleanCamelCaseId("---") => "a"
1312
1290
  */
1313
- declare const genCleanValuePath: (text: string) => string;
1291
+ declare const genCleanCamelCaseId: (id: string) => string;
1314
1292
 
1315
1293
  declare class MongoConnector {
1316
1294
  static getInstance(): any;
@@ -2042,4 +2020,4 @@ declare function GET_GLOBAL_BULLMQ_CONFIG({ env, redisCredentials }: {
2042
2020
  };
2043
2021
  }): Object;
2044
2022
 
2045
- export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
2023
+ export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/node.js CHANGED
@@ -1949,8 +1949,6 @@ __export(node_exports, {
1949
1949
  extractAllBlocksFromTpl: () => extractAllBlocksFromTpl,
1950
1950
  extractAndOrganizeBlocks: () => extractAndOrganizeBlocks,
1951
1951
  genCleanCamelCaseId: () => genCleanCamelCaseId,
1952
- genCleanContentTypeId: () => genCleanContentTypeId,
1953
- genCleanValuePath: () => genCleanValuePath,
1954
1952
  genTagId: () => genTagId,
1955
1953
  generateFilterKey: () => generateFilterKey,
1956
1954
  getAIChatModelByTenant: () => import_getModelByTenant2.getAIChatModelByTenant,
@@ -3722,54 +3720,28 @@ var autoGenFilterConfigsFromTpl = ({
3722
3720
  };
3723
3721
 
3724
3722
  // src/utils/genCleanCamelCaseId.ts
3725
- var genCleanCamelCaseId = (id, options) => {
3723
+ var MAX_LENGTH = 40;
3724
+ function fnv1a(str, length = 8) {
3725
+ let hash = 2166136261;
3726
+ for (let i = 0; i < str.length; i++) {
3727
+ hash ^= str.charCodeAt(i);
3728
+ hash = hash * 16777619 >>> 0;
3729
+ }
3730
+ return hash.toString(36).slice(0, length);
3731
+ }
3732
+ var genCleanCamelCaseId = (id) => {
3726
3733
  if (!id || typeof id !== "string") return id;
3727
- const { stripNonAlphanumeric = false, maxLength } = options || {};
3728
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
3729
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
3730
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
3731
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
3734
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
3732
3735
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
3733
3736
  if (words.length === 0) return "a";
3734
3737
  let result = words.map((word, i) => {
3735
3738
  const lower = word.toLowerCase();
3736
3739
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
3737
3740
  }).join("");
3741
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
3742
+ result = strippedResult || fnv1a(result);
3738
3743
  if (/^\d/.test(result)) result = "a" + result;
3739
- if (stripNonAlphanumeric) {
3740
- result = result.replace(/[^a-zA-Z0-9]/g, "");
3741
- }
3742
- if (maxLength) {
3743
- result = result.slice(0, maxLength);
3744
- }
3745
- return result;
3746
- };
3747
-
3748
- // src/utils/genCleanContentTypeId.ts
3749
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
3750
-
3751
- // src/utils/genCleanValuePath.ts
3752
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
3753
- function genShortId(length = 4) {
3754
- let result = "";
3755
- for (let i = 0; i < length; i++) {
3756
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
3757
- }
3758
- return result;
3759
- }
3760
- var genCleanValuePath = (text) => {
3761
- const maxLength = 10;
3762
- const shortIdLength = 4;
3763
- if (!text || typeof text !== "string") return text;
3764
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
3765
- if (idempotencyPattern.test(text)) return text;
3766
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
3767
- if (!cleaned) {
3768
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
3769
- }
3770
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
3771
- const truncated = cleaned.slice(0, maxLength);
3772
- return `${truncated}_${genShortId(shortIdLength)}`;
3744
+ return result.slice(0, MAX_LENGTH);
3773
3745
  };
3774
3746
 
3775
3747
  // src/node.ts
@@ -4189,8 +4161,6 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
4189
4161
  extractAllBlocksFromTpl,
4190
4162
  extractAndOrganizeBlocks,
4191
4163
  genCleanCamelCaseId,
4192
- genCleanContentTypeId,
4193
- genCleanValuePath,
4194
4164
  genTagId,
4195
4165
  generateFilterKey,
4196
4166
  getAIChatModelByTenant,
package/dist/node.mjs CHANGED
@@ -3663,54 +3663,28 @@ var autoGenFilterConfigsFromTpl = ({
3663
3663
  };
3664
3664
 
3665
3665
  // src/utils/genCleanCamelCaseId.ts
3666
- var genCleanCamelCaseId = (id, options) => {
3666
+ var MAX_LENGTH = 40;
3667
+ function fnv1a(str, length = 8) {
3668
+ let hash = 2166136261;
3669
+ for (let i = 0; i < str.length; i++) {
3670
+ hash ^= str.charCodeAt(i);
3671
+ hash = hash * 16777619 >>> 0;
3672
+ }
3673
+ return hash.toString(36).slice(0, length);
3674
+ }
3675
+ var genCleanCamelCaseId = (id) => {
3667
3676
  if (!id || typeof id !== "string") return id;
3668
- const { stripNonAlphanumeric = false, maxLength } = options || {};
3669
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
3670
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
3671
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
3672
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
3677
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
3673
3678
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
3674
3679
  if (words.length === 0) return "a";
3675
3680
  let result = words.map((word, i) => {
3676
3681
  const lower = word.toLowerCase();
3677
3682
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
3678
3683
  }).join("");
3684
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
3685
+ result = strippedResult || fnv1a(result);
3679
3686
  if (/^\d/.test(result)) result = "a" + result;
3680
- if (stripNonAlphanumeric) {
3681
- result = result.replace(/[^a-zA-Z0-9]/g, "");
3682
- }
3683
- if (maxLength) {
3684
- result = result.slice(0, maxLength);
3685
- }
3686
- return result;
3687
- };
3688
-
3689
- // src/utils/genCleanContentTypeId.ts
3690
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
3691
-
3692
- // src/utils/genCleanValuePath.ts
3693
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
3694
- function genShortId(length = 4) {
3695
- let result = "";
3696
- for (let i = 0; i < length; i++) {
3697
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
3698
- }
3699
- return result;
3700
- }
3701
- var genCleanValuePath = (text) => {
3702
- const maxLength = 10;
3703
- const shortIdLength = 4;
3704
- if (!text || typeof text !== "string") return text;
3705
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
3706
- if (idempotencyPattern.test(text)) return text;
3707
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
3708
- if (!cleaned) {
3709
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
3710
- }
3711
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
3712
- const truncated = cleaned.slice(0, maxLength);
3713
- return `${truncated}_${genShortId(shortIdLength)}`;
3687
+ return result.slice(0, MAX_LENGTH);
3714
3688
  };
3715
3689
 
3716
3690
  // src/node.ts
@@ -4145,8 +4119,6 @@ export {
4145
4119
  extractAllBlocksFromTpl,
4146
4120
  extractAndOrganizeBlocks,
4147
4121
  genCleanCamelCaseId,
4148
- genCleanContentTypeId,
4149
- genCleanValuePath,
4150
4122
  genTagId,
4151
4123
  generateFilterKey,
4152
4124
  export_getAIChatModelByTenant as getAIChatModelByTenant,
@@ -1260,49 +1260,27 @@ declare const UI_CONTENT: {
1260
1260
  };
1261
1261
  };
1262
1262
 
1263
- interface GenCleanCamelCaseIdOptions {
1264
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1265
- stripNonAlphanumeric?: boolean;
1266
- /** Truncate result to this max length. Default: no limit */
1267
- maxLength?: number;
1268
- }
1269
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1270
-
1271
1263
  /**
1272
- * Generates a clean, deterministic contentType ID from text.
1273
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1264
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1274
1265
  *
1275
- * Idempotent: running twice produces the same output.
1266
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1267
+ * 2. Strips all non a-zA-Z0-9 from result
1268
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1269
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1270
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1271
+ * 5. Prepends 'a' if starts with digit
1272
+ * 6. Truncates to 40 chars
1276
1273
  *
1277
- * @example
1278
- * genCleanContentTypeId("My Articles!") => "myArticles"
1279
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1280
- * genCleanContentTypeId("123 Test") => "a123Test"
1281
- */
1282
- declare const genCleanContentTypeId: (text: string) => string;
1283
-
1284
- /**
1285
- * Generates a clean, short, unique ID from text input.
1286
- * Used for block valuePaths in templates.
1287
- *
1288
- * 1. Strips all characters except A-Z, a-z, 0-9
1289
- * 2. Lowercases the result
1290
- * 3. Prepends 'a' if starts with a digit
1291
- * 4. Truncates to maxLength (default 10)
1292
- * 5. Appends _{random id} (default 4 chars)
1293
- *
1294
- * Idempotent: if input already matches the output pattern
1295
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1296
- *
1297
- * @param text - The input text to convert
1298
- * @param maxLength - Max characters before the random suffix (default 10)
1299
- * @param shortIdLength - Length of the random suffix (default 4)
1274
+ * Idempotent: running twice produces the same output.
1300
1275
  *
1301
1276
  * @example
1302
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1303
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1304
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1277
+ * genCleanCamelCaseId("Author Name") => "authorName"
1278
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1279
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1280
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1281
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1282
+ * genCleanCamelCaseId("---") => "a"
1305
1283
  */
1306
- declare const genCleanValuePath: (text: string) => string;
1284
+ declare const genCleanCamelCaseId: (id: string) => string;
1307
1285
 
1308
- export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
1286
+ export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
@@ -1260,49 +1260,27 @@ declare const UI_CONTENT: {
1260
1260
  };
1261
1261
  };
1262
1262
 
1263
- interface GenCleanCamelCaseIdOptions {
1264
- /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1265
- stripNonAlphanumeric?: boolean;
1266
- /** Truncate result to this max length. Default: no limit */
1267
- maxLength?: number;
1268
- }
1269
- declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1270
-
1271
1263
  /**
1272
- * Generates a clean, deterministic contentType ID from text.
1273
- * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1264
+ * Generates a clean, deterministic, alphanumeric camelCase ID from text.
1274
1265
  *
1275
- * Idempotent: running twice produces the same output.
1266
+ * 1. Splits on non-alphanumeric characters, builds camelCase
1267
+ * 2. Strips all non a-zA-Z0-9 from result
1268
+ * 3. If input had no unicode letters/numbers at all (e.g. "---"), returns 'a'
1269
+ * 4. If camelCase had content but nothing survived ASCII strip (pure non-Latin),
1270
+ * generates a deterministic hash (FNV-1a) of the unicode camelCase
1271
+ * 5. Prepends 'a' if starts with digit
1272
+ * 6. Truncates to 40 chars
1276
1273
  *
1277
- * @example
1278
- * genCleanContentTypeId("My Articles!") => "myArticles"
1279
- * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1280
- * genCleanContentTypeId("123 Test") => "a123Test"
1281
- */
1282
- declare const genCleanContentTypeId: (text: string) => string;
1283
-
1284
- /**
1285
- * Generates a clean, short, unique ID from text input.
1286
- * Used for block valuePaths in templates.
1287
- *
1288
- * 1. Strips all characters except A-Z, a-z, 0-9
1289
- * 2. Lowercases the result
1290
- * 3. Prepends 'a' if starts with a digit
1291
- * 4. Truncates to maxLength (default 10)
1292
- * 5. Appends _{random id} (default 4 chars)
1293
- *
1294
- * Idempotent: if input already matches the output pattern
1295
- * (lowercase alphanumeric + _hash suffix), returns as-is.
1296
- *
1297
- * @param text - The input text to convert
1298
- * @param maxLength - Max characters before the random suffix (default 10)
1299
- * @param shortIdLength - Length of the random suffix (default 4)
1274
+ * Idempotent: running twice produces the same output.
1300
1275
  *
1301
1276
  * @example
1302
- * genCleanValuePath("Author Name") => "authorname_x7k2"
1303
- * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1304
- * genCleanValuePath("123 Test") => "a123test_ab4d"
1277
+ * genCleanCamelCaseId("Author Name") => "authorName"
1278
+ * genCleanCamelCaseId("authorName") => "authorName" (idempotent)
1279
+ * genCleanCamelCaseId("123 Test") => "a123Test"
1280
+ * genCleanCamelCaseId("café latté") => "cafLatt"
1281
+ * genCleanCamelCaseId("مرحبا") => deterministic hash
1282
+ * genCleanCamelCaseId("---") => "a"
1305
1283
  */
1306
- declare const genCleanValuePath: (text: string) => string;
1284
+ declare const genCleanCamelCaseId: (id: string) => string;
1307
1285
 
1308
- export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
1286
+ export { BASE_BULLMQ_CONFIG, FILTER_IDS, TEMP_removeDuplicateFilters, UI_CONTENT, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genTagId, generateFilterKey, getFilterKeyForBlock, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/universal.js CHANGED
@@ -35,8 +35,6 @@ __export(universal_exports, {
35
35
  extractAllBlocksFromTpl: () => extractAllBlocksFromTpl,
36
36
  extractAndOrganizeBlocks: () => extractAndOrganizeBlocks,
37
37
  genCleanCamelCaseId: () => genCleanCamelCaseId,
38
- genCleanContentTypeId: () => genCleanContentTypeId,
39
- genCleanValuePath: () => genCleanValuePath,
40
38
  genTagId: () => genTagId,
41
39
  generateFilterKey: () => generateFilterKey,
42
40
  getFilterKeyForBlock: () => getFilterKeyForBlock,
@@ -2128,54 +2126,28 @@ var autoGenFilterConfigsFromTpl = ({
2128
2126
  };
2129
2127
 
2130
2128
  // src/utils/genCleanCamelCaseId.ts
2131
- var genCleanCamelCaseId = (id, options) => {
2129
+ var MAX_LENGTH = 40;
2130
+ function fnv1a(str, length = 8) {
2131
+ let hash = 2166136261;
2132
+ for (let i = 0; i < str.length; i++) {
2133
+ hash ^= str.charCodeAt(i);
2134
+ hash = hash * 16777619 >>> 0;
2135
+ }
2136
+ return hash.toString(36).slice(0, length);
2137
+ }
2138
+ var genCleanCamelCaseId = (id) => {
2132
2139
  if (!id || typeof id !== "string") return id;
2133
- const { stripNonAlphanumeric = false, maxLength } = options || {};
2134
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
2135
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
2136
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
2137
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
2140
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
2138
2141
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
2139
2142
  if (words.length === 0) return "a";
2140
2143
  let result = words.map((word, i) => {
2141
2144
  const lower = word.toLowerCase();
2142
2145
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
2143
2146
  }).join("");
2147
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
2148
+ result = strippedResult || fnv1a(result);
2144
2149
  if (/^\d/.test(result)) result = "a" + result;
2145
- if (stripNonAlphanumeric) {
2146
- result = result.replace(/[^a-zA-Z0-9]/g, "");
2147
- }
2148
- if (maxLength) {
2149
- result = result.slice(0, maxLength);
2150
- }
2151
- return result;
2152
- };
2153
-
2154
- // src/utils/genCleanContentTypeId.ts
2155
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
2156
-
2157
- // src/utils/genCleanValuePath.ts
2158
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
2159
- function genShortId(length = 4) {
2160
- let result = "";
2161
- for (let i = 0; i < length; i++) {
2162
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
2163
- }
2164
- return result;
2165
- }
2166
- var genCleanValuePath = (text) => {
2167
- const maxLength = 10;
2168
- const shortIdLength = 4;
2169
- if (!text || typeof text !== "string") return text;
2170
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
2171
- if (idempotencyPattern.test(text)) return text;
2172
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
2173
- if (!cleaned) {
2174
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
2175
- }
2176
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
2177
- const truncated = cleaned.slice(0, maxLength);
2178
- return `${truncated}_${genShortId(shortIdLength)}`;
2150
+ return result.slice(0, MAX_LENGTH);
2179
2151
  };
2180
2152
  // Annotate the CommonJS export names for ESM import in node:
2181
2153
  0 && (module.exports = {
@@ -2194,8 +2166,6 @@ var genCleanValuePath = (text) => {
2194
2166
  extractAllBlocksFromTpl,
2195
2167
  extractAndOrganizeBlocks,
2196
2168
  genCleanCamelCaseId,
2197
- genCleanContentTypeId,
2198
- genCleanValuePath,
2199
2169
  genTagId,
2200
2170
  generateFilterKey,
2201
2171
  getFilterKeyForBlock,
@@ -2070,54 +2070,28 @@ var autoGenFilterConfigsFromTpl = ({
2070
2070
  };
2071
2071
 
2072
2072
  // src/utils/genCleanCamelCaseId.ts
2073
- var genCleanCamelCaseId = (id, options) => {
2073
+ var MAX_LENGTH = 40;
2074
+ function fnv1a(str, length = 8) {
2075
+ let hash = 2166136261;
2076
+ for (let i = 0; i < str.length; i++) {
2077
+ hash ^= str.charCodeAt(i);
2078
+ hash = hash * 16777619 >>> 0;
2079
+ }
2080
+ return hash.toString(36).slice(0, length);
2081
+ }
2082
+ var genCleanCamelCaseId = (id) => {
2074
2083
  if (!id || typeof id !== "string") return id;
2075
- const { stripNonAlphanumeric = false, maxLength } = options || {};
2076
- const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
2077
- const withinMaxLength = maxLength ? id.length <= maxLength : true;
2078
- const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
2079
- if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
2084
+ if ((/^[a-z][a-zA-Z0-9]*$/.test(id) || /^a\d[a-zA-Z0-9]*$/.test(id)) && id.length <= MAX_LENGTH) return id;
2080
2085
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
2081
2086
  if (words.length === 0) return "a";
2082
2087
  let result = words.map((word, i) => {
2083
2088
  const lower = word.toLowerCase();
2084
2089
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
2085
2090
  }).join("");
2091
+ const strippedResult = result.replace(/[^a-zA-Z0-9]/g, "");
2092
+ result = strippedResult || fnv1a(result);
2086
2093
  if (/^\d/.test(result)) result = "a" + result;
2087
- if (stripNonAlphanumeric) {
2088
- result = result.replace(/[^a-zA-Z0-9]/g, "");
2089
- }
2090
- if (maxLength) {
2091
- result = result.slice(0, maxLength);
2092
- }
2093
- return result;
2094
- };
2095
-
2096
- // src/utils/genCleanContentTypeId.ts
2097
- var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
2098
-
2099
- // src/utils/genCleanValuePath.ts
2100
- var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
2101
- function genShortId(length = 4) {
2102
- let result = "";
2103
- for (let i = 0; i < length; i++) {
2104
- result += CHARS[Math.floor(Math.random() * CHARS.length)];
2105
- }
2106
- return result;
2107
- }
2108
- var genCleanValuePath = (text) => {
2109
- const maxLength = 10;
2110
- const shortIdLength = 4;
2111
- if (!text || typeof text !== "string") return text;
2112
- const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
2113
- if (idempotencyPattern.test(text)) return text;
2114
- let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
2115
- if (!cleaned) {
2116
- throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
2117
- }
2118
- if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
2119
- const truncated = cleaned.slice(0, maxLength);
2120
- return `${truncated}_${genShortId(shortIdLength)}`;
2094
+ return result.slice(0, MAX_LENGTH);
2121
2095
  };
2122
2096
  export {
2123
2097
  BASE_BULLMQ_CONFIG,
@@ -2135,8 +2109,6 @@ export {
2135
2109
  extractAllBlocksFromTpl,
2136
2110
  extractAndOrganizeBlocks,
2137
2111
  genCleanCamelCaseId,
2138
- genCleanContentTypeId,
2139
- genCleanValuePath,
2140
2112
  genTagId,
2141
2113
  generateFilterKey,
2142
2114
  getFilterKeyForBlock,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.28.1",
6
+ "version": "1.28.2",
7
7
  "description": "Utility functions for both browser and Node.js",
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/index.mjs",