@acorex-platform/framework-client 0.0.10 → 0.0.15

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 (119) hide show
  1. package/fesm2022/acorex-platform-framework-client-auth.mjs +285 -50
  2. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs → acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs} +55 -36
  4. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-framework-client-common-settings-dialog.helper-DQNOCRL6.mjs +29 -0
  6. package/fesm2022/acorex-platform-framework-client-common-settings-dialog.helper-DQNOCRL6.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-framework-client-common.mjs +1576 -408
  8. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-framework-client-core.mjs +561 -25
  10. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +154 -42
  12. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -1
  13. package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs} +7 -7
  14. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs +77 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +2378 -1274
  18. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +92 -84
  20. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -1
  21. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs} +3 -3
  22. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs.map} +1 -1
  23. package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs → acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs} +6 -6
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs +256 -0
  26. package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +17599 -17114
  28. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -1
  29. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +120 -70
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
  31. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +302 -263
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
  33. package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs} +2 -2
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs → acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs} +3 -3
  36. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs} +5 -5
  38. package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs.map → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs.map} +1 -1
  39. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs} +3 -3
  40. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs.map} +1 -1
  41. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs} +10 -10
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs.map +1 -0
  43. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs} +5 -5
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs +61 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +2218 -1504
  48. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
  49. package/fesm2022/acorex-platform-framework-client-realtime.mjs +227 -0
  50. package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-framework-client-runtime.mjs +201 -13
  52. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
  53. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs +158 -0
  54. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs +119 -0
  56. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs.map +1 -0
  57. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs} +5 -6
  58. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs +48 -0
  60. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs +42 -0
  62. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs +19 -0
  64. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +123 -96
  66. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
  67. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs} +3 -3
  68. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs.map +1 -0
  69. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs} +3 -3
  70. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs.map +1 -0
  71. package/fesm2022/{acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs → acorex-platform-framework-client-themes-shared-settings.provider-D2XPkc71.mjs} +11 -2
  72. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-D2XPkc71.mjs.map +1 -0
  73. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs} +7 -7
  74. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs.map +1 -0
  75. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs} +7 -7
  76. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +124 -112
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
  79. package/fesm2022/acorex-platform-framework-client-workflow.mjs +26 -483
  80. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -1
  81. package/package.json +14 -9
  82. package/types/acorex-platform-framework-client-auth.d.ts +76 -5
  83. package/types/acorex-platform-framework-client-common.d.ts +499 -129
  84. package/types/acorex-platform-framework-client-core.d.ts +291 -10
  85. package/types/acorex-platform-framework-client-layout-builder.d.ts +81 -31
  86. package/types/acorex-platform-framework-client-layout-components.d.ts +295 -41
  87. package/types/acorex-platform-framework-client-layout-entity.d.ts +1895 -1743
  88. package/types/acorex-platform-framework-client-layout-views.d.ts +8 -18
  89. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +20 -35
  90. package/types/acorex-platform-framework-client-layout-widgets.d.ts +167 -33
  91. package/types/acorex-platform-framework-client-realtime.d.ts +100 -0
  92. package/types/acorex-platform-framework-client-runtime.d.ts +60 -2
  93. package/types/acorex-platform-framework-client-themes-default.d.ts +4 -48
  94. package/types/acorex-platform-framework-client-workflow.d.ts +16 -127
  95. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs.map +0 -1
  96. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs.map +0 -1
  97. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs.map +0 -1
  98. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs.map +0 -1
  99. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs.map +0 -1
  101. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs +0 -34
  103. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs +0 -160
  105. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs +0 -121
  107. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs.map +0 -1
  109. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs +0 -48
  110. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs.map +0 -1
  111. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs +0 -42
  112. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs.map +0 -1
  113. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs +0 -19
  114. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs.map +0 -1
  115. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs.map +0 -1
  116. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs.map +0 -1
  117. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs.map +0 -1
  118. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs.map +0 -1
  119. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs.map +0 -1
