@bgroup/wise-form 1.0.3 → 1.0.6

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 (273) hide show
  1. package/README.md +1 -1
  2. package/package.json +23 -4
  3. package/postcss.config.js +6 -0
  4. package/src/form/styles.css +11 -37
  5. package/src/form/view/components/containers/index.tsx +33 -4
  6. package/src/form/view/components/error.tsx +0 -3
  7. package/src/form/view/components/field/container.tsx +1 -1
  8. package/src/form/view/components/field/index.tsx +42 -7
  9. package/src/form/view/components/rows/row-container.tsx +37 -9
  10. package/src/form/view/components/rows/wrapper.tsx +17 -6
  11. package/src/form/view/components/wrapped-form.tsx +38 -5
  12. package/src/form/view/hooks/use-model.ts +91 -25
  13. package/src/form/view/index.tsx +32 -6
  14. package/src/formulas/variants/array-formula.ts +6 -1
  15. package/src/models/base.ts +31 -5
  16. package/src/models/callback-manager.ts +169 -91
  17. package/src/models/field.ts +504 -538
  18. package/src/models/wrapper.ts +32 -7
  19. package/tailwind.config.js +11 -0
  20. package/tsconfig.json +2 -0
  21. package/vite.config.ts +59 -0
  22. package/dist/components/ui/Checkbox.d.ts +0 -14
  23. package/dist/components/ui/Checkbox.d.ts.map +0 -1
  24. package/dist/components/ui/Checkbox.js +0 -43
  25. package/dist/components/ui/Checkbox.js.map +0 -1
  26. package/dist/components/ui/CheckboxGroup.d.ts +0 -15
  27. package/dist/components/ui/CheckboxGroup.d.ts.map +0 -1
  28. package/dist/components/ui/CheckboxGroup.js +0 -33
  29. package/dist/components/ui/CheckboxGroup.js.map +0 -1
  30. package/dist/components/ui/Input.d.ts +0 -14
  31. package/dist/components/ui/Input.d.ts.map +0 -1
  32. package/dist/components/ui/Input.js +0 -49
  33. package/dist/components/ui/Input.js.map +0 -1
  34. package/dist/components/ui/Radio.d.ts +0 -14
  35. package/dist/components/ui/Radio.d.ts.map +0 -1
  36. package/dist/components/ui/Radio.js +0 -43
  37. package/dist/components/ui/Radio.js.map +0 -1
  38. package/dist/components/ui/Select.d.ts +0 -18
  39. package/dist/components/ui/Select.d.ts.map +0 -1
  40. package/dist/components/ui/Select.js +0 -44
  41. package/dist/components/ui/Select.js.map +0 -1
  42. package/dist/components/ui/Textarea.d.ts +0 -13
  43. package/dist/components/ui/Textarea.d.ts.map +0 -1
  44. package/dist/components/ui/Textarea.js +0 -42
  45. package/dist/components/ui/Textarea.js.map +0 -1
  46. package/dist/components/ui/index.d.ts +0 -13
  47. package/dist/components/ui/index.d.ts.map +0 -1
  48. package/dist/components/ui/index.js +0 -7
  49. package/dist/components/ui/index.js.map +0 -1
  50. package/dist/form/index.d.ts +0 -10
  51. package/dist/form/index.d.ts.map +0 -1
  52. package/dist/form/index.js +0 -5
  53. package/dist/form/index.js.map +0 -1
  54. package/dist/form/interfaces/field-container.d.ts +0 -8
  55. package/dist/form/interfaces/field-container.d.ts.map +0 -1
  56. package/dist/form/interfaces/field-container.js +0 -2
  57. package/dist/form/interfaces/field-container.js.map +0 -1
  58. package/dist/form/interfaces/interfaces.d.ts +0 -8
  59. package/dist/form/interfaces/interfaces.d.ts.map +0 -1
  60. package/dist/form/interfaces/interfaces.js +0 -2
  61. package/dist/form/interfaces/interfaces.js.map +0 -1
  62. package/dist/form/interfaces/settings.d.ts +0 -10
  63. package/dist/form/interfaces/settings.d.ts.map +0 -1
  64. package/dist/form/interfaces/settings.js +0 -2
  65. package/dist/form/interfaces/settings.js.map +0 -1
  66. package/dist/form/interfaces/template.d.ts +0 -6
  67. package/dist/form/interfaces/template.d.ts.map +0 -1
  68. package/dist/form/interfaces/template.js +0 -2
  69. package/dist/form/interfaces/template.js.map +0 -1
  70. package/dist/form/interfaces/wise-form-specs.d.ts +0 -9
  71. package/dist/form/interfaces/wise-form-specs.d.ts.map +0 -1
  72. package/dist/form/interfaces/wise-form-specs.js +0 -2
  73. package/dist/form/interfaces/wise-form-specs.js.map +0 -1
  74. package/dist/form/view/components/containers/index.d.ts +0 -3
  75. package/dist/form/view/components/containers/index.d.ts.map +0 -1
  76. package/dist/form/view/components/containers/index.js +0 -12
  77. package/dist/form/view/components/containers/index.js.map +0 -1
  78. package/dist/form/view/components/error.d.ts +0 -5
  79. package/dist/form/view/components/error.d.ts.map +0 -1
  80. package/dist/form/view/components/error.js +0 -8
  81. package/dist/form/view/components/error.js.map +0 -1
  82. package/dist/form/view/components/field/container.d.ts +0 -5
  83. package/dist/form/view/components/field/container.d.ts.map +0 -1
  84. package/dist/form/view/components/field/container.js +0 -5
  85. package/dist/form/view/components/field/container.js.map +0 -1
  86. package/dist/form/view/components/field/index.d.ts +0 -18
  87. package/dist/form/view/components/field/index.d.ts.map +0 -1
  88. package/dist/form/view/components/field/index.js +0 -89
  89. package/dist/form/view/components/field/index.js.map +0 -1
  90. package/dist/form/view/components/field/selection.d.ts +0 -2
  91. package/dist/form/view/components/field/selection.d.ts.map +0 -1
  92. package/dist/form/view/components/field/selection.js +0 -35
  93. package/dist/form/view/components/field/selection.js.map +0 -1
  94. package/dist/form/view/components/field/use-field.d.ts +0 -4
  95. package/dist/form/view/components/field/use-field.d.ts.map +0 -1
  96. package/dist/form/view/components/field/use-field.js +0 -41
  97. package/dist/form/view/components/field/use-field.js.map +0 -1
  98. package/dist/form/view/components/rows/row-container.d.ts +0 -18
  99. package/dist/form/view/components/rows/row-container.d.ts.map +0 -1
  100. package/dist/form/view/components/rows/row-container.js +0 -89
  101. package/dist/form/view/components/rows/row-container.js.map +0 -1
  102. package/dist/form/view/components/rows/wrapper.d.ts +0 -12
  103. package/dist/form/view/components/rows/wrapper.d.ts.map +0 -1
  104. package/dist/form/view/components/rows/wrapper.js +0 -27
  105. package/dist/form/view/components/rows/wrapper.js.map +0 -1
  106. package/dist/form/view/components/wrapped-form.d.ts +0 -6
  107. package/dist/form/view/components/wrapped-form.d.ts.map +0 -1
  108. package/dist/form/view/components/wrapped-form.js +0 -26
  109. package/dist/form/view/components/wrapped-form.js.map +0 -1
  110. package/dist/form/view/context.d.ts +0 -23
  111. package/dist/form/view/context.d.ts.map +0 -1
  112. package/dist/form/view/context.js +0 -7
  113. package/dist/form/view/context.js.map +0 -1
  114. package/dist/form/view/hooks/use-model.d.ts +0 -10
  115. package/dist/form/view/hooks/use-model.d.ts.map +0 -1
  116. package/dist/form/view/hooks/use-model.js +0 -31
  117. package/dist/form/view/hooks/use-model.js.map +0 -1
  118. package/dist/form/view/hooks/use-template.d.ts +0 -14
  119. package/dist/form/view/hooks/use-template.d.ts.map +0 -1
  120. package/dist/form/view/hooks/use-template.js +0 -57
  121. package/dist/form/view/hooks/use-template.js.map +0 -1
  122. package/dist/form/view/hooks/use-types.d.ts +0 -2
  123. package/dist/form/view/hooks/use-types.d.ts.map +0 -1
  124. package/dist/form/view/hooks/use-types.js +0 -19
  125. package/dist/form/view/hooks/use-types.js.map +0 -1
  126. package/dist/form/view/index.d.ts +0 -3
  127. package/dist/form/view/index.d.ts.map +0 -1
  128. package/dist/form/view/index.js +0 -38
  129. package/dist/form/view/index.js.map +0 -1
  130. package/dist/formulas/helpers/condition-types.d.ts +0 -5
  131. package/dist/formulas/helpers/condition-types.d.ts.map +0 -1
  132. package/dist/formulas/helpers/condition-types.js +0 -5
  133. package/dist/formulas/helpers/condition-types.js.map +0 -1
  134. package/dist/formulas/helpers/evaluations.d.ts +0 -15
  135. package/dist/formulas/helpers/evaluations.d.ts.map +0 -1
  136. package/dist/formulas/helpers/evaluations.js +0 -44
  137. package/dist/formulas/helpers/evaluations.js.map +0 -1
  138. package/dist/formulas/helpers/formula.d.ts +0 -6
  139. package/dist/formulas/helpers/formula.d.ts.map +0 -1
  140. package/dist/formulas/helpers/formula.js +0 -26
  141. package/dist/formulas/helpers/formula.js.map +0 -1
  142. package/dist/formulas/helpers/lexer.d.ts +0 -10
  143. package/dist/formulas/helpers/lexer.d.ts.map +0 -1
  144. package/dist/formulas/helpers/lexer.js +0 -73
  145. package/dist/formulas/helpers/lexer.js.map +0 -1
  146. package/dist/formulas/helpers/parser.d.ts +0 -24
  147. package/dist/formulas/helpers/parser.d.ts.map +0 -1
  148. package/dist/formulas/helpers/parser.js +0 -48
  149. package/dist/formulas/helpers/parser.js.map +0 -1
  150. package/dist/formulas/helpers/token.d.ts +0 -14
  151. package/dist/formulas/helpers/token.d.ts.map +0 -1
  152. package/dist/formulas/helpers/token.js +0 -14
  153. package/dist/formulas/helpers/token.js.map +0 -1
  154. package/dist/formulas/index.d.ts +0 -59
  155. package/dist/formulas/index.d.ts.map +0 -1
  156. package/dist/formulas/index.js +0 -186
  157. package/dist/formulas/index.js.map +0 -1
  158. package/dist/formulas/types/formulas.d.ts +0 -68
  159. package/dist/formulas/types/formulas.d.ts.map +0 -1
  160. package/dist/formulas/types/formulas.js +0 -2
  161. package/dist/formulas/types/formulas.js.map +0 -1
  162. package/dist/formulas/types/index.d.ts +0 -5
  163. package/dist/formulas/types/index.d.ts.map +0 -1
  164. package/dist/formulas/types/index.js +0 -2
  165. package/dist/formulas/types/index.js.map +0 -1
  166. package/dist/formulas/variants/array-formula.d.ts +0 -24
  167. package/dist/formulas/variants/array-formula.d.ts.map +0 -1
  168. package/dist/formulas/variants/array-formula.js +0 -142
  169. package/dist/formulas/variants/array-formula.js.map +0 -1
  170. package/dist/formulas/variants/base.d.ts +0 -6
  171. package/dist/formulas/variants/base.d.ts.map +0 -1
  172. package/dist/formulas/variants/base.js +0 -3
  173. package/dist/formulas/variants/base.js.map +0 -1
  174. package/dist/formulas/variants/basic.d.ts +0 -18
  175. package/dist/formulas/variants/basic.d.ts.map +0 -1
  176. package/dist/formulas/variants/basic.js +0 -128
  177. package/dist/formulas/variants/basic.js.map +0 -1
  178. package/dist/formulas/variants/comparison.d.ts +0 -25
  179. package/dist/formulas/variants/comparison.d.ts.map +0 -1
  180. package/dist/formulas/variants/comparison.js +0 -153
  181. package/dist/formulas/variants/comparison.js.map +0 -1
  182. package/dist/formulas/variants/conditional.d.ts +0 -18
  183. package/dist/formulas/variants/conditional.d.ts.map +0 -1
  184. package/dist/formulas/variants/conditional.js +0 -183
  185. package/dist/formulas/variants/conditional.js.map +0 -1
  186. package/dist/formulas/variants/iterative-array.d.ts +0 -20
  187. package/dist/formulas/variants/iterative-array.d.ts.map +0 -1
  188. package/dist/formulas/variants/iterative-array.js +0 -155
  189. package/dist/formulas/variants/iterative-array.js.map +0 -1
  190. package/dist/formulas/variants/per-value.d.ts +0 -20
  191. package/dist/formulas/variants/per-value.d.ts.map +0 -1
  192. package/dist/formulas/variants/per-value.js +0 -154
  193. package/dist/formulas/variants/per-value.js.map +0 -1
  194. package/dist/index.d.ts +0 -5
  195. package/dist/index.d.ts.map +0 -1
  196. package/dist/index.js +0 -6
  197. package/dist/index.js.map +0 -1
  198. package/dist/models/base.d.ts +0 -55
  199. package/dist/models/base.d.ts.map +0 -1
  200. package/dist/models/base.js +0 -146
  201. package/dist/models/base.js.map +0 -1
  202. package/dist/models/callback-manager.d.ts +0 -7
  203. package/dist/models/callback-manager.d.ts.map +0 -1
  204. package/dist/models/callback-manager.js +0 -89
  205. package/dist/models/callback-manager.js.map +0 -1
  206. package/dist/models/field.d.ts +0 -121
  207. package/dist/models/field.d.ts.map +0 -1
  208. package/dist/models/field.js +0 -515
  209. package/dist/models/field.js.map +0 -1
  210. package/dist/models/index.d.ts +0 -13
  211. package/dist/models/index.d.ts.map +0 -1
  212. package/dist/models/index.js +0 -7
  213. package/dist/models/index.js.map +0 -1
  214. package/dist/models/model.d.ts +0 -37
  215. package/dist/models/model.d.ts.map +0 -1
  216. package/dist/models/model.js +0 -245
  217. package/dist/models/model.js.map +0 -1
  218. package/dist/models/plugins/base.d.ts +0 -9
  219. package/dist/models/plugins/base.d.ts.map +0 -1
  220. package/dist/models/plugins/base.js +0 -3
  221. package/dist/models/plugins/base.js.map +0 -1
  222. package/dist/models/plugins/formula.d.ts +0 -18
  223. package/dist/models/plugins/formula.d.ts.map +0 -1
  224. package/dist/models/plugins/formula.js +0 -82
  225. package/dist/models/plugins/formula.js.map +0 -1
  226. package/dist/models/plugins/index.d.ts +0 -11
  227. package/dist/models/plugins/index.d.ts.map +0 -1
  228. package/dist/models/plugins/index.js +0 -52
  229. package/dist/models/plugins/index.js.map +0 -1
  230. package/dist/models/plugins/plugins.d.ts +0 -7
  231. package/dist/models/plugins/plugins.d.ts.map +0 -1
  232. package/dist/models/plugins/plugins.js +0 -7
  233. package/dist/models/plugins/plugins.js.map +0 -1
  234. package/dist/models/types/base-wise-model.d.ts +0 -7
  235. package/dist/models/types/base-wise-model.d.ts.map +0 -1
  236. package/dist/models/types/base-wise-model.js +0 -2
  237. package/dist/models/types/base-wise-model.js.map +0 -1
  238. package/dist/models/types/callbacks.d.ts +0 -19
  239. package/dist/models/types/callbacks.d.ts.map +0 -1
  240. package/dist/models/types/callbacks.js +0 -2
  241. package/dist/models/types/callbacks.js.map +0 -1
  242. package/dist/models/types/disabled.d.ts +0 -8
  243. package/dist/models/types/disabled.d.ts.map +0 -1
  244. package/dist/models/types/disabled.js +0 -2
  245. package/dist/models/types/disabled.js.map +0 -1
  246. package/dist/models/types/form-field.d.ts +0 -25
  247. package/dist/models/types/form-field.d.ts.map +0 -1
  248. package/dist/models/types/form-field.js +0 -2
  249. package/dist/models/types/form-field.js.map +0 -1
  250. package/dist/models/types/model.d.ts +0 -13
  251. package/dist/models/types/model.d.ts.map +0 -1
  252. package/dist/models/types/model.js +0 -2
  253. package/dist/models/types/model.js.map +0 -1
  254. package/dist/models/types/plugins.d.ts +0 -13
  255. package/dist/models/types/plugins.d.ts.map +0 -1
  256. package/dist/models/types/plugins.js +0 -2
  257. package/dist/models/types/plugins.js.map +0 -1
  258. package/dist/models/types/wrapped-form-model-props.d.ts +0 -11
  259. package/dist/models/types/wrapped-form-model-props.d.ts.map +0 -1
  260. package/dist/models/types/wrapped-form-model-props.js +0 -2
  261. package/dist/models/types/wrapped-form-model-props.js.map +0 -1
  262. package/dist/models/wrapper.d.ts +0 -30
  263. package/dist/models/wrapper.d.ts.map +0 -1
  264. package/dist/models/wrapper.js +0 -213
  265. package/dist/models/wrapper.js.map +0 -1
  266. package/dist/settings/index.d.ts +0 -7
  267. package/dist/settings/index.d.ts.map +0 -1
  268. package/dist/settings/index.js +0 -26
  269. package/dist/settings/index.js.map +0 -1
  270. package/dist/utils/pending-promise.d.ts +0 -6
  271. package/dist/utils/pending-promise.d.ts.map +0 -1
  272. package/dist/utils/pending-promise.js +0 -24
  273. package/dist/utils/pending-promise.js.map +0 -1
