@acorex/platform 0.0.0-ACOREX

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 (116) hide show
  1. package/README.md +7 -0
  2. package/auth/README.md +3 -0
  3. package/common/README.md +3 -0
  4. package/core/README.md +4 -0
  5. package/fesm2022/acorex-platform-auth.mjs +1362 -0
  6. package/fesm2022/acorex-platform-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs +127 -0
  8. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-common.mjs +4601 -0
  10. package/fesm2022/acorex-platform-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-core.mjs +4374 -0
  12. package/fesm2022/acorex-platform-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-domain.mjs +3234 -0
  14. package/fesm2022/acorex-platform-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-builder.mjs +2847 -0
  16. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
  18. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-layout-components.mjs +8583 -0
  20. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-layout-designer.mjs +2474 -0
  22. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-layout-entity.mjs +19150 -0
  24. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-layout-views.mjs +1468 -0
  26. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-layout-widget-core.mjs +2950 -0
  28. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs +72 -0
  30. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs +158 -0
  32. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  34. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs +172 -0
  36. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs +111 -0
  38. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs +274 -0
  40. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs +64 -0
  42. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs +34 -0
  44. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-layout-widgets.mjs +29791 -0
  46. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-native.mjs +155 -0
  48. package/fesm2022/acorex-platform-native.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-runtime-catalog-command-definition.mjs +20 -0
  50. package/fesm2022/acorex-platform-runtime-catalog-command-definition.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-runtime-catalog-query-definition.mjs +20 -0
  52. package/fesm2022/acorex-platform-runtime-catalog-query-definition.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-runtime.mjs +899 -0
  54. package/fesm2022/acorex-platform-runtime.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs +160 -0
  56. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs +120 -0
  58. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs +237 -0
  60. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs +31 -0
  62. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs +25 -0
  64. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  66. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-themes-default.mjs +2589 -0
  68. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs +55 -0
  70. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs +57 -0
  72. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs +168 -0
  74. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs +65 -0
  76. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs +64 -0
  78. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-themes-shared.mjs +2125 -0
  80. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-workflow.mjs +2501 -0
  82. package/fesm2022/acorex-platform-workflow.mjs.map +1 -0
  83. package/fesm2022/acorex-platform.mjs +6 -0
  84. package/fesm2022/acorex-platform.mjs.map +1 -0
  85. package/layout/builder/README.md +1578 -0
  86. package/layout/components/README.md +3 -0
  87. package/layout/designer/README.md +4 -0
  88. package/layout/entity/README.md +4 -0
  89. package/layout/views/README.md +3 -0
  90. package/layout/widget-core/README.md +4 -0
  91. package/layout/widgets/README.md +3 -0
  92. package/native/README.md +4 -0
  93. package/package.json +103 -0
  94. package/runtime/README.md +3 -0
  95. package/themes/default/README.md +3 -0
  96. package/themes/shared/README.md +3 -0
  97. package/types/acorex-platform-auth.d.ts +680 -0
  98. package/types/acorex-platform-common.d.ts +2926 -0
  99. package/types/acorex-platform-core.d.ts +2896 -0
  100. package/types/acorex-platform-domain.d.ts +2353 -0
  101. package/types/acorex-platform-layout-builder.d.ts +926 -0
  102. package/types/acorex-platform-layout-components.d.ts +2903 -0
  103. package/types/acorex-platform-layout-designer.d.ts +422 -0
  104. package/types/acorex-platform-layout-entity.d.ts +3189 -0
  105. package/types/acorex-platform-layout-views.d.ts +667 -0
  106. package/types/acorex-platform-layout-widget-core.d.ts +1086 -0
  107. package/types/acorex-platform-layout-widgets.d.ts +5478 -0
  108. package/types/acorex-platform-native.d.ts +28 -0
  109. package/types/acorex-platform-runtime-catalog-command-definition.d.ts +137 -0
  110. package/types/acorex-platform-runtime-catalog-query-definition.d.ts +125 -0
  111. package/types/acorex-platform-runtime.d.ts +470 -0
  112. package/types/acorex-platform-themes-default.d.ts +573 -0
  113. package/types/acorex-platform-themes-shared.d.ts +170 -0
  114. package/types/acorex-platform-workflow.d.ts +1806 -0
  115. package/types/acorex-platform.d.ts +2 -0
  116. package/workflow/README.md +4 -0
