@cldmv/slothlet 2.11.0 → 3.0.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.
Files changed (189) hide show
  1. package/AGENT-USAGE.md +355 -325
  2. package/README.md +554 -238
  3. package/dist/lib/builders/api-assignment.mjs +605 -0
  4. package/dist/lib/builders/api_builder.mjs +1073 -0
  5. package/dist/lib/builders/builder.mjs +94 -0
  6. package/dist/lib/builders/modes-processor.mjs +1816 -0
  7. package/dist/lib/errors.mjs +227 -0
  8. package/dist/lib/factories/component-base.mjs +96 -0
  9. package/dist/lib/factories/context.mjs +38 -0
  10. package/dist/lib/handlers/api-cache-manager.mjs +216 -0
  11. package/dist/lib/handlers/api-manager.mjs +2364 -0
  12. package/dist/lib/handlers/context-async.mjs +184 -0
  13. package/dist/lib/handlers/context-live.mjs +184 -0
  14. package/dist/lib/handlers/hook-manager.mjs +789 -0
  15. package/dist/lib/handlers/lifecycle-token.mjs +44 -0
  16. package/dist/lib/handlers/lifecycle.mjs +131 -0
  17. package/dist/lib/handlers/materialize-manager.mjs +64 -0
  18. package/dist/lib/handlers/metadata.mjs +500 -0
  19. package/dist/lib/handlers/ownership.mjs +338 -0
  20. package/dist/lib/handlers/unified-wrapper.mjs +3031 -0
  21. package/dist/lib/helpers/class-instance-wrapper.mjs +125 -0
  22. package/dist/lib/helpers/config.mjs +343 -0
  23. package/dist/lib/helpers/eventemitter-context.mjs +365 -0
  24. package/dist/lib/helpers/hint-detector.mjs +63 -0
  25. package/dist/lib/helpers/modes-utils.mjs +53 -0
  26. package/dist/lib/helpers/resolve-from-caller.mjs +119 -116
  27. package/dist/lib/helpers/sanitize.mjs +247 -168
  28. package/dist/lib/helpers/utilities.mjs +46 -81
  29. package/dist/lib/i18n/languages/de-de.json +377 -0
  30. package/dist/lib/i18n/languages/en-gb.json +377 -0
  31. package/dist/lib/i18n/languages/en-us.json +377 -0
  32. package/dist/lib/i18n/languages/es-mx.json +377 -0
  33. package/dist/lib/i18n/languages/fr-fr.json +377 -0
  34. package/dist/lib/i18n/languages/hi-in.json +377 -0
  35. package/dist/lib/i18n/languages/ja-jp.json +377 -0
  36. package/dist/lib/i18n/languages/ko-kr.json +377 -0
  37. package/dist/lib/i18n/languages/pt-br.json +377 -0
  38. package/dist/lib/i18n/languages/ru-ru.json +377 -0
  39. package/dist/lib/i18n/languages/zh-cn.json +377 -0
  40. package/dist/lib/i18n/translations.mjs +140 -0
  41. package/dist/lib/modes/eager.mjs +75 -0
  42. package/dist/lib/modes/lazy.mjs +97 -0
  43. package/dist/lib/processors/flatten.mjs +453 -0
  44. package/dist/lib/processors/loader.mjs +355 -0
  45. package/dist/lib/processors/type-generator.mjs +291 -0
  46. package/dist/lib/processors/typescript.mjs +188 -0
  47. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +80 -522
  48. package/dist/lib/runtime/runtime-livebindings.mjs +45 -390
  49. package/dist/lib/runtime/runtime.mjs +39 -159
  50. package/dist/slothlet.mjs +525 -744
  51. package/docs/API-RULES.md +338 -486
  52. package/index.cjs +4 -4
  53. package/index.mjs +82 -45
  54. package/package.json +138 -25
  55. package/types/dist/lib/builders/api-assignment.d.mts +97 -0
  56. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -0
  57. package/types/dist/lib/builders/api_builder.d.mts +96 -0
  58. package/types/dist/lib/builders/api_builder.d.mts.map +1 -0
  59. package/types/dist/lib/builders/builder.d.mts +60 -0
  60. package/types/dist/lib/builders/builder.d.mts.map +1 -0
  61. package/types/dist/lib/builders/modes-processor.d.mts +32 -0
  62. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -0
  63. package/types/dist/lib/errors.d.mts +118 -0
  64. package/types/dist/lib/errors.d.mts.map +1 -0
  65. package/types/dist/lib/factories/component-base.d.mts +182 -0
  66. package/types/dist/lib/factories/component-base.d.mts.map +1 -0
  67. package/types/dist/lib/factories/context.d.mts +26 -0
  68. package/types/dist/lib/factories/context.d.mts.map +1 -0
  69. package/types/dist/lib/handlers/api-cache-manager.d.mts +208 -0
  70. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -0
  71. package/types/dist/lib/handlers/api-manager.d.mts +392 -0
  72. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -0
  73. package/types/dist/lib/handlers/context-async.d.mts +66 -0
  74. package/types/dist/lib/handlers/context-async.d.mts.map +1 -0
  75. package/types/dist/lib/handlers/context-live.d.mts +65 -0
  76. package/types/dist/lib/handlers/context-live.d.mts.map +1 -0
  77. package/types/dist/lib/handlers/hook-manager.d.mts +199 -0
  78. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -0
  79. package/types/dist/lib/handlers/lifecycle-token.d.mts +49 -0
  80. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -0
  81. package/types/dist/lib/handlers/lifecycle.d.mts +90 -0
  82. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -0
  83. package/types/dist/lib/handlers/materialize-manager.d.mts +75 -0
  84. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -0
  85. package/types/dist/lib/handlers/metadata.d.mts +215 -0
  86. package/types/dist/lib/handlers/metadata.d.mts.map +1 -0
  87. package/types/dist/lib/handlers/ownership.d.mts +170 -0
  88. package/types/dist/lib/handlers/ownership.d.mts.map +1 -0
  89. package/types/dist/lib/handlers/unified-wrapper.d.mts +250 -0
  90. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -0
  91. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +54 -0
  92. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -0
  93. package/types/dist/lib/helpers/config.d.mts +96 -0
  94. package/types/dist/lib/helpers/config.d.mts.map +1 -0
  95. package/types/dist/lib/helpers/eventemitter-context.d.mts +31 -0
  96. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -0
  97. package/types/dist/lib/helpers/hint-detector.d.mts +20 -0
  98. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -0
  99. package/types/dist/lib/helpers/modes-utils.d.mts +35 -0
  100. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -0
  101. package/types/dist/lib/helpers/resolve-from-caller.d.mts +29 -145
  102. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  103. package/types/dist/lib/helpers/sanitize.d.mts +95 -94
  104. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  105. package/types/dist/lib/helpers/utilities.d.mts +53 -116
  106. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  107. package/types/dist/lib/i18n/translations.d.mts +39 -0
  108. package/types/dist/lib/i18n/translations.d.mts.map +1 -0
  109. package/types/dist/lib/modes/eager.d.mts +36 -0
  110. package/types/dist/lib/modes/eager.d.mts.map +1 -0
  111. package/types/dist/lib/modes/lazy.d.mts +49 -0
  112. package/types/dist/lib/modes/lazy.d.mts.map +1 -0
  113. package/types/dist/lib/processors/flatten.d.mts +114 -0
  114. package/types/dist/lib/processors/flatten.d.mts.map +1 -0
  115. package/types/dist/lib/processors/loader.d.mts +47 -0
  116. package/types/dist/lib/processors/loader.d.mts.map +1 -0
  117. package/types/dist/lib/processors/type-generator.d.mts +19 -0
  118. package/types/dist/lib/processors/type-generator.d.mts.map +1 -0
  119. package/types/dist/lib/processors/typescript.d.mts +55 -0
  120. package/types/dist/lib/processors/typescript.d.mts.map +1 -0
  121. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +47 -42
  122. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  123. package/types/dist/lib/runtime/runtime-livebindings.d.mts +34 -65
  124. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  125. package/types/dist/lib/runtime/runtime.d.mts +39 -9
  126. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  127. package/types/dist/slothlet.d.mts +184 -111
  128. package/types/dist/slothlet.d.mts.map +1 -1
  129. package/types/index.d.mts +1 -3
  130. package/dist/lib/engine/README.md +0 -21
  131. package/dist/lib/engine/slothlet_child.mjs +0 -59
  132. package/dist/lib/engine/slothlet_engine.mjs +0 -372
  133. package/dist/lib/engine/slothlet_esm.mjs +0 -230
  134. package/dist/lib/engine/slothlet_helpers.mjs +0 -455
  135. package/dist/lib/engine/slothlet_worker.mjs +0 -149
  136. package/dist/lib/helpers/als-eventemitter.mjs +0 -256
  137. package/dist/lib/helpers/api_builder/add_api.mjs +0 -553
  138. package/dist/lib/helpers/api_builder/analysis.mjs +0 -532
  139. package/dist/lib/helpers/api_builder/construction.mjs +0 -495
  140. package/dist/lib/helpers/api_builder/decisions.mjs +0 -748
  141. package/dist/lib/helpers/api_builder/metadata.mjs +0 -248
  142. package/dist/lib/helpers/api_builder.mjs +0 -41
  143. package/dist/lib/helpers/auto-wrap.mjs +0 -62
  144. package/dist/lib/helpers/hooks.mjs +0 -389
  145. package/dist/lib/helpers/instance-manager.mjs +0 -111
  146. package/dist/lib/helpers/metadata-api.mjs +0 -201
  147. package/dist/lib/helpers/multidefault.mjs +0 -216
  148. package/dist/lib/modes/slothlet_eager.mjs +0 -154
  149. package/dist/lib/modes/slothlet_lazy.mjs +0 -594
  150. package/docs/API-RULES-CONDITIONS.md +0 -712
  151. package/types/dist/lib/engine/slothlet_child.d.mts +0 -2
  152. package/types/dist/lib/engine/slothlet_child.d.mts.map +0 -1
  153. package/types/dist/lib/engine/slothlet_engine.d.mts +0 -31
  154. package/types/dist/lib/engine/slothlet_engine.d.mts.map +0 -1
  155. package/types/dist/lib/engine/slothlet_esm.d.mts +0 -19
  156. package/types/dist/lib/engine/slothlet_esm.d.mts.map +0 -1
  157. package/types/dist/lib/engine/slothlet_helpers.d.mts +0 -25
  158. package/types/dist/lib/engine/slothlet_helpers.d.mts.map +0 -1
  159. package/types/dist/lib/engine/slothlet_worker.d.mts +0 -2
  160. package/types/dist/lib/engine/slothlet_worker.d.mts.map +0 -1
  161. package/types/dist/lib/helpers/als-eventemitter.d.mts +0 -56
  162. package/types/dist/lib/helpers/als-eventemitter.d.mts.map +0 -1
  163. package/types/dist/lib/helpers/api_builder/add_api.d.mts +0 -102
  164. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +0 -1
  165. package/types/dist/lib/helpers/api_builder/analysis.d.mts +0 -189
  166. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +0 -1
  167. package/types/dist/lib/helpers/api_builder/construction.d.mts +0 -107
  168. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +0 -1
  169. package/types/dist/lib/helpers/api_builder/decisions.d.mts +0 -213
  170. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +0 -1
  171. package/types/dist/lib/helpers/api_builder/metadata.d.mts +0 -99
  172. package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +0 -1
  173. package/types/dist/lib/helpers/api_builder.d.mts +0 -6
  174. package/types/dist/lib/helpers/api_builder.d.mts.map +0 -1
  175. package/types/dist/lib/helpers/auto-wrap.d.mts +0 -49
  176. package/types/dist/lib/helpers/auto-wrap.d.mts.map +0 -1
  177. package/types/dist/lib/helpers/hooks.d.mts +0 -342
  178. package/types/dist/lib/helpers/hooks.d.mts.map +0 -1
  179. package/types/dist/lib/helpers/instance-manager.d.mts +0 -41
  180. package/types/dist/lib/helpers/instance-manager.d.mts.map +0 -1
  181. package/types/dist/lib/helpers/metadata-api.d.mts +0 -132
  182. package/types/dist/lib/helpers/metadata-api.d.mts.map +0 -1
  183. package/types/dist/lib/helpers/multidefault.d.mts +0 -90
  184. package/types/dist/lib/helpers/multidefault.d.mts.map +0 -1
  185. package/types/dist/lib/modes/slothlet_eager.d.mts +0 -65
  186. package/types/dist/lib/modes/slothlet_eager.d.mts.map +0 -1
  187. package/types/dist/lib/modes/slothlet_lazy.d.mts +0 -31
  188. package/types/dist/lib/modes/slothlet_lazy.d.mts.map +0 -1
  189. package/types/index.d.mts.map +0 -1