@@ -100,28 +100,54 @@ export class BaseWiseModel extends ReactiveModel<IBaseWiseModel> {
100
100
  /**
101
101
  * Retrieves a field or nested wrapper by name. Supports dot notation for accessing deeply nested fields.
102
102
  * @param {string} name - The name of the field or nested wrapper to retrieve.
103
+ * @param {Set<BaseWiseModel | WrappedFormModel>} visited - Set of already visited models to prevent infinite recursion.
103
104
  * @returns {FormField | WrappedFormModel | undefined} The requested instance, or undefined if not found.
104
105
  */
105
- getField(name: string) {
106
- if (!name) return console.warn('You need to provide a name to get a field in form ', this.#settings.name);
106
+ getField(name: string, visited: Set<BaseWiseModel | WrappedFormModel> = new Set()) {
107
+ if (!name) {
108
+ console.warn('[WiseForm.getField] Empty field name provided in form', this.#settings.name);
109
+ return;
110
+ }
111
+
112
+ // Protección contra recursión circular
113
+ if (visited.has(this)) {
114
+ console.error(
115
+ `[WiseForm.getField] Circular reference detected in form "${this.#settings.name}" while searching for field "${name}". This usually indicates a configuration issue with nested wrappers.`
116
+ );
117
+ console.log(
118
+ `[WiseForm.getField] PROTECTION ACTIVE: Circular reference prevented. Form: "${this.#settings.name}", Field: "${name}", Visited models count: ${visited.size}`
119
+ );
120
+ return undefined;
121
+ }
122
+ visited.add(this);
107
123
 
108
124
  if (!name.includes('.')) {
109
125
  let field = this.#fields.get(name);
110
126
 
111
127
  if (!field) {
112
128
  this.#wrappers.forEach(item => {
113
- const foundField = item.getField(name);
114
- if (foundField) field = foundField;
129
+ if (!visited.has(item)) {
130
+ const foundField = item.getField(name, visited);
131
+ if (foundField) field = foundField;
132
+ }
115
133
  });
116
134
  }
117
135
  return field;
118
136
  }
119
137
 
138
+ // Dot notation path
120
139
  const [wrapperName, ...others] = name.split('.');
121
140
  const currentWrapper = this.#wrappers.get(wrapperName);
122
141
 
142
+ if (!currentWrapper) {
143
+ console.warn(
144
+ `[WiseForm.getField] Wrapper "${wrapperName}" not found in form "${this.#settings.name}" while searching for "${name}". Available wrappers: ${Array.from(this.#wrappers.keys()).join(', ') || 'none'}`
145
+ );
146
+ return undefined;
147
+ }
148
+
123
149
  const otherWrapper = others.join('.');
124
- return currentWrapper.getField(otherWrapper);
150
+ return currentWrapper.getField(otherWrapper, visited);
125
151
  }
126
152
 
127
153
  /**
@@ -2,101 +2,179 @@ import type { FormField } from './field';
2
2
  import type { FormModel } from './model';
3
3
  import { CallbackFunction, ICallbackProps } from './types/callbacks';
4
4
 
5
+ /**
6
+ * Maximum number of recursive executions allowed for a callback before preventing further execution.
7
+ * This can be overridden if specific use cases require a higher limit.
8
+ */
9
+ export const MAX_CALLBACK_RECURSION_DEPTH = 3;
10
+
11
+ /**
12
+ * Map to track callback execution depth per unique execution context.
13
+ * Key format: "callbackName:fieldName:dependencyName" or "callbackName:fieldName" if no dependency
14
+ */
15
+ const callbackExecutionDepth = new Map<string, number>();
16
+
17
+ /**
18
+ * Set to track callbacks currently executing (prevents concurrent executions of the same callback)
19
+ */
20
+ const currentlyExecutingCallbacks = new Set<string>();
21
+
5
22
  export class CallbackManager {
6
- #field: FormField;
7
- #model: FormModel;
8
- #callbacks: CallbackFunction[] = [];
9
- #listeners = [];
10
- constructor(model, field) {
11
- this.#field = field;
12
- this.#model = model;
13
- this.#callbacks = model.callbacks;
14
- this.initialize();
23
+ #field: FormField;
24
+ #model: FormModel;
25
+ #callbacks: CallbackFunction[] = [];
26
+ #listeners = [];
27
+ constructor(model, field) {
28
+ this.#field = field;
29
+ this.#model = model;
30
+ this.#callbacks = model.callbacks;
31
+ this.initialize();
32
+ }
33
+
34
+ initialize() {
35
+ const instance = this.#field;
36
+ const checkField = async settings => {
37
+ const dependency = this.#model.getField(this.#model.getFieldName(settings.field));
38
+ await dependency.isReady;
39
+ const required = ['field', 'callback'];
40
+ required.forEach(prop => {
41
+ if (!settings[prop]) throw new Error(`${settings?.field} is missing ${prop}`);
42
+ });
43
+
44
+ if (!dependency) throw new Error(`${settings?.field} is not a registered field`);
45
+
46
+ if (!this.#callbacks[settings.callback]) {
47
+ throw new Error(`${settings.callback} is not a registered callback ${settings.name}`);
48
+ }
49
+
50
+ // saved in listener array to be able to remove the listener if is required.
51
+ const event = settings.event || 'value.change';
52
+ const caller = () => this.executeCallback(settings);
53
+ this.#listeners.push(caller);
54
+ dependency.on(event, caller);
55
+
56
+ //callback({ dependency, settings, field: instance, form: this });
57
+ };
58
+
59
+ instance?.specs?.dependentOn.forEach(checkField);
60
+ }
61
+
62
+ /**
63
+ * Generates a unique execution key for tracking callback recursion.
64
+ * Format: "callbackName:fieldName:dependencyName"
65
+ */
66
+ #getExecutionKey(callbackName: string, fieldName: string, dependencyName?: string): string {
67
+ const fieldNameStr = fieldName || 'unknown';
68
+ const dependencyNameStr = dependencyName || 'unknown';
69
+ return `${callbackName}:${fieldNameStr}:${dependencyNameStr}`;
70
+ }
71
+
72
+ executeCallback = async settings => {
73
+ if (!settings) {
74
+ console.warn('the field does not have dependentOn settings');
75
+ return;
15
76
  }
16
77
 
17
- initialize() {
18
- const instance = this.#field;
19
- const checkField = async (settings) => {
20
- const dependency = this.#model.getField(
21
- this.#model.getFieldName(settings.field)
22
- );
23
- await dependency.isReady;
24
- const required = ['field', 'callback'];
25
- required.forEach((prop) => {
26
- if (!settings[prop])
27
- throw new Error(`${settings?.field} is missing ${prop}`);
28
- });
29
-
30
- if (!dependency)
31
- throw new Error(`${settings?.field} is not a registered field`);
32
-
33
- if (!this.#callbacks[settings.callback]) {
34
- throw new Error(
35
- `${settings.callback} is not a registered callback ${settings.name}`
36
- );
37
- }
38
-
39
- // saved in listener array to be able to remove the listener if is required.
40
- const event = settings.event || 'value.change';
41
- const caller = () => this.executeCallback(settings);
42
- this.#listeners.push(caller);
43
- dependency.on(event, caller);
44
-
45
- //callback({ dependency, settings, field: instance, form: this });
46
- };
47
-
48
- instance?.specs?.dependentOn.forEach(checkField);
78
+ const callbackName = settings.callback;
79
+ if (!callbackName) {
80
+ console.warn('[CallbackManager] executeCallback called without callback name in settings');
81
+ return;
49
82
  }
50
83
 
51
- executeCallback = async (settings) => {
52
- const params: ICallbackProps = {
53
- ...settings,
54
- form: this.#model,
55
- field: this.#field,
56
-
57
- settings,
58
- };
59
- if (!settings) {
60
- console.warn('the field does not have dependentOn settings');
61
- }
62
- const callback: CallbackFunction = this.#callbacks[settings.callback];
63
-
64
- const dependency = this.#model.getField(
65
- this.#model.getFieldName(settings.field)
66
- );
67
- await dependency.isReady;
68
- const fields = { [dependency.name]: dependency };
69
- if (settings.hasOwnProperty('fields')) {
70
- for (const field of settings.fields) {
71
- const instance = this.#model.getField(
72
- this.#model.getFieldName(field)
73
- );
74
- if (instance) await instance.isReady;
75
- const propName =
76
- typeof field === 'string' ? field : field.alias;
77
- fields[propName] = instance;
78
- }
79
-
80
- params.fields = fields;
81
- }
82
- params.dependency = dependency;
83
-
84
- //global wiseForm params
85
- if (settings.hasOwnProperty('params')) {
86
- const specs = {};
87
- settings.params.forEach((param) => {
88
- if (!this.#model?.getParams(param)) {
89
- console.warn(
90
- `param ${param} is not registered in the form`
91
- );
92
- return;
93
- }
94
-
95
- specs[param] = this.#model.getParams(param);
96
- });
97
- params.specs = specs;
84
+ const fieldName = (this.#field as { name?: string })?.name || 'unknown';
85
+ const dependencyFieldName = typeof settings.field === 'string' ? settings.field : settings.field?.field || 'unknown';
86
+
87
+ const executionKey = this.#getExecutionKey(callbackName, fieldName, dependencyFieldName);
88
+
89
+ // Check if already executing (prevent concurrent executions)
90
+ if (currentlyExecutingCallbacks.has(executionKey)) {
91
+ console.warn(
92
+ `[CallbackManager] Callback "${callbackName}" is already executing for field "${fieldName}" with dependency "${dependencyFieldName}". Skipping concurrent execution.`
93
+ );
94
+ return;
95
+ }
96
+
97
+ // Check recursion depth
98
+ const currentDepth = callbackExecutionDepth.get(executionKey) || 0;
99
+ const maxDepth = settings.maxRecursionDepth !== undefined ? settings.maxRecursionDepth : MAX_CALLBACK_RECURSION_DEPTH;
100
+
101
+ if (currentDepth >= maxDepth) {
102
+ console.error(
103
+ `[CallbackManager] RECURSION DETECTED: Callback "${callbackName}" exceeded maximum recursion depth of ${maxDepth}`,
104
+ `\n Field: ${fieldName}`,
105
+ `\n Dependency: ${dependencyFieldName}`,
106
+ `\n Current depth: ${currentDepth + 1}`,
107
+ `\n Execution key: ${executionKey}`,
108
+ `\n This indicates a circular dependency in callbacks. Please review the form configuration.`
109
+ );
110
+ return;
111
+ }
112
+
113
+ // Mark as currently executing and increment depth
114
+ currentlyExecutingCallbacks.add(executionKey);
115
+ callbackExecutionDepth.set(executionKey, currentDepth + 1);
116
+
117
+ try {
118
+ const params: ICallbackProps = {
119
+ ...settings,
120
+ form: this.#model,
121
+ field: this.#field,
122
+ settings,
123
+ };
124
+
125
+ const callback: CallbackFunction = this.#callbacks[callbackName];
126
+
127
+ if (!callback) {
128
+ console.error(`[CallbackManager] Callback "${callbackName}" not found in registered callbacks`);
129
+ return;
130
+ }
131
+
132
+ const dependency = this.#model.getField(this.#model.getFieldName(settings.field));
133
+ await dependency.isReady;
134
+ const fields = { [dependency.name]: dependency };
135
+ if (settings.hasOwnProperty('fields')) {
136
+ for (const field of settings.fields) {
137
+ const instance = this.#model.getField(this.#model.getFieldName(field));
138
+ if (instance) await instance.isReady;
139
+ const propName = typeof field === 'string' ? field : field.alias;
140
+ fields[propName] = instance;
98
141
  }
99
- callback(params);
100
- };
101
- }
102
142
 
143
+ params.fields = fields;
144
+ }
145
+ params.dependency = dependency;
146
+
147
+ //global wiseForm params
148
+ if (settings.hasOwnProperty('params')) {
149
+ const specs = {};
150
+ settings.params.forEach(param => {
151
+ if (!this.#model?.getParams(param)) {
152
+ console.warn(`param ${param} is not registered in the form`);
153
+ return;
154
+ }
155
+
156
+ specs[param] = this.#model.getParams(param);
157
+ });
158
+ params.specs = specs;
159
+ }
160
+
161
+ // Execute the callback (handle both sync and async callbacks)
162
+ const result = callback(params) as any;
163
+ if (result && typeof result.then === 'function') {
164
+ await result;
165
+ }
166
+ } catch (error) {
167
+ console.error(`[CallbackManager] Error executing callback "${callbackName}":`, error);
168
+ } finally {
169
+ // Clean up execution tracking
170
+ currentlyExecutingCallbacks.delete(executionKey);
171
+
172
+ const finalDepth = callbackExecutionDepth.get(executionKey) || 0;
173
+ if (finalDepth <= 1) {
174
+ callbackExecutionDepth.delete(executionKey);
175
+ } else {
176
+ callbackExecutionDepth.set(executionKey, finalDepth - 1);
177
+ }
178
+ }
179
+ };
180
+ }