@@ -0,0 +1,3234 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, NgModule, Injectable, makeEnvironmentProviders } from '@angular/core';
3
+ import { AXPRuntimeModule } from '@acorex/platform/runtime';
4
+
5
+ /**
6
+ * Token for entity CRUD setup. Consumed by AXPDomainModule.
7
+ * The actual provider is provideEntity() from @acorex/platform/layout/entity
8
+ * to avoid circular dependency (domain must not depend on layout/entity).
9
+ */
10
+ const AXP_ENTITY_CRUD_SETUP = new InjectionToken('AXP_ENTITY_CRUD_SETUP');
11
+
12
+ //#endregion
13
+ //#region ---- Injection Token ----
14
+ const AXP_ENTITY_DEFINITION_CRUD_SERVICE = new InjectionToken('AXP_ENTITY_DEFINITION_CRUD_SERVICE', {
15
+ providedIn: null,
16
+ factory: () => null,
17
+ });
18
+ //#endregion
19
+
20
+ //#region ---- Dependency Injection Tokens ----
21
+ /**
22
+ * Targeted middleware extension (name or regex) for property resolution.
23
+ */
24
+ const AXP_PROPERTY_EXTENSION = new InjectionToken('AXP_PROPERTY_EXTENSION');
25
+ /**
26
+ * Bootstrap hook: register property definitions known at build time.
27
+ */
28
+ const AXP_PROPERTY_SETUP = new InjectionToken('AXP_PROPERTY_SETUP');
29
+ /**
30
+ * Bootstrap hook: register global middleware for every property resolution.
31
+ */
32
+ const AXP_PROPERTY_MIDDLEWARE_SETUP = new InjectionToken('AXP_PROPERTY_MIDDLEWARE_SETUP');
33
+ /**
34
+ * Bootstrap hook: register on-demand property loaders.
35
+ */
36
+ const AXP_PROPERTY_LOADER_SETUP = new InjectionToken('AXP_PROPERTY_LOADER_SETUP');
37
+ //#endregion
38
+
39
+ //#region ---- Dependency Injection Tokens ----
40
+ /**
41
+ * Injection token for schema-specific middleware extensions.
42
+ *
43
+ * Used for targeted middleware that applies only to schemas matching
44
+ * specific patterns (name or regex). This enables fine-grained control
45
+ * over which schemas receive which middleware.
46
+ */
47
+ const AXP_DOMAIN_EXTENSION = new InjectionToken('AXP_DOMAIN_EXTENSION');
48
+ /**
49
+ * Injection token for schema setup initialization.
50
+ *
51
+ * Used during application bootstrap to register schemas that are
52
+ * known at build time. Multiple providers can use this token to
53
+ * contribute schemas to the registry.
54
+ */
55
+ const AXP_DOMAIN_SETUP = new InjectionToken('AXP_DOMAIN_SETUP');
56
+ /**
57
+ * Injection token for schema middleware setup initialization.
58
+ *
59
+ * Used during application bootstrap to register global middleware
60
+ * that applies to all schema resolutions. This enables centralized
61
+ * schema processing logic.
62
+ */
63
+ const AXP_DOMAIN_MIDDLEWARE_SETUP = new InjectionToken('AXP_DOMAIN_MIDDLEWARE_SETUP');
64
+ /**
65
+ * Injection token for schema loader setup initialization.
66
+ *
67
+ * Used during application bootstrap to register schema loaders
68
+ * that can provide schemas on-demand when they're not found in
69
+ * the registry.
70
+ */
71
+ const AXP_DOMAIN_LOADER_SETUP = new InjectionToken('AXP_DOMAIN_LOADER_SETUP');
72
+ //#endregion
73
+
74
+ class AXPDomainModule {
75
+ constructor() {
76
+ this._commandSetup = inject(AXP_ENTITY_CRUD_SETUP, { optional: true });
77
+ /**
78
+ * Optional bootstrap hooks for registered property definitions (see {@link AXP_PROPERTY_SETUP} and related tokens).
79
+ */
80
+ this._propertySetup = inject(AXP_PROPERTY_SETUP, { optional: true });
81
+ this._propertyMiddlewareSetup = inject(AXP_PROPERTY_MIDDLEWARE_SETUP, { optional: true });
82
+ this._propertyLoaderSetup = inject(AXP_PROPERTY_LOADER_SETUP, { optional: true });
83
+ /**
84
+ * Injection token for domain loader setup initialization.
85
+ *
86
+ * Used during application bootstrap to register domain loaders
87
+ * that can provide domain definitions on-demand when they're not found in
88
+ * the registry.
89
+ */
90
+ this._domainLoaderSetup = inject(AXP_DOMAIN_LOADER_SETUP, { optional: true });
91
+ this._domainMiddlewareSetup = inject(AXP_DOMAIN_MIDDLEWARE_SETUP, { optional: true });
92
+ this._domainSetup = inject(AXP_DOMAIN_SETUP, { optional: true });
93
+ }
94
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
95
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainModule, imports: [AXPRuntimeModule] }); }
96
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainModule, imports: [AXPRuntimeModule] }); }
97
+ }
98
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainModule, decorators: [{
99
+ type: NgModule,
100
+ args: [{
101
+ imports: [
102
+ AXPRuntimeModule
103
+ ]
104
+ }]
105
+ }] });
106
+
107
+ var AXPDomainActionSide;
108
+ (function (AXPDomainActionSide) {
109
+ AXPDomainActionSide["Client"] = "client";
110
+ AXPDomainActionSide["Server"] = "server";
111
+ AXPDomainActionSide["Both"] = "both";
112
+ })(AXPDomainActionSide || (AXPDomainActionSide = {}));
113
+ var AXPEntityCommandScope;
114
+ (function (AXPEntityCommandScope) {
115
+ AXPEntityCommandScope["TypeLevel"] = "typeLevel";
116
+ AXPEntityCommandScope["Selected"] = "selected";
117
+ AXPEntityCommandScope["Individual"] = "individual";
118
+ AXPEntityCommandScope["Section"] = "section";
119
+ })(AXPEntityCommandScope || (AXPEntityCommandScope = {}));
120
+
121
+ //#endregion
122
+
123
+ var AXPEntityType;
124
+ (function (AXPEntityType) {
125
+ AXPEntityType[AXPEntityType["Entity"] = 0] = "Entity";
126
+ AXPEntityType[AXPEntityType["AggregateRoot"] = 1] = "AggregateRoot";
127
+ AXPEntityType[AXPEntityType["ValueObject"] = 2] = "ValueObject";
128
+ })(AXPEntityType || (AXPEntityType = {}));
129
+
130
+ var AXPRelationshipKind;
131
+ (function (AXPRelationshipKind) {
132
+ AXPRelationshipKind[AXPRelationshipKind["Association"] = 0] = "Association";
133
+ AXPRelationshipKind[AXPRelationshipKind["Composition"] = 1] = "Composition";
134
+ AXPRelationshipKind[AXPRelationshipKind["Aggregation"] = 2] = "Aggregation";
135
+ })(AXPRelationshipKind || (AXPRelationshipKind = {}));
136
+ var AXPRelationshipCardinality;
137
+ (function (AXPRelationshipCardinality) {
138
+ AXPRelationshipCardinality[AXPRelationshipCardinality["OneToOne"] = 0] = "OneToOne";
139
+ AXPRelationshipCardinality[AXPRelationshipCardinality["OneToMany"] = 1] = "OneToMany";
140
+ AXPRelationshipCardinality[AXPRelationshipCardinality["ManyToMany"] = 2] = "ManyToMany";
141
+ })(AXPRelationshipCardinality || (AXPRelationshipCardinality = {}));
142
+
143
+ //#region ---- Serializable menu definition (storage / CRUD contract) ----
144
+ //#endregion
145
+
146
+ //#region ---- Serializable permission definition (storage / CRUD contract) ----
147
+ //#endregion
148
+
149
+ //#endregion
150
+
151
+ //#endregion
152
+
153
+ //#region ---- AXPAggregateModel ----
154
+ /**
155
+ * Simple runtime model for aggregate definitions with parent references and navigation helpers.
156
+ * Relations are stored in a separate collection; join when needed.
157
+ */
158
+ class AXPAggregateModel {
159
+ constructor(definition, parent) {
160
+ this.name = definition.name;
161
+ this.title = definition.title;
162
+ this.validations = definition.validations || [];
163
+ this.actions = definition.actions || [];
164
+ this.parent = parent;
165
+ // Extract entity references - convert all formats to simple string map
166
+ this.entityReferences = this.extractEntityReferences(definition.entities);
167
+ }
168
+ //#endregion
169
+ //#region ---- Entity Reference Extraction ----
170
+ /**
171
+ * Extract entity references from various entity definition formats
172
+ * and convert to simple string map
173
+ */
174
+ extractEntityReferences(entities) {
175
+ if (!entities) {
176
+ return {};
177
+ }
178
+ if (Array.isArray(entities)) {
179
+ // Format 1: Array of entity definitions - convert to self-references
180
+ const references = {};
181
+ for (const entity of entities) {
182
+ if (entity.name) {
183
+ // Self-reference format: module.aggregate.entity
184
+ references[entity.name] = `${this.parent.name}.${this.name}.${entity.name}`;
185
+ }
186
+ }
187
+ return references;
188
+ }
189
+ if (typeof entities === 'object') {
190
+ // Format 2: Object with string references - use as-is
191
+ return { ...entities };
192
+ }
193
+ return {};
194
+ }
195
+ //#endregion
196
+ //#region ---- Navigation Helpers ----
197
+ /**
198
+ * Get entity reference by name
199
+ *
200
+ * @param entityName Name of the entity
201
+ * @returns Entity reference string or undefined if not found
202
+ */
203
+ getEntityReference(entityName) {
204
+ return this.entityReferences[entityName];
205
+ }
206
+ /**
207
+ * Get full path for an entity in this aggregate
208
+ *
209
+ * @param entityName Name of the entity
210
+ * @returns Full entity path
211
+ */
212
+ getEntityPath(entityName) {
213
+ return `${this.parent.name}.${this.name}.${entityName}`;
214
+ }
215
+ /**
216
+ * Get all available entity names
217
+ *
218
+ * @returns Array of entity names
219
+ */
220
+ getEntityNames() {
221
+ return Object.keys(this.entityReferences);
222
+ }
223
+ /**
224
+ * Check if entity exists in this aggregate
225
+ *
226
+ * @param entityName Name of the entity
227
+ * @returns True if entity exists
228
+ */
229
+ hasEntity(entityName) {
230
+ return entityName in this.entityReferences;
231
+ }
232
+ //#endregion
233
+ //#region ---- Parent Navigation ----
234
+ /**
235
+ * Get parent module
236
+ *
237
+ * @returns Parent module model
238
+ */
239
+ getModule() {
240
+ return this.parent;
241
+ }
242
+ /**
243
+ * Get full aggregate path (module.aggregate)
244
+ *
245
+ * @returns Full path string
246
+ */
247
+ getPath() {
248
+ return `${this.parent.name}.${this.name}`;
249
+ }
250
+ //#endregion
251
+ //#region ---- Utility Methods ----
252
+ /**
253
+ * Get aggregate statistics
254
+ *
255
+ * @returns Statistics object
256
+ */
257
+ getStatistics() {
258
+ return {
259
+ entityCount: Object.keys(this.entityReferences).length,
260
+ actionCount: this.actions.length,
261
+ validationCount: this.validations.length
262
+ };
263
+ }
264
+ /**
265
+ * Convert back to interface definition
266
+ *
267
+ * @returns Aggregate definition
268
+ */
269
+ toDefinition() {
270
+ return {
271
+ name: this.name,
272
+ title: this.title,
273
+ entities: this.entityReferences, // Return as reference map
274
+ validations: this.validations,
275
+ actions: this.actions
276
+ };
277
+ }
278
+ //#endregion
279
+ //#region ---- Validation Methods ----
280
+ /**
281
+ * Validate the aggregate structure (synchronous validation only)
282
+ *
283
+ * @returns Array of validation errors
284
+ */
285
+ validate() {
286
+ const errors = [];
287
+ // Validate aggregate structure
288
+ if (!this.name)
289
+ errors.push('Aggregate name is required');
290
+ if (!this.title)
291
+ errors.push('Aggregate title is required');
292
+ // Validate entity references
293
+ for (const [entityName, entityRef] of Object.entries(this.entityReferences)) {
294
+ if (!entityRef || typeof entityRef !== 'string') {
295
+ errors.push(`Invalid entity reference for '${entityName}': must be a non-empty string`);
296
+ }
297
+ }
298
+ return errors;
299
+ }
300
+ }
301
+ //#endregion
302
+
303
+ //#region ---- AXPModuleModel ----
304
+ /**
305
+ * Simple runtime model for module definitions with navigation helpers.
306
+ *
307
+ * This model is a pure data structure that provides:
308
+ * - Access to module properties and metadata
309
+ * - Navigation helpers for finding aggregates
310
+ * - Hierarchy navigation and path generation
311
+ *
312
+ * All loading and resolution logic has been moved to AXPDomainRegistry.
313
+ */
314
+ class AXPModuleModel {
315
+ constructor(definition) {
316
+ this.aggregates = [];
317
+ this.name = definition.name;
318
+ this.title = definition.title;
319
+ // Initialize aggregates with parent reference (simplified constructor)
320
+ this.aggregates = (definition.aggregates || []).map(aggDef => new AXPAggregateModel(aggDef, this));
321
+ }
322
+ //#endregion
323
+ //#region ---- Navigation Helpers ----
324
+ /**
325
+ * Find aggregate by name
326
+ *
327
+ * @param name Aggregate name
328
+ * @returns Aggregate model or undefined if not found
329
+ */
330
+ findAggregate(name) {
331
+ return this.aggregates.find(agg => agg.name === name);
332
+ }
333
+ /**
334
+ * Get all aggregates
335
+ *
336
+ * @returns Array of all aggregate models
337
+ */
338
+ getAllAggregates() {
339
+ return [...this.aggregates];
340
+ }
341
+ /**
342
+ * Check if aggregate exists in this module
343
+ *
344
+ * @param name Aggregate name
345
+ * @returns True if aggregate exists
346
+ */
347
+ hasAggregate(name) {
348
+ return this.aggregates.some(agg => agg.name === name);
349
+ }
350
+ /**
351
+ * Get all aggregate names
352
+ *
353
+ * @returns Array of aggregate names
354
+ */
355
+ getAggregateNames() {
356
+ return this.aggregates.map(agg => agg.name);
357
+ }
358
+ /**
359
+ * Find entity reference across all aggregates
360
+ *
361
+ * @param entityName Entity name to search for
362
+ * @returns Object with aggregate and entity reference, or undefined if not found
363
+ */
364
+ findEntityReference(entityName) {
365
+ for (const aggregate of this.aggregates) {
366
+ const entityReference = aggregate.getEntityReference(entityName);
367
+ if (entityReference) {
368
+ return { aggregate, entityReference };
369
+ }
370
+ }
371
+ return undefined;
372
+ }
373
+ /**
374
+ * Get entity path for a specific entity in a specific aggregate
375
+ *
376
+ * @param aggregateName Aggregate name
377
+ * @param entityName Entity name
378
+ * @returns Full entity path
379
+ */
380
+ getEntityPath(aggregateName, entityName) {
381
+ return `${this.name}.${aggregateName}.${entityName}`;
382
+ }
383
+ /**
384
+ * Get aggregate path
385
+ *
386
+ * @param aggregateName Aggregate name
387
+ * @returns Full aggregate path
388
+ */
389
+ getAggregatePath(aggregateName) {
390
+ return `${this.name}.${aggregateName}`;
391
+ }
392
+ //#endregion
393
+ //#region ---- Query Methods ----
394
+ /**
395
+ * Get all entity references across all aggregates
396
+ *
397
+ * @returns Map of entity name to full path
398
+ */
399
+ getAllEntityReferences() {
400
+ const allReferences = {};
401
+ for (const aggregate of this.aggregates) {
402
+ const aggregateReferences = aggregate.entityReferences;
403
+ for (const [entityName, entityRef] of Object.entries(aggregateReferences)) {
404
+ // Use full path as key to avoid conflicts
405
+ const fullKey = `${aggregate.name}.${entityName}`;
406
+ allReferences[fullKey] = entityRef;
407
+ }
408
+ }
409
+ return allReferences;
410
+ }
411
+ /**
412
+ * Relations are stored in a separate collection. Use relation queries for join when needed.
413
+ * @deprecated Relations are no longer nested in aggregates.
414
+ */
415
+ getAllRelations() {
416
+ return [];
417
+ }
418
+ //#endregion
419
+ //#region ---- Validation Methods ----
420
+ /**
421
+ * Validate the module structure (synchronous validation only)
422
+ *
423
+ * @returns Array of validation errors
424
+ */
425
+ validate() {
426
+ const errors = [];
427
+ // Validate module structure
428
+ if (!this.name)
429
+ errors.push('Module name is required');
430
+ if (!this.title)
431
+ errors.push('Module title is required');
432
+ // Check for duplicate aggregate names
433
+ const aggregateNames = this.aggregates.map(agg => agg.name);
434
+ const duplicateNames = aggregateNames.filter((name, index) => aggregateNames.indexOf(name) !== index);
435
+ if (duplicateNames.length > 0) {
436
+ errors.push(`Duplicate aggregate names: ${duplicateNames.join(', ')}`);
437
+ }
438
+ // Validate aggregates
439
+ for (const aggregate of this.aggregates) {
440
+ const aggErrors = aggregate.validate();
441
+ if (aggErrors.length > 0) {
442
+ errors.push(...aggErrors.map((err) => `${aggregate.name}: ${err}`));
443
+ }
444
+ }
445
+ return errors;
446
+ }
447
+ //#endregion
448
+ //#region ---- Utility Methods ----
449
+ /**
450
+ * Get module statistics
451
+ *
452
+ * @returns Statistics object
453
+ */
454
+ getStatistics() {
455
+ const aggregateStats = this.aggregates.map(agg => agg.getStatistics());
456
+ return {
457
+ aggregateCount: this.aggregates.length,
458
+ entityCount: aggregateStats.reduce((sum, stat) => sum + stat.entityCount, 0),
459
+ actionCount: aggregateStats.reduce((sum, stat) => sum + stat.actionCount, 0),
460
+ validationCount: aggregateStats.reduce((sum, stat) => sum + stat.validationCount, 0)
461
+ };
462
+ }
463
+ /**
464
+ * Convert back to interface definition
465
+ *
466
+ * @returns Module definition
467
+ */
468
+ toDefinition() {
469
+ return {
470
+ name: this.name,
471
+ title: this.title,
472
+ aggregates: this.aggregates.map(agg => agg.toDefinition())
473
+ };
474
+ }
475
+ }
476
+ //#endregion
477
+
478
+ //#region ---- AXPPropertyModel ----
479
+ /**
480
+ * Runtime model for a {@link AXPPropertyDefinition} with helper methods.
481
+ */
482
+ class AXPPropertyModel {
483
+ constructor(definition) {
484
+ this.name = definition.name;
485
+ this.title = definition.title;
486
+ this.interface = definition.interface;
487
+ this.disabled = definition.disabled;
488
+ this.dataType = definition.dataType ?? 'string';
489
+ this.validations = definition.validations ?? [];
490
+ this.features = definition.features ?? {};
491
+ this.metadata = definition.metadata;
492
+ this.description = definition.description;
493
+ this.icon = definition.icon;
494
+ this.defaultValue = definition.defaultValue;
495
+ }
496
+ //#endregion
497
+ /**
498
+ * Get widget options
499
+ */
500
+ getWidgetOptions() {
501
+ return this.interface.options;
502
+ }
503
+ /**
504
+ * Check if the property is disabled
505
+ */
506
+ isDisabled() {
507
+ return this.disabled ?? false;
508
+ }
509
+ //#endregion
510
+ //#region ---- Feature Methods ----
511
+ /**
512
+ * Check if searchable
513
+ */
514
+ isSearchable() {
515
+ return this.features.searchable?.enabled ?? false;
516
+ }
517
+ /**
518
+ * Check if full text searchable
519
+ */
520
+ isFullTextSearchable() {
521
+ return this.features.searchable?.fullText ?? false;
522
+ }
523
+ /**
524
+ * Check if filterable
525
+ */
526
+ isFilterable() {
527
+ return this.features.filterable?.enabled ?? false;
528
+ }
529
+ /**
530
+ * Check if inline filterable
531
+ */
532
+ isInlineFilterable() {
533
+ return this.features.filterable?.inline ?? false;
534
+ }
535
+ /**
536
+ * Check if sortable
537
+ */
538
+ isSortable() {
539
+ return this.features.sortable?.enabled ?? false;
540
+ }
541
+ /**
542
+ * Check if auditable
543
+ */
544
+ isAuditable() {
545
+ return this.features.auditable?.enabled ?? false;
546
+ }
547
+ //#endregion
548
+ //#region ---- Validation Methods ----
549
+ /**
550
+ * Validate the property definition structure
551
+ */
552
+ async validate() {
553
+ const errors = [];
554
+ // Validate interface
555
+ if (!this.interface.name) {
556
+ errors.push('Widget type is required');
557
+ }
558
+ return errors;
559
+ }
560
+ //#endregion
561
+ //#region ---- Utility Methods ----
562
+ /**
563
+ * Get property capabilities
564
+ */
565
+ getCapabilities() {
566
+ return {
567
+ searchable: this.isSearchable(),
568
+ fullTextSearchable: this.isFullTextSearchable(),
569
+ filterable: this.isFilterable(),
570
+ inlineFilterable: this.isInlineFilterable(),
571
+ sortable: this.isSortable(),
572
+ auditable: this.isAuditable(),
573
+ disabled: this.isDisabled()
574
+ };
575
+ }
576
+ /**
577
+ * Get property summary
578
+ */
579
+ getSummary() {
580
+ const capabilities = this.getCapabilities();
581
+ const capabilityCount = Object.values(capabilities).filter(Boolean).length;
582
+ return {
583
+ widget: this.interface.name,
584
+ hasOptions: !!this.interface.options,
585
+ hasMetadata: !!this.metadata,
586
+ hasValidations: !!this.validations && this.validations.length > 0,
587
+ capabilityCount
588
+ };
589
+ }
590
+ /**
591
+ * Convert back to interface definition
592
+ */
593
+ toDefinition() {
594
+ return {
595
+ name: this.name,
596
+ title: this.title,
597
+ description: this.description,
598
+ icon: this.icon,
599
+ dataType: this.dataType,
600
+ defaultValue: this.defaultValue,
601
+ disabled: this.disabled,
602
+ interface: this.interface,
603
+ validations: this.validations,
604
+ features: this.features,
605
+ metadata: this.metadata
606
+ };
607
+ }
608
+ }
609
+
610
+ //#region ---- Property definition helpers ----
611
+ /**
612
+ * Strips entity-only fields so the payload matches {@link AXPPropertyDefinition}.
613
+ */
614
+ function toPropertyDefinition(def) {
615
+ const { actions, fieldFeatures, ...propertyDefinition } = def;
616
+ return propertyDefinition;
617
+ }
618
+ //#endregion
619
+ //#region ---- AXPEntityFieldModel ----
620
+ /**
621
+ * Runtime model for entity field definitions with parent references and helper methods
622
+ */
623
+ class AXPEntityFieldModel {
624
+ constructor(definition, parent, propertyService) {
625
+ this.entityPropertyDefinition = definition;
626
+ this.name = definition.name;
627
+ this.title = definition.title;
628
+ this.description = definition.description;
629
+ this.validations = definition.validations;
630
+ this.actions = definition.actions;
631
+ this.fieldFeatures = definition.fieldFeatures;
632
+ this.defaultValue = definition.defaultValue;
633
+ this.parent = parent;
634
+ this.propertyService = propertyService;
635
+ this.property = new AXPPropertyModel(toPropertyDefinition(definition));
636
+ this.propertyName = definition.name;
637
+ }
638
+ //#region ---- Feature Check Methods ----
639
+ /**
640
+ * Check if field is nullable
641
+ */
642
+ isNullable() {
643
+ return this.fieldFeatures?.nullable ?? false;
644
+ }
645
+ /**
646
+ * Check if field is readonly
647
+ */
648
+ isReadonly() {
649
+ return this.fieldFeatures?.readOnly ?? false;
650
+ }
651
+ /**
652
+ * Check if field is required (has required validation)
653
+ */
654
+ isRequired() {
655
+ return this.validations?.some((validation) => validation.rule === "required") ?? false;
656
+ }
657
+ /**
658
+ * Check if field is searchable
659
+ */
660
+ isSearchable() {
661
+ return this.property.features?.searchable?.enabled ?? false;
662
+ }
663
+ /**
664
+ * Check if field supports full text search
665
+ */
666
+ isFullTextSearchable() {
667
+ return this.property.features?.searchable?.fullText ?? false;
668
+ }
669
+ /**
670
+ * Check if field is filterable
671
+ */
672
+ isFilterable() {
673
+ return this.property.features?.filterable?.enabled ?? false;
674
+ }
675
+ /**
676
+ * Check if field has inline filtering
677
+ */
678
+ hasInlineFiltering() {
679
+ return this.property.features?.filterable?.inline ?? false;
680
+ }
681
+ /**
682
+ * Check if field is sortable
683
+ */
684
+ isSortable() {
685
+ return this.property.features?.sortable?.enabled ?? false;
686
+ }
687
+ /**
688
+ * Check if field is auditable
689
+ */
690
+ isAuditable() {
691
+ return this.property.features?.auditable?.enabled ?? false;
692
+ }
693
+ //#endregion
694
+ //#region ---- Property info ----
695
+ /**
696
+ * Registered property name (same as entity field name in the embedded model).
697
+ */
698
+ getPropertyName() {
699
+ return this.propertyName;
700
+ }
701
+ /**
702
+ * Registration metadata when this field's definition name is registered; otherwise embedded summary.
703
+ */
704
+ getPropertyInfo() {
705
+ if (this.propertyService.isRegistered(this.property.name)) {
706
+ return this.propertyService.getRegisteredProperty(this.property.name);
707
+ }
708
+ return this.property.getSummary();
709
+ }
710
+ /**
711
+ * Rebuild the property model from embedded definition (e.g. after definition updates).
712
+ */
713
+ refreshProperty() {
714
+ this.property = new AXPPropertyModel(toPropertyDefinition(this.entityPropertyDefinition));
715
+ }
716
+ //#endregion
717
+ //#region ---- Navigation Methods ----
718
+ /**
719
+ * Get parent entity
720
+ */
721
+ getEntity() {
722
+ return this.parent;
723
+ }
724
+ /**
725
+ * Get parent aggregate
726
+ */
727
+ getAggregate() {
728
+ return this.parent?.parent;
729
+ }
730
+ /**
731
+ * Get parent module
732
+ */
733
+ getModule() {
734
+ return this.parent?.parent?.parent;
735
+ }
736
+ /**
737
+ * Get full path (module.aggregate.entity.field)
738
+ */
739
+ getPath() {
740
+ return `${this.parent.parent.parent.name}.${this.parent.parent.name}.${this.parent.name}.${this.name}`;
741
+ }
742
+ //#endregion
743
+ //#region ---- Validation Methods ----
744
+ /**
745
+ * Validate the field structure
746
+ */
747
+ async validate() {
748
+ const errors = [];
749
+ if (!this.name)
750
+ errors.push("Field name is required");
751
+ if (!this.title)
752
+ errors.push("Field title is required");
753
+ if (!this.property.interface?.name) {
754
+ errors.push("Widget type (interface.name) is required");
755
+ }
756
+ if (this.property) {
757
+ const propertyErrors = await this.property.validate();
758
+ if (propertyErrors.length > 0) {
759
+ errors.push(...propertyErrors.map((err) => `Property: ${err}`));
760
+ }
761
+ }
762
+ return errors;
763
+ }
764
+ //#endregion
765
+ //#region ---- Utility Methods ----
766
+ /**
767
+ * Get field statistics
768
+ */
769
+ getStatistics() {
770
+ return {
771
+ hasValidations: !!this.validations && this.validations.length > 0,
772
+ hasActions: !!this.actions && this.actions.length > 0,
773
+ hasFieldFeatures: !!this.fieldFeatures,
774
+ hasDefaultValue: this.defaultValue !== undefined,
775
+ validationCount: this.validations?.length ?? 0,
776
+ actionCount: this.actions?.length ?? 0,
777
+ };
778
+ }
779
+ /**
780
+ * Get field capabilities
781
+ */
782
+ getCapabilities() {
783
+ return {
784
+ searchable: this.isSearchable(),
785
+ filterable: this.isFilterable(),
786
+ sortable: this.isSortable(),
787
+ auditable: this.isAuditable(),
788
+ nullable: this.isNullable(),
789
+ readonly: this.isReadonly(),
790
+ required: this.isRequired(),
791
+ };
792
+ }
793
+ /**
794
+ * Convert back to interface definition
795
+ */
796
+ toDefinition() {
797
+ return {
798
+ ...this.property.toDefinition(),
799
+ actions: this.actions,
800
+ fieldFeatures: this.fieldFeatures,
801
+ };
802
+ }
803
+ }
804
+ //#endregion
805
+
806
+ //#region ---- AXPEntityModel ----
807
+ /**
808
+ * Runtime model for entity definitions with parent references and helper methods
809
+ */
810
+ class AXPEntityModel {
811
+ constructor(definition, parent, propertyService) {
812
+ this.fields = [];
813
+ this.name = definition.name;
814
+ this.title = definition.title;
815
+ this.type = definition.type;
816
+ this.parent = parent;
817
+ this.propertyService = propertyService;
818
+ // Initialize fields with parent reference and property service
819
+ this.fields = definition.fields.map(fieldDef => new AXPEntityFieldModel(fieldDef, this, propertyService));
820
+ }
821
+ //#endregion
822
+ //#region ---- Query Methods ----
823
+ /**
824
+ * Find field by name
825
+ */
826
+ findField(name) {
827
+ return this.fields.find(field => field.name === name);
828
+ }
829
+ /**
830
+ * Get all fields
831
+ */
832
+ getAllFields() {
833
+ return [...this.fields];
834
+ }
835
+ /**
836
+ * Get required fields
837
+ */
838
+ getRequiredFields() {
839
+ return this.fields.filter(field => field.isRequired());
840
+ }
841
+ /**
842
+ * Get readonly fields
843
+ */
844
+ getReadonlyFields() {
845
+ return this.fields.filter(field => field.isReadonly());
846
+ }
847
+ /**
848
+ * Get searchable fields
849
+ */
850
+ getSearchableFields() {
851
+ return this.fields.filter(field => field.isSearchable());
852
+ }
853
+ /**
854
+ * Get filterable fields
855
+ */
856
+ getFilterableFields() {
857
+ return this.fields.filter(field => field.isFilterable());
858
+ }
859
+ /**
860
+ * Get sortable fields
861
+ */
862
+ getSortableFields() {
863
+ return this.fields.filter(field => field.isSortable());
864
+ }
865
+ //#endregion
866
+ //#region ---- Type Checking Methods ----
867
+ /**
868
+ * Check if this entity is an aggregate root
869
+ */
870
+ isAggregateRoot() {
871
+ return this.type === AXPEntityType.AggregateRoot;
872
+ }
873
+ /**
874
+ * Check if this entity is a regular entity
875
+ */
876
+ isEntity() {
877
+ return this.type === AXPEntityType.Entity;
878
+ }
879
+ /**
880
+ * Check if this entity is a value object
881
+ */
882
+ isValueObject() {
883
+ return this.type === AXPEntityType.ValueObject;
884
+ }
885
+ //#endregion
886
+ //#region ---- Navigation Methods ----
887
+ /**
888
+ * Get parent aggregate
889
+ */
890
+ getAggregate() {
891
+ return this.parent;
892
+ }
893
+ /**
894
+ * Get parent module
895
+ */
896
+ getModule() {
897
+ return this.parent?.parent;
898
+ }
899
+ /**
900
+ * Get full path (module.aggregate.entity)
901
+ */
902
+ getPath() {
903
+ return `${this.parent.parent.name}.${this.parent.name}.${this.name}`;
904
+ }
905
+ //#endregion
906
+ //#region ---- Validation Methods ----
907
+ /**
908
+ * Validate the entity structure
909
+ */
910
+ async validate() {
911
+ const errors = [];
912
+ // Validate entity structure
913
+ if (!this.name)
914
+ errors.push('Entity name is required');
915
+ if (!this.title)
916
+ errors.push('Entity title is required');
917
+ // Validate fields
918
+ for (const field of this.fields) {
919
+ const fieldErrors = await field.validate();
920
+ if (fieldErrors.length > 0) {
921
+ errors.push(...fieldErrors.map(err => `${field.name}: ${err}`));
922
+ }
923
+ }
924
+ return errors;
925
+ }
926
+ //#endregion
927
+ //#region ---- Utility Methods ----
928
+ /**
929
+ * Get entity statistics
930
+ */
931
+ getStatistics() {
932
+ return {
933
+ fieldCount: this.fields.length,
934
+ requiredFieldCount: this.getRequiredFields().length,
935
+ readonlyFieldCount: this.getReadonlyFields().length,
936
+ searchableFieldCount: this.getSearchableFields().length,
937
+ filterableFieldCount: this.getFilterableFields().length,
938
+ sortableFieldCount: this.getSortableFields().length
939
+ };
940
+ }
941
+ /**
942
+ * Convert back to interface definition
943
+ */
944
+ toDefinition() {
945
+ return {
946
+ name: this.name,
947
+ title: this.title,
948
+ fields: this.fields.map(field => field.toDefinition()),
949
+ type: this.type
950
+ };
951
+ }
952
+ }
953
+
954
+ //#region ---- AXPRelationModel ----
955
+ /**
956
+ * Runtime model for standalone relation definitions.
957
+ * Relations live in their own collection; join when needed.
958
+ */
959
+ class AXPRelationModel {
960
+ constructor(definition, parent) {
961
+ this.type = definition.type;
962
+ this.kind = definition.kind;
963
+ this.target = { ...definition.target };
964
+ this.source = { ...definition.source };
965
+ this.isRequired = definition.isRequired;
966
+ this.parent = parent;
967
+ }
968
+ //#endregion
969
+ //#region ---- Cardinality Methods ----
970
+ /**
971
+ * Check if relation is one-to-one
972
+ */
973
+ isOneToOne() {
974
+ return this.type === AXPRelationshipCardinality.OneToOne;
975
+ }
976
+ /**
977
+ * Check if relation is one-to-many
978
+ */
979
+ isOneToMany() {
980
+ return this.type === AXPRelationshipCardinality.OneToMany;
981
+ }
982
+ /**
983
+ * Check if relation is many-to-many
984
+ */
985
+ isManyToMany() {
986
+ return this.type === AXPRelationshipCardinality.ManyToMany;
987
+ }
988
+ //#endregion
989
+ //#region ---- Kind Methods ----
990
+ /**
991
+ * Check if relation is association
992
+ */
993
+ isAssociation() {
994
+ return this.kind === AXPRelationshipKind.Association;
995
+ }
996
+ /**
997
+ * Check if relation is composition
998
+ */
999
+ isComposition() {
1000
+ return this.kind === AXPRelationshipKind.Composition;
1001
+ }
1002
+ /**
1003
+ * Check if relation is aggregation
1004
+ */
1005
+ isAggregation() {
1006
+ return this.kind === AXPRelationshipKind.Aggregation;
1007
+ }
1008
+ //#endregion
1009
+ //#region ---- Navigation Methods ----
1010
+ /**
1011
+ * Get parent aggregate
1012
+ */
1013
+ getAggregate() {
1014
+ return this.parent;
1015
+ }
1016
+ /**
1017
+ * Get parent module
1018
+ */
1019
+ getModule() {
1020
+ return this.parent?.parent;
1021
+ }
1022
+ /**
1023
+ * Get full path (module.aggregate.relation)
1024
+ */
1025
+ getPath() {
1026
+ return `${this.parent.parent.name}.${this.parent.name}.${this.source.entity}->${this.target.entity}`;
1027
+ }
1028
+ //#endregion
1029
+ //#region ---- Query Methods ----
1030
+ /**
1031
+ * Get relation description
1032
+ */
1033
+ getDescription() {
1034
+ const cardinalityDesc = this.getCardinalityDescription();
1035
+ const kindDesc = this.getKindDescription();
1036
+ const requiredDesc = this.isRequired ? 'required' : 'optional';
1037
+ return `${cardinalityDesc} ${kindDesc} (${requiredDesc})`;
1038
+ }
1039
+ /**
1040
+ * Get cardinality description
1041
+ */
1042
+ getCardinalityDescription() {
1043
+ switch (this.type) {
1044
+ case AXPRelationshipCardinality.OneToOne:
1045
+ return 'one-to-one';
1046
+ case AXPRelationshipCardinality.OneToMany:
1047
+ return 'one-to-many';
1048
+ case AXPRelationshipCardinality.ManyToMany:
1049
+ return 'many-to-many';
1050
+ default:
1051
+ return 'unknown';
1052
+ }
1053
+ }
1054
+ /**
1055
+ * Get kind description
1056
+ */
1057
+ getKindDescription() {
1058
+ switch (this.kind) {
1059
+ case AXPRelationshipKind.Association:
1060
+ return 'association';
1061
+ case AXPRelationshipKind.Composition:
1062
+ return 'composition';
1063
+ case AXPRelationshipKind.Aggregation:
1064
+ return 'aggregation';
1065
+ default:
1066
+ return 'unknown';
1067
+ }
1068
+ }
1069
+ /**
1070
+ * Get relation summary
1071
+ */
1072
+ getSummary() {
1073
+ return `${this.source.entity}.${this.source.key} -> ${this.target.aggregate}.${this.target.entity}.${this.target.key}`;
1074
+ }
1075
+ //#endregion
1076
+ //#region ---- Validation Methods ----
1077
+ /**
1078
+ * Validate the relation structure
1079
+ */
1080
+ validate() {
1081
+ const errors = [];
1082
+ // Validate source
1083
+ if (!this.source.entity)
1084
+ errors.push('Source entity is required');
1085
+ if (!this.source.key)
1086
+ errors.push('Source key is required');
1087
+ // Validate target
1088
+ if (!this.target.aggregate)
1089
+ errors.push('Target aggregate is required');
1090
+ if (!this.target.entity)
1091
+ errors.push('Target entity is required');
1092
+ if (!this.target.key)
1093
+ errors.push('Target key is required');
1094
+ return errors;
1095
+ }
1096
+ //#endregion
1097
+ //#region ---- Utility Methods ----
1098
+ /**
1099
+ * Get relation characteristics
1100
+ */
1101
+ getCharacteristics() {
1102
+ return {
1103
+ type: this.getCardinalityDescription(),
1104
+ kind: this.getKindDescription(),
1105
+ required: this.isRequired,
1106
+ isOwning: this.isComposition(),
1107
+ isNavigable: !this.isComposition() || this.isOneToOne()
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Check if relation involves entity
1112
+ */
1113
+ involvesEntity(entityName) {
1114
+ return this.source.entity === entityName || this.target.entity === entityName;
1115
+ }
1116
+ /**
1117
+ * Check if relation targets aggregate
1118
+ */
1119
+ targetsAggregate(aggregateName) {
1120
+ return this.target.aggregate === aggregateName;
1121
+ }
1122
+ /**
1123
+ * Get the other entity in the relation
1124
+ */
1125
+ getOtherEntity(entityName) {
1126
+ if (this.source.entity === entityName) {
1127
+ return this.target.entity;
1128
+ }
1129
+ else if (this.target.entity === entityName) {
1130
+ return this.source.entity;
1131
+ }
1132
+ return null;
1133
+ }
1134
+ /**
1135
+ * Convert back to interface definition (AXPRelationDefinition with full source/target)
1136
+ */
1137
+ toDefinition() {
1138
+ return {
1139
+ type: this.type,
1140
+ kind: this.kind,
1141
+ target: { ...this.target },
1142
+ source: { ...this.source },
1143
+ isRequired: this.isRequired
1144
+ };
1145
+ }
1146
+ }
1147
+
1148
+ //#region ---- AXPModuleHelper ----
1149
+ /**
1150
+ * Helper utility class for working with module models
1151
+ *
1152
+ * @deprecated This helper class is deprecated and will be updated to work with the new domain registry system.
1153
+ * Use AXPDomainService for domain operations instead.
1154
+ *
1155
+ * TODO: Update this helper to work with the new simplified models and domain registry system.
1156
+ */
1157
+ class AXPModuleHelper {
1158
+ //#endregion
1159
+ //#region ---- Deprecated Methods - To Be Updated ----
1160
+ /**
1161
+ * @deprecated Use AXPDomainService.findEntitiesByName() instead
1162
+ */
1163
+ static findEntitiesByName(module, name) {
1164
+ console.warn('AXPModuleHelper.findEntitiesByName is deprecated. Use AXPDomainService instead.');
1165
+ return [];
1166
+ }
1167
+ /**
1168
+ * @deprecated Use AXPDomainService with entity field queries instead
1169
+ */
1170
+ static findFieldsByName(module, fieldName) {
1171
+ console.warn('AXPModuleHelper.findFieldsByName is deprecated. Use AXPDomainService instead.');
1172
+ return [];
1173
+ }
1174
+ /**
1175
+ * @deprecated Use AXPDomainService with entity type queries instead
1176
+ */
1177
+ static findEntitiesByType(module, type) {
1178
+ console.warn('AXPModuleHelper.findEntitiesByType is deprecated. Use AXPDomainService instead.');
1179
+ return [];
1180
+ }
1181
+ /**
1182
+ * @deprecated Use AXPDomainService with field validation queries instead
1183
+ */
1184
+ static findFieldsWithValidation(module, validationRule) {
1185
+ console.warn('AXPModuleHelper.findFieldsWithValidation is deprecated. Use AXPDomainService instead.');
1186
+ return [];
1187
+ }
1188
+ /**
1189
+ * @deprecated Use AXPDomainService with field queries instead
1190
+ */
1191
+ static findFieldsByDataType(module, dataType) {
1192
+ console.warn('AXPModuleHelper.findFieldsByDataType is deprecated. Use AXPDomainService instead.');
1193
+ return [];
1194
+ }
1195
+ /**
1196
+ * @deprecated Use AXPDomainService with required field queries instead
1197
+ */
1198
+ static findRequiredFields(module) {
1199
+ console.warn('AXPModuleHelper.findRequiredFields is deprecated. Use AXPDomainService instead.');
1200
+ return [];
1201
+ }
1202
+ /**
1203
+ * @deprecated Use AXPDomainService with nullable field queries instead
1204
+ */
1205
+ static findNullableFields(module) {
1206
+ console.warn('AXPModuleHelper.findNullableFields is deprecated. Use AXPDomainService instead.');
1207
+ return [];
1208
+ }
1209
+ /**
1210
+ * @deprecated Use AXPDomainService with default value queries instead
1211
+ */
1212
+ static findFieldsWithDefaultValues(module) {
1213
+ console.warn('AXPModuleHelper.findFieldsWithDefaultValues is deprecated. Use AXPDomainService instead.');
1214
+ return [];
1215
+ }
1216
+ /**
1217
+ * @deprecated Use AXPDomainService with registered property name queries instead
1218
+ */
1219
+ static findFieldsByRegisteredPropertyName(module, propertyName) {
1220
+ console.warn('AXPModuleHelper.findFieldsByRegisteredPropertyName is deprecated. Use AXPDomainService instead.');
1221
+ return [];
1222
+ }
1223
+ /**
1224
+ * @deprecated Use module.validate() method instead
1225
+ */
1226
+ static validateModule(module) {
1227
+ return module.validate();
1228
+ }
1229
+ /**
1230
+ * @deprecated Use aggregate relation queries instead
1231
+ */
1232
+ static findRelationsByType(module, relationType) {
1233
+ console.warn('AXPModuleHelper.findRelationsByType is deprecated. Use relation queries instead.');
1234
+ return [];
1235
+ }
1236
+ /**
1237
+ * @deprecated Use AXPDomainService with entity dependency queries instead
1238
+ */
1239
+ static findEntityDependencies(module, entityName) {
1240
+ console.warn('AXPModuleHelper.findEntityDependencies is deprecated. Use AXPDomainService instead.');
1241
+ return [];
1242
+ }
1243
+ /**
1244
+ * @deprecated Use module.getStatistics() method instead
1245
+ */
1246
+ static getModuleStatistics(module) {
1247
+ return module.getStatistics();
1248
+ }
1249
+ /**
1250
+ * @deprecated Use AXPDomainService with entity queries instead
1251
+ */
1252
+ static getAllEntities(module) {
1253
+ console.warn('AXPModuleHelper.getAllEntities is deprecated. Use AXPDomainService instead.');
1254
+ return [];
1255
+ }
1256
+ /**
1257
+ * @deprecated Use AXPDomainService with field queries instead
1258
+ */
1259
+ static getAllFields(module) {
1260
+ console.warn('AXPModuleHelper.getAllFields is deprecated. Use AXPDomainService instead.');
1261
+ return [];
1262
+ }
1263
+ }
1264
+
1265
+ //#region ---- Schema Middleware Context ----
1266
+ /**
1267
+ * Context class for schema middleware operations providing a fluent API for schema manipulation.
1268
+ *
1269
+ * This class serves as the interface between middleware functions and schema definitions,
1270
+ * offering methods to modify various aspects of a schema including:
1271
+ * - Widget configuration and options
1272
+ * - Validation rules
1273
+ * - Feature flags (searchable, filterable, sortable)
1274
+ * - Metadata and custom properties
1275
+ * - UI behavior (visibility, readonly state)
1276
+ *
1277
+ * All methods return `this` to enable fluent method chaining.
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * // Example middleware using the context
1282
+ * (context) => {
1283
+ * context
1284
+ * .addValidation({ rule: 'required' })
1285
+ * .searchable(true)
1286
+ * .withDefaultValue('default text')
1287
+ * .readonly(false);
1288
+ * }
1289
+ * ```
1290
+ */
1291
+ class AXPPropertyMiddlewareContext {
1292
+ constructor(definition) {
1293
+ // Deep clone to avoid mutating the original definition
1294
+ this._definition = JSON.parse(JSON.stringify(definition));
1295
+ }
1296
+ //#region ---- Schema Access Properties ----
1297
+ /**
1298
+ * Get the current schema definition (readonly access)
1299
+ */
1300
+ get definition() {
1301
+ return this._definition;
1302
+ }
1303
+ /**
1304
+ * Get the schema name for conditional logic
1305
+ */
1306
+ get name() {
1307
+ return this._definition.name;
1308
+ }
1309
+ //#endregion
1310
+ //#region ---- Default Value Management ----
1311
+ /**
1312
+ * Set a default value for the widget
1313
+ * @param defaultValue The default value to set
1314
+ */
1315
+ withDefaultValue(defaultValue) {
1316
+ if (!this._definition.interface.options) {
1317
+ this._definition.interface.options = {};
1318
+ }
1319
+ this._definition.interface.options['defaultValue'] = defaultValue;
1320
+ return this;
1321
+ }
1322
+ /**
1323
+ * Remove the default value from the widget
1324
+ */
1325
+ removeDefaultValue() {
1326
+ if (this._definition.interface.options) {
1327
+ delete this._definition.interface.options['defaultValue'];
1328
+ }
1329
+ return this;
1330
+ }
1331
+ //#endregion
1332
+ //#region ---- Validation Management ----
1333
+ /**
1334
+ * Add multiple validation rules to the schema
1335
+ * @param rules Array of validation rules to add
1336
+ */
1337
+ withValidation(rules) {
1338
+ if (!this._definition.validations) {
1339
+ this._definition.validations = [];
1340
+ }
1341
+ this._definition.validations.push(...rules);
1342
+ return this;
1343
+ }
1344
+ /**
1345
+ * Add a single validation rule to the schema
1346
+ * @param rule The validation rule to add
1347
+ */
1348
+ addValidation(rule) {
1349
+ if (!this._definition.validations) {
1350
+ this._definition.validations = [];
1351
+ }
1352
+ this._definition.validations.push(rule);
1353
+ return this;
1354
+ }
1355
+ /**
1356
+ * Add a single validation rule (alias for addValidation for backward compatibility)
1357
+ * @param rule The validation rule to add
1358
+ */
1359
+ withValidationRule(rule) {
1360
+ return this.addValidation(rule);
1361
+ }
1362
+ /**
1363
+ * Remove all validation rules from the schema
1364
+ */
1365
+ clearValidations() {
1366
+ this._definition.validations = [];
1367
+ return this;
1368
+ }
1369
+ /**
1370
+ * Remove a specific validation rule by its rule name
1371
+ * @param ruleName The name of the rule to remove (e.g., 'required', 'email')
1372
+ */
1373
+ removeValidation(ruleName) {
1374
+ if (this._definition.validations) {
1375
+ this._definition.validations = this._definition.validations.filter((v) => v.rule !== ruleName);
1376
+ }
1377
+ return this;
1378
+ }
1379
+ //#endregion
1380
+ //#region ---- Widget Configuration ----
1381
+ /**
1382
+ * Set or merge widget options
1383
+ * @param options Object containing widget-specific options to merge
1384
+ */
1385
+ withWidgetOptions(options) {
1386
+ const current = this._definition.interface.options ?? {};
1387
+ this._definition.interface.options = {
1388
+ ...current,
1389
+ ...options
1390
+ };
1391
+ return this;
1392
+ }
1393
+ /**
1394
+ * Remove a specific widget option by key
1395
+ * @param optionKey The key of the option to remove
1396
+ */
1397
+ removeWidgetOption(optionKey) {
1398
+ if (this._definition.interface.options) {
1399
+ delete this._definition.interface.options[optionKey];
1400
+ }
1401
+ return this;
1402
+ }
1403
+ /**
1404
+ * Change the widget type for this schema
1405
+ * @param widgetType The new widget type identifier
1406
+ */
1407
+ withWidgetType(widgetType) {
1408
+ this._definition.interface.name = widgetType;
1409
+ return this;
1410
+ }
1411
+ //#endregion
1412
+ //#region ---- Feature Configuration ----
1413
+ /**
1414
+ * Configure general features using an object
1415
+ * @param features Object containing feature configurations to merge
1416
+ */
1417
+ withFeatures(features) {
1418
+ this._definition.features = {
1419
+ ...this._definition.features,
1420
+ ...features
1421
+ };
1422
+ return this;
1423
+ }
1424
+ /**
1425
+ * Configure searchable feature with fine-grained control
1426
+ * @param enabled Whether searching is enabled
1427
+ * @param fullText Whether full-text search is enabled
1428
+ */
1429
+ searchable(enabled = true, fullText = false) {
1430
+ if (!this._definition.features) {
1431
+ this._definition.features = {};
1432
+ }
1433
+ this._definition.features.searchable = { enabled, fullText };
1434
+ return this;
1435
+ }
1436
+ /**
1437
+ * Configure filterable feature with inline filter support
1438
+ * @param enabled Whether filtering is enabled
1439
+ * @param inline Whether inline filtering is supported
1440
+ */
1441
+ filterable(enabled = true, inline = false) {
1442
+ if (!this._definition.features) {
1443
+ this._definition.features = {};
1444
+ }
1445
+ this._definition.features.filterable = { enabled, inline };
1446
+ return this;
1447
+ }
1448
+ /**
1449
+ * Configure sortable feature
1450
+ * @param enabled Whether sorting is enabled
1451
+ */
1452
+ sortable(enabled = true) {
1453
+ if (!this._definition.features) {
1454
+ this._definition.features = {};
1455
+ }
1456
+ this._definition.features.sortable = { enabled };
1457
+ return this;
1458
+ }
1459
+ //#endregion
1460
+ //#region ---- Metadata Management ----
1461
+ /**
1462
+ * Add or merge metadata to the schema
1463
+ * @param metadata Object containing metadata to merge
1464
+ */
1465
+ withMetadata(metadata) {
1466
+ this._definition.metadata = {
1467
+ ...this._definition.metadata,
1468
+ ...metadata
1469
+ };
1470
+ return this;
1471
+ }
1472
+ /**
1473
+ * Remove a specific metadata property by key
1474
+ * @param key The metadata key to remove
1475
+ */
1476
+ removeMetadata(key) {
1477
+ if (this._definition.metadata) {
1478
+ delete this._definition.metadata[key];
1479
+ }
1480
+ return this;
1481
+ }
1482
+ //#endregion
1483
+ //#region ---- UI State Management ----
1484
+ /**
1485
+ * Set the disabled state of the widget
1486
+ * @param isDisabled Whether the widget should be disabled
1487
+ */
1488
+ disabled(isDisabled = true) {
1489
+ this._definition.disabled = isDisabled;
1490
+ return this;
1491
+ }
1492
+ /**
1493
+ * Set the visibility of the widget
1494
+ * @param visible Whether the widget should be visible
1495
+ */
1496
+ withVisibility(visible) {
1497
+ if (!this._definition.interface.options) {
1498
+ this._definition.interface.options = {};
1499
+ }
1500
+ this._definition.interface.options['visible'] = visible;
1501
+ return this;
1502
+ }
1503
+ /**
1504
+ * Set the readonly state of the widget
1505
+ * @param isReadonly Whether the widget should be readonly
1506
+ */
1507
+ readonly(isReadonly = true) {
1508
+ if (!this._definition.interface.options) {
1509
+ this._definition.interface.options = {};
1510
+ }
1511
+ this._definition.interface.options['readonly'] = isReadonly;
1512
+ return this;
1513
+ }
1514
+ }
1515
+ //#endregion
1516
+ //#region ---- Builder-Based Middleware Types ----
1517
+
1518
+ //#endregion
1519
+ //#region ---- Schema Registry Service ----
1520
+ /**
1521
+ * Central registry for managing schema definitions, middleware, and dynamic loading.
1522
+ *
1523
+ * Provides:
1524
+ * - Schema registration and retrieval
1525
+ * - Middleware processing pipeline
1526
+ * - On-demand schema loading
1527
+ * - Caching and performance optimization
1528
+ */
1529
+ class AXPPropertyRegistry {
1530
+ constructor() {
1531
+ this._registrations = new Map();
1532
+ this._globalMiddleware = [];
1533
+ this._modelCache = new Map();
1534
+ this._loaders = null;
1535
+ }
1536
+ //#region ---- Registration Methods ----
1537
+ /**
1538
+ * Register a schema definition with optional metadata and middleware
1539
+ */
1540
+ register(definition, options = {}) {
1541
+ if (!definition.name || definition.name.trim() === '') {
1542
+ throw new Error('Property name is required and cannot be empty');
1543
+ }
1544
+ const registered = {
1545
+ name: definition.name,
1546
+ definition,
1547
+ options,
1548
+ registeredAt: new Date()
1549
+ };
1550
+ this._registrations.set(definition.name, registered);
1551
+ this.invalidateCache(definition.name);
1552
+ }
1553
+ /**
1554
+ * Register multiple schemas at once for batch operations
1555
+ */
1556
+ registerBatch(entries) {
1557
+ for (const entry of entries) {
1558
+ this.register(entry.definition, entry.options || {});
1559
+ }
1560
+ }
1561
+ /**
1562
+ * Remove a schema from the registry
1563
+ */
1564
+ unregister(name) {
1565
+ const removed = this._registrations.delete(name);
1566
+ if (removed) {
1567
+ this.invalidateCache(name);
1568
+ }
1569
+ return removed;
1570
+ }
1571
+ /**
1572
+ * Check if a schema is currently registered
1573
+ */
1574
+ isRegistered(name) {
1575
+ return this._registrations.has(name);
1576
+ }
1577
+ //#endregion
1578
+ //#region ---- Resolution Methods ----
1579
+ /**
1580
+ * Resolve a schema by name with full middleware processing and caching.
1581
+ * Supports on-demand loading if schema is not registered.
1582
+ */
1583
+ async resolve(name) {
1584
+ // Check cache first for performance
1585
+ if (this._modelCache.has(name)) {
1586
+ return this._modelCache.get(name);
1587
+ }
1588
+ // Try to get from registered schemas first
1589
+ let registered = this._registrations.get(name);
1590
+ // If not found, attempt on-demand loading
1591
+ if (!registered) {
1592
+ const definition = await this.loadFromLoaders(name);
1593
+ if (definition) {
1594
+ registered = {
1595
+ name,
1596
+ definition,
1597
+ options: {},
1598
+ registeredAt: new Date()
1599
+ };
1600
+ this._registrations.set(name, registered);
1601
+ }
1602
+ else {
1603
+ throw new Error(`Property '${name}' is not registered and no loader can provide it`);
1604
+ }
1605
+ }
1606
+ // Clone and process through middleware pipeline
1607
+ const definition = this.cloneDefinition(registered.definition);
1608
+ const context = new AXPPropertyMiddlewareContext(definition);
1609
+ // Apply global middleware first
1610
+ for (const middleware of this._globalMiddleware) {
1611
+ await middleware(context);
1612
+ }
1613
+ // Apply registration-specific middleware
1614
+ if (registered.options.middleware) {
1615
+ for (const middleware of registered.options.middleware) {
1616
+ await middleware(context);
1617
+ }
1618
+ }
1619
+ // Create final model and cache it
1620
+ const model = new AXPPropertyModel(context.definition);
1621
+ this._modelCache.set(name, model);
1622
+ return model;
1623
+ }
1624
+ /**
1625
+ * Synchronous resolution without middleware processing.
1626
+ * Use only when middleware is not needed for performance.
1627
+ */
1628
+ resolveSync(name) {
1629
+ const registered = this._registrations.get(name);
1630
+ if (!registered) {
1631
+ throw new Error(`Property '${name}' is not registered`);
1632
+ }
1633
+ return new AXPPropertyModel(this.cloneDefinition(registered.definition));
1634
+ }
1635
+ //#endregion
1636
+ //#region ---- Middleware Management ----
1637
+ /**
1638
+ * Add global middleware that applies to all schema resolutions
1639
+ */
1640
+ addGlobalMiddleware(middleware) {
1641
+ if (typeof middleware !== 'function') {
1642
+ throw new Error('Middleware must be a function');
1643
+ }
1644
+ this._globalMiddleware.push(middleware);
1645
+ }
1646
+ /**
1647
+ * Remove specific global middleware
1648
+ */
1649
+ removeGlobalMiddleware(middleware) {
1650
+ const index = this._globalMiddleware.indexOf(middleware);
1651
+ if (index > -1) {
1652
+ this._globalMiddleware.splice(index, 1);
1653
+ return true;
1654
+ }
1655
+ return false;
1656
+ }
1657
+ /**
1658
+ * Clear all global middleware
1659
+ */
1660
+ clearGlobalMiddleware() {
1661
+ this._globalMiddleware.length = 0;
1662
+ }
1663
+ //#endregion
1664
+ //#region ---- Loader Management ----
1665
+ /**
1666
+ * Add a schema loader for on-demand loading
1667
+ */
1668
+ addLoader(loader) {
1669
+ if (this._loaders === null) {
1670
+ this._loaders = [];
1671
+ }
1672
+ this._loaders.push(new loader());
1673
+ }
1674
+ //#endregion
1675
+ //#region ---- Query Methods ----
1676
+ /**
1677
+ * Get all registered schema names
1678
+ */
1679
+ getRegisteredNames() {
1680
+ return Array.from(this._registrations.keys());
1681
+ }
1682
+ /**
1683
+ * Get detailed registration information for a schema
1684
+ */
1685
+ getRegisteredProperty(name) {
1686
+ return this._registrations.get(name);
1687
+ }
1688
+ /**
1689
+ * Get all registered properties with their metadata
1690
+ */
1691
+ getAllRegisteredProperties() {
1692
+ return Array.from(this._registrations.values());
1693
+ }
1694
+ /**
1695
+ * Find registered properties by tag for categorization
1696
+ */
1697
+ findByTag(tag) {
1698
+ return Array.from(this._registrations.values()).filter((registration) => registration.options.tags?.includes(tag));
1699
+ }
1700
+ /**
1701
+ * Find registered properties by widget type for widget-specific operations
1702
+ */
1703
+ findByWidget(widgetType) {
1704
+ return Array.from(this._registrations.values()).filter((registration) => registration.definition.interface.name === widgetType);
1705
+ }
1706
+ /**
1707
+ * Get comprehensive registry statistics
1708
+ */
1709
+ getStatistics() {
1710
+ const propertiesByWidget = {};
1711
+ const propertiesByTag = {};
1712
+ for (const registration of this._registrations.values()) {
1713
+ const widget = registration.definition.interface.name;
1714
+ propertiesByWidget[widget] = (propertiesByWidget[widget] || 0) + 1;
1715
+ if (registration.options.tags) {
1716
+ for (const tag of registration.options.tags) {
1717
+ propertiesByTag[tag] = (propertiesByTag[tag] || 0) + 1;
1718
+ }
1719
+ }
1720
+ }
1721
+ return {
1722
+ propertyCount: this._registrations.size,
1723
+ globalMiddlewareCount: this._globalMiddleware.length,
1724
+ propertiesByWidget,
1725
+ propertiesByTag
1726
+ };
1727
+ }
1728
+ //#endregion
1729
+ //#region ---- Cache Management ----
1730
+ /**
1731
+ * Clear all cached models (useful when schemas need to be reloaded)
1732
+ */
1733
+ clearCache() {
1734
+ this._modelCache.clear();
1735
+ }
1736
+ /**
1737
+ * Invalidate cache for a specific schema
1738
+ */
1739
+ invalidateCache(name) {
1740
+ this._modelCache.delete(name);
1741
+ }
1742
+ /**
1743
+ * Clear all registered schemas, middleware, and cached models
1744
+ */
1745
+ clear() {
1746
+ this._registrations.clear();
1747
+ this._globalMiddleware.length = 0;
1748
+ this._modelCache.clear();
1749
+ this._loaders = null;
1750
+ }
1751
+ //#endregion
1752
+ //#region ---- Private Helper Methods ----
1753
+ /**
1754
+ * Attempt to load schema from registered loaders
1755
+ */
1756
+ async loadFromLoaders(name) {
1757
+ const loaders = this.getLoaders();
1758
+ for (const loader of loaders) {
1759
+ if (loader.canLoad(name)) {
1760
+ try {
1761
+ const definition = await loader.load(name);
1762
+ if (definition) {
1763
+ console.log(`Property '${name}' loaded from ${loader.constructor.name}`);
1764
+ return definition;
1765
+ }
1766
+ }
1767
+ catch (error) {
1768
+ console.warn(`Loader ${loader.constructor.name} failed to load '${name}':`, error);
1769
+ }
1770
+ }
1771
+ }
1772
+ return null;
1773
+ }
1774
+ /**
1775
+ * Get loaders sorted by priority (lazy initialization)
1776
+ */
1777
+ getLoaders() {
1778
+ if (this._loaders === null) {
1779
+ try {
1780
+ this._loaders = [];
1781
+ // Sort by priority (higher priority first)
1782
+ this._loaders.sort((a, b) => {
1783
+ const priorityA = a.priority ?? 0;
1784
+ const priorityB = b.priority ?? 0;
1785
+ return priorityB - priorityA;
1786
+ });
1787
+ }
1788
+ catch (error) {
1789
+ console.warn('Failed to initialize property loaders:', error);
1790
+ this._loaders = [];
1791
+ }
1792
+ }
1793
+ return this._loaders;
1794
+ }
1795
+ /**
1796
+ * Create a deep clone of a schema definition to prevent mutations
1797
+ */
1798
+ cloneDefinition(definition) {
1799
+ return JSON.parse(JSON.stringify(definition));
1800
+ }
1801
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1802
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyRegistry, providedIn: 'root' }); }
1803
+ }
1804
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyRegistry, decorators: [{
1805
+ type: Injectable,
1806
+ args: [{ providedIn: 'root' }]
1807
+ }], ctorParameters: () => [] });
1808
+
1809
+ //#region ---- Property Service ----
1810
+ /**
1811
+ * High-level facade for registered property definitions ({@link AXPPropertyDefinition}).
1812
+ *
1813
+ * Wraps {@link AXPPropertyRegistry} with convenience methods for resolution and discovery.
1814
+ */
1815
+ class AXPPropertyService {
1816
+ constructor() {
1817
+ this.registry = inject(AXPPropertyRegistry);
1818
+ }
1819
+ //#region ---- Registration ----
1820
+ /**
1821
+ * Register a property definition with optional configuration
1822
+ */
1823
+ register(definition, options) {
1824
+ this.registry.register(definition, options);
1825
+ }
1826
+ /**
1827
+ * Register multiple property definitions at once
1828
+ */
1829
+ registerBatch(entries) {
1830
+ this.registry.registerBatch(entries);
1831
+ }
1832
+ /**
1833
+ * Remove a property definition from the registry
1834
+ */
1835
+ unregister(name) {
1836
+ return this.registry.unregister(name);
1837
+ }
1838
+ /**
1839
+ * Check if a property definition is registered by name
1840
+ */
1841
+ isRegistered(name) {
1842
+ return this.registry.isRegistered(name);
1843
+ }
1844
+ /**
1845
+ * Get all registered property names
1846
+ */
1847
+ getRegisteredNames() {
1848
+ return this.registry.getRegisteredNames();
1849
+ }
1850
+ /**
1851
+ * Clear all registrations and reset the registry
1852
+ */
1853
+ clear() {
1854
+ this.registry.clear();
1855
+ }
1856
+ //#endregion
1857
+ //#region ---- Resolution ----
1858
+ /**
1859
+ * Resolve a registered property by name with full middleware processing
1860
+ */
1861
+ async resolve(name) {
1862
+ return this.registry.resolve(name);
1863
+ }
1864
+ /**
1865
+ * Resolve synchronously without middleware (for performance)
1866
+ */
1867
+ resolveSync(name) {
1868
+ return this.registry.resolveSync(name);
1869
+ }
1870
+ /**
1871
+ * Resolve multiple registered properties for form building and batch operations
1872
+ */
1873
+ async resolveMultiple(fieldConfigs) {
1874
+ const resolvedFields = await Promise.all(fieldConfigs.map(async (config) => ({
1875
+ name: config.name,
1876
+ property: await this.resolve(config.propertyName),
1877
+ })));
1878
+ return resolvedFields;
1879
+ }
1880
+ //#endregion
1881
+ //#region ---- Middleware Management ----
1882
+ /**
1883
+ * Add global middleware that applies to all property resolutions
1884
+ */
1885
+ addGlobalMiddleware(middleware) {
1886
+ this.registry.addGlobalMiddleware(middleware);
1887
+ }
1888
+ /**
1889
+ * Remove specific global middleware
1890
+ */
1891
+ removeGlobalMiddleware(middleware) {
1892
+ return this.registry.removeGlobalMiddleware(middleware);
1893
+ }
1894
+ /**
1895
+ * Clear all global middleware
1896
+ */
1897
+ clearGlobalMiddleware() {
1898
+ this.registry.clearGlobalMiddleware();
1899
+ }
1900
+ //#endregion
1901
+ //#region ---- Query and Discovery ----
1902
+ /**
1903
+ * Get detailed information about a registered property definition
1904
+ */
1905
+ getRegisteredProperty(name) {
1906
+ return this.registry.getRegisteredProperty(name);
1907
+ }
1908
+ /**
1909
+ * Get all registered property definitions with their metadata
1910
+ */
1911
+ getAllRegisteredProperties() {
1912
+ return this.registry.getAllRegisteredProperties();
1913
+ }
1914
+ /**
1915
+ * Find registered properties by tag for categorization and grouping
1916
+ */
1917
+ findByTag(tag) {
1918
+ return this.registry.findByTag(tag);
1919
+ }
1920
+ /**
1921
+ * Find registered properties by widget type for widget-specific operations
1922
+ */
1923
+ findByWidget(widgetType) {
1924
+ return this.registry.findByWidget(widgetType);
1925
+ }
1926
+ /**
1927
+ * Get comprehensive registry statistics and analytics
1928
+ */
1929
+ getStatistics() {
1930
+ const stats = this.registry.getStatistics();
1931
+ // Enhance with top-used analytics
1932
+ const mostUsedWidgets = Object.entries(stats.propertiesByWidget)
1933
+ .sort(([, a], [, b]) => b - a)
1934
+ .slice(0, 5)
1935
+ .map(([widget]) => widget);
1936
+ const mostUsedTags = Object.entries(stats.propertiesByTag)
1937
+ .sort(([, a], [, b]) => b - a)
1938
+ .slice(0, 5)
1939
+ .map(([tag]) => tag);
1940
+ return {
1941
+ ...stats,
1942
+ mostUsedWidgets,
1943
+ mostUsedTags
1944
+ };
1945
+ }
1946
+ //#endregion
1947
+ //#region ---- Validation and Analysis ----
1948
+ /**
1949
+ * Validate that all referenced property names exist in the registry
1950
+ */
1951
+ validatePropertyReferences(propertyNames) {
1952
+ const missingPropertyNames = [];
1953
+ for (const propertyName of propertyNames) {
1954
+ if (!this.isRegistered(propertyName)) {
1955
+ missingPropertyNames.push(propertyName);
1956
+ }
1957
+ }
1958
+ return {
1959
+ valid: missingPropertyNames.length === 0,
1960
+ missingPropertyNames,
1961
+ };
1962
+ }
1963
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1964
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyService, providedIn: 'root' }); }
1965
+ }
1966
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPropertyService, decorators: [{
1967
+ type: Injectable,
1968
+ args: [{ providedIn: 'root' }]
1969
+ }] });
1970
+
1971
+ //#endregion
1972
+
1973
+ //#endregion
1974
+ //#region ---- Provider Functions ----
1975
+ /**
1976
+ * Provide schema loaders for on-demand schema loading.
1977
+ *
1978
+ * Schema loaders enable the registry to load schemas that are not registered
1979
+ * at build time. This is useful for:
1980
+ * - Loading schemas from external APIs
1981
+ * - Dynamic schema generation
1982
+ * - Lazy loading of large schema sets
1983
+ * - Development-time schema hot-reloading
1984
+ *
1985
+ * @param loaders Array of loader classes or loader configurations
1986
+ * @returns Environment providers for dependency injection
1987
+ *
1988
+ * @example
1989
+ * ```typescript
1990
+ * // Simple loader registration
1991
+ * providePropertyLoaders([
1992
+ * HttpSchemaLoader,
1993
+ * FileSystemSchemaLoader
1994
+ * ])
1995
+ *
1996
+ * // With priority configuration
1997
+ * providePropertyLoaders([
1998
+ * { loader: HttpSchemaLoader, priority: 10 },
1999
+ * { loader: FileSystemSchemaLoader, priority: 5 }
2000
+ * ])
2001
+ * ```
2002
+ */
2003
+ function providePropertyLoaders(loaders) {
2004
+ return makeEnvironmentProviders([
2005
+ {
2006
+ provide: AXP_PROPERTY_LOADER_SETUP,
2007
+ useFactory: async () => {
2008
+ const registry = inject(AXPPropertyRegistry);
2009
+ // Register each loader with the registry
2010
+ for (const loader of loaders) {
2011
+ const loaderClass = typeof loader === 'function' ? loader : loader.loader;
2012
+ registry.addLoader(loaderClass);
2013
+ }
2014
+ return true;
2015
+ },
2016
+ multi: true
2017
+ }
2018
+ ]);
2019
+ }
2020
+ //#endregion
2021
+
2022
+ //#endregion
2023
+ //#region ---- Provider Functions ----
2024
+ /**
2025
+ * Provide schema setups for registration during application bootstrap.
2026
+ *
2027
+ * This is the primary way to register schemas that are known at build time.
2028
+ * Schemas registered this way are immediately available in the registry.
2029
+ *
2030
+ * @param schemas Array of schema entries to register
2031
+ * @returns Environment providers for dependency injection
2032
+ *
2033
+ * @example
2034
+ * ```typescript
2035
+ * providePropertySetups([
2036
+ * {
2037
+ * definition: emailSchema,
2038
+ * options: {
2039
+ * tags: ['user', 'contact'],
2040
+ * description: 'Email field schema'
2041
+ * }
2042
+ * },
2043
+ * {
2044
+ * definition: phoneSchema,
2045
+ * options: { tags: ['contact'] }
2046
+ * }
2047
+ * ])
2048
+ * ```
2049
+ */
2050
+ function providePropertySetups(entries) {
2051
+ return makeEnvironmentProviders([
2052
+ {
2053
+ provide: AXP_PROPERTY_SETUP,
2054
+ useFactory: () => {
2055
+ const registry = inject(AXPPropertyRegistry);
2056
+ for (const entry of entries) {
2057
+ registry.register(entry.definition, entry.options);
2058
+ }
2059
+ return true;
2060
+ },
2061
+ multi: true
2062
+ }
2063
+ ]);
2064
+ }
2065
+ /**
2066
+ * Convenience function to provide a single schema setup.
2067
+ *
2068
+ * Useful when you need to register just one schema or want to keep
2069
+ * schema registrations separate for organizational purposes.
2070
+ *
2071
+ * @param definition The schema definition to register
2072
+ * @param options Optional registration options and metadata
2073
+ * @returns Environment providers for dependency injection
2074
+ *
2075
+ * @example
2076
+ * ```typescript
2077
+ * provideProperty(emailSchema, {
2078
+ * tags: ['user', 'contact'],
2079
+ * description: 'User email field'
2080
+ * })
2081
+ * ```
2082
+ */
2083
+ function provideProperty(definition, options) {
2084
+ return providePropertySetups([{ definition, options }]);
2085
+ }
2086
+ /**
2087
+ * Provide schema setups from a factory function for dynamic registration.
2088
+ *
2089
+ * Useful when schemas need to be generated at runtime, loaded from external
2090
+ * sources, or depend on configuration that's not available at build time.
2091
+ *
2092
+ * @param schemaFactory Factory function that returns schema entries
2093
+ * @returns Environment providers for dependency injection
2094
+ *
2095
+ * @example
2096
+ * ```typescript
2097
+ * providePropertiesFromFactory(async () => {
2098
+ * const config = await loadConfiguration();
2099
+ * return config.schemas.map(schema => ({
2100
+ * definition: schema,
2101
+ * options: { tags: ['dynamic'] }
2102
+ * }));
2103
+ * })
2104
+ * ```
2105
+ */
2106
+ function providePropertiesFromFactory(factory) {
2107
+ return makeEnvironmentProviders([
2108
+ {
2109
+ provide: AXP_PROPERTY_SETUP,
2110
+ useFactory: async () => {
2111
+ const registry = inject(AXPPropertyRegistry);
2112
+ const entries = await factory();
2113
+ for (const entry of entries) {
2114
+ registry.register(entry.definition, entry.options);
2115
+ }
2116
+ return true;
2117
+ },
2118
+ multi: true
2119
+ }
2120
+ ]);
2121
+ }
2122
+ //#endregion
2123
+
2124
+ //#endregion
2125
+ //#region ---- Provider Functions ----
2126
+ /**
2127
+ * Provide schema middleware for registration in the application.
2128
+ *
2129
+ * Supports both global middleware (applies to all schemas) and targeted middleware
2130
+ * (applies only to schemas matching specific patterns).
2131
+ *
2132
+ * @param middleware Array of middleware entries to register
2133
+ * @returns Environment providers for dependency injection
2134
+ *
2135
+ * @example
2136
+ * ```typescript
2137
+ * // Global middleware
2138
+ * providePropertyMiddleware([
2139
+ * (context) => {
2140
+ * if (context.definition.dataType === 'string') {
2141
+ * context.searchable(true);
2142
+ * }
2143
+ * }
2144
+ * ])
2145
+ *
2146
+ * // Targeted middleware
2147
+ * providePropertyMiddleware([
2148
+ * {
2149
+ * target: /^user_/,
2150
+ * middleware: (context) => context.withMetadata({ category: 'user' })
2151
+ * }
2152
+ * ])
2153
+ * ```
2154
+ */
2155
+ function providePropertyMiddleware(middleware) {
2156
+ const global = [];
2157
+ const targeted = [];
2158
+ // Separate global and targeted middleware for different processing
2159
+ for (const entry of middleware) {
2160
+ if (typeof entry === 'function') {
2161
+ global.push(entry);
2162
+ }
2163
+ else {
2164
+ targeted.push(entry);
2165
+ }
2166
+ }
2167
+ return makeEnvironmentProviders([
2168
+ // Setup global middleware that applies to all schemas
2169
+ ...(global.length > 0 ? [{
2170
+ provide: AXP_PROPERTY_MIDDLEWARE_SETUP,
2171
+ useFactory: () => {
2172
+ const registry = inject(AXPPropertyRegistry);
2173
+ for (const mw of global) {
2174
+ registry.addGlobalMiddleware(mw);
2175
+ }
2176
+ return true;
2177
+ },
2178
+ multi: true
2179
+ }] : []),
2180
+ // Register targeted middleware entries for future extension support
2181
+ ...targeted.map(entry => ({
2182
+ provide: AXP_PROPERTY_EXTENSION,
2183
+ useValue: entry,
2184
+ multi: true
2185
+ }))
2186
+ ]);
2187
+ }
2188
+ //#endregion
2189
+
2190
+ //#region ---- Property registry core ----
2191
+ //#endregion
2192
+
2193
+ //#region ---- Domain Middleware Context ----
2194
+ /**
2195
+ * Context class for domain middleware operations providing a fluent API for domain definition manipulation.
2196
+ *
2197
+ * This class serves as the interface between middleware functions and domain definitions,
2198
+ * offering methods to modify various aspects of a domain definition including:
2199
+ * - Metadata and custom properties
2200
+ * - Validation rules
2201
+ * - Feature flags and configuration
2202
+ * - Transformations and augmentations
2203
+ *
2204
+ * All methods return `this` to enable fluent method chaining.
2205
+ */
2206
+ class AXPDomainMiddlewareContext {
2207
+ constructor(definition, type) {
2208
+ // Deep clone to avoid mutating the original definition
2209
+ this._definition = JSON.parse(JSON.stringify(definition));
2210
+ this._type = type;
2211
+ }
2212
+ /**
2213
+ * Get the current definition (read-only access)
2214
+ */
2215
+ get definition() {
2216
+ return this._definition;
2217
+ }
2218
+ /**
2219
+ * Get the type of domain object being processed
2220
+ */
2221
+ get type() {
2222
+ return this._type;
2223
+ }
2224
+ /**
2225
+ * Set metadata for the domain definition
2226
+ */
2227
+ setMetadata(key, value) {
2228
+ const def = this._definition;
2229
+ if (!def.metadata) {
2230
+ def.metadata = {};
2231
+ }
2232
+ def.metadata[key] = value;
2233
+ return this;
2234
+ }
2235
+ /**
2236
+ * Get metadata value
2237
+ */
2238
+ getMetadata(key) {
2239
+ const def = this._definition;
2240
+ return def.metadata?.[key];
2241
+ }
2242
+ /**
2243
+ * Add validation rule to the definition (for entities and aggregates)
2244
+ */
2245
+ addValidation(rule) {
2246
+ if ('validations' in this._definition) {
2247
+ this._definition.validations = this._definition.validations || [];
2248
+ this._definition.validations.push(rule);
2249
+ }
2250
+ return this;
2251
+ }
2252
+ /**
2253
+ * Transform fields (for entities)
2254
+ */
2255
+ transformFields(transformer) {
2256
+ if ('fields' in this._definition && this._definition.fields) {
2257
+ this._definition.fields = this._definition.fields.map(transformer);
2258
+ }
2259
+ return this;
2260
+ }
2261
+ /**
2262
+ * Add field to entity definition
2263
+ */
2264
+ addField(field) {
2265
+ if ('fields' in this._definition) {
2266
+ this._definition.fields = this._definition.fields || [];
2267
+ this._definition.fields.push(field);
2268
+ }
2269
+ return this;
2270
+ }
2271
+ /**
2272
+ * Set title with transformation
2273
+ */
2274
+ setTitle(title) {
2275
+ this._definition.title = title;
2276
+ return this;
2277
+ }
2278
+ /**
2279
+ * Apply conditional logic
2280
+ */
2281
+ when(condition, callback) {
2282
+ if (condition) {
2283
+ callback(this);
2284
+ }
2285
+ return this;
2286
+ }
2287
+ /**
2288
+ * Replace the entire definition
2289
+ */
2290
+ replaceDefinition(newDefinition) {
2291
+ this._definition = JSON.parse(JSON.stringify(newDefinition));
2292
+ return this;
2293
+ }
2294
+ }
2295
+ //#endregion
2296
+
2297
+ //#region ---- Domain Registry Service ----
2298
+ /**
2299
+ * Central registry for managing domain definitions, middleware, and dynamic loading.
2300
+ *
2301
+ * Provides:
2302
+ * - Path-based domain resolution (module.aggregate.entity)
2303
+ * - Definition-level caching with fresh model creation
2304
+ * - Type-specific middleware processing
2305
+ * - On-demand domain loading with first-wins strategy
2306
+ * - Model creation and dependency injection
2307
+ */
2308
+ class AXPDomainRegistry {
2309
+ constructor() {
2310
+ // Cache definitions (before middleware), not models
2311
+ this._definitionCache = new Map();
2312
+ // Type-specific middleware collections
2313
+ this._moduleMiddleware = [];
2314
+ this._aggregateMiddleware = [];
2315
+ this._entityMiddleware = [];
2316
+ // Registered loaders for on-demand loading
2317
+ this._loaders = [];
2318
+ // Injected dependencies
2319
+ this.propertyService = inject(AXPPropertyService);
2320
+ }
2321
+ //#region ---- Main Resolution Method ----
2322
+ /**
2323
+ * Resolve a domain object by path with full middleware processing.
2324
+ * Supports definition-level caching with fresh model creation.
2325
+ *
2326
+ * @param path Domain path (e.g., "user-management.user-aggregate.user")
2327
+ * @returns Promise that resolves to the domain model
2328
+ */
2329
+ async resolve(path) {
2330
+ const pathInfo = this.parsePath(path);
2331
+ // Check definition cache first
2332
+ let definition = this._definitionCache.get(path);
2333
+ if (!definition) {
2334
+ // Load from first available loader
2335
+ definition = await this.loadFromLoaders(path, pathInfo.type);
2336
+ if (!definition) {
2337
+ throw new Error(`Cannot resolve domain path: ${path}`);
2338
+ }
2339
+ // Cache the raw definition
2340
+ this._definitionCache.set(path, definition);
2341
+ }
2342
+ // Apply type-specific middleware (creates new processed definition)
2343
+ const processedDefinition = await this.applyTypeMiddleware(definition, pathInfo.type, path);
2344
+ // Create and return fresh model instance (not cached)
2345
+ return this.createModel(processedDefinition, pathInfo);
2346
+ }
2347
+ //#endregion
2348
+ //#region ---- Path Parsing ----
2349
+ /**
2350
+ * Parse domain path string into structured information
2351
+ *
2352
+ * @param path Domain path to parse
2353
+ * @returns Parsed path information
2354
+ */
2355
+ parsePath(path) {
2356
+ const parts = path.split('.');
2357
+ if (parts.length === 1) {
2358
+ return {
2359
+ module: parts[0],
2360
+ type: 'module',
2361
+ fullPath: path
2362
+ };
2363
+ }
2364
+ else if (parts.length === 2) {
2365
+ return {
2366
+ module: parts[0],
2367
+ aggregate: parts[1],
2368
+ type: 'aggregate',
2369
+ fullPath: path
2370
+ };
2371
+ }
2372
+ else if (parts.length === 3) {
2373
+ return {
2374
+ module: parts[0],
2375
+ aggregate: parts[1],
2376
+ entity: parts[2],
2377
+ type: 'entity',
2378
+ fullPath: path
2379
+ };
2380
+ }
2381
+ throw new Error(`Invalid domain path format: ${path}. Expected: module, module.aggregate, or module.aggregate.entity`);
2382
+ }
2383
+ //#endregion
2384
+ //#region ---- Loader Management ----
2385
+ /**
2386
+ * Register a domain loader for on-demand loading
2387
+ *
2388
+ * @param loader The loader instance to register
2389
+ */
2390
+ addLoader(loader) {
2391
+ this._loaders.push(loader);
2392
+ // Sort by priority (highest first) after adding
2393
+ this._loaders.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2394
+ }
2395
+ /**
2396
+ * Load definition from registered loaders using first-wins strategy
2397
+ *
2398
+ * @param path Domain path to load
2399
+ * @param type Type of domain object
2400
+ * @returns Promise that resolves to definition or undefined
2401
+ */
2402
+ async loadFromLoaders(path, type) {
2403
+ for (const loader of this._loaders) {
2404
+ if (loader.canLoad(path, type)) {
2405
+ try {
2406
+ const definition = await loader.load(path, type);
2407
+ if (definition) {
2408
+ console.log(`✓ Loaded ${path} from ${loader.constructor.name}`);
2409
+ return definition; // Return first successful result
2410
+ }
2411
+ }
2412
+ catch (error) {
2413
+ console.warn(`⚠ Loader ${loader.constructor.name} failed for ${path}:`, error);
2414
+ // Continue to next loader instead of throwing
2415
+ }
2416
+ }
2417
+ }
2418
+ return undefined; // No loader could provide the definition
2419
+ }
2420
+ //#endregion
2421
+ //#region ---- Middleware Management ----
2422
+ /**
2423
+ * Add middleware for module processing
2424
+ */
2425
+ addModuleMiddleware(middleware) {
2426
+ this._moduleMiddleware.push(middleware);
2427
+ }
2428
+ /**
2429
+ * Add middleware for aggregate processing
2430
+ */
2431
+ addAggregateMiddleware(middleware) {
2432
+ this._aggregateMiddleware.push(middleware);
2433
+ }
2434
+ /**
2435
+ * Add middleware for entity processing
2436
+ */
2437
+ addEntityMiddleware(middleware) {
2438
+ this._entityMiddleware.push(middleware);
2439
+ }
2440
+ /**
2441
+ * Apply type-specific middleware to definition
2442
+ *
2443
+ * @param definition Raw definition from cache or loader
2444
+ * @param type Type of domain object
2445
+ * @param path Original path for context
2446
+ * @returns Processed definition
2447
+ */
2448
+ async applyTypeMiddleware(definition, type, path) {
2449
+ const middleware = this.getMiddlewareForType(type);
2450
+ if (middleware.length === 0) {
2451
+ return definition; // No middleware to apply
2452
+ }
2453
+ const context = new AXPDomainMiddlewareContext(definition, type);
2454
+ // Apply all middleware for this type
2455
+ for (const mw of middleware) {
2456
+ await mw(context);
2457
+ }
2458
+ return context.definition;
2459
+ }
2460
+ /**
2461
+ * Get middleware collection for specific type
2462
+ *
2463
+ * @param type Domain object type
2464
+ * @returns Array of middleware functions
2465
+ */
2466
+ getMiddlewareForType(type) {
2467
+ switch (type) {
2468
+ case 'module': return this._moduleMiddleware;
2469
+ case 'aggregate': return this._aggregateMiddleware;
2470
+ case 'entity': return this._entityMiddleware;
2471
+ default: return [];
2472
+ }
2473
+ }
2474
+ //#endregion
2475
+ //#region ---- Model Creation ----
2476
+ /**
2477
+ * Create domain model from processed definition
2478
+ *
2479
+ * @param definition Processed definition (after middleware)
2480
+ * @param pathInfo Parsed path information
2481
+ * @returns Created domain model
2482
+ */
2483
+ createModel(definition, pathInfo) {
2484
+ switch (pathInfo.type) {
2485
+ case 'module':
2486
+ return new AXPModuleModel(definition);
2487
+ case 'aggregate':
2488
+ // For aggregate, we need to resolve parent module first
2489
+ // This will be simplified when we refactor the models
2490
+ const moduleDefinition = {
2491
+ name: pathInfo.module,
2492
+ title: pathInfo.module,
2493
+ aggregates: [definition]
2494
+ };
2495
+ const parentModule = new AXPModuleModel(moduleDefinition);
2496
+ return new AXPAggregateModel(definition, parentModule);
2497
+ case 'entity':
2498
+ // For entity, we need both module and aggregate parents
2499
+ // This will be simplified when we refactor the models
2500
+ const entityAggregateDefinition = {
2501
+ name: pathInfo.aggregate,
2502
+ title: pathInfo.aggregate || '',
2503
+ entities: [definition],
2504
+ validations: [],
2505
+ actions: []
2506
+ };
2507
+ const entityModuleDefinition = {
2508
+ name: pathInfo.module,
2509
+ title: pathInfo.module,
2510
+ aggregates: [entityAggregateDefinition]
2511
+ };
2512
+ const entityModule = new AXPModuleModel(entityModuleDefinition);
2513
+ const entityAggregate = new AXPAggregateModel(entityAggregateDefinition, entityModule);
2514
+ return new AXPEntityModel(definition, entityAggregate, this.propertyService);
2515
+ default:
2516
+ throw new Error(`Unknown domain type: ${pathInfo.type}`);
2517
+ }
2518
+ }
2519
+ //#endregion
2520
+ //#region ---- Cache Management ----
2521
+ /**
2522
+ * Clear definition cache for a specific path
2523
+ *
2524
+ * @param path Path to clear from cache
2525
+ */
2526
+ invalidateCache(path) {
2527
+ this._definitionCache.delete(path);
2528
+ }
2529
+ /**
2530
+ * Clear all cached definitions
2531
+ */
2532
+ clearCache() {
2533
+ this._definitionCache.clear();
2534
+ }
2535
+ /**
2536
+ * Get cache statistics
2537
+ */
2538
+ getCacheStats() {
2539
+ return {
2540
+ size: this._definitionCache.size,
2541
+ paths: Array.from(this._definitionCache.keys())
2542
+ };
2543
+ }
2544
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2545
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainRegistry, providedIn: 'root' }); }
2546
+ }
2547
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainRegistry, decorators: [{
2548
+ type: Injectable,
2549
+ args: [{ providedIn: 'root' }]
2550
+ }] });
2551
+
2552
+ //#region ---- Domain Service ----
2553
+ /**
2554
+ * High-level facade service for domain operations.
2555
+ *
2556
+ * Provides simplified interfaces to the domain registry with additional
2557
+ * business logic and convenience methods for common operations.
2558
+ *
2559
+ * This service acts as the main entry point for domain model resolution
2560
+ * and provides a clean API for consuming applications.
2561
+ */
2562
+ class AXPDomainService {
2563
+ constructor() {
2564
+ // Injected dependencies
2565
+ this.registry = inject(AXPDomainRegistry);
2566
+ }
2567
+ //#region ---- Module Operations ----
2568
+ /**
2569
+ * Get module by name
2570
+ *
2571
+ * @param name Module name
2572
+ * @returns Promise that resolves to module model
2573
+ */
2574
+ async getModule(name) {
2575
+ return this.registry.resolve(name);
2576
+ }
2577
+ /**
2578
+ * Check if module exists
2579
+ *
2580
+ * @param name Module name
2581
+ * @returns Promise that resolves to boolean
2582
+ */
2583
+ async moduleExists(name) {
2584
+ try {
2585
+ await this.getModule(name);
2586
+ return true;
2587
+ }
2588
+ catch {
2589
+ return false;
2590
+ }
2591
+ }
2592
+ //#endregion
2593
+ //#region ---- Aggregate Operations ----
2594
+ /**
2595
+ * Get aggregate by module and aggregate names
2596
+ *
2597
+ * @param moduleName Module name
2598
+ * @param aggregateName Aggregate name
2599
+ * @returns Promise that resolves to aggregate model
2600
+ */
2601
+ async getAggregate(moduleName, aggregateName) {
2602
+ const path = `${moduleName}.${aggregateName}`;
2603
+ return this.registry.resolve(path);
2604
+ }
2605
+ /**
2606
+ * Get aggregate by path
2607
+ *
2608
+ * @param path Aggregate path (module.aggregate)
2609
+ * @returns Promise that resolves to aggregate model
2610
+ */
2611
+ async getAggregateByPath(path) {
2612
+ return this.registry.resolve(path);
2613
+ }
2614
+ /**
2615
+ * Check if aggregate exists
2616
+ *
2617
+ * @param moduleName Module name
2618
+ * @param aggregateName Aggregate name
2619
+ * @returns Promise that resolves to boolean
2620
+ */
2621
+ async aggregateExists(moduleName, aggregateName) {
2622
+ try {
2623
+ await this.getAggregate(moduleName, aggregateName);
2624
+ return true;
2625
+ }
2626
+ catch {
2627
+ return false;
2628
+ }
2629
+ }
2630
+ //#endregion
2631
+ //#region ---- Entity Operations ----
2632
+ /**
2633
+ * Get entity by full path
2634
+ *
2635
+ * @param path Full entity path (module.aggregate.entity)
2636
+ * @returns Promise that resolves to entity model
2637
+ */
2638
+ async getEntity(path) {
2639
+ return this.registry.resolve(path);
2640
+ }
2641
+ /**
2642
+ * Get entity by component parts
2643
+ *
2644
+ * @param moduleName Module name
2645
+ * @param aggregateName Aggregate name
2646
+ * @param entityName Entity name
2647
+ * @returns Promise that resolves to entity model
2648
+ */
2649
+ async getEntityByParts(moduleName, aggregateName, entityName) {
2650
+ const path = `${moduleName}.${aggregateName}.${entityName}`;
2651
+ return this.getEntity(path);
2652
+ }
2653
+ /**
2654
+ * Check if entity exists
2655
+ *
2656
+ * @param path Full entity path
2657
+ * @returns Promise that resolves to boolean
2658
+ */
2659
+ async entityExists(path) {
2660
+ try {
2661
+ await this.getEntity(path);
2662
+ return true;
2663
+ }
2664
+ catch {
2665
+ return false;
2666
+ }
2667
+ }
2668
+ //#endregion
2669
+ //#region ---- Batch Operations ----
2670
+ /**
2671
+ * Get multiple modules in parallel
2672
+ *
2673
+ * @param moduleNames Array of module names
2674
+ * @returns Promise that resolves to array of module models
2675
+ */
2676
+ async getModules(moduleNames) {
2677
+ return Promise.all(moduleNames.map(name => this.getModule(name)));
2678
+ }
2679
+ /**
2680
+ * Get multiple aggregates in parallel
2681
+ *
2682
+ * @param aggregatePaths Array of aggregate paths
2683
+ * @returns Promise that resolves to array of aggregate models
2684
+ */
2685
+ async getAggregates(aggregatePaths) {
2686
+ return Promise.all(aggregatePaths.map(path => this.getAggregateByPath(path)));
2687
+ }
2688
+ /**
2689
+ * Get multiple entities in parallel
2690
+ *
2691
+ * @param entityPaths Array of entity paths
2692
+ * @returns Promise that resolves to array of entity models
2693
+ */
2694
+ async getEntities(entityPaths) {
2695
+ return Promise.all(entityPaths.map(path => this.getEntity(path)));
2696
+ }
2697
+ //#endregion
2698
+ //#region ---- Search and Discovery ----
2699
+ /**
2700
+ * Find entities by name across all loaded modules
2701
+ *
2702
+ * @param entityName Entity name to search for
2703
+ * @returns Promise that resolves to array of matching entity paths
2704
+ */
2705
+ async findEntitiesByName(entityName) {
2706
+ // This would require additional registry functionality
2707
+ // For now, return empty array as placeholder
2708
+ return [];
2709
+ }
2710
+ /**
2711
+ * Get all aggregates for a module
2712
+ *
2713
+ * @param moduleName Module name
2714
+ * @returns Promise that resolves to array of aggregate models
2715
+ */
2716
+ async getModuleAggregates(moduleName) {
2717
+ const module = await this.getModule(moduleName);
2718
+ return module.getAllAggregates();
2719
+ }
2720
+ /**
2721
+ * Get all entity references for an aggregate
2722
+ *
2723
+ * @param moduleName Module name
2724
+ * @param aggregateName Aggregate name
2725
+ * @returns Promise that resolves to entity references map
2726
+ */
2727
+ async getAggregateEntityReferences(moduleName, aggregateName) {
2728
+ const aggregate = await this.getAggregate(moduleName, aggregateName);
2729
+ return aggregate.entityReferences;
2730
+ }
2731
+ //#endregion
2732
+ //#region ---- Validation Operations ----
2733
+ /**
2734
+ * Validate module and all its components
2735
+ *
2736
+ * @param moduleName Module name
2737
+ * @returns Promise that resolves to validation errors array
2738
+ */
2739
+ async validateModule(moduleName) {
2740
+ try {
2741
+ const module = await this.getModule(moduleName);
2742
+ return module.validate();
2743
+ }
2744
+ catch (error) {
2745
+ return [`Failed to load module '${moduleName}': ${error instanceof Error ? error.message : 'Unknown error'}`];
2746
+ }
2747
+ }
2748
+ /**
2749
+ * Validate aggregate and its components
2750
+ *
2751
+ * @param moduleName Module name
2752
+ * @param aggregateName Aggregate name
2753
+ * @returns Promise that resolves to validation errors array
2754
+ */
2755
+ async validateAggregate(moduleName, aggregateName) {
2756
+ try {
2757
+ const aggregate = await this.getAggregate(moduleName, aggregateName);
2758
+ return aggregate.validate();
2759
+ }
2760
+ catch (error) {
2761
+ return [`Failed to load aggregate '${moduleName}.${aggregateName}': ${error instanceof Error ? error.message : 'Unknown error'}`];
2762
+ }
2763
+ }
2764
+ //#endregion
2765
+ //#region ---- Utility Operations ----
2766
+ /**
2767
+ * Get statistics for a module
2768
+ *
2769
+ * @param moduleName Module name
2770
+ * @returns Promise that resolves to module statistics
2771
+ */
2772
+ async getModuleStatistics(moduleName) {
2773
+ const module = await this.getModule(moduleName);
2774
+ return module.getStatistics();
2775
+ }
2776
+ /**
2777
+ * Get statistics for an aggregate
2778
+ *
2779
+ * @param moduleName Module name
2780
+ * @param aggregateName Aggregate name
2781
+ * @returns Promise that resolves to aggregate statistics
2782
+ */
2783
+ async getAggregateStatistics(moduleName, aggregateName) {
2784
+ const aggregate = await this.getAggregate(moduleName, aggregateName);
2785
+ return aggregate.getStatistics();
2786
+ }
2787
+ /**
2788
+ * Clear registry cache
2789
+ */
2790
+ clearCache() {
2791
+ this.registry.clearCache();
2792
+ }
2793
+ /**
2794
+ * Get cache statistics
2795
+ *
2796
+ * @returns Cache statistics object
2797
+ */
2798
+ getCacheStatistics() {
2799
+ return this.registry.getCacheStats();
2800
+ }
2801
+ /**
2802
+ * Invalidate cache for specific path
2803
+ *
2804
+ * @param path Domain path to invalidate
2805
+ */
2806
+ invalidateCache(path) {
2807
+ this.registry.invalidateCache(path);
2808
+ }
2809
+ //#endregion
2810
+ //#region ---- Helper Methods ----
2811
+ /**
2812
+ * Parse domain path into components
2813
+ *
2814
+ * @param path Domain path to parse
2815
+ * @returns Parsed path components
2816
+ */
2817
+ parsePath(path) {
2818
+ const parts = path.split('.');
2819
+ if (parts.length === 1) {
2820
+ return { module: parts[0], type: 'module' };
2821
+ }
2822
+ else if (parts.length === 2) {
2823
+ return { module: parts[0], aggregate: parts[1], type: 'aggregate' };
2824
+ }
2825
+ else if (parts.length === 3) {
2826
+ return { module: parts[0], aggregate: parts[1], entity: parts[2], type: 'entity' };
2827
+ }
2828
+ throw new Error(`Invalid domain path format: ${path}`);
2829
+ }
2830
+ /**
2831
+ * Build path from components
2832
+ *
2833
+ * @param module Module name
2834
+ * @param aggregate Aggregate name (optional)
2835
+ * @param entity Entity name (optional)
2836
+ * @returns Built path string
2837
+ */
2838
+ buildPath(module, aggregate, entity) {
2839
+ if (entity && aggregate) {
2840
+ return `${module}.${aggregate}.${entity}`;
2841
+ }
2842
+ else if (aggregate) {
2843
+ return `${module}.${aggregate}`;
2844
+ }
2845
+ else {
2846
+ return module;
2847
+ }
2848
+ }
2849
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2850
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainService, providedIn: 'root' }); }
2851
+ }
2852
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDomainService, decorators: [{
2853
+ type: Injectable,
2854
+ args: [{ providedIn: 'root' }]
2855
+ }] });
2856
+
2857
+ //#endregion
2858
+
2859
+ //#endregion
2860
+ //#region ---- Provider Functions ----
2861
+ /**
2862
+ * Provide domain loaders for on-demand domain loading.
2863
+ *
2864
+ * Domain loaders enable the registry to load domain definitions that are not registered
2865
+ * at build time. This is useful for:
2866
+ * - Loading domain definitions from external APIs
2867
+ * - Dynamic domain generation
2868
+ * - Lazy loading of large domain sets
2869
+ * - Development-time domain hot-reloading
2870
+ *
2871
+ * @param loaders Array of loader classes, instances, or loader configurations
2872
+ * @returns Environment providers for dependency injection
2873
+ *
2874
+ * @example
2875
+ * ```typescript
2876
+ * // Simple loader registration
2877
+ * provideDomainLoaders([
2878
+ * HttpDomainLoader,
2879
+ * FileSystemDomainLoader,
2880
+ * new MemoryDomainLoader()
2881
+ * ])
2882
+ *
2883
+ * // With priority configuration
2884
+ * provideDomainLoaders([
2885
+ * { loader: HttpDomainLoader, priority: 10 },
2886
+ * { loader: FileSystemDomainLoader, priority: 5 },
2887
+ * { loader: new MemoryDomainLoader(), priority: 15 }
2888
+ * ])
2889
+ * ```
2890
+ */
2891
+ function provideDomainLoaders(loaders) {
2892
+ return makeEnvironmentProviders([
2893
+ {
2894
+ provide: AXP_DOMAIN_LOADER_SETUP,
2895
+ useFactory: async () => {
2896
+ const registry = inject(AXPDomainRegistry);
2897
+ if (!registry) {
2898
+ console.warn('Domain registry not available during loader setup');
2899
+ return false;
2900
+ }
2901
+ // Register each loader with the registry
2902
+ for (const loaderConfig of loaders) {
2903
+ try {
2904
+ let loaderInstance;
2905
+ let priority;
2906
+ if (typeof loaderConfig === 'function') {
2907
+ // Type<AXPDomainLoader>
2908
+ loaderInstance = new loaderConfig();
2909
+ priority = loaderInstance.priority;
2910
+ }
2911
+ else if (typeof loaderConfig === 'object' && 'canLoad' in loaderConfig) {
2912
+ // AXPDomainLoader instance
2913
+ loaderInstance = loaderConfig;
2914
+ priority = loaderInstance.priority;
2915
+ }
2916
+ else if (typeof loaderConfig === 'object' && 'loader' in loaderConfig) {
2917
+ // AXPDomainLoaderConfig
2918
+ if (typeof loaderConfig.loader === 'function') {
2919
+ loaderInstance = new loaderConfig.loader();
2920
+ }
2921
+ else {
2922
+ loaderInstance = loaderConfig.loader;
2923
+ }
2924
+ priority = loaderConfig.priority ?? loaderInstance.priority;
2925
+ }
2926
+ else {
2927
+ console.warn('Invalid loader configuration:', loaderConfig);
2928
+ continue;
2929
+ }
2930
+ // Apply custom priority if specified
2931
+ if (priority !== undefined && priority !== loaderInstance.priority) {
2932
+ loaderInstance.priority = priority;
2933
+ }
2934
+ registry.addLoader(loaderInstance);
2935
+ console.log(`✓ Registered domain loader: ${loaderInstance.constructor.name} (priority: ${loaderInstance.priority ?? 0})`);
2936
+ }
2937
+ catch (error) {
2938
+ console.error('Failed to register domain loader:', error);
2939
+ }
2940
+ }
2941
+ return true;
2942
+ },
2943
+ multi: true
2944
+ }
2945
+ ]);
2946
+ }
2947
+ /**
2948
+ * Provide a single domain loader for convenience
2949
+ *
2950
+ * @param loader Loader class, instance, or configuration
2951
+ * @returns Environment providers for dependency injection
2952
+ */
2953
+ function provideDomainLoader(loader) {
2954
+ return provideDomainLoaders([loader]);
2955
+ }
2956
+ //#endregion
2957
+
2958
+ //#region ---- Injection Tokens ----
2959
+ /**
2960
+ * Injection tokens for type-specific middleware setup
2961
+ */
2962
+ const AXP_MODULE_MIDDLEWARE_SETUP = 'AXP_MODULE_MIDDLEWARE_SETUP';
2963
+ const AXP_AGGREGATE_MIDDLEWARE_SETUP = 'AXP_AGGREGATE_MIDDLEWARE_SETUP';
2964
+ const AXP_ENTITY_MIDDLEWARE_SETUP = 'AXP_ENTITY_MIDDLEWARE_SETUP';
2965
+ //#endregion
2966
+ //#region ---- Provider Functions ----
2967
+ /**
2968
+ * Provide middleware for module processing.
2969
+ *
2970
+ * Module middleware is applied when resolving module definitions and can:
2971
+ * - Add metadata and custom properties
2972
+ * - Validate module structure
2973
+ * - Transform module definitions
2974
+ * - Add computed properties
2975
+ *
2976
+ * @param middleware Array of middleware functions to register
2977
+ * @returns Environment providers for dependency injection
2978
+ *
2979
+ * @example
2980
+ * ```typescript
2981
+ * provideModuleMiddleware([
2982
+ * (context) => {
2983
+ * // Add common module metadata
2984
+ * context.setMetadata('loadedAt', new Date());
2985
+ * context.setMetadata('version', '1.0.0');
2986
+ * },
2987
+ * (context) => {
2988
+ * // Add namespace prefix if not present
2989
+ * if (!context.definition.name.includes('.')) {
2990
+ * context.definition.name = `app.${context.definition.name}`;
2991
+ * }
2992
+ * }
2993
+ * ])
2994
+ * ```
2995
+ */
2996
+ function provideModuleMiddleware(middleware) {
2997
+ return makeEnvironmentProviders([
2998
+ {
2999
+ provide: AXP_MODULE_MIDDLEWARE_SETUP,
3000
+ useFactory: () => {
3001
+ // Access registry through DI when available
3002
+ const registry = globalThis.__domainRegistry__;
3003
+ if (!registry) {
3004
+ console.warn('Domain registry not available during module middleware setup');
3005
+ return false;
3006
+ }
3007
+ // Register all module middleware
3008
+ for (const mw of middleware) {
3009
+ registry.addModuleMiddleware(mw);
3010
+ }
3011
+ console.log(`✓ Registered ${middleware.length} module middleware functions`);
3012
+ return true;
3013
+ },
3014
+ multi: true
3015
+ }
3016
+ ]);
3017
+ }
3018
+ /**
3019
+ * Provide middleware for aggregate processing.
3020
+ *
3021
+ * Aggregate middleware is applied when resolving aggregate definitions and can:
3022
+ * - Add metadata and custom properties
3023
+ * - Validate aggregate structure
3024
+ * - Transform entity references
3025
+ * - Add computed relations
3026
+ *
3027
+ * @param middleware Array of middleware functions to register
3028
+ * @returns Environment providers for dependency injection
3029
+ *
3030
+ * @example
3031
+ * ```typescript
3032
+ * provideAggregateMiddleware([
3033
+ * (context) => {
3034
+ * // Add aggregate metadata
3035
+ * context.setMetadata('type', 'business-aggregate');
3036
+ * },
3037
+ * (context) => {
3038
+ * // Validate entity references
3039
+ * const entities = context.definition.entities;
3040
+ * if (Object.keys(entities).length === 0) {
3041
+ * throw new Error('Aggregate must have at least one entity');
3042
+ * }
3043
+ * }
3044
+ * ])
3045
+ * ```
3046
+ */
3047
+ function provideAggregateMiddleware(middleware) {
3048
+ return makeEnvironmentProviders([
3049
+ {
3050
+ provide: AXP_AGGREGATE_MIDDLEWARE_SETUP,
3051
+ useFactory: () => {
3052
+ // Access registry through DI when available
3053
+ const registry = globalThis.__domainRegistry__;
3054
+ if (!registry) {
3055
+ console.warn('Domain registry not available during aggregate middleware setup');
3056
+ return false;
3057
+ }
3058
+ // Register all aggregate middleware
3059
+ for (const mw of middleware) {
3060
+ registry.addAggregateMiddleware(mw);
3061
+ }
3062
+ console.log(`✓ Registered ${middleware.length} aggregate middleware functions`);
3063
+ return true;
3064
+ },
3065
+ multi: true
3066
+ }
3067
+ ]);
3068
+ }
3069
+ /**
3070
+ * Provide middleware for entity processing.
3071
+ *
3072
+ * Entity middleware is applied when resolving entity definitions and can:
3073
+ * - Add auto-generated fields (id, timestamps)
3074
+ * - Apply field transformations
3075
+ * - Add validation rules
3076
+ * - Set default values
3077
+ *
3078
+ * @param middleware Array of middleware functions to register
3079
+ * @returns Environment providers for dependency injection
3080
+ *
3081
+ * @example
3082
+ * ```typescript
3083
+ * provideEntityMiddleware([
3084
+ * (context) => {
3085
+ * // Add auto-generated ID field if not present
3086
+ * if (!context.definition.fields.some(f => f.name === 'id')) {
3087
+ * context.addField({
3088
+ * name: 'id',
3089
+ * type: 'uuid',
3090
+ * required: true,
3091
+ * generated: true
3092
+ * });
3093
+ * }
3094
+ * },
3095
+ * (context) => {
3096
+ * // Add timestamp fields
3097
+ * context.addField({
3098
+ * name: 'createdAt',
3099
+ * type: 'datetime',
3100
+ * required: true,
3101
+ * generated: true
3102
+ * });
3103
+ * context.addField({
3104
+ * name: 'updatedAt',
3105
+ * type: 'datetime',
3106
+ * required: true,
3107
+ * generated: true
3108
+ * });
3109
+ * }
3110
+ * ])
3111
+ * ```
3112
+ */
3113
+ function provideEntityMiddleware(middleware) {
3114
+ return makeEnvironmentProviders([
3115
+ {
3116
+ provide: AXP_ENTITY_MIDDLEWARE_SETUP,
3117
+ useFactory: () => {
3118
+ // Access registry through DI when available
3119
+ const registry = globalThis.__domainRegistry__;
3120
+ if (!registry) {
3121
+ console.warn('Domain registry not available during entity middleware setup');
3122
+ return false;
3123
+ }
3124
+ // Register all entity middleware
3125
+ for (const mw of middleware) {
3126
+ registry.addEntityMiddleware(mw);
3127
+ }
3128
+ console.log(`✓ Registered ${middleware.length} entity middleware functions`);
3129
+ return true;
3130
+ },
3131
+ multi: true
3132
+ }
3133
+ ]);
3134
+ }
3135
+ /**
3136
+ * Provide combined middleware for all domain types.
3137
+ *
3138
+ * This is a convenience function that allows registering middleware for multiple
3139
+ * domain types in a single call.
3140
+ *
3141
+ * @param config Configuration object with middleware for each type
3142
+ * @returns Environment providers for dependency injection
3143
+ *
3144
+ * @example
3145
+ * ```typescript
3146
+ * provideDomainMiddleware({
3147
+ * modules: [
3148
+ * (context) => context.setMetadata('loadedAt', new Date())
3149
+ * ],
3150
+ * aggregates: [
3151
+ * (context) => context.setMetadata('type', 'business-aggregate')
3152
+ * ],
3153
+ * entities: [
3154
+ * (context) => {
3155
+ * if (!context.definition.fields.some(f => f.name === 'id')) {
3156
+ * context.addField({ name: 'id', type: 'uuid', required: true });
3157
+ * }
3158
+ * }
3159
+ * ]
3160
+ * })
3161
+ * ```
3162
+ */
3163
+ function provideDomainMiddleware(config) {
3164
+ const allProviders = [];
3165
+ // Add module middleware providers
3166
+ if (config.modules && config.modules.length > 0) {
3167
+ allProviders.push({
3168
+ provide: AXP_MODULE_MIDDLEWARE_SETUP,
3169
+ useFactory: () => {
3170
+ const registry = globalThis.__domainRegistry__;
3171
+ if (!registry) {
3172
+ console.warn('Domain registry not available during module middleware setup');
3173
+ return false;
3174
+ }
3175
+ for (const mw of config.modules) {
3176
+ registry.addModuleMiddleware(mw);
3177
+ }
3178
+ return true;
3179
+ },
3180
+ multi: true
3181
+ });
3182
+ }
3183
+ // Add aggregate middleware providers
3184
+ if (config.aggregates && config.aggregates.length > 0) {
3185
+ allProviders.push({
3186
+ provide: AXP_AGGREGATE_MIDDLEWARE_SETUP,
3187
+ useFactory: () => {
3188
+ const registry = globalThis.__domainRegistry__;
3189
+ if (!registry) {
3190
+ console.warn('Domain registry not available during aggregate middleware setup');
3191
+ return false;
3192
+ }
3193
+ for (const mw of config.aggregates) {
3194
+ registry.addAggregateMiddleware(mw);
3195
+ }
3196
+ return true;
3197
+ },
3198
+ multi: true
3199
+ });
3200
+ }
3201
+ // Add entity middleware providers
3202
+ if (config.entities && config.entities.length > 0) {
3203
+ allProviders.push({
3204
+ provide: AXP_ENTITY_MIDDLEWARE_SETUP,
3205
+ useFactory: () => {
3206
+ const registry = globalThis.__domainRegistry__;
3207
+ if (!registry) {
3208
+ console.warn('Domain registry not available during entity middleware setup');
3209
+ return false;
3210
+ }
3211
+ for (const mw of config.entities) {
3212
+ registry.addEntityMiddleware(mw);
3213
+ }
3214
+ return true;
3215
+ },
3216
+ multi: true
3217
+ });
3218
+ }
3219
+ return makeEnvironmentProviders(allProviders);
3220
+ }
3221
+ //#endregion
3222
+
3223
+ //#region ---- Core Registry System ----
3224
+ // Main registry and service
3225
+ //#endregion
3226
+
3227
+ //#endregion
3228
+
3229
+ /**
3230
+ * Generated bundle index. Do not edit.
3231
+ */
3232
+
3233
+ export { AXPAggregateModel, AXPDomainActionSide, AXPDomainMiddlewareContext, AXPDomainModule, AXPDomainRegistry, AXPDomainService, AXPEntityCommandScope, AXPEntityFieldModel, AXPEntityModel, AXPEntityType, AXPModuleHelper, AXPModuleModel, AXPPropertyMiddlewareContext, AXPPropertyModel, AXPPropertyRegistry, AXPPropertyService, AXPRelationModel, AXPRelationshipCardinality, AXPRelationshipKind, AXP_AGGREGATE_MIDDLEWARE_SETUP, AXP_ENTITY_CRUD_SETUP, AXP_ENTITY_DEFINITION_CRUD_SERVICE, AXP_ENTITY_MIDDLEWARE_SETUP, AXP_MODULE_MIDDLEWARE_SETUP, AXP_PROPERTY_EXTENSION, AXP_PROPERTY_LOADER_SETUP, AXP_PROPERTY_MIDDLEWARE_SETUP, AXP_PROPERTY_SETUP, provideAggregateMiddleware, provideDomainLoader, provideDomainLoaders, provideDomainMiddleware, provideEntityMiddleware, provideModuleMiddleware, providePropertiesFromFactory, provideProperty, providePropertyLoaders, providePropertyMiddleware, providePropertySetups, toPropertyDefinition };
3234
+ //# sourceMappingURL=acorex-platform-domain.mjs.map