@furo/open-models 0.0.0-alpha.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/CustomPrototypes.d.ts +6 -0
  4. package/dist/CustomPrototypes.js +4 -0
  5. package/dist/CustomPrototypes.js.map +1 -0
  6. package/dist/FDM_OPTIONS.d.ts +16 -0
  7. package/dist/FDM_OPTIONS.js +8 -0
  8. package/dist/FDM_OPTIONS.js.map +1 -0
  9. package/dist/FieldConstraints.d.ts +15 -0
  10. package/dist/FieldConstraints.js +3 -0
  11. package/dist/FieldConstraints.js.map +1 -0
  12. package/dist/FieldNode.d.ts +339 -0
  13. package/dist/FieldNode.js +835 -0
  14. package/dist/FieldNode.js.map +1 -0
  15. package/dist/OM_OPTIONS.d.ts +16 -0
  16. package/dist/OM_OPTIONS.js +8 -0
  17. package/dist/OM_OPTIONS.js.map +1 -0
  18. package/dist/OPEN_MODELS_OPTIONS.d.ts +16 -0
  19. package/dist/OPEN_MODELS_OPTIONS.js +8 -0
  20. package/dist/OPEN_MODELS_OPTIONS.js.map +1 -0
  21. package/dist/OPTIONS.d.ts +16 -0
  22. package/dist/OPTIONS.js +8 -0
  23. package/dist/OPTIONS.js.map +1 -0
  24. package/dist/Registry.d.ts +17 -0
  25. package/dist/Registry.js +29 -0
  26. package/dist/Registry.js.map +1 -0
  27. package/dist/Validator.d.ts +7 -0
  28. package/dist/Validator.js +3 -0
  29. package/dist/Validator.js.map +1 -0
  30. package/dist/ValueState.d.ts +37 -0
  31. package/dist/ValueState.js +39 -0
  32. package/dist/ValueState.js.map +1 -0
  33. package/dist/index.d.ts +21 -0
  34. package/dist/index.js +19 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/primitives/BOOLEAN.d.ts +14 -0
  37. package/dist/primitives/BOOLEAN.js +56 -0
  38. package/dist/primitives/BOOLEAN.js.map +1 -0
  39. package/dist/primitives/ENUM.d.ts +17 -0
  40. package/dist/primitives/ENUM.js +76 -0
  41. package/dist/primitives/ENUM.js.map +1 -0
  42. package/dist/primitives/INT32.d.ts +18 -0
  43. package/dist/primitives/INT32.js +98 -0
  44. package/dist/primitives/INT32.js.map +1 -0
  45. package/dist/primitives/STRING.d.ts +16 -0
  46. package/dist/primitives/STRING.js +99 -0
  47. package/dist/primitives/STRING.js.map +1 -0
  48. package/dist/proxies/ARRAY.d.ts +165 -0
  49. package/dist/proxies/ARRAY.js +398 -0
  50. package/dist/proxies/ARRAY.js.map +1 -0
  51. package/dist/proxies/MAP.d.ts +101 -0
  52. package/dist/proxies/MAP.js +225 -0
  53. package/dist/proxies/MAP.js.map +1 -0
  54. package/dist/proxies/RECURSION.d.ts +13 -0
  55. package/dist/proxies/RECURSION.js +51 -0
  56. package/dist/proxies/RECURSION.js.map +1 -0
  57. package/dist/well_known/ANY.d.ts +20 -0
  58. package/dist/well_known/ANY.js +91 -0
  59. package/dist/well_known/ANY.js.map +1 -0
  60. package/dist/well_known/Int32Value.d.ts +17 -0
  61. package/dist/well_known/Int32Value.js +115 -0
  62. package/dist/well_known/Int32Value.js.map +1 -0
  63. package/dist/well_known/Int64Value.d.ts +16 -0
  64. package/dist/well_known/Int64Value.js +105 -0
  65. package/dist/well_known/Int64Value.js.map +1 -0
  66. package/package.json +83 -0