@@ -0,0 +1,227 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Optional, Inject, Injectable, inject, makeEnvironmentProviders, APP_INITIALIZER } from '@angular/core';
3
+ import { AXPSessionService, AXPSessionStatus } from '@acorex-platform/framework-client/auth';
4
+ import { io } from 'socket.io-client';
5
+ import { BehaviorSubject } from 'rxjs';
6
+
7
+ //#region ---- Client realtime types ----
8
+ //#endregion
9
+
10
+ //#region ---- Client topic constants (mirror server) ----
11
+ const AXP_REALTIME_TOPIC_INBOX_REFRESH = 'inbox.refresh';
12
+ const AXP_REALTIME_TOPIC_CONVERSATION_MESSAGE = 'conversation.message';
13
+ const AXP_REALTIME_TOPIC_ENTITY_MUTATION = 'entity.mutation';
14
+ const AXP_REALTIME_SOCKET_EVENT = 'realtime';
15
+ const AXP_REALTIME_SUBSCRIBE_CONVERSATION = 'subscribe.conversation';
16
+ const AXP_REALTIME_UNSUBSCRIBE_CONVERSATION = 'unsubscribe.conversation';
17
+ //#endregion
18
+
19
+ //#region ---- Realtime URL helpers ----
20
+ /**
21
+ * Builds Socket.IO namespace URL from an HTTP API baseUrl (`…/api` → `…/realtime`).
22
+ */
23
+ function resolveRealtimeSocketUrl(apiBaseUrl) {
24
+ const trimmed = apiBaseUrl.trim().replace(/\/+$/, '');
25
+ const withoutApi = trimmed.endsWith('/api') ? trimmed.slice(0, -4) : trimmed;
26
+ return `${withoutApi}/realtime`;
27
+ }
28
+ //#endregion
29
+
30
+ //#region ---- DI tokens ----
31
+ const AXP_REALTIME_CONFIG = new InjectionToken('AXP_REALTIME_CONFIG');
32
+ const AXP_REALTIME_TOPIC_HANDLER = new InjectionToken('AXP_REALTIME_TOPIC_HANDLER');
33
+ //#endregion
34
+
35
+ //#region ---- Topic dispatcher ----
36
+ class AXPRealtimeDispatcherService {
37
+ constructor(registrations) {
38
+ this.handlers = new Map();
39
+ const list = !registrations
40
+ ? []
41
+ : Array.isArray(registrations)
42
+ ? registrations
43
+ : [registrations];
44
+ for (const registration of list) {
45
+ this.register(registration);
46
+ }
47
+ }
48
+ register(registration) {
49
+ const topic = registration.topic?.trim();
50
+ if (!topic || typeof registration.handle !== 'function') {
51
+ return;
52
+ }
53
+ const existing = this.handlers.get(topic) ?? [];
54
+ existing.push(registration);
55
+ this.handlers.set(topic, existing);
56
+ }
57
+ async dispatch(envelope) {
58
+ const topic = envelope?.topic?.trim();
59
+ if (!topic) {
60
+ return;
61
+ }
62
+ const list = this.handlers.get(topic);
63
+ if (!list?.length) {
64
+ return;
65
+ }
66
+ for (const registration of list) {
67
+ await registration.handle(envelope);
68
+ }
69
+ }
70
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeDispatcherService, deps: [{ token: AXP_REALTIME_TOPIC_HANDLER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
71
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeDispatcherService }); }
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeDispatcherService, decorators: [{
74
+ type: Injectable
75
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
76
+ type: Optional
77
+ }, {
78
+ type: Inject,
79
+ args: [AXP_REALTIME_TOPIC_HANDLER]
80
+ }] }] });
81
+
82
+ //#region ---- Connection service ----
83
+ class AXPRealtimeConnectionService {
84
+ constructor(config) {
85
+ this.config = config;
86
+ this.session = inject(AXPSessionService);
87
+ this.dispatcher = inject(AXPRealtimeDispatcherService);
88
+ this.socket = null;
89
+ this.statusSub = null;
90
+ this.joinedConversations = new Set();
91
+ this.connectionStatusSubject = new BehaviorSubject('disconnected');
92
+ /** Latest Socket.IO connection state for feature modules (e.g. chat). */
93
+ this.connectionStatus$ = this.connectionStatusSubject.asObservable();
94
+ if (config?.autoConnect === false) {
95
+ return;
96
+ }
97
+ this.statusSub = this.session.status$.subscribe((status) => {
98
+ if (status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized) {
99
+ this.connect();
100
+ }
101
+ else {
102
+ this.disconnect();
103
+ }
104
+ });
105
+ }
106
+ ngOnDestroy() {
107
+ this.statusSub?.unsubscribe();
108
+ this.disconnect();
109
+ }
110
+ connect() {
111
+ const token = this.session.getToken()?.trim();
112
+ const url = this.config?.url?.trim();
113
+ if (!token || !url) {
114
+ this.connectionStatusSubject.next('disconnected');
115
+ return;
116
+ }
117
+ if (this.socket?.connected) {
118
+ return;
119
+ }
120
+ // Avoid tearing down a socket that is still handshaking (duplicate connect calls).
121
+ if (this.socket && !this.socket.connected) {
122
+ return;
123
+ }
124
+ this.connectionStatusSubject.next('connecting');
125
+ this.socket = io(url, {
126
+ transports: ['websocket', 'polling'],
127
+ auth: { token },
128
+ autoConnect: true,
129
+ reconnection: true,
130
+ });
131
+ this.socket.on(AXP_REALTIME_SOCKET_EVENT, (envelope) => {
132
+ void this.dispatcher.dispatch(envelope);
133
+ });
134
+ this.socket.on('connect', () => {
135
+ this.connectionStatusSubject.next('connected');
136
+ for (const conversationId of this.joinedConversations) {
137
+ this.socket?.emit(AXP_REALTIME_SUBSCRIBE_CONVERSATION, { conversationId });
138
+ }
139
+ });
140
+ this.socket.on('disconnect', () => {
141
+ this.connectionStatusSubject.next('disconnected');
142
+ });
143
+ this.socket.on('connect_error', () => {
144
+ this.connectionStatusSubject.next('disconnected');
145
+ });
146
+ }
147
+ disconnect() {
148
+ if (this.socket) {
149
+ this.socket.removeAllListeners();
150
+ this.socket.disconnect();
151
+ this.socket = null;
152
+ }
153
+ this.connectionStatusSubject.next('disconnected');
154
+ }
155
+ /** Re-auth with the latest access token (e.g. after refresh). */
156
+ reconnectWithCurrentToken() {
157
+ this.disconnect();
158
+ this.connect();
159
+ }
160
+ /** Whether the underlying Socket.IO client is connected. */
161
+ isSocketConnected() {
162
+ return this.socket?.connected === true;
163
+ }
164
+ subscribeConversation(conversationId) {
165
+ const id = conversationId?.trim();
166
+ if (!id) {
167
+ return;
168
+ }
169
+ this.joinedConversations.add(id);
170
+ this.socket?.emit(AXP_REALTIME_SUBSCRIBE_CONVERSATION, { conversationId: id });
171
+ }
172
+ unsubscribeConversation(conversationId) {
173
+ const id = conversationId?.trim();
174
+ if (!id) {
175
+ return;
176
+ }
177
+ this.joinedConversations.delete(id);
178
+ this.socket?.emit(AXP_REALTIME_UNSUBSCRIBE_CONVERSATION, { conversationId: id });
179
+ }
180
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeConnectionService, deps: [{ token: AXP_REALTIME_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
181
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeConnectionService }); }
182
+ }
183
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPRealtimeConnectionService, decorators: [{
184
+ type: Injectable
185
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
186
+ type: Optional
187
+ }, {
188
+ type: Inject,
189
+ args: [AXP_REALTIME_CONFIG]
190
+ }] }] });
191
+
192
+ //#region ---- provideRealtime ----
193
+ function provideRealtime(config = {}) {
194
+ const url = config.url?.trim() ||
195
+ (config.apiBaseUrl?.trim() ? resolveRealtimeSocketUrl(config.apiBaseUrl) : undefined);
196
+ return makeEnvironmentProviders([
197
+ {
198
+ provide: AXP_REALTIME_CONFIG,
199
+ useValue: {
200
+ autoConnect: config.autoConnect !== false,
201
+ url,
202
+ },
203
+ },
204
+ AXPRealtimeDispatcherService,
205
+ AXPRealtimeConnectionService,
206
+ {
207
+ provide: APP_INITIALIZER,
208
+ multi: true,
209
+ useFactory: () => {
210
+ const connection = inject(AXPRealtimeConnectionService);
211
+ return () => {
212
+ // Connection lifecycle is owned by AXPRealtimeConnectionService (session.status$).
213
+ // Eagerly invoke connect once so an already-authenticated session connects on bootstrap.
214
+ connection.connect();
215
+ };
216
+ },
217
+ },
218
+ ]);
219
+ }
220
+ //#endregion
221
+
222
+ /**
223
+ * Generated bundle index. Do not edit.
224
+ */
225
+
226
+ export { AXPRealtimeConnectionService, AXPRealtimeDispatcherService, AXP_REALTIME_CONFIG, AXP_REALTIME_SOCKET_EVENT, AXP_REALTIME_SUBSCRIBE_CONVERSATION, AXP_REALTIME_TOPIC_CONVERSATION_MESSAGE, AXP_REALTIME_TOPIC_ENTITY_MUTATION, AXP_REALTIME_TOPIC_HANDLER, AXP_REALTIME_TOPIC_INBOX_REFRESH, AXP_REALTIME_UNSUBSCRIBE_CONVERSATION, provideRealtime, resolveRealtimeSocketUrl };
227
+ //# sourceMappingURL=acorex-platform-framework-client-realtime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-framework-client-realtime.mjs","sources":["../../../../../libs/framework/client/realtime/src/lib/realtime.types.ts","../../../../../libs/framework/client/realtime/src/lib/realtime.topics.ts","../../../../../libs/framework/client/realtime/src/lib/realtime-url.util.ts","../../../../../libs/framework/client/realtime/src/lib/tokens.ts","../../../../../libs/framework/client/realtime/src/lib/realtime-dispatcher.service.ts","../../../../../libs/framework/client/realtime/src/lib/realtime-connection.service.ts","../../../../../libs/framework/client/realtime/src/lib/realtime.providers.ts","../../../../../libs/framework/client/realtime/src/acorex-platform-framework-client-realtime.ts"],"sourcesContent":["//#region ---- Client realtime types ----\n\nexport interface AXPRealtimeAudience {\n userIds?: string[];\n // TODO(realtime): See server realtime.types.ts — generic refType/refId scopes.\n conversationId?: string;\n /** When true, deliver to all connected users in the envelope tenant. */\n tenant?: boolean;\n}\n\nexport interface AXPRealtimeEnvelope {\n topic: string;\n tenantId: string;\n audience: AXPRealtimeAudience;\n payload: unknown;\n correlationId?: string;\n emittedAt: string;\n}\n\nexport interface AXPRealtimeInboxRefreshPayload {\n deliveryId?: string;\n}\n\nexport interface AXPRealtimeConversationMessagePayload {\n conversationId: string;\n messageId: string;\n op: 'created' | 'updated' | 'deleted';\n}\n\nexport interface AXPRealtimeEntityMutationPayload {\n entityName: string;\n operation: 'created' | 'updated' | 'deleted';\n id?: string;\n}\n\nexport interface AXPRealtimeConfig {\n /**\n * Socket.IO namespace URL, e.g. `http://localhost:5500/realtime`.\n * Derived from API baseUrl when omitted (strip trailing `/api`).\n */\n url?: string;\n /** When false, connection is not started automatically. Default true. */\n autoConnect?: boolean;\n}\n\nexport type AXPRealtimeTopicHandler = (envelope: AXPRealtimeEnvelope) => void | Promise<void>;\n\nexport interface AXPRealtimeTopicHandlerRegistration {\n topic: string;\n handle: AXPRealtimeTopicHandler;\n}\n\n//#endregion\n","//#region ---- Client topic constants (mirror server) ----\n\nexport const AXP_REALTIME_TOPIC_INBOX_REFRESH = 'inbox.refresh';\nexport const AXP_REALTIME_TOPIC_CONVERSATION_MESSAGE = 'conversation.message';\nexport const AXP_REALTIME_TOPIC_ENTITY_MUTATION = 'entity.mutation';\n\nexport const AXP_REALTIME_SOCKET_EVENT = 'realtime';\nexport const AXP_REALTIME_SUBSCRIBE_CONVERSATION = 'subscribe.conversation';\nexport const AXP_REALTIME_UNSUBSCRIBE_CONVERSATION = 'unsubscribe.conversation';\n\n//#endregion\n","//#region ---- Realtime URL helpers ----\n\n/**\n * Builds Socket.IO namespace URL from an HTTP API baseUrl (`…/api` → `…/realtime`).\n */\nexport function resolveRealtimeSocketUrl(apiBaseUrl: string): string {\n const trimmed = apiBaseUrl.trim().replace(/\\/+$/, '');\n const withoutApi = trimmed.endsWith('/api') ? trimmed.slice(0, -4) : trimmed;\n return `${withoutApi}/realtime`;\n}\n\n//#endregion\n","import { InjectionToken } from '@angular/core';\nimport type {\n AXPRealtimeConfig,\n AXPRealtimeTopicHandlerRegistration,\n} from './realtime.types';\n\n//#region ---- DI tokens ----\n\nexport const AXP_REALTIME_CONFIG = new InjectionToken<AXPRealtimeConfig>('AXP_REALTIME_CONFIG');\n\nexport const AXP_REALTIME_TOPIC_HANDLER = new InjectionToken<AXPRealtimeTopicHandlerRegistration>(\n 'AXP_REALTIME_TOPIC_HANDLER',\n);\n\n//#endregion\n","import { Inject, Injectable, Optional } from '@angular/core';\nimport { AXP_REALTIME_TOPIC_HANDLER } from './tokens';\nimport type {\n AXPRealtimeEnvelope,\n AXPRealtimeTopicHandlerRegistration,\n} from './realtime.types';\n\n//#region ---- Topic dispatcher ----\n\n@Injectable()\nexport class AXPRealtimeDispatcherService {\n private readonly handlers = new Map<string, AXPRealtimeTopicHandlerRegistration[]>();\n\n constructor(\n @Optional()\n @Inject(AXP_REALTIME_TOPIC_HANDLER)\n registrations?: AXPRealtimeTopicHandlerRegistration[] | AXPRealtimeTopicHandlerRegistration,\n ) {\n const list = !registrations\n ? []\n : Array.isArray(registrations)\n ? registrations\n : [registrations];\n for (const registration of list) {\n this.register(registration);\n }\n }\n\n register(registration: AXPRealtimeTopicHandlerRegistration): void {\n const topic = registration.topic?.trim();\n if (!topic || typeof registration.handle !== 'function') {\n return;\n }\n const existing = this.handlers.get(topic) ?? [];\n existing.push(registration);\n this.handlers.set(topic, existing);\n }\n\n async dispatch(envelope: AXPRealtimeEnvelope): Promise<void> {\n const topic = envelope?.topic?.trim();\n if (!topic) {\n return;\n }\n const list = this.handlers.get(topic);\n if (!list?.length) {\n return;\n }\n for (const registration of list) {\n await registration.handle(envelope);\n }\n }\n}\n\n//#endregion\n","import { Inject, Injectable, OnDestroy, Optional, inject } from '@angular/core';\nimport { AXPSessionService, AXPSessionStatus } from '@acorex-platform/framework-client/auth';\nimport { io, type Socket } from 'socket.io-client';\nimport { BehaviorSubject, Subscription } from 'rxjs';\nimport { AXPRealtimeDispatcherService } from './realtime-dispatcher.service';\nimport {\n AXP_REALTIME_SOCKET_EVENT,\n AXP_REALTIME_SUBSCRIBE_CONVERSATION,\n AXP_REALTIME_UNSUBSCRIBE_CONVERSATION,\n} from './realtime.topics';\nimport type { AXPRealtimeConfig, AXPRealtimeEnvelope } from './realtime.types';\nimport { AXP_REALTIME_CONFIG } from './tokens';\n\n//#region ---- Connection service ----\n\n@Injectable()\nexport class AXPRealtimeConnectionService implements OnDestroy {\n private readonly session = inject(AXPSessionService);\n private readonly dispatcher = inject(AXPRealtimeDispatcherService);\n private socket: Socket | null = null;\n private statusSub: Subscription | null = null;\n private readonly joinedConversations = new Set<string>();\n private readonly connectionStatusSubject = new BehaviorSubject<\n 'connected' | 'connecting' | 'disconnected'\n >('disconnected');\n\n /** Latest Socket.IO connection state for feature modules (e.g. chat). */\n readonly connectionStatus$ = this.connectionStatusSubject.asObservable();\n\n constructor(\n @Optional()\n @Inject(AXP_REALTIME_CONFIG)\n private readonly config: AXPRealtimeConfig | null,\n ) {\n if (config?.autoConnect === false) {\n return;\n }\n this.statusSub = this.session.status$.subscribe((status) => {\n if (status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized) {\n this.connect();\n } else {\n this.disconnect();\n }\n });\n }\n\n ngOnDestroy(): void {\n this.statusSub?.unsubscribe();\n this.disconnect();\n }\n\n connect(): void {\n const token = this.session.getToken()?.trim();\n const url = this.config?.url?.trim();\n if (!token || !url) {\n this.connectionStatusSubject.next('disconnected');\n return;\n }\n\n if (this.socket?.connected) {\n return;\n }\n\n // Avoid tearing down a socket that is still handshaking (duplicate connect calls).\n if (this.socket && !this.socket.connected) {\n return;\n }\n\n this.connectionStatusSubject.next('connecting');\n\n this.socket = io(url, {\n transports: ['websocket', 'polling'],\n auth: { token },\n autoConnect: true,\n reconnection: true,\n });\n\n this.socket.on(AXP_REALTIME_SOCKET_EVENT, (envelope: AXPRealtimeEnvelope) => {\n void this.dispatcher.dispatch(envelope);\n });\n\n this.socket.on('connect', () => {\n this.connectionStatusSubject.next('connected');\n for (const conversationId of this.joinedConversations) {\n this.socket?.emit(AXP_REALTIME_SUBSCRIBE_CONVERSATION, { conversationId });\n }\n });\n\n this.socket.on('disconnect', () => {\n this.connectionStatusSubject.next('disconnected');\n });\n\n this.socket.on('connect_error', () => {\n this.connectionStatusSubject.next('disconnected');\n });\n }\n\n disconnect(): void {\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.disconnect();\n this.socket = null;\n }\n this.connectionStatusSubject.next('disconnected');\n }\n\n /** Re-auth with the latest access token (e.g. after refresh). */\n reconnectWithCurrentToken(): void {\n this.disconnect();\n this.connect();\n }\n\n /** Whether the underlying Socket.IO client is connected. */\n isSocketConnected(): boolean {\n return this.socket?.connected === true;\n }\n\n subscribeConversation(conversationId: string): void {\n const id = conversationId?.trim();\n if (!id) {\n return;\n }\n this.joinedConversations.add(id);\n this.socket?.emit(AXP_REALTIME_SUBSCRIBE_CONVERSATION, { conversationId: id });\n }\n\n unsubscribeConversation(conversationId: string): void {\n const id = conversationId?.trim();\n if (!id) {\n return;\n }\n this.joinedConversations.delete(id);\n this.socket?.emit(AXP_REALTIME_UNSUBSCRIBE_CONVERSATION, { conversationId: id });\n }\n}\n\n//#endregion\n","import {\n APP_INITIALIZER,\n inject,\n makeEnvironmentProviders,\n type EnvironmentProviders,\n} from '@angular/core';\nimport { AXPRealtimeConnectionService } from './realtime-connection.service';\nimport { AXPRealtimeDispatcherService } from './realtime-dispatcher.service';\nimport type { AXPRealtimeConfig } from './realtime.types';\nimport { resolveRealtimeSocketUrl } from './realtime-url.util';\nimport { AXP_REALTIME_CONFIG } from './tokens';\n\n//#region ---- provideRealtime ----\n\nexport function provideRealtime(\n config: AXPRealtimeConfig & { apiBaseUrl?: string } = {},\n): EnvironmentProviders {\n const url =\n config.url?.trim() ||\n (config.apiBaseUrl?.trim() ? resolveRealtimeSocketUrl(config.apiBaseUrl) : undefined);\n\n return makeEnvironmentProviders([\n {\n provide: AXP_REALTIME_CONFIG,\n useValue: {\n autoConnect: config.autoConnect !== false,\n url,\n } satisfies AXPRealtimeConfig,\n },\n AXPRealtimeDispatcherService,\n AXPRealtimeConnectionService,\n {\n provide: APP_INITIALIZER,\n multi: true,\n useFactory: () => {\n const connection = inject(AXPRealtimeConnectionService);\n return () => {\n // Connection lifecycle is owned by AXPRealtimeConnectionService (session.status$).\n // Eagerly invoke connect once so an already-authenticated session connects on bootstrap.\n connection.connect();\n };\n },\n },\n ]);\n}\n\n//#endregion\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, Injectable, InjectionToken, EnvironmentInjector, runInInjectionContext, makeEnvironmentProviders, input, ElementRef, effect, Directive, isDevMode, NgModule } from '@angular/core';
3
- import { AXPBroadcastEventService, AXPDistributedEventListenerService } from '@acorex-platform/framework-client/core';
3
+ import { AXPBroadcastEventService, AXPDistributedEventListenerService, AXPHookService } from '@acorex-platform/framework-client/core';
4
4
  import { castArray } from 'lodash-es';