@@ -18,260 +18,339 @@
18
18
 
19
19
 
20
20
 
21
+ import { ComponentBase } from "@cldmv/slothlet/factories/component-base";
21
22
 
22
- function globToRegex(pattern, caseSensitive = true) {
23
- try {
23
+
24
+
25
+
26
+
27
+
28
+ export class Sanitize extends ComponentBase {
29
+ static slothletProperty = "sanitize";
30
+
31
+
32
+
33
+
34
+
35
+
36
+ #compileGlobPattern(pattern, caseSensitive = true) {
24
37
 
25
38
  if (pattern.startsWith("**") && pattern.endsWith("**") && pattern.length > 4) {
26
39
  const innerString = pattern.slice(2, -2);
27
-
28
40
  const escapedString = innerString.replace(/[.+^${}()|[\]\\*?]/g, "\\$&");
29
-
30
-
31
41
  const flags = caseSensitive ? "" : "i";
32
42
  return new RegExp(`(?<=.)${escapedString}(?=.)`, flags);
33
43
  }
34
44
 
35
45
 
36
-
37
- let regexPattern = pattern
46
+ const regexPattern = pattern
38
47
  .replace(/[.+^${}()|[\]\\]/g, "\\$&")
39
48
  .replace(/\*/g, ".*")
40
49
  .replace(/\?/g, ".");
41
50
 
42
51
  const flags = caseSensitive ? "" : "i";
43
52
  return new RegExp(`^${regexPattern}$`, flags);
44
- } catch (_) {
45
- return null;
46
53
  }
47
- }
48
-
49
-
50
- export function sanitizePathName(input, opts = {}) {
51
- const { lowerFirst = true, preserveAllUpper = false, preserveAllLower = false, rules = {} } = opts;
52
54
 
53
- const leaveRules = (rules.leave || []).map((s) => String(s));
54
- const leaveInsensitiveRules = (rules.leaveInsensitive || []).map((s) => String(s));
55
- const upperRules = (rules.upper || []).map((s) => String(s));
56
- const lowerRules = (rules.lower || []).map((s) => String(s));
55
+
56
+ #matchesAnyPattern(input, patterns, caseSensitive = false) {
57
+ for (const pattern of patterns) {
58
+ if (pattern.includes("*") || pattern.includes("?")) {
59
+ const regex = this.#compileGlobPattern(pattern, caseSensitive);
60
+ if (regex && regex.test(input)) return true;
61
+ } else {
62
+ const match = caseSensitive ? input === pattern : input.toLowerCase() === pattern.toLowerCase();
63
+ if (match) return true;
64
+ }
65
+ }
66
+ return false;
67
+ }
57
68
 
58
- let s = String(input).trim();
69
+
70
+ #extractPatternLiterals(pattern) {
71
+ return pattern.split(/[*?]+/).filter(Boolean);
72
+ }
59
73
 