@@ -0,0 +1,835 @@
1
+ /* eslint-disable max-classes-per-file, no-use-before-define */
2
+ /**
3
+ * notes: i18n is not part of the api anymore
4
+ */
5
+ import { ValueState } from './ValueState.js';
6
+ import { ToString, ValueOf } from './CustomPrototypes.js';
7
+ import { CustomConstraints, Validators } from './Validator.js';
8
+ import { OPEN_MODELS_OPTIONS } from './OPEN_MODELS_OPTIONS.js';
9
+ export class FieldNode {
10
+ /**
11
+ * Root node of a node
12
+ */
13
+ get __rootNode() {
14
+ return this.___rootNode;
15
+ }
16
+ /**
17
+ * Root node of a node. Do not set this value by yourself.
18
+ * @param value
19
+ */
20
+ set __rootNode(value) {
21
+ this.___rootNode = value;
22
+ }
23
+ /**
24
+ * Empty state of the node
25
+ */
26
+ get __isEmpty() {
27
+ return this.___isEmpty;
28
+ }
29
+ /**
30
+ * Empty state of the node
31
+ * @param empty
32
+ */
33
+ set __isEmpty(empty) {
34
+ if (empty) {
35
+ this.___isEmpty = true;
36
+ }
37
+ else {
38
+ this.___updateNotEmptyPath();
39
+ this.__rootNode.__meta.isPristine = false;
40
+ }
41
+ }
42
+ constructor(_initData, parent, parentAttributeName) {
43
+ this.___isEmpty = true;
44
+ /**
45
+ * Marker for primitive types
46
+ */
47
+ this.__isPrimitive = false;
48
+ /**
49
+ * Meta data of a field node.
50
+ */
51
+ this.__meta = {
52
+ readonly: false,
53
+ required: false,
54
+ initialValue: undefined,
55
+ isPristine: true,
56
+ isValid: true,
57
+ valueState: ValueState.None,
58
+ stateMessage: '',
59
+ typeName: '',
60
+ nodeFields: [],
61
+ isArrayNode: false,
62
+ isRecursionNode: false,
63
+ isAnyNode: false,
64
+ eventListener: new Map(),
65
+ };
66
+ this.___readonlyState = new Map();
67
+ this.__parentNode = parent;
68
+ if (parent) {
69
+ this.___rootNode = parent.__rootNode;
70
+ }
71
+ else {
72
+ this.___rootNode = this;
73
+ }
74
+ this.__meta.fieldName = parentAttributeName;
75
+ }
76
+ /**
77
+ * Get a FieldNode by giving a field path using the proto names for the fields.
78
+ *
79
+ *
80
+ *
81
+ * - `email_addresses[3].type[2]` for the second `type` value in the third `email_addresses` message.
82
+ * - `user.location.street` for the street in location in user.
83
+ *
84
+ * @param {string} deepPath - Path of the field.
85
+ */
86
+ __getFieldNodeByPath(deepPath = '') {
87
+ // replace array paths
88
+ const path = deepPath.replaceAll(/[[\]]/g, '.').split('.');
89
+ if (path.length > 0 && path[0] !== '') {
90
+ // rest wieder in error reinwerfen
91
+ // eslint-disable-next-line no-param-reassign
92
+ deepPath = path.slice(1).join('.');
93
+ // convert to camel
94
+ const fieldName = this.__toLowerCamelCase(path[0]);
95
+ if (deepPath === '') {
96
+ if (this[fieldName]) {
97
+ return this[fieldName];
98
+ }
99
+ }
100
+ else if (this[fieldName]) {
101
+ return this[fieldName].__getFieldNodeByPath(deepPath);
102
+ }
103
+ return undefined;
104
+ }
105
+ return undefined;
106
+ }
107
+ set __readonly(v) {
108
+ this.__meta.readonly = v;
109
+ // dispatch to children
110
+ this.__childNodes.forEach(child => {
111
+ if (child instanceof FieldNode) {
112
+ // store readonly state if
113
+ const cro = this.___readonlyState.get(child);
114
+ if (cro === undefined) {
115
+ this.___readonlyState.set(child, child.__readonly);
116
+ }
117
+ if (!child.__readonly && v) {
118
+ // eslint-disable-next-line no-param-reassign
119
+ child.__readonly = v;
120
+ }
121
+ }
122
+ });
123
+ }
124
+ get __readonly() {
125
+ return this.__meta.readonly;
126
+ }
127
+ /**
128
+ * Build up the model with the literal type. The literal type matches the interface from ITypeName.
129
+ * @param {} literal - The literal type matches the interface from ITypeName.
130
+ */
131
+ __fromLiteral(literal) {
132
+ this.__updateWithLiteral(literal);
133
+ // clear the state of the validity and value state
134
+ this.__setModelValidStateTrue();
135
+ this.__dispatchEvent(new CustomEvent('model-injected', {
136
+ composed: true,
137
+ bubbles: false,
138
+ detail: this,
139
+ }));
140
+ }
141
+ __setModelValidStateTrue() {
142
+ this.__meta.isValid = true;
143
+ this.__setValueState(ValueState.None, ['']);
144
+ this.__childNodes.forEach(child => {
145
+ if (child instanceof FieldNode) {
146
+ child.__setModelValidStateTrue();
147
+ }
148
+ });
149
+ }
150
+ /**
151
+ * Updates the model from literal data, without changing the validity and value state.
152
+ *
153
+ * @param literal
154
+ */
155
+ __updateWithLiteral(literal) {
156
+ this.__clear();
157
+ // store injected literal for reset()
158
+ this.__meta.initialValue = literal;
159
+ const data = structuredClone(literal);
160
+ // go through available fields
161
+ this.__meta.nodeFields.forEach(field => {
162
+ // __clear fields which are not available in literal
163
+ // if the field does not exist on the incoming literal, reset or __clear the value on the fieldNode
164
+ // make an undefined on complex types
165
+ if (data[field.fieldName] === undefined) {
166
+ if (this[`_${field.fieldName}`] !== undefined) {
167
+ // primitives go to their default values
168
+ this[`_${field.fieldName}`].__clear();
169
+ }
170
+ return;
171
+ }
172
+ this[`_${field.fieldName}`].__updateWithLiteral(data[field.fieldName]);
173
+ this[`_${field.fieldName}`].__meta.isPristine = true;
174
+ });
175
+ this.__notifyFieldValueChange(false);
176
+ }
177
+ __stringify() {
178
+ return JSON.stringify(this.__toJson());
179
+ }
180
+ /**
181
+ * Converts the model to a JSON struct wich matches the protobuf spec.
182
+ * If the `UseProtoNames` option is set to false, lowerCamelCase is produced.
183
+ */
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ __toJson() {
186
+ const d = {};
187
+ this.__meta.nodeFields.forEach(f => {
188
+ // use jsonName if UseProtoNames is set, otherwise convert to lowerCamel without X prefix
189
+ const jsonName = OPEN_MODELS_OPTIONS.UseProtoNames
190
+ ? f.jsonName
191
+ : this.__toLowerCamelCaseWithoutXPrefix(f.jsonName);
192
+ if (OPEN_MODELS_OPTIONS.EmitUnpopulated) {
193
+ if (this[`_${f.fieldName}`].__isEmpty &&
194
+ !this[`_${f.fieldName}`]
195
+ .__isPrimitive) {
196
+ d[jsonName] = null;
197
+ }
198
+ else {
199
+ d[jsonName] = this[`_${f.fieldName}`].__toJson();
200
+ }
201
+ }
202
+ else if ((this[`_${f.fieldName}`] &&
203
+ (!this[`_${f.fieldName}`]
204
+ .__isEmpty ||
205
+ this[`_${f.fieldName}`].__meta
206
+ .required)) ||
207
+ (this[`_${f.fieldName}`]
208
+ .__isPrimitive &&
209
+ OPEN_MODELS_OPTIONS.EmitDefaultValues)) {
210
+ d[jsonName] = this[`_${f.fieldName}`].__toJson();
211
+ }
212
+ return null;
213
+ });
214
+ return d;
215
+ }
216
+ /**
217
+ * Build up the model, using the transport Json. If UseProtoNames is set to false, lowerCamelCase is expected.
218
+ *
219
+ * @param {JSON} data - Transport Json
220
+ */
221
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
+ __fromJson(data) {
223
+ const l = this.__mapJsonToLiteral(data);
224
+ this.__fromLiteral(l);
225
+ }
226
+ /**
227
+ * Helper function to create a literal type from a json type
228
+ * @param {JSON} data - Transport Json
229
+ */
230
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
+ __mapJsonToLiteral(data) {
232
+ const literal = {};
233
+ // map json to literal
234
+ this.__meta.nodeFields.forEach(f => {
235
+ const jsonName = OPEN_MODELS_OPTIONS.UseProtoNames
236
+ ? f.jsonName
237
+ : this.__toLowerCamelCaseWithoutXPrefix(f.jsonName);
238
+ if (data[jsonName] !== undefined) {
239
+ literal[f.fieldName] = this[`_${f.fieldName}`].__mapJsonToLiteral(data[jsonName]);
240
+ }
241
+ });
242
+ return literal;
243
+ }
244
+ /**
245
+ * Converts the model to a literal type. The literal type matches the interface from ITypeName.
246
+ */
247
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
+ __toLiteral() {
249
+ const d = {};
250
+ this.__meta.nodeFields.forEach(f => {
251
+ if (this[`_${f.fieldName}`] &&
252
+ (!this[`_${f.fieldName}`].__isEmpty ||
253
+ this[`_${f.fieldName}`].__meta
254
+ .required)) {
255
+ d[f.fieldName] = this[`_${f.fieldName}`].__toLiteral();
256
+ }
257
+ return null;
258
+ });
259
+ return d;
260
+ }
261
+ get __fieldPath() {
262
+ const parts = [];
263
+ this.___pathBuilder(parts);
264
+ return parts.join('.').replaceAll('.[', '['); // replace for array paths
265
+ }
266
+ ___pathBuilder(parts) {
267
+ // the root node does not have a fieldName
268
+ if (this.__meta.fieldName !== undefined) {
269
+ parts.unshift(OPEN_MODELS_OPTIONS.UseProtoNames
270
+ ? this.__toSnakeCase(this.__meta.fieldName)
271
+ : this.__meta.fieldName);
272
+ this.__parentNode?.___pathBuilder(parts);
273
+ }
274
+ return parts;
275
+ }
276
+ get __label() {
277
+ return OPEN_MODELS_OPTIONS.labelFormatter(`${this.__getBaseName()}.label`);
278
+ }
279
+ get __placeholder() {
280
+ return `${this.__getBaseName()}.placeholder`;
281
+ }
282
+ get __ariaDescription() {
283
+ return `${this.__getBaseName()}.description`;
284
+ }
285
+ /**
286
+ * Returns a list of all states of a node and its children.
287
+ *
288
+ * ```json
289
+ * [
290
+ * {
291
+ * "field": "id",
292
+ * "state": "Error",
293
+ * "message": "This field is invalid"
294
+ * },
295
+ * {
296
+ * "field": "display_name",
297
+ * "state": "Error",
298
+ * "message": "This field is invalid"
299
+ * }
300
+ * ]
301
+ * ```
302
+ */
303
+ // eslint-disable-next-line class-methods-use-this
304
+ __getAllStates() {
305
+ return this.___getAllStates([]);
306
+ }
307
+ ___getAllStates(carrier) {
308
+ if (this.__meta.valueState !== ValueState.None) {
309
+ carrier.push({
310
+ field: this.__fieldPath,
311
+ state: this.__meta.valueState,
312
+ message: this.__meta.stateMessage,
313
+ });
314
+ }
315
+ this.__childNodes.forEach(child => {
316
+ if (child instanceof FieldNode) {
317
+ child.___getAllStates(carrier);
318
+ }
319
+ });
320
+ return carrier;
321
+ }
322
+ /**
323
+ * Applies a list of states to a node and its children.
324
+ *
325
+ * Keep in mind, that the message must be already translated.
326
+ *
327
+ * ```json
328
+ * {
329
+ * "field": "id",
330
+ * "state": "Error",
331
+ * "message": "This field is invalid"
332
+ *}
333
+ *
334
+ * ```
335
+ *
336
+ */
337
+ __applyValueStates(...states) {
338
+ states.forEach(state => {
339
+ const fn = this.__getFieldNodeByPath(state.field);
340
+ if (fn !== undefined) {
341
+ fn.__meta.valueState = state.state;
342
+ fn.__meta.stateMessage = state.message;
343
+ const validStateBefore = fn.__meta.isValid;
344
+ fn.__meta.isValid = state.state !== ValueState.Error;
345
+ if (fn.__meta.isValid !== validStateBefore) {
346
+ fn.__dispatchEvent(new CustomEvent('validity-changed', {
347
+ detail: fn,
348
+ bubbles: true,
349
+ }));
350
+ }
351
+ fn.__dispatchEvent(new CustomEvent('state-changed', {
352
+ detail: fn,
353
+ bubbles: false,
354
+ }));
355
+ }
356
+ });
357
+ }
358
+ /**
359
+ * Returns the valid state of a node.
360
+ * - A node is valid when all children are valid.
361
+ * - A node is valid when freshly initialized, even when some children have invalid values.
362
+ *
363
+ * use `validate()` to be sure to have the correct validity state.
364
+ *
365
+ * Use __meta.StateMessage to receive the state of a node. The `__meta.StateMessage` is only available on an explicit field, where the validity state is populated upwards.
366
+ */
367
+ get __isValid() {
368
+ return this.__meta.isValid;
369
+ }
370
+ /**
371
+ * Initialized fields are pristine as long nothing changes inside the model.
372
+ *
373
+ * Use this on rootNodes only.
374
+ */
375
+ get __isPristine() {
376
+ return this.__meta.isPristine;
377
+ }
378
+ /**
379
+ * The toString() method of Object instances returns a string representing this object. This method is meant to be overridden by `CustomPrototypes.ToString`.
380
+ * If no `CustomPrototypes` is set, it will try to find a display_name attribute on the node and uses that for the output.
381
+ *
382
+ * If there is no display_name `[object TypeName]` is returned.
383
+ */
384
+ toString() {
385
+ const ts = ToString.get(this.__meta.typeName || '');
386
+ if (ts) {
387
+ return ts(this);
388
+ }
389
+ const found = this.__meta.nodeFields.find(fieldDescriptor => fieldDescriptor.fieldName === 'displayName');
390
+ if (found &&
391
+ this['displayName'].__meta.typeName ===
392
+ 'primitives.STRING') {
393
+ return this['displayName'].toString();
394
+ }
395
+ return `[object ${this.__meta.typeName}]`;
396
+ }
397
+ /**
398
+ * The valueOf() method of Object instances converts the this value to an object.
399
+ * This method is meant to be overridden by derived objects for custom type conversion logic.
400
+ */
401
+ valueOf() {
402
+ const ts = ValueOf.get(this.__meta.typeName || '');
403
+ if (ts) {
404
+ return ts(this);
405
+ }
406
+ return NaN;
407
+ }
408
+ /**
409
+ * Helper method to build up labels, placeholders,...
410
+ * @protected
411
+ */
412
+ __getBaseName() {
413
+ const parts = [];
414
+ this.___fieldNameBuilder(parts);
415
+ return parts.join('.');
416
+ }
417
+ /**
418
+ * Helper method to construct the __fieldPath
419
+ * @param parts
420
+ * @protected
421
+ */
422
+ ___fieldNameBuilder(parts) {
423
+ // stop if parent node has same typeName, we are on a recursion.
424
+ if (this.__meta.isRecursionNode || this.__meta.isAnyNode) {
425
+ parts.unshift(this.__meta.typeName);
426
+ }
427
+ else {
428
+ // do not add the index to the baseName on Array fields
429
+ if (!this.__meta.isArrayNode) {
430
+ // the root node does not have a fieldName, so we use the typeName
431
+ parts.unshift(
432
+ // eslint-disable-next-line no-nested-ternary
433
+ this.__meta.fieldName
434
+ ? OPEN_MODELS_OPTIONS.UseProtoNames
435
+ ? this.__toSnakeCase(this.__meta.fieldName)
436
+ : this.__meta.fieldName
437
+ : this.__meta.typeName || '');
438
+ }
439
+ this.__parentNode?.___fieldNameBuilder(parts);
440
+ }
441
+ return parts;
442
+ }
443
+ /**
444
+ * Validate the node and all child nodes of it.
445
+ */
446
+ __validate() {
447
+ const validStateBefore = this.__meta.isValid;
448
+ // trigger the local listeners
449
+ this.__validationExecuter(this);
450
+ // dispatch to children
451
+ let allChildrenValid = true;
452
+ this.__childNodes.forEach(child => {
453
+ if (child instanceof FieldNode) {
454
+ child.__validate();
455
+ if (!child.__isValid) {
456
+ allChildrenValid = false;
457
+ }
458
+ }
459
+ });
460
+ this.__meta.isValid = allChildrenValid && this.__isValid;
461
+ if (this.__meta.isValid !== validStateBefore) {
462
+ this.__dispatchEvent(new CustomEvent('validity-changed', {
463
+ detail: this,
464
+ bubbles: false,
465
+ }));
466
+ }
467
+ }
468
+ /**
469
+ * Validates all parents of an element. This is done when you set a value on any child/attribute of a node directly
470
+ * @param {FieldNode} node - Any FieldNode
471
+ */
472
+ __validateBottomUp(node) {
473
+ // "this" is usually the parent node of "node"
474
+ this.__validationExecuter(node);
475
+ // climb up and check children state to define the own state of "this"
476
+ node.__climbUpValidation();
477
+ }
478
+ /**
479
+ * Validates all parent nodes if a sub-field was changed.
480
+ * @protected
481
+ */
482
+ __climbUpValidation() {
483
+ const validStateBefore = this.__meta.isValid;
484
+ this.__validationExecuter(this);
485
+ let allChildrenValid = true;
486
+ this.__childNodes.forEach(child => {
487
+ if (child instanceof FieldNode) {
488
+ if (!child.__isValid) {
489
+ allChildrenValid = false;
490
+ }
491
+ }
492
+ });
493
+ // if any child is not valid, this is also not valid
494
+ this.__meta.isValid = allChildrenValid && this.__isValid;
495
+ if (this.__meta.isValid !== validStateBefore) {
496
+ this.__dispatchEvent(new CustomEvent('validity-changed', {
497
+ detail: this,
498
+ bubbles: false,
499
+ }));
500
+ }
501
+ if (this.__parentNode) {
502
+ this.__parentNode.__climbUpValidation();
503
+ }
504
+ }
505
+ /**
506
+ * Additional "constraint" checker for primitive types, i.e. INT32 can only range from -2147483648 to 2147483647, but js uses always a float64 to handle numbers
507
+ * @protected
508
+ */
509
+ // eslint-disable-next-line class-methods-use-this
510
+ __checkTypeBoundaries() {
511
+ return undefined;
512
+ }
513
+ /**
514
+ * Executes the validation process.
515
+ * @param node
516
+ * @protected
517
+ */
518
+ // eslint-disable-next-line class-methods-use-this
519
+ __validationExecuter(node) {
520
+ const validatorFunc = Validators.get(node.__meta.typeName);
521
+ const customConstraintsFunc = CustomConstraints.get(node.__meta.typeName);
522
+ const fieldConstraints = node.__getConstraints();
523
+ let constraintMessage;
524
+ constraintMessage = this.__checkTypeBoundaries();
525
+ if (validatorFunc ||
526
+ fieldConstraints ||
527
+ customConstraintsFunc ||
528
+ constraintMessage) {
529
+ if (fieldConstraints && !customConstraintsFunc) {
530
+ constraintMessage = node.__checkConstraints(fieldConstraints);
531
+ }
532
+ if (customConstraintsFunc) {
533
+ constraintMessage = customConstraintsFunc(node);
534
+ }
535
+ if (validatorFunc) {
536
+ validatorFunc(node);
537
+ }
538
+ else if (constraintMessage === undefined) {
539
+ node.__setValueState(ValueState.None, ['']);
540
+ }
541
+ if (constraintMessage !== undefined) {
542
+ node.__setValueState(ValueState.Error, constraintMessage);
543
+ }
544
+ }
545
+ else {
546
+ node.__setValueState(ValueState.None, ['']);
547
+ }
548
+ }
549
+ /**
550
+ * Receives all constraints of a node. Use this in your custom validators or components.
551
+ */
552
+ __getConstraints() {
553
+ if (this.__meta.isArrayNode && this.__parentNode?.__parentNode) {
554
+ const fieldDescriptor = this.__parentNode.__parentNode.__meta.nodeFields.find(f => f.fieldName === this.__parentNode.__meta.fieldName);
555
+ return fieldDescriptor?.constraints;
556
+ }
557
+ if (this.__parentNode) {
558
+ const fieldDescriptor = this.__parentNode.__meta.nodeFields.find(f => f.fieldName === this.__meta.fieldName);
559
+ return fieldDescriptor?.constraints;
560
+ }
561
+ return undefined;
562
+ }
563
+ /**
564
+ * Set the value state
565
+ * @param {ValueState} state - The state of the node.
566
+ * @param {string[]} message - Description for the formatter.
567
+ */
568
+ __setValueState(state, message) {
569
+ this.__meta.valueState = state;
570
+ this.__meta.stateMessage =
571
+ OPEN_MODELS_OPTIONS.valueStateMessageFormatter(message);
572
+ // set invalid on error state
573
+ // the event is sent from ...
574
+ this.__meta.isValid = state !== ValueState.Error;
575
+ this.__dispatchEvent(new CustomEvent('state-changed', {
576
+ detail: this,
577
+ bubbles: false,
578
+ }));
579
+ }
580
+ /**
581
+ * Resets the node to the last inserted literal or to the initial state.
582
+ * // todo: reset to default values not just the empty state
583
+ */
584
+ __reset() {
585
+ if (this.__meta.initialValue !== undefined) {
586
+ this.__updateWithLiteral(this.__meta.initialValue);
587
+ return;
588
+ }
589
+ // else re init every field downwards
590
+ this.__clear();
591
+ }
592
+ /**
593
+ * Clear clears the element downwards and set __isEmpty to true on all sub nodes.
594
+ *
595
+ * A cleared field is not populated on `__toLiteral` or `__toJson` when the option `EmitUnpopulated` or `EmitDefaultValues` is set to false.
596
+ */
597
+ __clear() {
598
+ this.__isEmpty = true;
599
+ // __clear every childNode too
600
+ this.__meta.nodeFields.forEach(descriptor => {
601
+ this[`_${descriptor.fieldName}`].__clear();
602
+ this.__notifyFieldValueChange(false);
603
+ });
604
+ // todo: set to value to init
605
+ // todo: set state to None
606
+ // todo: set valid to true
607
+ }
608
+ /**
609
+ * Helper method to update a skalar / primitive field of a type. Used by the generated models.
610
+ * Triggers also the validation and clearance, if needed.
611
+ *
612
+ * @param {FieldNode} targetNode - The field of a FieldNode
613
+ * @param {string | boolean | number} value - The value you want to set
614
+ * @protected
615
+ */
616
+ // eslint-disable-next-line class-methods-use-this
617
+ __PrimitivesSetter(targetNode, value) {
618
+ // do not do anything if current value equals val
619
+ if (targetNode._value !== value) {
620
+ targetNode._value = value; // eslint-disable-line no-param-reassign
621
+ targetNode.__isEmpty = false; // eslint-disable-line no-param-reassign
622
+ this.__validateBottomUp(targetNode);
623
+ targetNode.__notifyFieldValueChange(true);
624
+ }
625
+ }
626
+ /**
627
+ * Helper method to update a field of a type. Used by the generated models.
628
+ * Triggers also the validation and clearance, if needed.
629
+ *
630
+ * @param {FieldNode} targetNode - The field of a FieldNode
631
+ * @param {} literalData - The literal type matches the interface from ITypeName.
632
+ * @protected
633
+ */
634
+ // eslint-disable-next-line class-methods-use-this
635
+ __TypeSetter(targetNode, literalData) {
636
+ if (literalData === undefined || literalData === null) {
637
+ targetNode.__clear();
638
+ this.__validateBottomUp(targetNode);
639
+ targetNode.__notifyFieldValueChange(true);
640
+ return;
641
+ }
642
+ targetNode.__updateWithLiteral(literalData); // keep the references by getting the values
643
+ // this is the parent of the target_node
644
+ targetNode.___updateNotEmptyPath();
645
+ this.__validateBottomUp(targetNode);
646
+ targetNode.__notifyFieldValueChange(true);
647
+ }
648
+ /**
649
+ * Notifies field changes
650
+ * @param bubbles
651
+ * @protected
652
+ */
653
+ __notifyFieldValueChange(bubbles) {
654
+ this.__dispatchEvent(new CustomEvent('this-field-value-changed', {
655
+ detail: this,
656
+ bubbles: false,
657
+ }));
658
+ if (bubbles) {
659
+ this.__dispatchEvent(new CustomEvent('field-value-changed', {
660
+ detail: this,
661
+ bubbles: true,
662
+ }));
663
+ }
664
+ else {
665
+ this.__dispatchEvent(new CustomEvent('field-value-changed', {
666
+ detail: this,
667
+ bubbles: false,
668
+ }));
669
+ }
670
+ }
671
+ /**
672
+ * Returns the child nodes of a node.
673
+ */
674
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
675
+ get __childNodes() {
676
+ return this.__meta.nodeFields.map(field => this[field.fieldName]);
677
+ }
678
+ /**
679
+ * Broadcast an event to all child nodes of a field node.
680
+ * @param event
681
+ */
682
+ __broadcastEvent(event) {
683
+ // trigger the local listeners
684
+ this.__triggerNodeEvents(event);
685
+ // dispatch to children
686
+ this.__childNodes.forEach(child => {
687
+ if (child instanceof FieldNode) {
688
+ child.__broadcastEvent(event);
689
+ }
690
+ });
691
+ }
692
+ /**
693
+ * Dispatches a custom event on a FieldNode
694
+ * @param {CustomEvent} event - A generic custom event.
695
+ */
696
+ __dispatchEvent(event) {
697
+ // trigger the events on current node
698
+ this.__triggerNodeEvents(event);
699
+ // go to parent
700
+ if (this.__parentNode !== undefined && event.bubbles) {
701
+ return this.__parentNode.__dispatchEvent(event);
702
+ }
703
+ return event;
704
+ }
705
+ /**
706
+ * Helper method to invoke/execute the event on the current node
707
+ * @param event
708
+ * @protected
709
+ */
710
+ __triggerNodeEvents(event) {
711
+ if (this.__meta.eventListener.has(event.type) &&
712
+ this.__meta.eventListener.get(event.type).length > 0) {
713
+ this.__meta.eventListener
714
+ .get(event.type)
715
+ .forEach((t, i, listenerArray) => {
716
+ t.callbackfn(event);
717
+ if (t.options !== undefined &&
718
+ typeof t.options !== 'boolean' &&
719
+ t.options.once) {
720
+ // eslint-disable-next-line no-param-reassign
721
+ delete listenerArray[i];
722
+ }
723
+ });
724
+ }
725
+ }
726
+ /**
727
+ * Add a handler to a node
728
+ * @param {string} type - A case-sensitive string representing the event type to listen for.
729
+ * @param {function} listener - The object that receives a notification (an object that implements the Event interface) when an event of the specified type occurs. This must be null, an object with a handleEvent() method, or a JavaScript function. See The event listener callback for details on the callback itself.
730
+ * @param {} options - An object that specifies characteristics about the event listener. \n\nThe available option is `once:boolean`
731
+ */
732
+ __addEventListener(type, listener, options) {
733
+ if (!this.__meta.eventListener.has(type)) {
734
+ this.__meta.eventListener.set(type, []);
735
+ }
736
+ this.__meta.eventListener
737
+ .get(type)
738
+ .push({ callbackfn: listener, options });
739
+ }
740
+ // add a listener for a custom event, defined and triggered by yourself
741
+ // this could be something like 'focus-requested'
742
+ __addCustomEventListener(type, handler, options) {
743
+ if (!this.__meta.eventListener.has(type)) {
744
+ this.__meta.eventListener.set(type, []);
745
+ }
746
+ this.__meta.eventListener.get(type).push({ callbackfn: handler, options });
747
+ }
748
+ /**
749
+ * Removes the handler from a node
750
+ * @param type
751
+ * @param handler
752
+ * @param options
753
+ */
754
+ __removeEventListener(type, handler,
755
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
756
+ options) {
757
+ if (this.__meta.eventListener.has(type)) {
758
+ this.__meta.eventListener.set(type, this.__meta.eventListener
759
+ .get(type)
760
+ .filter(e => e.callbackfn !== handler));
761
+ }
762
+ }
763
+ /**
764
+ * Removes the handler from a node
765
+ * @param type
766
+ * @param handler
767
+ * @param options
768
+ */
769
+ __removeCustomEventListener(type, handler,
770
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
771
+ options) {
772
+ if (this.__meta.eventListener.has(type)) {
773
+ this.__meta.eventListener.set(type, this.__meta.eventListener
774
+ .get(type)
775
+ .filter(e => e.callbackfn !== handler));
776
+ }
777
+ }
778
+ /**
779
+ * if some child is not empty, set isEmpty to false, all the way up to the root node
780
+ * @private
781
+ */
782
+ ___updateNotEmptyPath() {
783
+ this.___isEmpty = false;
784
+ if (this.__parentNode !== undefined && this.__parentNode.__isEmpty) {
785
+ this.__parentNode.___updateNotEmptyPath();
786
+ }
787
+ }
788
+ // eslint-disable-next-line class-methods-use-this
789
+ __checkConstraints(fieldConstraints) {
790
+ if (fieldConstraints.required) {
791
+ if (this.__isEmpty) {
792
+ return ['constraint.violation.required'];
793
+ }
794
+ }
795
+ return undefined;
796
+ }
797
+ // eslint-disable-next-line class-methods-use-this
798
+ __toLowerCamelCase(string) {
799
+ if (OPEN_MODELS_OPTIONS.UseProtoNames) {
800
+ const [start, ...rest] = (string[0] === '_' ? string.replace('_', 'X') : string).split('_');
801
+ return (start +
802
+ rest
803
+ .map(s => {
804
+ if (s.length === 0)
805
+ return '';
806
+ return s[0].toUpperCase() + s.slice(1);
807
+ })
808
+ .join(''));
809
+ }
810
+ return string;
811
+ }
812
+ // eslint-disable-next-line class-methods-use-this
813
+ __toLowerCamelCaseWithoutXPrefix(string) {
814
+ if (OPEN_MODELS_OPTIONS.UseProtoNames) {
815
+ const [start, ...rest] = (string[0] === '_' ? string.replace('_', 'X') : string).split('_');
816
+ return (start +
817
+ rest
818
+ .map(s => {
819
+ if (s.length === 0)
820
+ return '';
821
+ return s[0].toUpperCase() + s.slice(1);
822
+ })
823
+ .join(''));
824
+ }
825
+ return string;
826
+ }
827
+ // eslint-disable-next-line class-methods-use-this
828
+ __toSnakeCase(string) {
829
+ return string
830
+ .split(/(?=[A-Z])/)
831
+ .map(word => word.toLowerCase())
832
+ .join('_');
833
+ }
834
+ }
835
+ //# sourceMappingURL=FieldNode.js.map