5
5
 
6
6
  //#region ---- Imports ----
@@ -33,9 +33,11 @@ class AXPCommandEventsService {
33
33
  */
34
34
  serializeForBroadcast(event) {
35
35
  return {
36
+ executionId: event.executionId,
36
37
  stage: event.stage,
37
38
  key: event.key,
38
39
  success: event.success,
40
+ durationMs: event.durationMs,
39
41
  input: this.stripNonSerializable(event.input),
40
42
  result: event.result === undefined || event.result === null
41
43
  ? event.result
@@ -154,14 +156,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
154
156
  // plugins/command.extension.token.ts
155
157
  const AXP_COMMAND_EXTENSION = new InjectionToken('AXP_COMMAND_EXTENSION');
156
158
 
159
+ //#region ---- Runtime remote execute hooks ----
160
+ /**
161
+ * Distributed hook keys for remote (non-local) command/query execution.
162
+ * Connectivity (or another transport) implements these via {@link AXPHookService.runAsync}.
163
+ */
164
+ const AXPRuntimeRemoteHookKeys = {
165
+ CommandExecute: 'runtime.command.execute',
166
+ QueryExecute: 'runtime.query.execute',
167
+ };
168
+ //#endregion
169
+
157
170
  class AXPCommandExecutor {
158
171
  constructor() {
159
172
  this.injector = inject(EnvironmentInjector);
160
173
  this.registry = inject(AXPCommandRegistry);
161
174
  this.extensions = inject(AXP_COMMAND_EXTENSION, { optional: true });
162
175
  this.commandEvents = inject(AXPCommandEventsService);
176
+ this.hooks = inject(AXPHookService);
163
177
  }
164
178
  async execute(key, input) {
179
+ if (this.registry.exists(key)) {
180
+ return this.executeLocal(key, input);
181
+ }
182
+ return this.executeRemote(key, input);
183
+ }
184
+ async executeLocal(key, input) {
165
185
  const loader = this.registry.getLoader(key);
166
186
  if (!loader) {
167
187
  throw new Error(`Command '${key}' is not registered`);
@@ -173,44 +193,65 @@ class AXPCommandExecutor {
173
193
  const wrappedMiddlewares = [
174
194
  ...globalMiddlewares,
175
195
  ...pluginMiddlewares,
176
- ].map(middleware => {
196
+ ].map((middleware) => {
177
197
  return (input, next) => runInInjectionContext(this.injector, () => middleware(input, next));
178
198
  });
179
199
  const composed = composeCommandMiddlewares(wrappedMiddlewares)(handler);
200
+ return this.runWithEvents(key, input, () => composed(input));
201
+ }
202
+ async executeRemote(key, input) {
203
+ return this.runWithEvents(key, input, async () => {
204
+ const payload = await this.hooks.runAsync(AXPRuntimeRemoteHookKeys.CommandExecute, { name: key, input });
205
+ if (!payload.handled) {
206
+ throw new Error(`Command '${key}' is not registered and no remote handler is available`);
207
+ }
208
+ return (payload.result ?? null);
209
+ });
210
+ }
211
+ async runWithEvents(key, input, run) {
212
+ const executionId = crypto.randomUUID();
213
+ const startedAt = performance.now();
180
214
  const beforeEvent = {
215
+ executionId,
181
216
  stage: 'before',
182
217
  key,
183
218
  input,
184
219
  };
185
220
  await this.commandEvents.emit(beforeEvent);
186
221
  try {
187
- const result = await composed(input);
222
+ const result = await run();
223
+ const durationMs = Math.round(performance.now() - startedAt);
188
224
  const afterEvent = {
225
+ executionId,
189
226
  stage: 'after',
190
227
  key,
191
228
  input,
192
229
  success: true,
193
230
  result,
231
+ durationMs,
194
232
  };
195
233
  await this.commandEvents.emit(afterEvent);
196
234
  return result;
197
235
  }
198
236
  catch (error) {
237
+ const durationMs = Math.round(performance.now() - startedAt);
199
238
  const afterEvent = {
239
+ executionId,
200
240
  stage: 'after',
201
241
  key,
202
242
  input,
203
243
  success: false,
204
244
  error,
245
+ durationMs,
205
246
  };
206
247
  await this.commandEvents.emit(afterEvent);
207
248
  throw error;
208
249
  }
209
250
  }
210
251
  getPluginMiddlewares(key) {
211
- return this.extensions
212
- ?.filter(ext => ext.target instanceof RegExp ? ext.target.test(key) : ext.target === key)
213
- .map(ext => ext.middleware) ?? [];
252
+ return (this.extensions
253
+ ?.filter((ext) => (ext.target instanceof RegExp ? ext.target.test(key) : ext.target === key))
254
+ .map((ext) => ext.middleware) ?? []);
214
255
  }
215
256
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCommandExecutor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
216
257
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCommandExecutor, providedIn: 'root' }); }
@@ -421,6 +462,96 @@ function provideQuerySetups(entries) {
421
462
  ]);
422
463
  }
423
464
 
465
+ //#region ---- Imports ----
466
+ //#endregion
467
+ const QUERY_EXECUTION_EVENT_KEY = '@queries:execution';
468
+ class AXPQueryEventsService {
469
+ constructor() {
470
+ //#region ---- Services & Dependencies ----
471
+ this.broadcast = inject(AXPBroadcastEventService);
472
+ this.distributedEvents = inject(AXPDistributedEventListenerService, { optional: true });
473
+ }
474
+ //#endregion
475
+ //#region ---- Public API ----
476
+ listen(handler) {
477
+ const subscription = this.broadcast
478
+ .listen(QUERY_EXECUTION_EVENT_KEY)
479
+ .subscribe(({ data }) => handler(data));
480
+ return () => subscription.unsubscribe();
481
+ }
482
+ async emit(event) {
483
+ const safePayload = this.serializeForBroadcast(event);
484
+ this.broadcast.publish(QUERY_EXECUTION_EVENT_KEY, safePayload);
485
+ await this.distributedEvents?.dispatch(QUERY_EXECUTION_EVENT_KEY, safePayload);
486
+ }
487
+ //#endregion
488
+ //#region ---- Broadcast payload ----
489
+ serializeForBroadcast(event) {
490
+ return {
491
+ executionId: event.executionId,
492
+ stage: event.stage,
493
+ key: event.key,
494
+ success: event.success,
495
+ durationMs: event.durationMs,
496
+ input: this.stripNonSerializable(event.input),
497
+ result: event.result === undefined || event.result === null
498
+ ? event.result
499
+ : this.stripNonSerializable(event.result),
500
+ error: event.error instanceof Error
501
+ ? { name: event.error.name, message: event.error.message }
502
+ : this.stripNonSerializable(event.error),
503
+ };
504
+ }
505
+ stripNonSerializable(value) {
506
+ const seen = new WeakSet();
507
+ const walk = (v) => {
508
+ if (v === null || v === undefined) {
509
+ return v;
510
+ }
511
+ const t = typeof v;
512
+ if (t === 'function' || t === 'symbol') {
513
+ return undefined;
514
+ }
515
+ if (t === 'bigint') {
516
+ return v.toString();
517
+ }
518
+ if (t !== 'object') {
519
+ return v;
520
+ }
521
+ if (v instanceof Date) {
522
+ return v.toISOString();
523
+ }
524
+ if (Array.isArray(v)) {
525
+ if (seen.has(v)) {
526
+ return '[Circular]';
527
+ }
528
+ seen.add(v);
529
+ return v.map((item) => walk(item)).filter((item) => item !== undefined);
530
+ }
531
+ if (seen.has(v)) {
532
+ return '[Circular]';
533
+ }
534
+ seen.add(v);
535
+ const obj = v;
536
+ const out = {};
537
+ for (const key of Object.keys(obj)) {
538
+ const child = walk(obj[key]);
539
+ if (child !== undefined) {
540
+ out[key] = child;
541
+ }
542
+ }
543
+ return out;
544
+ };
545
+ return walk(value);
546
+ }
547
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPQueryEventsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
548
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPQueryEventsService, providedIn: 'root' }); }
549
+ }
550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPQueryEventsService, decorators: [{
551
+ type: Injectable,
552
+ args: [{ providedIn: 'root' }]
553
+ }] });
554
+
424
555
  function composeQueryMiddlewares(middlewares) {
425
556
  return (handler) => {
426
557
  let composed = handler;
@@ -438,26 +569,83 @@ class AXPQueryExecutor {
438
569
  this.injector = inject(EnvironmentInjector);
439
570
  this.registry = inject(AXPQueryRegistry);
440
571
  this.extensions = inject(AXP_QUERY_EXTENSION, { optional: true });
572
+ this.queryEvents = inject(AXPQueryEventsService);
573
+ this.hooks = inject(AXPHookService);
441
574
  }
442
575
  async fetch(key, input) {
576
+ if (this.registry.exists(key)) {
577
+ return this.fetchLocal(key, input);
578
+ }
579
+ return this.fetchRemote(key, input);
580
+ }
581
+ async fetchLocal(key, input) {
443
582
  const loader = this.registry.getLoader(key);
444
583
  if (!loader) {
445
- return null;
584
+ throw new Error(`Query '${key}' is not registered`);
446
585
  }
447
586
  const query = await loader();
448
587
  const handler = (i) => query.fetch(i);
449
588
  const pluginMiddlewares = this.getPluginMiddlewares(key);
450
589
  const globalMiddlewares = this.registry.getMiddlewares();
451
- const wrappedMiddlewares = [...globalMiddlewares, ...pluginMiddlewares].map(middleware => {
590
+ const wrappedMiddlewares = [...globalMiddlewares, ...pluginMiddlewares].map((middleware) => {
452
591
  return (input, next) => runInInjectionContext(this.injector, () => middleware(input, next));
453
592
  });
454
593
  const composed = composeQueryMiddlewares(wrappedMiddlewares)(handler);
455
- return composed(input);
594
+ return this.runWithEvents(key, input, () => composed(input));
595
+ }
596
+ async fetchRemote(key, input) {
597
+ return this.runWithEvents(key, input, async () => {
598
+ const payload = await this.hooks.runAsync(AXPRuntimeRemoteHookKeys.QueryExecute, { name: key, input });
599
+ if (!payload.handled) {
600
+ throw new Error(`Query '${key}' is not registered and no remote handler is available`);
601
+ }
602
+ return (payload.result ?? null);
603
+ });
604
+ }
605
+ async runWithEvents(key, input, run) {
606
+ const executionId = crypto.randomUUID();
607
+ const startedAt = performance.now();
608
+ const beforeEvent = {
609
+ executionId,
610
+ stage: 'before',
611
+ key,
612
+ input,
613
+ };
614
+ await this.queryEvents.emit(beforeEvent);
615
+ try {
616
+ const result = await run();
617
+ const durationMs = Math.round(performance.now() - startedAt);
618
+ const afterEvent = {
619
+ executionId,
620
+ stage: 'after',
621
+ key,
622
+ input,
623
+ success: true,
624
+ result,
625
+ durationMs,
626
+ };
627
+ await this.queryEvents.emit(afterEvent);
628
+ return result;
629
+ }
630
+ catch (error) {
631
+ const durationMs = Math.round(performance.now() - startedAt);
632
+ const afterEvent = {
633
+ executionId,
634
+ stage: 'after',
635
+ key,
636
+ input,
637
+ success: false,
638
+ error,
639
+ durationMs,
640
+ };
641
+ await this.queryEvents.emit(afterEvent);
642
+ throw error;
643
+ }
456
644
  }
457
645
  getPluginMiddlewares(key) {
458
- return this.extensions
459
- ?.filter(ext => ext.target instanceof RegExp ? ext.target.test(key) : ext.target === key)
460
- .map(ext => ext.middleware) ?? [];
646
+ return (this.extensions
647
+ ?.filter((ext) => (ext.target instanceof RegExp ? ext.target.test(key) : ext.target === key))
648
+ .map((ext) => ext.middleware) ?? []);
461
649
  }
462
650
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPQueryExecutor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
463
651
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPQueryExecutor, providedIn: 'root' }); }
@@ -907,5 +1095,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
907
1095
  * Generated bundle index. Do not edit.
908
1096
  */
909
1097
 
910
- export { AXPCommandEventsService, AXPCommandExecutor, AXPCommandRegistry, AXPCommandService, AXPPolicyEngineContext, AXPPolicyEngineDirective, AXPPolicyEngineRegistry, AXPPolicyEngineService, AXPQueryExecutor, AXPQueryRegistry, AXPQueryService, AXPRuntimeModule, AXPViewExecutor, AXPViewRegistry, AXPViewService, AXP_COMMAND_EXTENSION, AXP_COMMAND_MIDDLEWARE_SETUP, AXP_COMMAND_SETUP, AXP_POLICY_ENGINE_MIDDLEWARE_SETUP, AXP_POLICY_EXTENSION, AXP_QUERY_EXTENSION, AXP_QUERY_MIDDLEWARE_SETUP, AXP_QUERY_SETUP, AXP_VIEW_EXTENSION, AXP_VIEW_SETUP, composeCommandMiddlewares, composePolicyEngineMiddlewares, composeQueryMiddlewares, composeViewMiddlewares, provideCommand, provideCommandMiddleware, provideCommandSetups, providePolicySetups, provideQueryMiddleware, provideQuerySetups, provideViewSetup, provideViewSetups };
1098
+ export { AXPCommandEventsService, AXPCommandExecutor, AXPCommandRegistry, AXPCommandService, AXPPolicyEngineContext, AXPPolicyEngineDirective, AXPPolicyEngineRegistry, AXPPolicyEngineService, AXPQueryEventsService, AXPQueryExecutor, AXPQueryRegistry, AXPQueryService, AXPRuntimeModule, AXPRuntimeRemoteHookKeys, AXPViewExecutor, AXPViewRegistry, AXPViewService, AXP_COMMAND_EXTENSION, AXP_COMMAND_MIDDLEWARE_SETUP, AXP_COMMAND_SETUP, AXP_POLICY_ENGINE_MIDDLEWARE_SETUP, AXP_POLICY_EXTENSION, AXP_QUERY_EXTENSION, AXP_QUERY_MIDDLEWARE_SETUP, AXP_QUERY_SETUP, AXP_VIEW_EXTENSION, AXP_VIEW_SETUP, composeCommandMiddlewares, composePolicyEngineMiddlewares, composeQueryMiddlewares, composeViewMiddlewares, provideCommand, provideCommandMiddleware, provideCommandSetups, providePolicySetups, provideQueryMiddleware, provideQuerySetups, provideViewSetup, provideViewSetups };
911
1099
  //# sourceMappingURL=acorex-platform-framework-client-runtime.mjs.map