60
74
 
61
75
 
62
-
63
76
 
64
- let parts = s.split(/[^A-Za-z0-9_$]+/).filter(Boolean);
65
- if (parts.length === 0) return "_";
66
77
 
67
78
 
68
- while (parts.length && !/^[A-Za-z_$]/.test(parts[0][0])) {
69
- parts[0] = parts[0].replace(/^[^A-Za-z_$]+/, "");
70
- if (!parts[0]) parts.shift();
71
- }
72
- if (parts.length === 0) return "_";
79
+ #applySegmentRules(segment, index, originalString, config) {
80
+ const { preserveAllUpper, preserveAllLower, leaveRules, leaveInsensitiveRules, upperRules, lowerRules } = config;
73
81
 
74
-
75
- const segmentMatchesPreSplitPattern = (segment, patterns, caseSensitive = false) => {
76
- for (const pattern of patterns) {
77
-
78
-
79
-
82
+
83
+ if (this.#matchesAnyPattern(segment, leaveRules, true)) {
84
+ return segment;
85
+ }
86
+
87
+
88
+ if (this.#matchesAnyPattern(segment, leaveInsensitiveRules, false)) {
89
+ return segment;
90
+ }
80
91
 
92
+
93
+
94
+ if (preserveAllUpper && segment === segment.toUpperCase() && segment !== segment.toLowerCase() && /[A-Z]/.test(segment)) {
95
+ return segment;
96
+ }
97
+
98
+
99
+
100
+ if (preserveAllLower && segment === segment.toLowerCase() && segment !== segment.toUpperCase() && /[a-z]/.test(segment)) {
101
+ return segment;
102
+ }
103
+
104
+
105
+
106
+ for (const pattern of [...upperRules, ...lowerRules]) {
81
107
  if (pattern.includes("*") || pattern.includes("?")) {
82
- const regex = globToRegex(pattern, caseSensitive);
83
- if (regex && regex.test(s)) {
108
+ const regex = this.#compileGlobPattern(pattern, false);
109
+ if (regex && regex.test(originalString)) {
84
110
 
85
-
86
- const literalParts = pattern.split(/[*?]+/).filter(Boolean);
87
-
88
- for (const literal of literalParts) {
111
+ const literals = this.#extractPatternLiterals(pattern);
112
+ for (const literal of literals) {
89
113
 
90
- const cleanLiteral = literal.replace(/[^A-Za-z0-9_$]/g, "");
91
- if (cleanLiteral) {
92
- const match = caseSensitive ? segment === cleanLiteral : segment.toLowerCase() === cleanLiteral.toLowerCase();
93
- if (match) {
94
- return true;
95
- }
114
+ const cleanLiteral = literal.replace(/[^A-Za-z0-9_$]/g, "").replace(/^_+|_+$/g, "");
115
+ if (cleanLiteral && segment.toLowerCase() === cleanLiteral.toLowerCase()) {
116
+ return upperRules.includes(pattern) ? segment.toUpperCase() : segment.toLowerCase();
96
117
  }
97
118
  }
98
119
  }
99
120
  } else {
100
121
 
101
- const match = caseSensitive ? segment === pattern : segment.toLowerCase() === pattern.toLowerCase();
102
- if (match) {
103
- return true;
122
+ if (segment.toLowerCase() === pattern.toLowerCase()) {
123
+ return upperRules.includes(pattern) ? segment.toUpperCase() : segment.toLowerCase();
104
124
  }
105
125
  }
106
126
  }
107
- return false;
108
- };
109
127
 
110
- const applyRule = (seg, index) => {
111
128
 
112
- if (segmentMatchesPreSplitPattern(seg, leaveRules, true)) {
113
- return seg;
129
+ let transformed = this.#applyWithinSegmentPatterns(segment, upperRules, lowerRules);
130
+ if (transformed !== segment) {
131
+ return transformed;
114
132
  }
115
133
 
116
134
 
117
- if (segmentMatchesPreSplitPattern(seg, leaveInsensitiveRules, false)) {
118
- return seg;
119
- }
120
-
121
135
 
122
- if (preserveAllUpper && seg === seg.toUpperCase() && seg !== seg.toLowerCase() && /[A-Z]/.test(seg)) {
123
- return seg;
124
- }
136
+ return segment;
137
+ }
138
+
139
+
140
+ #applyWithinSegmentPatterns(segment, upperRules, lowerRules) {
141
+ let result = segment;
142
+
143
+ const applyBoundaryPattern = (pattern, toUpper) => {
144
+
145
+ if (pattern.startsWith("**") && pattern.endsWith("**") && pattern.length > 4) {
146
+ const innerString = pattern.slice(2, -2);
147
+ const innerRegex = new RegExp(innerString.replace(/[.+^${}()|[\]\\*?]/g, "\\$&"), "gi");
148
+ const matches = [...result.matchAll(innerRegex)];
149
+
150
+ for (const match of matches) {
151
+ const startPos = match.index;
152
+ const endPos = startPos + match[0].length;
153
+ const hasCharBefore = startPos > 0;
154
+ const hasCharAfter = endPos < result.length;
155
+
156
+ if (hasCharBefore && hasCharAfter) {
157
+ const replacement = toUpper ? innerString.toUpperCase() : innerString.toLowerCase();
158
+ result = result.substring(0, startPos) + replacement + result.substring(endPos);
159
+ break;
160
+ }
161
+ }
162
+ }
163
+
164
+ else if (pattern.includes("*") && !pattern.startsWith("**")) {
165
+
166
+ const literalParts = pattern.split("*").filter(Boolean);
167
+ for (const literal of literalParts) {
168
+ const literalRegex = new RegExp(literal.replace(/[.+^${}()|[\]\\]/g, "\\$&"), "gi");
169
+ const replacement = toUpper ? literal.toUpperCase() : literal.toLowerCase();
170
+ result = result.replace(literalRegex, replacement);
171
+ }
172
+ }
173
+ };
174
+
175
+ upperRules.forEach((pattern) => applyBoundaryPattern(pattern, true));
176
+ lowerRules.forEach((pattern) => applyBoundaryPattern(pattern, false));
177
+
178
+ return result;
179
+ }
180
+
181
+
182
+
183
+
184
+
185
+
186
+ sanitizePropertyName(input, options = {}) {
187
+ const { lowerFirst = true, preserveAllUpper = false, preserveAllLower = false, rules = {} } = options;
125
188
 
126
189
 
127
- if (preserveAllLower && seg === seg.toLowerCase() && seg !== seg.toUpperCase() && /[a-z]/.test(seg)) {
128
- return seg;
129
- }
190
+ const leaveRules = (rules.leave || []).map((s) => String(s));
191
+ const leaveInsensitiveRules = (rules.leaveInsensitive || []).map((s) => String(s));
192
+ const upperRules = (rules.upper || []).map((s) => String(s));
193
+ const lowerRules = (rules.lower || []).map((s) => String(s));
194
+
195
+ const originalString = String(input).trim();
130
196
 
131
197
 
132
- if (segmentMatchesPreSplitPattern(seg, upperRules, false)) {
133
- return seg.toUpperCase();
134
- }
198
+ const isAllUpper =
199
+ originalString === originalString.toUpperCase() && originalString !== originalString.toLowerCase() && /[A-Z]/.test(originalString);
200
+ const isAllLower =
201
+ originalString === originalString.toLowerCase() && originalString !== originalString.toUpperCase() && /[a-z]/.test(originalString);
135
202
 
203
+ if (preserveAllUpper && isAllUpper) {
204
+ return originalString;
205
+ }
206
+
136
207
 
137
- if (segmentMatchesPreSplitPattern(seg, lowerRules, false)) {
138
- return seg.toLowerCase();
208
+ if (preserveAllLower && isAllLower && !/-/.test(originalString)) {
209
+ return originalString;
139
210
  }
140
211
 
141
212
 
142
- let transformedSeg = seg;
143
-
144
213
 
145
- for (const pattern of upperRules) {
146
- if (pattern.includes("*") || pattern.includes("?")) {
147
-
148
- if (!segmentMatchesPreSplitPattern(seg, [pattern], false)) {
149
-
150
- if (pattern.startsWith("**") && pattern.endsWith("**") && pattern.length > 4) {
151
- const innerString = pattern.slice(2, -2);
152
-
153
-
154
- const innerRegex = new RegExp(innerString.replace(/[.+^${}()|[\]\\*?]/g, "\\$&"), "gi");
155
-
156
-
157
- const matches = [...transformedSeg.matchAll(innerRegex)];
158
- for (const match of matches) {
159
- const startPos = match.index;
160
- const endPos = startPos + match[0].length;
214
+ let primarySegments = originalString.split(/[-]+|[^A-Za-z0-9_$]+/).filter(Boolean);
161
215
 
162
-
163
- const hasCharBefore = startPos > 0;
164
- const hasCharAfter = endPos < transformedSeg.length;
216
+
217
+ if (primarySegments.length === 0) return "_";
165
218
 
166
- if (hasCharBefore && hasCharAfter) {
167
-
168
- transformedSeg = transformedSeg.substring(0, startPos) + innerString.toUpperCase() + transformedSeg.substring(endPos);
169
- break;
170
- }
171
- }
172
- } else {
173
-
174
- const literalParts = pattern.split(/[*?]+/).filter(Boolean);
175
- for (const literal of literalParts) {
176
- if (literal) {
177
-
178
- const literalRegex = new RegExp(literal.replace(/[.+^${}()|[\]\\]/g, "\\$&"), "gi");
179
- transformedSeg = transformedSeg.replace(literalRegex, literal.toUpperCase());
180
- }
181
- }
182
- }
183
- }
184
- }
219
+
220
+ while (primarySegments.length && !/^[A-Za-z_$]/.test(primarySegments[0][0])) {
221
+ primarySegments[0] = primarySegments[0].replace(/^[^A-Za-z_$]+/, "");
222
+ if (!primarySegments[0]) primarySegments.shift();
185
223
  }
224
+ if (primarySegments.length === 0) return "_";
186
225
 
187
226
 
188
- for (const pattern of lowerRules) {
189
- if (pattern.includes("*") || pattern.includes("?")) {
227
+
228
+
229
+
230
+
231
+ const lowerRuleApplied = [];
232
+ const processedPrimarySegments = primarySegments.map((primarySeg, primaryIdx) => {
233
+
234
+ const parts = primarySeg.split(/(_+)/);
235
+
236
+
237
+ const processedParts = parts.map((part, partIdx) => {
190
238
 
191
- if (!segmentMatchesPreSplitPattern(seg, [pattern], false)) {
192
-
193
- if (pattern.startsWith("**") && pattern.endsWith("**") && pattern.length > 4) {
194
- const innerString = pattern.slice(2, -2);
195
-
196
-
197
- const innerRegex = new RegExp(innerString.replace(/[.+^${}()|[\]\\*?]/g, "\\$&"), "gi");
239
+ if (partIdx % 2 === 1) return part;
240
+ if (!part) return part;
198
241
 
199
-
200
- const matches = [...transformedSeg.matchAll(innerRegex)];
201
- for (const match of matches) {
202
- const startPos = match.index;
203
- const endPos = startPos + match[0].length;
242
+
243
+ const cleanSeg = part.replace(/[^A-Za-z0-9_$]/g, "");
204
244
 
205
-
206
- const hasCharBefore = startPos > 0;
207
- const hasCharAfter = endPos < transformedSeg.length;
245
+
246
+ const config = { preserveAllUpper, preserveAllLower, leaveRules, leaveInsensitiveRules, upperRules, lowerRules };
247
+ const result = this.#applySegmentRules(cleanSeg, 0, originalString, config);
208
248
 
209
- if (hasCharBefore && hasCharAfter) {
210
-
211
- transformedSeg = transformedSeg.substring(0, startPos) + innerString.toLowerCase() + transformedSeg.substring(endPos);
212
- break;
249
+
250
+
251
+ const matchesLower = lowerRules.some((pattern) => {
252
+ if (pattern.includes("*") || pattern.includes("?")) {
253
+ const regex = this.#compileGlobPattern(pattern, false);
254
+ if (regex && regex.test(originalString)) {
255
+
256
+ const literals = this.#extractPatternLiterals(pattern);
257
+ for (const literal of literals) {
258
+ const cleanLiteral = literal.replace(/[^A-Za-z0-9_$]/g, "").replace(/^_+|_+$/g, "");
259
+ if (cleanLiteral && cleanSeg.toLowerCase() === cleanLiteral.toLowerCase()) {
260
+ return true;
261
+ }
213
262
  }
214
263
  }
215
264
  } else {
216
265
 
217
- const literalParts = pattern.split(/[*?]+/).filter(Boolean);
218
- for (const literal of literalParts) {
219
- if (literal) {
220
- const literalRegex = new RegExp(literal.replace(/[.+^${}()|[\]\\]/g, "\\$&"), "gi");
221
- transformedSeg = transformedSeg.replace(literalRegex, literal.toLowerCase());
222
- }
266
+ if (cleanSeg.toLowerCase() === pattern.toLowerCase()) {
267
+ return true;
223
268
  }
224
269
  }
270
+ return false;
271
+ });
272
+
273
+ if (matchesLower && result === cleanSeg.toLowerCase()) {
274
+ lowerRuleApplied[primaryIdx] = true;
225
275
  }
226
- }
227
- }
276
+
277
+ return result;
278
+ });
279
+
280
+
281
+ return processedParts.join("");
282
+ });
228
283
 
229
284
 
230
-
231
- for (const pattern of upperRules) {
232
- if (!pattern.includes("*") && !pattern.includes("?")) {
233
-
234
- const match = seg.toLowerCase() === pattern.toLowerCase();
235
- if (match) {
236
- return seg.toUpperCase();
237
- }
285
+ const camelCasedSegments = processedPrimarySegments.map((seg, idx) => {
286
+
287
+ const matchesLeave = this.#matchesAnyPattern(seg, leaveRules, true);
288
+ const matchesLeaveInsensitive = this.#matchesAnyPattern(seg, leaveInsensitiveRules, false);
289
+ const matchesUpper = this.#matchesAnyPattern(seg, upperRules, false);
290
+
291
+
292
+ const hasUnderscores = seg.includes("_");
293
+ const isAllUpper = !hasUnderscores && preserveAllUpper && seg === seg.toUpperCase() && seg !== seg.toLowerCase() && /[A-Z]/.test(seg);
294
+ const isAllLower = !hasUnderscores && preserveAllLower && seg === seg.toLowerCase() && seg !== seg.toUpperCase() && /[a-z]/.test(seg);
295
+
296
+
297
+ if (matchesLeave || matchesLeaveInsensitive || matchesUpper || isAllUpper || isAllLower) {
298
+ return seg;
238
299
  }
239
- }
240
300
 
241
-
242
- for (const pattern of lowerRules) {
243
- if (!pattern.includes("*") && !pattern.includes("?")) {
301
+
302
+
303
+ let transformed;
304
+ if (idx === 0) {
244
305
 
245
- const match = seg.toLowerCase() === pattern.toLowerCase();
246
- if (match) {
247
- return seg.toLowerCase();
306
+ transformed = lowerFirst ? seg[0].toLowerCase() + seg.slice(1) : seg;
307
+ } else {
308
+
309
+
310
+ if (lowerRuleApplied[idx]) {
311
+ transformed = seg;
312
+ } else {
313
+ transformed = seg[0].toUpperCase() + seg.slice(1);
248
314
  }
249
315
  }
250
- }
251
316
 
252
-
253
- if (transformedSeg !== seg) {
254
- return transformedSeg;
255
- }
317
+ return transformed;
318
+ });
256
319
 
257
320
 
258
- if (index === 0) {
259
-
260
- return lowerFirst ? seg[0].toLowerCase() + seg.slice(1) : seg;
261
- }
262
-
263
- return seg[0].toUpperCase() + seg.slice(1);
264
- };
321
+ let result = camelCasedSegments.join("");
322
+ result = result.replace(/[^A-Za-z0-9_$]/g, "");
323
+
324
+ return result;
325
+ }
265
326
 
266
327
 
267
- let out = parts.map((seg, i) => applyRule(seg.replace(/[^A-Za-z0-9_$]/g, ""), i)).join("");
328
+ getModuleId(filePath, baseDir) {
329
+
330
+ let relative = filePath.replace(baseDir, "").replace(/\\/g, "/");
331
+ relative = relative.replace(/^\//, "");
332
+ relative = relative.replace(/\.(mjs|cjs|js)$/, "");
333
+
334
+ return relative;
335
+ }
268
336
 
269
337
 
270
- out = out.replace(/[^A-Za-z0-9_$]/g, "");
271
- if (!out || !/^[A-Za-z_$]/.test(out[0])) out = "_" + out;
338
+ shouldPreserveFunctionCase(name) {
339
+ const preservePatterns = [
340
+ /^[A-Z]{2,}$/,
341
+ /[A-Z]{2,}/
342
+ ];
272
343
 
273
- return out;
344
+ return preservePatterns.some((pattern) => pattern.test(name));
345
+ }
274
346
  }
275
347
 
276
348
 
277
349
 
350
+
351
+
352
+
353
+ export function sanitizePropertyName(input, options = {}) {
354
+ const sanitizer = new Sanitize(null);
355
+ return sanitizer.sanitizePropertyName(input, options);
356
+ }
@@ -18,104 +18,69 @@
18
18
 
19
19
 
20
20
 
21
+ import { ComponentBase } from "@cldmv/slothlet/factories/component-base";
21
22
 
22
- export function safeDefine(obj, key, value, enumerable = false, config = null) {
23
- const desc = Object.getOwnPropertyDescriptor(obj, key);
24
- if (!desc) {
25
- Object.defineProperty(obj, key, {
26
- value,
27
- writable: true,
28
- configurable: true,
29
- enumerable
30
- });
31
- } else if (desc.configurable) {
32
- Object.defineProperty(obj, key, {
33
- value,
34
- writable: true,
35
- configurable: true,
36
- enumerable
37
- });
38
- } else if (config && config.debug) {
39
- console.warn(`Could not redefine boundApi.${key}: not configurable`);
40
- }
41
- }
42
23
 
24
+ export class Utilities extends ComponentBase {
25
+ static slothletProperty = "utilities";
43
26
 
44
- export function deepMerge(target, source) {
45
- if (!source || typeof source !== "object" || Array.isArray(source)) {
46
- return source;
27
+
28
+ isPlainObject(obj) {
29
+ if (typeof obj !== "object" || obj === null) return false;
30
+ const proto = Object.getPrototypeOf(obj);
31
+ return proto === null || proto === Object.prototype;
47
32
  }
48
33
 
49
- for (const key in source) {
50
- if (!Object.prototype.hasOwnProperty.call(source, key)) {
51
- continue;
52
- }
53
-
54
-
55
- if (key === "__proto__" || key === "prototype" || key === "constructor") {
56
- continue;
57
- }
58
-
59
- const sourceValue = source[key];
60
- const targetValue = target[key];
61
-
62
- if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue)) {
63
- target[key] = deepMerge(
64
- targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) ? targetValue : {},
65
- sourceValue
66
- );
67
- } else {
68
- target[key] = sourceValue;
34
+
35
+ deepMerge(target, source) {
36
+ if (!this.isPlainObject(target) || !this.isPlainObject(source)) {
37
+ return source;
69
38
  }
70
- }
71
-
72
- return target;
73
- }
74
39
 
40
+ const result = { ...target };
75
41
 
76
- export function mutateLiveBindingFunction(target, source) {
77
- if (typeof source === "function") {
78
- target._impl = (...args) => source(...args);
79
-
80
- for (const key of Object.keys(target)) {
81
- if (key !== "_impl" && key !== "__ctx") delete target[key];
82
- }
83
-
84
- for (const key of Object.getOwnPropertyNames(source)) {
85
- if (key !== "length" && key !== "name" && key !== "prototype" && key !== "_impl" && key !== "__ctx") {
86
- try {
87
- target[key] = source[key];
88
- } catch {
42
+ for (const key in source) {
43
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
44
+ if (this.isPlainObject(source[key])) {
45
+
89
46
 
47
+ result[key] = this.deepMerge(this.isPlainObject(target[key]) ? target[key] : {}, source[key]);
48
+ } else {
49
+ result[key] = source[key];
90
50
  }
91
51
  }
92
52
  }
93
- } else if (typeof source === "object" && source !== null) {
94
-
95
- for (const key of Object.keys(target)) {
96
- if (key !== "_impl" && key !== "__ctx") delete target[key];
97
- }
98
-
99
- for (const [key, value] of Object.entries(source)) {
100
- if (key !== "__ctx") {
101
- target[key] = value;
102
- }
103
- }
104
-
105
- const managementMethods = ["shutdown", "addApi", "describe"];
106
- for (const method of managementMethods) {
107
- const desc = Object.getOwnPropertyDescriptor(source, method);
108
- if (desc) {
53
+
54
+ return result;
55
+ }
56
+
57
+
58
+ deepClone(obj) {
59
+ try {
60
+ return structuredClone(obj);
61
+ } catch {
62
+
63
+
64
+ const objType = obj?.__type || typeof obj;
65
+ if (obj === null || (objType !== "object" && objType !== "function")) return obj;
66
+ if (obj instanceof Date) return new Date(obj.getTime());
67
+ if (Array.isArray(obj)) return obj.map((item) => this.deepClone(item));
68
+
69
+ const cloned = {};
70
+ for (const key in obj) {
109
71
  try {
110
- Object.defineProperty(target, method, desc);
72
+ cloned[key] = this.deepClone(obj[key]);
111
73
  } catch {
112
74
 
75
+ cloned[key] = obj[key];
113
76
  }
114
77
  }
78
+ return cloned;
115
79
  }
116
-
117
- if (typeof source._impl === "function") {
118
- target._impl = source._impl;
119
- }
80
+ }
81
+
82
+
83
+ generateId() {
84
+ return `slothlet_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
120
85
  }
121
86
  }