@gears-frontx/react 0.1.0 → 0.2.0-alpha.2

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.
package/dist/index.js CHANGED
@@ -1,16 +1,18 @@
1
- // src/HAI3Provider.tsx
2
- import { useMemo, useEffect } from "react";
1
+ // src/FrontXProvider.tsx
2
+ import { useMemo as useMemo2, useEffect as useEffect3, useState as useState2 } from "react";
3
3
  import { Provider as ReduxProvider } from "react-redux";
4
- import { createHAI3App } from "@gears-frontx/framework";
4
+ import {
5
+ createFrontXApp
6
+ } from "@gears-frontx/framework";
5
7
 
6
- // src/HAI3Context.tsx
8
+ // src/FrontXContext.tsx
7
9
  import { createContext, useContext } from "react";
8
- var HAI3Context = createContext(null);
9
- function useHAI3() {
10
- const context = useContext(HAI3Context);
10
+ var FrontXContext = createContext(null);
11
+ function useFrontX() {
12
+ const context = useContext(FrontXContext);
11
13
  if (!context) {
12
14
  throw new Error(
13
- "useHAI3 must be used within a HAI3Provider. Wrap your application with <HAI3Provider> to access HAI3 features."
15
+ "useFrontX must be used within a FrontXProvider. Wrap your application with <FrontXProvider> to access Gears FrontX features."
14
16
  );
15
17
  }
16
18
  return context;
@@ -35,34 +37,590 @@ var MfeProvider = ({ value, children }) => {
35
37
  return /* @__PURE__ */ jsx(MfeContext.Provider, { value, children });
36
38
  };
37
39
 
38
- // src/HAI3Provider.tsx
40
+ // src/queryClient.tsx
41
+ import React, {
42
+ createContext as createContext3,
43
+ useContext as useContext3,
44
+ useEffect as useEffect2,
45
+ useLayoutEffect,
46
+ useMemo,
47
+ useState
48
+ } from "react";
49
+ import {
50
+ QueryClientContext,
51
+ QueryClientProvider,
52
+ useInfiniteQuery,
53
+ useMutation,
54
+ useQuery,
55
+ useSuspenseInfiniteQuery,
56
+ useSuspenseQuery
57
+ } from "@tanstack/react-query";
58
+ import {
59
+ subscribeQueryCacheRuntimeChanged
60
+ } from "@gears-frontx/framework";
61
+
62
+ // src/hooks/QueryCache.ts
63
+ import { eventBus } from "@gears-frontx/framework";
64
+ var QUERY_CLIENT_BROADCAST_TARGET = /* @__PURE__ */ Symbol.for("frontx:query-cache:broadcast-target");
65
+ var QUERY_CLIENT_BROADCAST_COUNTER = /* @__PURE__ */ Symbol.for("frontx:query-cache:broadcast-counter");
66
+ function emitCacheEvent(event, payload) {
67
+ eventBus.emit(event, payload);
68
+ }
69
+ function ensureBroadcastSource(queryClient) {
70
+ const clientWithMetadata = queryClient;
71
+ const existingSource = clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET];
72
+ if (typeof existingSource === "string" && existingSource.length > 0) {
73
+ return existingSource;
74
+ }
75
+ const host = globalThis;
76
+ const nextCounter = (host[QUERY_CLIENT_BROADCAST_COUNTER] ?? 0) + 1;
77
+ host[QUERY_CLIENT_BROADCAST_COUNTER] = nextCounter;
78
+ const source = `query-cache-${nextCounter}`;
79
+ clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET] = source;
80
+ return source;
81
+ }
82
+ function resolveKey(target) {
83
+ if (target !== null && typeof target === "object" && !Array.isArray(target) && "key" in target && "fetch" in target && Array.isArray(target.key) && typeof target.fetch === "function") {
84
+ return target.key;
85
+ }
86
+ return target;
87
+ }
88
+ function toQueryUpdater(dataOrUpdater) {
89
+ if (typeof dataOrUpdater === "function") {
90
+ return dataOrUpdater;
91
+ }
92
+ return () => dataOrUpdater;
93
+ }
94
+ function mapQueryState(state) {
95
+ return {
96
+ data: state.data,
97
+ dataUpdatedAt: state.dataUpdatedAt,
98
+ error: state.error,
99
+ errorUpdatedAt: state.errorUpdatedAt,
100
+ fetchFailureCount: state.fetchFailureCount,
101
+ fetchFailureReason: state.fetchFailureReason,
102
+ fetchStatus: state.fetchStatus,
103
+ isInvalidated: state.isInvalidated,
104
+ status: state.status
105
+ };
106
+ }
107
+ function createQueryCache(queryClient) {
108
+ return {
109
+ get: (key) => {
110
+ return queryClient.getQueryData(resolveKey(key));
111
+ },
112
+ getState: (key) => {
113
+ const state = queryClient.getQueryState(resolveKey(key));
114
+ return state ? mapQueryState(state) : void 0;
115
+ },
116
+ set: (key, dataOrUpdater) => {
117
+ const resolvedKey = resolveKey(key);
118
+ queryClient.setQueryData(resolvedKey, toQueryUpdater(dataOrUpdater));
119
+ emitCacheEvent("cache/set", {
120
+ queryKey: resolvedKey,
121
+ dataOrUpdater,
122
+ source: ensureBroadcastSource(queryClient)
123
+ });
124
+ },
125
+ cancel: (key) => {
126
+ return queryClient.cancelQueries({ queryKey: resolveKey(key) });
127
+ },
128
+ invalidate: (key) => {
129
+ const resolvedKey = resolveKey(key);
130
+ const result = queryClient.invalidateQueries({ queryKey: resolvedKey });
131
+ emitCacheEvent("cache/invalidate", {
132
+ queryKey: resolvedKey,
133
+ source: ensureBroadcastSource(queryClient)
134
+ });
135
+ return result;
136
+ },
137
+ invalidateMany: (filters) => {
138
+ if (filters?.queryKey == null) {
139
+ return Promise.resolve();
140
+ }
141
+ const resolvedKey = resolveKey(filters.queryKey);
142
+ if (!Array.isArray(resolvedKey) || resolvedKey.length === 0) {
143
+ return Promise.resolve();
144
+ }
145
+ const payload = {
146
+ ...filters,
147
+ queryKey: resolvedKey
148
+ };
149
+ const result = queryClient.invalidateQueries(payload);
150
+ emitCacheEvent("cache/invalidate", {
151
+ ...payload,
152
+ source: ensureBroadcastSource(queryClient)
153
+ });
154
+ return result;
155
+ },
156
+ remove: (key) => {
157
+ const resolvedKey = resolveKey(key);
158
+ queryClient.removeQueries({ queryKey: resolvedKey });
159
+ emitCacheEvent("cache/remove", {
160
+ queryKey: resolvedKey,
161
+ source: ensureBroadcastSource(queryClient)
162
+ });
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/hooks/useLatest.ts
168
+ import { useEffect, useRef } from "react";
169
+ function useLatest(value) {
170
+ const ref = useRef(value);
171
+ useEffect(() => {
172
+ ref.current = value;
173
+ });
174
+ return ref;
175
+ }
176
+
177
+ // src/queryClient.tsx
39
178
  import { jsx as jsx2 } from "react/jsx-runtime";
40
- var HAI3Provider = ({
179
+ var APP_QUERY_CLIENT_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client");
180
+ var APP_QUERY_CLIENT_RESOLVER_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-resolver");
181
+ var APP_QUERY_CLIENT_ACTIVATOR_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-activator");
182
+ var FrontXQueryClientContext = createContext3(void 0);
183
+ var useCommitEffect = typeof globalThis.window === "undefined" ? useEffect2 : useLayoutEffect;
184
+ function resolveFrontXQueryClient(app) {
185
+ const clientApp = app;
186
+ return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();
187
+ }
188
+ function hasFrontXQueryClientActivator(app) {
189
+ return typeof app[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL] === "function";
190
+ }
191
+ function activateFrontXQueryClient(app) {
192
+ const clientApp = app;
193
+ return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL]?.();
194
+ }
195
+ async function invalidateQueryCacheForApp(app, target) {
196
+ const queryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);
197
+ if (!queryClient) {
198
+ throw new Error(
199
+ "[invalidateQueryCacheForApp] No QueryClient on app. Ensure queryCache() or queryCacheShared() is in the plugin chain."
200
+ );
201
+ }
202
+ await createQueryCache(queryClient).invalidate(target);
203
+ }
204
+ function bootstrapFrontXQueryClient(app) {
205
+ return resolveFrontXQueryClient(app);
206
+ }
207
+ function useBootstrappedFrontXQueryClient(app) {
208
+ const fromApp = useMemo(() => bootstrapFrontXQueryClient(app), [app]);
209
+ const [eventResolvedClient, setEventResolvedClient] = useState(
210
+ void 0
211
+ );
212
+ const queryClient = fromApp ?? (eventResolvedClient?.app === app ? eventResolvedClient.queryClient : void 0);
213
+ useCommitEffect(() => {
214
+ if (fromApp || !hasFrontXQueryClientActivator(app)) {
215
+ return;
216
+ }
217
+ const tryResolveQueryClient = () => {
218
+ const nextQueryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);
219
+ if (nextQueryClient) {
220
+ setEventResolvedClient((current) => {
221
+ if (current?.app === app && current.queryClient === nextQueryClient) {
222
+ return current;
223
+ }
224
+ return {
225
+ app,
226
+ queryClient: nextQueryClient
227
+ };
228
+ });
229
+ }
230
+ };
231
+ tryResolveQueryClient();
232
+ const subscription = subscribeQueryCacheRuntimeChanged(() => {
233
+ tryResolveQueryClient();
234
+ });
235
+ return () => {
236
+ subscription.unsubscribe();
237
+ };
238
+ }, [app, fromApp]);
239
+ return queryClient;
240
+ }
241
+ function FrontXQueryClientProvider({
242
+ queryClient,
243
+ children
244
+ }) {
245
+ if (!queryClient) {
246
+ return /* @__PURE__ */ jsx2(FrontXQueryClientContext.Provider, { value: void 0, children: /* @__PURE__ */ jsx2(QueryClientContext.Provider, { value: void 0, children }) });
247
+ }
248
+ return /* @__PURE__ */ jsx2(FrontXQueryClientContext.Provider, { value: queryClient, children: /* @__PURE__ */ jsx2(QueryClientProvider, { client: queryClient, children }) });
249
+ }
250
+ function useOptionalFrontXQueryClient() {
251
+ return useContext3(FrontXQueryClientContext);
252
+ }
253
+ function useRequiredFrontXQueryClient() {
254
+ const queryClient = useOptionalFrontXQueryClient();
255
+ if (!queryClient) {
256
+ throw new Error(
257
+ "[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition."
258
+ );
259
+ }
260
+ return queryClient;
261
+ }
262
+ function buildPageContext(page, pages, descriptor, descriptors) {
263
+ return {
264
+ page,
265
+ pages,
266
+ descriptor,
267
+ descriptors
268
+ };
269
+ }
270
+ function normalizeFetchStaleTime(staleTime) {
271
+ if (staleTime === "static") {
272
+ return Number.POSITIVE_INFINITY;
273
+ }
274
+ return typeof staleTime === "number" ? staleTime : void 0;
275
+ }
276
+ function resolveFetchStaleTime(queryClient, descriptor, overrideStaleTime) {
277
+ return normalizeFetchStaleTime(
278
+ overrideStaleTime ?? descriptor.staleTime ?? queryClient.getDefaultOptions().queries?.staleTime
279
+ );
280
+ }
281
+ function fetchDescriptor(descriptor, options) {
282
+ return descriptor.fetch(options);
283
+ }
284
+ function useFrontXQuery(descriptor, overrides) {
285
+ const queryClient = useRequiredFrontXQueryClient();
286
+ const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);
287
+ const result = useQuery({
288
+ queryKey: descriptor.key,
289
+ queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),
290
+ staleTime: overrides?.staleTime ?? descriptor.staleTime,
291
+ gcTime: overrides?.gcTime ?? descriptor.gcTime
292
+ });
293
+ return {
294
+ data: result.data,
295
+ error: result.error,
296
+ isLoading: result.isLoading,
297
+ isFetching: result.isFetching,
298
+ isError: result.isError,
299
+ refetch: result.refetch
300
+ };
301
+ }
302
+ function useFrontXSuspenseQuery(descriptor, overrides) {
303
+ const queryClient = useRequiredFrontXQueryClient();
304
+ const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);
305
+ const result = useSuspenseQuery({
306
+ queryKey: descriptor.key,
307
+ queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),
308
+ staleTime: overrides?.staleTime ?? descriptor.staleTime,
309
+ gcTime: overrides?.gcTime ?? descriptor.gcTime
310
+ });
311
+ return {
312
+ data: result.data,
313
+ error: result.error,
314
+ isFetching: result.isFetching,
315
+ refetch: async () => {
316
+ await result.refetch();
317
+ }
318
+ };
319
+ }
320
+ function useFrontXInfiniteQuery(options) {
321
+ const queryClient = useRequiredFrontXQueryClient();
322
+ const result = useInfiniteQuery({
323
+ queryKey: options.initialPage.key,
324
+ initialPageParam: options.initialPage,
325
+ queryFn: ({ pageParam, signal }) => fetchDescriptor(pageParam, {
326
+ signal,
327
+ staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime)
328
+ }),
329
+ getNextPageParam: (lastPage, allPages, lastPageDescriptor, allPageDescriptors) => options.getNextPage(
330
+ buildPageContext(
331
+ lastPage,
332
+ allPages,
333
+ lastPageDescriptor,
334
+ allPageDescriptors
335
+ )
336
+ ),
337
+ getPreviousPageParam: options.getPreviousPage ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) => options.getPreviousPage?.(
338
+ buildPageContext(
339
+ firstPage,
340
+ allPages,
341
+ firstPageDescriptor,
342
+ allPageDescriptors
343
+ )
344
+ ) : void 0,
345
+ select: (data) => data.pages,
346
+ staleTime: options.staleTime ?? options.initialPage.staleTime,
347
+ gcTime: options.gcTime ?? options.initialPage.gcTime,
348
+ maxPages: options.maxPages
349
+ });
350
+ return {
351
+ data: result.data,
352
+ error: result.error,
353
+ isLoading: result.isLoading,
354
+ isFetching: result.isFetching,
355
+ isError: result.isError,
356
+ hasNextPage: result.hasNextPage ?? false,
357
+ hasPreviousPage: result.hasPreviousPage ?? false,
358
+ isFetchingNextPage: result.isFetchingNextPage,
359
+ isFetchingPreviousPage: result.isFetchingPreviousPage,
360
+ fetchNextPage: async () => {
361
+ await result.fetchNextPage();
362
+ },
363
+ fetchPreviousPage: async () => {
364
+ await result.fetchPreviousPage();
365
+ },
366
+ refetch: async () => {
367
+ await result.refetch();
368
+ }
369
+ };
370
+ }
371
+ function useFrontXSuspenseInfiniteQuery(options) {
372
+ const queryClient = useRequiredFrontXQueryClient();
373
+ const result = useSuspenseInfiniteQuery({
374
+ queryKey: options.initialPage.key,
375
+ initialPageParam: options.initialPage,
376
+ queryFn: ({ pageParam, signal }) => fetchDescriptor(pageParam, {
377
+ signal,
378
+ staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime)
379
+ }),
380
+ getNextPageParam: (lastPage, allPages, lastPageDescriptor, allPageDescriptors) => options.getNextPage(
381
+ buildPageContext(
382
+ lastPage,
383
+ allPages,
384
+ lastPageDescriptor,
385
+ allPageDescriptors
386
+ )
387
+ ),
388
+ getPreviousPageParam: options.getPreviousPage ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) => options.getPreviousPage?.(
389
+ buildPageContext(
390
+ firstPage,
391
+ allPages,
392
+ firstPageDescriptor,
393
+ allPageDescriptors
394
+ )
395
+ ) : void 0,
396
+ select: (data) => data.pages,
397
+ staleTime: options.staleTime ?? options.initialPage.staleTime,
398
+ gcTime: options.gcTime ?? options.initialPage.gcTime,
399
+ maxPages: options.maxPages
400
+ });
401
+ return {
402
+ data: result.data,
403
+ error: result.error,
404
+ isFetching: result.isFetching,
405
+ hasNextPage: result.hasNextPage ?? false,
406
+ hasPreviousPage: result.hasPreviousPage ?? false,
407
+ isFetchingNextPage: result.isFetchingNextPage,
408
+ isFetchingPreviousPage: result.isFetchingPreviousPage,
409
+ fetchNextPage: async () => {
410
+ await result.fetchNextPage();
411
+ },
412
+ fetchPreviousPage: async () => {
413
+ await result.fetchPreviousPage();
414
+ },
415
+ refetch: async () => {
416
+ await result.refetch();
417
+ }
418
+ };
419
+ }
420
+ function useFrontXMutation(options, callbackCtx) {
421
+ useRequiredFrontXQueryClient();
422
+ const latestRef = useLatest({ options, callbackCtx });
423
+ const fetchAbortControllersRef = React.useRef(/* @__PURE__ */ new Set());
424
+ const abortFetchesOnUnmount = React.useEffectEvent(() => {
425
+ if (options.abortOnUnmount) {
426
+ for (const controller of fetchAbortControllersRef.current) {
427
+ controller.abort();
428
+ }
429
+ fetchAbortControllersRef.current.clear();
430
+ }
431
+ });
432
+ React.useEffect(() => {
433
+ return () => {
434
+ abortFetchesOnUnmount();
435
+ };
436
+ }, []);
437
+ const mutationFn = React.useCallback((variables, context) => {
438
+ const cancelOnSupersede = latestRef.current.options.cancelOnSupersede;
439
+ if (cancelOnSupersede) {
440
+ for (const controller2 of fetchAbortControllersRef.current) {
441
+ controller2.abort();
442
+ }
443
+ fetchAbortControllersRef.current.clear();
444
+ }
445
+ const controller = new AbortController();
446
+ fetchAbortControllersRef.current.add(controller);
447
+ const librarySignal = context.signal;
448
+ if (cancelOnSupersede && librarySignal) {
449
+ if (librarySignal.aborted) {
450
+ controller.abort();
451
+ } else {
452
+ librarySignal.addEventListener("abort", () => controller.abort(), { once: true });
453
+ }
454
+ }
455
+ return latestRef.current.options.endpoint.fetch(variables, { signal: controller.signal }).finally(() => {
456
+ fetchAbortControllersRef.current.delete(controller);
457
+ });
458
+ }, [latestRef]);
459
+ const onMutate = React.useCallback((variables, _context) => {
460
+ const current = latestRef.current;
461
+ if (!current.options.onMutate) {
462
+ return void 0;
463
+ }
464
+ return current.options.onMutate(variables, current.callbackCtx);
465
+ }, [latestRef]);
466
+ const onSuccess = React.useCallback((data, variables, context) => {
467
+ const current = latestRef.current;
468
+ return current.options.onSuccess?.(data, variables, context, current.callbackCtx);
469
+ }, [latestRef]);
470
+ const onError = React.useCallback((error, variables, context) => {
471
+ const current = latestRef.current;
472
+ return current.options.onError?.(error, variables, context, current.callbackCtx);
473
+ }, [latestRef]);
474
+ const onSettled = React.useCallback((data, error, variables, context) => {
475
+ const current = latestRef.current;
476
+ return current.options.onSettled?.(
477
+ data,
478
+ error,
479
+ variables,
480
+ context,
481
+ current.callbackCtx
482
+ );
483
+ }, [latestRef]);
484
+ const mutation = useMutation({
485
+ mutationFn,
486
+ onMutate: options.onMutate ? onMutate : void 0,
487
+ onSuccess: options.onSuccess ? onSuccess : void 0,
488
+ onError: options.onError ? onError : void 0,
489
+ onSettled: options.onSettled ? onSettled : void 0
490
+ });
491
+ return {
492
+ mutate: mutation.mutate,
493
+ mutateAsync: mutation.mutateAsync,
494
+ isPending: mutation.isPending,
495
+ error: mutation.error,
496
+ data: mutation.data,
497
+ reset: mutation.reset
498
+ };
499
+ }
500
+
501
+ // src/FrontXProvider.tsx
502
+ import { jsx as jsx3 } from "react/jsx-runtime";
503
+ function shallowEqual(a, b) {
504
+ if (a === b) return true;
505
+ if (!a || !b) return false;
506
+ const keysA = Object.keys(a);
507
+ if (keysA.length !== Object.keys(b).length) return false;
508
+ return keysA.every((k) => Object.is(a[k], b[k]));
509
+ }
510
+ function createProviderOwnedApp(config) {
511
+ return createFrontXApp(config);
512
+ }
513
+ var FrontXProvider = ({
41
514
  children,
42
515
  config,
43
516
  app: providedApp,
44
517
  mfeBridge
45
518
  }) => {
46
- const app = useMemo(() => {
519
+ const [stableConfig, setStableConfig] = useState2(config);
520
+ if (!shallowEqual(
521
+ stableConfig,
522
+ config
523
+ )) {
524
+ setStableConfig(config);
525
+ }
526
+ const app = useMemo2(() => {
47
527
  if (providedApp) {
48
528
  return providedApp;
49
529
  }
50
- return createHAI3App(config);
51
- }, [providedApp, config]);
52
- useEffect(() => {
530
+ return createProviderOwnedApp(stableConfig);
531
+ }, [providedApp, stableConfig]);
532
+ const queryClient = useBootstrappedFrontXQueryClient(app);
533
+ const deferQuerySubtree = hasFrontXQueryClientActivator(app) && queryClient === void 0;
534
+ useEffect3(() => {
535
+ if (queryClient || hasFrontXQueryClientActivator(app) || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test" || process.env.VITEST === "true") {
536
+ return;
537
+ }
538
+ console.warn(
539
+ "[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition. useApiQuery/useApiMutation will fail without it."
540
+ );
541
+ }, [app, queryClient]);
542
+ useEffect3(() => {
53
543
  return () => {
54
544
  if (!providedApp) {
55
545
  app.destroy();
56
546
  }
57
547
  };
58
548
  }, [app, providedApp]);
59
- const content = /* @__PURE__ */ jsx2(HAI3Context.Provider, { value: app, children: /* @__PURE__ */ jsx2(ReduxProvider, { store: app.store, children }) });
549
+ const content = /* @__PURE__ */ jsx3(FrontXContext.Provider, { value: app, children: /* @__PURE__ */ jsx3(ReduxProvider, { store: app.store, children: /* @__PURE__ */ jsx3(FrontXQueryClientProvider, { queryClient, children: deferQuerySubtree ? null : children }) }) });
60
550
  if (mfeBridge) {
61
- return /* @__PURE__ */ jsx2(MfeProvider, { value: mfeBridge, children: content });
551
+ return /* @__PURE__ */ jsx3(MfeProvider, { value: mfeBridge, children: content });
62
552
  }
63
553
  return content;
64
554
  };
65
555
 
556
+ // src/hooks/useCanAccess.ts
557
+ import { useEffect as useEffect4, useState as useState3 } from "react";
558
+ function serializeRecordValue(value) {
559
+ if (value === null) return "n:";
560
+ if (typeof value === "string") return `s:${value}`;
561
+ if (typeof value === "number") {
562
+ return `d:${Object.is(value, -0) ? "-0" : String(value)}`;
563
+ }
564
+ return value ? "b:1" : "b:0";
565
+ }
566
+ function accessQueryKey(query) {
567
+ const { action, resource, record } = query;
568
+ const normalizedRecord = record ? Object.entries(record).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => [k, serializeRecordValue(v)]) : null;
569
+ return JSON.stringify([action, resource, normalizedRecord]);
570
+ }
571
+ function getAuthRuntime(app) {
572
+ return app.auth ?? null;
573
+ }
574
+ var DENIED_NO_AUTH = { allow: false, isResolving: false };
575
+ function useCanAccess(query) {
576
+ const app = useFrontX();
577
+ const auth2 = getAuthRuntime(app);
578
+ const stableKey = accessQueryKey(query);
579
+ const [stableQuery, setStableQuery] = useState3(() => query);
580
+ const [runState, setRunState] = useState3({ auth: auth2, stableKey });
581
+ const [result, setResult] = useState3({ allow: false, isResolving: true });
582
+ if (runState.auth !== auth2 || runState.stableKey !== stableKey) {
583
+ setRunState({ auth: auth2, stableKey });
584
+ setStableQuery(query);
585
+ setResult({ allow: false, isResolving: true });
586
+ }
587
+ useEffect4(() => {
588
+ if (!auth2) {
589
+ return;
590
+ }
591
+ let alive = true;
592
+ const controller = new AbortController();
593
+ void auth2.canAccess(stableQuery, { signal: controller.signal }).then((decision) => {
594
+ if (alive) {
595
+ setResult({ allow: decision === "allow", isResolving: false });
596
+ }
597
+ }).catch(() => {
598
+ if (alive) {
599
+ setResult({ allow: false, isResolving: false });
600
+ }
601
+ });
602
+ return () => {
603
+ alive = false;
604
+ controller.abort();
605
+ };
606
+ }, [auth2, stableQuery]);
607
+ return auth2 ? result : DENIED_NO_AUTH;
608
+ }
609
+
610
+ // src/components/CanAccess.tsx
611
+ function CanAccess({
612
+ query,
613
+ allowed,
614
+ denied = null,
615
+ loading
616
+ }) {
617
+ const { allow, isResolving } = useCanAccess(query);
618
+ if (isResolving) {
619
+ return loading !== void 0 ? loading : denied;
620
+ }
621
+ return allow ? allowed : denied;
622
+ }
623
+
66
624
  // src/hooks/useAppDispatch.ts
67
625
  import { useDispatch } from "react-redux";
68
626
  function useAppDispatch() {
@@ -74,9 +632,9 @@ import { useSelector } from "react-redux";
74
632
  var useAppSelector = useSelector;
75
633
 
76
634
  // src/hooks/useTranslation.ts
77
- import { useMemo as useMemo2, useCallback, useSyncExternalStore } from "react";
635
+ import { useMemo as useMemo3, useCallback, useSyncExternalStore } from "react";
78
636
  function useTranslation() {
79
- const app = useHAI3();
637
+ const app = useFrontX();
80
638
  const { i18nRegistry: i18nRegistry2 } = app;
81
639
  const version = useSyncExternalStore(
82
640
  useCallback(
@@ -88,7 +646,7 @@ function useTranslation() {
88
646
  () => i18nRegistry2.getVersion(),
89
647
  () => i18nRegistry2.getVersion()
90
648
  );
91
- const language = useMemo2(() => {
649
+ const language = useMemo3(() => {
92
650
  void version;
93
651
  return i18nRegistry2.getLanguage();
94
652
  }, [i18nRegistry2, version]);
@@ -106,7 +664,7 @@ function useTranslation() {
106
664
  },
107
665
  [app.actions]
108
666
  );
109
- const isRTL = useMemo2(() => {
667
+ const isRTL = useMemo3(() => {
110
668
  void language;
111
669
  return i18nRegistry2.isRTL();
112
670
  }, [i18nRegistry2, language]);
@@ -119,16 +677,15 @@ function useTranslation() {
119
677
  }
120
678
 
121
679
  // src/hooks/useScreenTranslations.ts
122
- import { useState, useEffect as useEffect2, useMemo as useMemo3, useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore2 } from "react";
680
+ import { useState as useState4, useEffect as useEffect5, useMemo as useMemo4, useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore2 } from "react";
123
681
  function isTranslationLoader(input) {
124
682
  return typeof input === "function";
125
683
  }
126
684
  function useScreenTranslations(screensetId, screenId, translations) {
127
- const app = useHAI3();
685
+ const app = useFrontX();
128
686
  const { i18nRegistry: i18nRegistry2 } = app;
129
- const [loadedLanguage, setLoadedLanguage] = useState(null);
130
- const [isLoading, setIsLoading] = useState(false);
131
- const [error, setError] = useState(null);
687
+ const [loadedLanguage, setLoadedLanguage] = useState4(null);
688
+ const [error, setError] = useState4(null);
132
689
  const version = useSyncExternalStore2(
133
690
  useCallback2(
134
691
  (callback) => i18nRegistry2.subscribe(callback),
@@ -137,11 +694,11 @@ function useScreenTranslations(screensetId, screenId, translations) {
137
694
  () => i18nRegistry2.getVersion(),
138
695
  () => i18nRegistry2.getVersion()
139
696
  );
140
- const currentLanguage = useMemo3(() => {
697
+ const currentLanguage = useMemo4(() => {
141
698
  void version;
142
699
  return i18nRegistry2.getLanguage();
143
700
  }, [i18nRegistry2, version]);
144
- const loader = useMemo3(() => {
701
+ const loader = useMemo4(() => {
145
702
  if (isTranslationLoader(translations)) {
146
703
  return translations;
147
704
  }
@@ -154,12 +711,11 @@ function useScreenTranslations(screensetId, screenId, translations) {
154
711
  return module.default;
155
712
  };
156
713
  }, [translations]);
157
- useEffect2(() => {
714
+ useEffect5(() => {
158
715
  if (!currentLanguage || currentLanguage === loadedLanguage) {
159
716
  return;
160
717
  }
161
718
  let cancelled = false;
162
- setIsLoading(true);
163
719
  const loadTranslations = async () => {
164
720
  try {
165
721
  const namespace = `screen.${screensetId}.${screenId}`;
@@ -168,13 +724,11 @@ function useScreenTranslations(screensetId, screenId, translations) {
168
724
  i18nRegistry2.register(namespace, currentLanguage, loadedTranslations);
169
725
  if (!cancelled) {
170
726
  setLoadedLanguage(currentLanguage);
171
- setIsLoading(false);
172
727
  setError(null);
173
728
  }
174
729
  } catch (err) {
175
730
  if (!cancelled) {
176
731
  setError(err instanceof Error ? err : new Error(String(err)));
177
- setIsLoading(false);
178
732
  }
179
733
  }
180
734
  };
@@ -183,12 +737,12 @@ function useScreenTranslations(screensetId, screenId, translations) {
183
737
  cancelled = true;
184
738
  };
185
739
  }, [screensetId, screenId, loader, i18nRegistry2, currentLanguage, loadedLanguage]);
186
- const isLoaded = currentLanguage !== null && currentLanguage === loadedLanguage && !isLoading;
740
+ const isLoaded = currentLanguage !== null && currentLanguage === loadedLanguage;
187
741
  return { isLoaded, error };
188
742
  }
189
743
 
190
744
  // src/hooks/useFormatters.ts
191
- import { useMemo as useMemo4 } from "react";
745
+ import { useMemo as useMemo5 } from "react";
192
746
  import {
193
747
  formatDate as formatDateFn,
194
748
  formatTime as formatTimeFn,
@@ -203,7 +757,7 @@ import {
203
757
  } from "@gears-frontx/framework";
204
758
  function useFormatters() {
205
759
  const { language } = useTranslation();
206
- return useMemo4(
760
+ return useMemo5(
207
761
  () => {
208
762
  void language;
209
763
  return {
@@ -224,9 +778,9 @@ function useFormatters() {
224
778
  }
225
779
 
226
780
  // src/hooks/useTheme.ts
227
- import { useCallback as useCallback3, useMemo as useMemo5, useSyncExternalStore as useSyncExternalStore3 } from "react";
781
+ import { useCallback as useCallback3, useMemo as useMemo6, useSyncExternalStore as useSyncExternalStore3 } from "react";
228
782
  function useTheme() {
229
- const app = useHAI3();
783
+ const app = useFrontX();
230
784
  const { themeRegistry } = app;
231
785
  const version = useSyncExternalStore3(
232
786
  useCallback3(
@@ -238,12 +792,12 @@ function useTheme() {
238
792
  () => themeRegistry.getVersion(),
239
793
  () => themeRegistry.getVersion()
240
794
  );
241
- const currentTheme = useMemo5(() => {
795
+ const currentTheme = useMemo6(() => {
242
796
  void version;
243
797
  const theme = themeRegistry.getCurrent();
244
798
  return theme?.id;
245
799
  }, [themeRegistry, version]);
246
- const themes2 = useMemo5(() => {
800
+ const themes2 = useMemo6(() => {
247
801
  return themeRegistry.getAll().map((theme) => ({
248
802
  id: theme.id,
249
803
  name: theme.name
@@ -264,6 +818,145 @@ function useTheme() {
264
818
  };
265
819
  }
266
820
 
821
+ // src/hooks/useApiQuery.ts
822
+ function useApiQuery(descriptor, overrides) {
823
+ return useFrontXQuery(descriptor, overrides);
824
+ }
825
+
826
+ // src/hooks/useApiSuspenseQuery.ts
827
+ function useApiSuspenseQuery(descriptor, overrides) {
828
+ return useFrontXSuspenseQuery(descriptor, overrides);
829
+ }
830
+
831
+ // src/hooks/useApiInfiniteQuery.ts
832
+ function useApiInfiniteQuery(options) {
833
+ return useFrontXInfiniteQuery(options);
834
+ }
835
+
836
+ // src/hooks/useApiSuspenseInfiniteQuery.ts
837
+ function useApiSuspenseInfiniteQuery(options) {
838
+ return useFrontXSuspenseInfiniteQuery(options);
839
+ }
840
+
841
+ // src/hooks/useApiMutation.ts
842
+ import { useMemo as useMemo7 } from "react";
843
+ function useApiMutation(options) {
844
+ const queryClient = useRequiredFrontXQueryClient();
845
+ const queryCache2 = useMemo7(() => createQueryCache(queryClient), [queryClient]);
846
+ const callbackCtx = useMemo7(() => ({ queryCache: queryCache2 }), [queryCache2]);
847
+ return useFrontXMutation(options, callbackCtx);
848
+ }
849
+
850
+ // src/hooks/useApiStream.ts
851
+ import { useEffect as useEffect6, useMemo as useMemo8, useRef as useRef2, useState as useState5, useCallback as useCallback4 } from "react";
852
+ function useApiStream(descriptor, options) {
853
+ const mode = options?.mode ?? "latest";
854
+ const enabled = options?.enabled ?? true;
855
+ const [data, setData] = useState5(void 0);
856
+ const [events, setEvents] = useState5([]);
857
+ const [status, setStatus] = useState5(() => enabled ? "connecting" : "idle");
858
+ const [error, setError] = useState5(null);
859
+ const connectPromiseRef = useRef2(null);
860
+ const connectionIdRef = useRef2(null);
861
+ const disconnectRequestedRef = useRef2(false);
862
+ const descriptorRef = useLatest(descriptor);
863
+ const descriptorKey = useMemo8(() => JSON.stringify(descriptor.key), [descriptor.key]);
864
+ const resetKey = `${descriptorKey}:${mode}`;
865
+ const [prevResetKey, setPrevResetKey] = useState5(resetKey);
866
+ if (resetKey !== prevResetKey) {
867
+ setPrevResetKey(resetKey);
868
+ setData(void 0);
869
+ setEvents([]);
870
+ setError(null);
871
+ }
872
+ const connectKey = `${resetKey}:${enabled}`;
873
+ const [prevConnectKey, setPrevConnectKey] = useState5(connectKey);
874
+ if (connectKey !== prevConnectKey) {
875
+ setPrevConnectKey(connectKey);
876
+ if (enabled) {
877
+ setData(void 0);
878
+ setEvents([]);
879
+ setStatus("connecting");
880
+ setError(null);
881
+ }
882
+ }
883
+ const disconnect = useCallback4(() => {
884
+ disconnectRequestedRef.current = true;
885
+ if (connectionIdRef.current) {
886
+ descriptorRef.current.disconnect(connectionIdRef.current);
887
+ connectionIdRef.current = null;
888
+ disconnectRequestedRef.current = false;
889
+ }
890
+ setStatus("disconnected");
891
+ }, [descriptorRef]);
892
+ useEffect6(() => {
893
+ if (!enabled) {
894
+ return;
895
+ }
896
+ let cancelled = false;
897
+ disconnectRequestedRef.current = false;
898
+ const d = descriptorRef.current;
899
+ const connectPromise = d.connect(
900
+ (event) => {
901
+ if (cancelled) return;
902
+ setData(event);
903
+ setStatus("connected");
904
+ if (mode === "accumulate") {
905
+ setEvents((prev) => [...prev, event]);
906
+ }
907
+ },
908
+ () => {
909
+ if (cancelled) return;
910
+ setStatus("disconnected");
911
+ connectionIdRef.current = null;
912
+ }
913
+ );
914
+ connectPromiseRef.current = connectPromise;
915
+ connectPromise.then((id) => {
916
+ if (cancelled) return;
917
+ if (disconnectRequestedRef.current) {
918
+ d.disconnect(id);
919
+ disconnectRequestedRef.current = false;
920
+ return;
921
+ }
922
+ connectionIdRef.current = id;
923
+ setStatus("connected");
924
+ }).catch((err) => {
925
+ if (cancelled) return;
926
+ if (disconnectRequestedRef.current) {
927
+ disconnectRequestedRef.current = false;
928
+ setStatus("disconnected");
929
+ return;
930
+ }
931
+ setError(err instanceof Error ? err : new Error(String(err)));
932
+ setStatus("error");
933
+ });
934
+ return () => {
935
+ cancelled = true;
936
+ connectPromise.then(
937
+ (id) => d.disconnect(id),
938
+ () => {
939
+ }
940
+ );
941
+ connectPromiseRef.current = null;
942
+ connectionIdRef.current = null;
943
+ };
944
+ }, [descriptorKey, enabled, mode, descriptorRef]);
945
+ return { data, events, status: enabled ? status : "idle", error, disconnect };
946
+ }
947
+
948
+ // src/hooks/useQueryCache.ts
949
+ import { useMemo as useMemo9 } from "react";
950
+ function useQueryCache() {
951
+ const queryClient = useOptionalFrontXQueryClient();
952
+ if (!queryClient) {
953
+ throw new Error(
954
+ "[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition."
955
+ );
956
+ }
957
+ return useMemo9(() => createQueryCache(queryClient), [queryClient]);
958
+ }
959
+
267
960
  // src/mfe/hooks/useMfeBridge.ts
268
961
  function useMfeBridge() {
269
962
  const { bridge } = useMfeContext();
@@ -271,15 +964,15 @@ function useMfeBridge() {
271
964
  }
272
965
 
273
966
  // src/mfe/hooks/useSharedProperty.ts
274
- import { useSyncExternalStore as useSyncExternalStore4, useCallback as useCallback4 } from "react";
967
+ import { useSyncExternalStore as useSyncExternalStore4, useCallback as useCallback5 } from "react";
275
968
  function useSharedProperty(propertyTypeId) {
276
969
  const { bridge } = useMfeContext();
277
- const subscribe = useCallback4((callback) => {
970
+ const subscribe = useCallback5((callback) => {
278
971
  return bridge.subscribeToProperty(propertyTypeId, () => {
279
972
  callback();
280
973
  });
281
974
  }, [bridge, propertyTypeId]);
282
- const getSnapshot = useCallback4(() => {
975
+ const getSnapshot = useCallback5(() => {
283
976
  const property = bridge.getProperty(propertyTypeId);
284
977
  return property?.value;
285
978
  }, [bridge, propertyTypeId]);
@@ -288,14 +981,14 @@ function useSharedProperty(propertyTypeId) {
288
981
  }
289
982
 
290
983
  // src/mfe/hooks/useHostAction.ts
291
- import { useCallback as useCallback5 } from "react";
984
+ import { useCallback as useCallback6 } from "react";
292
985
  function useHostAction(actionTypeId) {
293
986
  const { bridge } = useMfeContext();
294
- return useCallback5((payload) => {
987
+ return useCallback6((payload) => {
295
988
  const chain = {
296
989
  action: {
297
990
  type: actionTypeId,
298
- target: bridge.domainId,
991
+ target: bridge.extDomainId,
299
992
  payload
300
993
  }
301
994
  };
@@ -309,23 +1002,23 @@ function useHostAction(actionTypeId) {
309
1002
  }
310
1003
 
311
1004
  // src/mfe/hooks/useDomainExtensions.ts
312
- import { useSyncExternalStore as useSyncExternalStore5, useCallback as useCallback6, useRef } from "react";
1005
+ import { useSyncExternalStore as useSyncExternalStore5, useCallback as useCallback7, useRef as useRef3 } from "react";
313
1006
  function useDomainExtensions(domainId) {
314
- const app = useHAI3();
315
- const registry = app.screensetsRegistry;
1007
+ const app = useFrontX();
1008
+ const registry = app.mfeRegistry;
316
1009
  if (!registry) {
317
1010
  throw new Error(
318
- "useDomainExtensions requires the microfrontends plugin. Add microfrontends() to your HAI3 app configuration."
1011
+ "useDomainExtensions requires the microfrontends plugin. Add microfrontends() to your Gears FrontX app configuration."
319
1012
  );
320
1013
  }
321
- const subscribe = useCallback6(
1014
+ const subscribe = useCallback7(
322
1015
  (onStoreChange) => {
323
1016
  return app.store.subscribe(onStoreChange);
324
1017
  },
325
1018
  [app.store]
326
1019
  );
327
- const cacheRef = useRef({ ids: "", extensions: [] });
328
- const getSnapshot = useCallback6(() => {
1020
+ const cacheRef = useRef3({ ids: "", extensions: [] });
1021
+ const getSnapshot = useCallback7(() => {
329
1022
  const extensions = registry.getExtensionsForDomain(domainId);
330
1023
  const ids = extensions.map((e) => e.id).join(",");
331
1024
  if (ids !== cacheRef.current.ids) {
@@ -336,216 +1029,230 @@ function useDomainExtensions(domainId) {
336
1029
  return useSyncExternalStore5(subscribe, getSnapshot, getSnapshot);
337
1030
  }
338
1031
 
339
- // src/mfe/hooks/useRegisteredPackages.ts
340
- import { useSyncExternalStore as useSyncExternalStore6, useCallback as useCallback7, useRef as useRef2 } from "react";
341
- function useRegisteredPackages() {
342
- const app = useHAI3();
343
- const registry = app.screensetsRegistry;
1032
+ // src/mfe/hooks/useMountedExtensions.ts
1033
+ import { useSyncExternalStore as useSyncExternalStore6, useCallback as useCallback8, useRef as useRef4 } from "react";
1034
+ function useMountedExtensions(domainId) {
1035
+ const app = useFrontX();
1036
+ const registry = app.mfeRegistry;
344
1037
  if (!registry) {
345
1038
  throw new Error(
346
- "useRegisteredPackages requires the microfrontends plugin. Add microfrontends() to your HAI3 app configuration."
1039
+ "useMountedExtensions requires the microfrontends plugin. Add microfrontends() to your Gears FrontX app configuration."
347
1040
  );
348
1041
  }
349
- const subscribe = useCallback7(
1042
+ const subscribe = useCallback8(
350
1043
  (onStoreChange) => {
351
1044
  return app.store.subscribe(onStoreChange);
352
1045
  },
353
1046
  [app.store]
354
1047
  );
355
- const cacheRef = useRef2({ packages: "", list: [] });
356
- const getSnapshot = useCallback7(() => {
357
- const list = registry.getRegisteredPackages();
358
- const packages = list.join(",");
359
- if (packages !== cacheRef.current.packages) {
360
- cacheRef.current = { packages, list };
1048
+ const cacheRef = useRef4({ key: "", extensions: [] });
1049
+ const getSnapshot = useCallback8(() => {
1050
+ const ids = registry.getMountedExtensions(domainId);
1051
+ const resolved = ids.map((id) => registry.getExtension(id)).filter((ext) => ext !== void 0);
1052
+ const key = ids.join(",");
1053
+ if (key !== cacheRef.current.key) {
1054
+ cacheRef.current = { key, extensions: resolved };
361
1055
  }
362
- return cacheRef.current.list;
363
- }, [registry]);
1056
+ return cacheRef.current.extensions;
1057
+ }, [registry, domainId]);
364
1058
  return useSyncExternalStore6(subscribe, getSnapshot, getSnapshot);
365
1059
  }
366
1060
 
367
- // src/mfe/hooks/useActivePackage.ts
368
- import { useSyncExternalStore as useSyncExternalStore7, useCallback as useCallback8, useRef as useRef3 } from "react";
369
- import { extractGtsPackage, FRONTX_SCREEN_DOMAIN } from "@gears-frontx/framework";
370
- function useActivePackage() {
371
- const app = useHAI3();
372
- const registry = app.screensetsRegistry;
1061
+ // src/mfe/hooks/useRegisteredPackages.ts
1062
+ import { useSyncExternalStore as useSyncExternalStore7, useCallback as useCallback9, useRef as useRef5 } from "react";
1063
+ function useRegisteredPackages() {
1064
+ const app = useFrontX();
1065
+ const registry = app.mfeRegistry;
373
1066
  if (!registry) {
374
1067
  throw new Error(
375
- "useActivePackage requires the microfrontends plugin. Add microfrontends() to your HAI3 app configuration."
1068
+ "useRegisteredPackages requires the microfrontends plugin. Add microfrontends() to your Gears FrontX app configuration."
376
1069
  );
377
1070
  }
378
- const subscribe = useCallback8(
1071
+ const subscribe = useCallback9(
379
1072
  (onStoreChange) => {
380
1073
  return app.store.subscribe(onStoreChange);
381
1074
  },
382
1075
  [app.store]
383
1076
  );
384
- const cacheRef = useRef3({ activePackage: void 0 });
385
- const getSnapshot = useCallback8(() => {
386
- const mountedExtensionId = registry.getMountedExtension(FRONTX_SCREEN_DOMAIN);
387
- if (!mountedExtensionId) {
388
- const result = void 0;
389
- if (result !== cacheRef.current.activePackage) {
390
- cacheRef.current = { activePackage: result };
391
- }
392
- return cacheRef.current.activePackage;
393
- }
394
- const activePackage = extractGtsPackage(mountedExtensionId);
395
- if (activePackage !== cacheRef.current.activePackage) {
396
- cacheRef.current = { activePackage };
1077
+ const cacheRef = useRef5({ packages: "", list: [] });
1078
+ const getSnapshot = useCallback9(() => {
1079
+ const list = registry.getRegisteredPackages();
1080
+ const packages = list.join(",");
1081
+ if (packages !== cacheRef.current.packages) {
1082
+ cacheRef.current = { packages, list };
397
1083
  }
398
- return cacheRef.current.activePackage;
1084
+ return cacheRef.current.list;
399
1085
  }, [registry]);
400
1086
  return useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
401
1087
  }
402
1088
 
403
- // src/mfe/components/RefContainerProvider.ts
404
- import { ContainerProvider } from "@gears-frontx/framework";
405
- var RefContainerProvider = class extends ContainerProvider {
406
- constructor(containerRef) {
407
- super();
408
- this.containerRef = containerRef;
1089
+ // src/mfe/ThemeAwareReactLifecycle.tsx
1090
+ import { createRoot } from "react-dom/client";
1091
+ import { jsx as jsx4 } from "react/jsx-runtime";
1092
+ function resolveProviderMountOptions(app, bridge, mountContext) {
1093
+ const extensionId = mountContext?.extensionId;
1094
+ const domainId = mountContext?.domainId;
1095
+ const isMountedMfe = typeof extensionId === "string" && typeof domainId === "string";
1096
+ if (isMountedMfe && !resolveFrontXQueryClient(app) && !hasFrontXQueryClientActivator(app)) {
1097
+ throw new Error(
1098
+ "[FrontXProvider] Mounted MFEs require queryCacheShared() in the child app and queryCache() in the host app before loading the MFE app."
1099
+ );
1100
+ }
1101
+ return {
1102
+ mfeBridge: isMountedMfe ? { bridge, extensionId, domainId } : void 0
1103
+ };
1104
+ }
1105
+ function MountRuntimeAwareProvider({
1106
+ app,
1107
+ mfeBridge,
1108
+ children
1109
+ }) {
1110
+ return /* @__PURE__ */ jsx4(FrontXProvider, { app, mfeBridge, children });
1111
+ }
1112
+ var ThemeAwareReactLifecycle = class {
1113
+ constructor(app) {
1114
+ this.app = app;
409
1115
  }
410
- getContainer(_extensionId) {
411
- if (!this.containerRef.current) {
412
- throw new Error("Container ref is not attached -- component may not be mounted yet");
1116
+ app;
1117
+ root = null;
1118
+ mount(container, bridge, mountContext) {
1119
+ if (container instanceof ShadowRoot) {
1120
+ this.adoptHostStylesIntoShadowRoot(container);
413
1121
  }
414
- return this.containerRef.current;
1122
+ this.injectBaseResets(container);
1123
+ this.initializeStyles(container);
1124
+ const providerMountOptions = resolveProviderMountOptions(this.app, bridge, mountContext);
1125
+ this.root = createRoot(container);
1126
+ this.root.render(
1127
+ /* @__PURE__ */ jsx4(
1128
+ MountRuntimeAwareProvider,
1129
+ {
1130
+ app: this.app,
1131
+ mfeBridge: providerMountOptions.mfeBridge,
1132
+ children: this.renderContent(bridge)
1133
+ }
1134
+ )
1135
+ );
1136
+ }
1137
+ unmount(_container) {
1138
+ if (this.root) {
1139
+ this.root.unmount();
1140
+ this.root = null;
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Copy all inline <style> and <link rel="stylesheet"> from the host document
1145
+ * into the shadow root so that Tailwind and component styles apply inside the MFE.
1146
+ */
1147
+ adoptHostStylesIntoShadowRoot(shadowRoot) {
1148
+ const styleElements = document.head.querySelectorAll("style");
1149
+ styleElements.forEach((el) => {
1150
+ const clone = document.createElement("style");
1151
+ clone.textContent = el.textContent ?? "";
1152
+ shadowRoot.appendChild(clone);
1153
+ });
1154
+ const linkElements = document.head.querySelectorAll('link[rel="stylesheet"]');
1155
+ linkElements.forEach((el) => {
1156
+ const clone = el.cloneNode(true);
1157
+ shadowRoot.appendChild(clone);
1158
+ });
1159
+ }
1160
+ /**
1161
+ * Box-model resets and :host defaults needed inside every shadow root.
1162
+ * These aren't part of Tailwind's compiled output but are required for
1163
+ * consistent rendering across browsers.
1164
+ */
1165
+ injectBaseResets(container) {
1166
+ const style = document.createElement("style");
1167
+ style.textContent = `
1168
+ *, *::before, *::after {
1169
+ box-sizing: border-box;
1170
+ border-width: 0;
1171
+ border-style: solid;
1172
+ border-color: currentColor;
1173
+ }
1174
+ * { margin: 0; padding: 0; }
1175
+ :host {
1176
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1177
+ line-height: 1.5;
1178
+ -webkit-font-smoothing: antialiased;
1179
+ -moz-osx-font-smoothing: grayscale;
1180
+ color: hsl(var(--foreground));
1181
+ background-color: hsl(var(--background));
1182
+ }
1183
+ `;
1184
+ container.appendChild(style);
415
1185
  }
416
- releaseContainer(_extensionId) {
1186
+ /**
1187
+ * Hook for subclasses to inject additional CSS not covered by the adopted host
1188
+ * stylesheet (e.g., MFE-specific @font-face rules or custom animations).
1189
+ * No-op by default: host styles adopted in adoptHostStylesIntoShadowRoot()
1190
+ * already include all Tailwind utilities compiled from MFE source files.
1191
+ */
1192
+ initializeStyles(_container) {
417
1193
  }
418
1194
  };
419
1195
 
420
1196
  // src/mfe/components/ExtensionDomainSlot.tsx
421
- import { useEffect as useEffect3, useRef as useRef4, useState as useState2 } from "react";
422
- import {
423
- FRONTX_ACTION_MOUNT_EXT,
424
- FRONTX_ACTION_UNMOUNT_EXT
425
- } from "@gears-frontx/framework";
426
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
1197
+ import { useEffect as useEffect7, useEffectEvent, useRef as useRef6, useState as useState6 } from "react";
1198
+ import { jsx as jsx5 } from "react/jsx-runtime";
427
1199
  function ExtensionDomainSlot(props) {
428
1200
  const {
429
1201
  registry,
430
1202
  domainId,
431
- extensionId,
432
1203
  className,
433
- onMounted,
434
- onUnmounted,
435
- onError,
436
- loadingComponent,
437
- errorComponent
1204
+ onAttached,
1205
+ onDetached,
1206
+ loadingComponent = null
438
1207
  } = props;
439
- const containerRef = useRef4(null);
440
- const [isLoading, setIsLoading] = useState2(true);
441
- const [error, setError] = useState2(null);
442
- const [bridge, setBridge] = useState2(null);
443
- useEffect3(() => {
444
- let mounted = true;
445
- let currentBridge = null;
446
- async function mountExtension2() {
447
- if (!containerRef.current) {
448
- return;
449
- }
450
- try {
451
- setIsLoading(true);
452
- setError(null);
453
- await registry.executeActionsChain({
454
- action: {
455
- type: FRONTX_ACTION_MOUNT_EXT,
456
- target: domainId,
457
- payload: {
458
- extensionId
459
- }
460
- }
461
- });
462
- if (!mounted) {
463
- await registry.executeActionsChain({
464
- action: {
465
- type: FRONTX_ACTION_UNMOUNT_EXT,
466
- target: domainId,
467
- payload: {
468
- extensionId
469
- }
470
- }
471
- });
472
- return;
473
- }
474
- const newBridge = registry.getParentBridge(extensionId);
475
- if (!newBridge) {
476
- throw new Error(`Failed to obtain bridge for extension ${extensionId} after mount`);
477
- }
478
- currentBridge = newBridge;
479
- setBridge(newBridge);
480
- setIsLoading(false);
481
- if (onMounted) {
482
- onMounted(newBridge);
483
- }
484
- } catch (err) {
485
- if (!mounted) {
486
- return;
487
- }
488
- const errorObj = err instanceof Error ? err : new Error(String(err));
489
- setError(errorObj);
490
- setIsLoading(false);
491
- if (onError) {
492
- onError(errorObj);
493
- }
494
- }
1208
+ const rootRef = useRef6(null);
1209
+ const emitAttached = useEffectEvent((root) => {
1210
+ onAttached?.(root);
1211
+ });
1212
+ const emitDetached = useEffectEvent(() => {
1213
+ onDetached?.();
1214
+ });
1215
+ const [attached, setAttached] = useState6(false);
1216
+ useEffect7(() => {
1217
+ const root = rootRef.current;
1218
+ if (!root) {
1219
+ return;
495
1220
  }
496
- void mountExtension2();
1221
+ const mounter = registry.getMounter(domainId);
1222
+ mounter.attach(root);
1223
+ setAttached(true);
1224
+ emitAttached(root);
497
1225
  return () => {
498
- mounted = false;
499
- if (currentBridge) {
500
- void registry.executeActionsChain({
501
- action: {
502
- type: FRONTX_ACTION_UNMOUNT_EXT,
503
- target: domainId,
504
- payload: {
505
- extensionId
506
- }
507
- }
508
- }).then(() => {
509
- if (onUnmounted) {
510
- onUnmounted();
511
- }
512
- });
513
- }
1226
+ void mounter.detach();
1227
+ setAttached(false);
1228
+ emitDetached();
514
1229
  };
515
- }, [registry, domainId, extensionId, onMounted, onUnmounted, onError]);
516
- if (isLoading) {
517
- return /* @__PURE__ */ jsx3("div", { className, "data-domain-id": domainId, "data-extension-id": extensionId, children: loadingComponent ?? /* @__PURE__ */ jsx3("div", { children: "Loading extension..." }) });
518
- }
519
- if (error) {
520
- return /* @__PURE__ */ jsx3("div", { className, "data-domain-id": domainId, "data-extension-id": extensionId, children: errorComponent ? errorComponent(error) : /* @__PURE__ */ jsxs("div", { children: [
521
- /* @__PURE__ */ jsx3("strong", { children: "Error loading extension:" }),
522
- /* @__PURE__ */ jsx3("pre", { children: error.message })
523
- ] }) });
524
- }
525
- return /* @__PURE__ */ jsx3(
1230
+ }, [registry, domainId]);
1231
+ return /* @__PURE__ */ jsx5(
526
1232
  "div",
527
1233
  {
528
- ref: containerRef,
1234
+ ref: rootRef,
529
1235
  className,
530
1236
  "data-domain-id": domainId,
531
- "data-extension-id": extensionId,
532
- "data-bridge-active": bridge !== null
1237
+ children: !attached ? loadingComponent : null
533
1238
  }
534
1239
  );
535
1240
  }
536
1241
 
537
1242
  // src/index.ts
538
1243
  import {
539
- createHAI3,
540
- createHAI3App as createHAI3App2,
1244
+ createFrontX,
1245
+ createFrontXApp as createFrontXApp2,
541
1246
  presets,
542
1247
  ACCOUNTS_DOMAIN,
543
1248
  I18nRegistry,
544
- screensets,
545
1249
  themes,
546
1250
  layout,
547
1251
  i18n,
548
1252
  effects,
1253
+ queryCache,
1254
+ queryCacheShared,
1255
+ subscribeQueryCacheRuntimeChanged as subscribeQueryCacheRuntimeChanged2,
549
1256
  createThemeRegistry,
550
1257
  createStore,
551
1258
  getStore,
@@ -560,14 +1267,12 @@ import {
560
1267
  footerSlice,
561
1268
  menuSlice,
562
1269
  sidebarSlice,
563
- screenSlice,
564
1270
  popupSlice,
565
1271
  overlaySlice,
566
1272
  headerActions,
567
1273
  footerActions,
568
1274
  menuActions,
569
1275
  sidebarActions,
570
- screenActions,
571
1276
  popupActions,
572
1277
  overlayActions,
573
1278
  setUser,
@@ -588,10 +1293,6 @@ import {
588
1293
  setSidebarVisible,
589
1294
  setSidebarWidth,
590
1295
  setSidebarConfig,
591
- setActiveScreen,
592
- setScreenLoading,
593
- navigateTo,
594
- clearActiveScreen,
595
1296
  openPopup,
596
1297
  closePopup,
597
1298
  closeTopPopup,
@@ -617,10 +1318,14 @@ import {
617
1318
  initMockEffects,
618
1319
  toggleMockMode,
619
1320
  MockEvents,
1321
+ auth,
1322
+ frontxApiTransport,
620
1323
  apiRegistry,
621
1324
  BaseApiService,
622
1325
  RestProtocol,
1326
+ RestEndpointProtocol,
623
1327
  SseProtocol,
1328
+ SseStreamProtocol,
624
1329
  RestMockPlugin,
625
1330
  SseMockPlugin,
626
1331
  MockEventSource,
@@ -644,7 +1349,7 @@ import {
644
1349
  } from "@gears-frontx/framework";
645
1350
  import { Language, TextDirection, LanguageDisplayMode } from "@gears-frontx/framework";
646
1351
  import {
647
- microfrontends,
1352
+ microfrontends as microfrontends2,
648
1353
  mock
649
1354
  } from "@gears-frontx/framework";
650
1355
  import {
@@ -657,12 +1362,13 @@ import {
657
1362
  import {
658
1363
  selectExtensionState,
659
1364
  selectRegisteredExtensions,
660
- selectExtensionError
1365
+ selectExtensionError,
1366
+ selectMountedExtensions
661
1367
  } from "@gears-frontx/framework";
662
1368
  import {
663
1369
  FRONTX_POPUP_DOMAIN,
664
1370
  FRONTX_SIDEBAR_DOMAIN,
665
- FRONTX_SCREEN_DOMAIN as FRONTX_SCREEN_DOMAIN2,
1371
+ FRONTX_SCREEN_DOMAIN,
666
1372
  FRONTX_OVERLAY_DOMAIN,
667
1373
  screenDomain,
668
1374
  sidebarDomain,
@@ -675,51 +1381,67 @@ import {
675
1381
  } from "@gears-frontx/framework";
676
1382
  import {
677
1383
  FRONTX_ACTION_LOAD_EXT,
678
- FRONTX_ACTION_MOUNT_EXT as FRONTX_ACTION_MOUNT_EXT2,
679
- FRONTX_ACTION_UNMOUNT_EXT as FRONTX_ACTION_UNMOUNT_EXT2
1384
+ FRONTX_ACTION_MOUNT_EXT,
1385
+ FRONTX_ACTION_UNMOUNT_EXT
680
1386
  } from "@gears-frontx/framework";
681
1387
  import {
682
1388
  FRONTX_SHARED_PROPERTY_THEME,
683
1389
  FRONTX_SHARED_PROPERTY_LANGUAGE
684
1390
  } from "@gears-frontx/framework";
685
1391
  import {
1392
+ ChildMfeBridge,
1393
+ ParentMfeBridge,
686
1394
  MfeHandler,
687
1395
  MfeBridgeFactory,
688
- ScreensetsRegistry,
689
- ScreensetsRegistryFactory,
690
- screensetsRegistryFactory,
691
- ContainerProvider as ContainerProvider2
1396
+ MfeRegistry,
1397
+ MfeRegistryFactory,
1398
+ mfeRegistryFactory,
1399
+ ActionHandler,
1400
+ ExtensionDomainImplementation,
1401
+ ExtensionDomainImplementationFactory,
1402
+ ExtensionMounter,
1403
+ MountStrategy,
1404
+ ConcurrentMountStrategy,
1405
+ OptionalMountStrategy,
1406
+ ExclusiveMountStrategy
692
1407
  } from "@gears-frontx/framework";
693
1408
  import { MfeHandlerMF, gtsPlugin } from "@gears-frontx/framework";
694
1409
  import { themeSchema, languageSchema, extensionScreenSchema } from "@gears-frontx/framework";
695
1410
  import {
696
1411
  createShadowRoot,
697
1412
  injectCssVariables,
698
- extractGtsPackage as extractGtsPackage2
1413
+ extractGtsPackage
699
1414
  } from "@gears-frontx/framework";
700
1415
  import { eventBus as frameworkEventBus } from "@gears-frontx/framework";
701
- var eventBus = frameworkEventBus;
1416
+ var eventBus2 = frameworkEventBus;
702
1417
  export {
703
1418
  ACCOUNTS_DOMAIN,
1419
+ ActionHandler,
704
1420
  ApiPlugin,
705
1421
  ApiPluginBase,
706
1422
  ApiProtocol,
707
1423
  BaseApiService,
708
- ContainerProvider2 as ContainerProvider,
1424
+ CanAccess,
1425
+ ChildMfeBridge,
1426
+ ConcurrentMountStrategy,
1427
+ ExclusiveMountStrategy,
1428
+ ExtensionDomainImplementation,
1429
+ ExtensionDomainImplementationFactory,
709
1430
  ExtensionDomainSlot,
1431
+ ExtensionMounter,
710
1432
  FRONTX_ACTION_LOAD_EXT,
711
- FRONTX_ACTION_MOUNT_EXT2 as FRONTX_ACTION_MOUNT_EXT,
712
- FRONTX_ACTION_UNMOUNT_EXT2 as FRONTX_ACTION_UNMOUNT_EXT,
1433
+ FRONTX_ACTION_MOUNT_EXT,
1434
+ FRONTX_ACTION_UNMOUNT_EXT,
713
1435
  FRONTX_MFE_ENTRY_MF,
714
1436
  FRONTX_OVERLAY_DOMAIN,
715
1437
  FRONTX_POPUP_DOMAIN,
716
- FRONTX_SCREEN_DOMAIN2 as FRONTX_SCREEN_DOMAIN,
1438
+ FRONTX_SCREEN_DOMAIN,
717
1439
  FRONTX_SCREEN_EXTENSION_TYPE,
718
1440
  FRONTX_SHARED_PROPERTY_LANGUAGE,
719
1441
  FRONTX_SHARED_PROPERTY_THEME,
720
1442
  FRONTX_SIDEBAR_DOMAIN,
721
- HAI3Context,
722
- HAI3Provider,
1443
+ FrontXContext,
1444
+ FrontXProvider,
723
1445
  I18nRegistry,
724
1446
  I18nRegistryImpl,
725
1447
  LAYOUT_SLICE_NAME,
@@ -732,45 +1454,51 @@ export {
732
1454
  MfeHandler,
733
1455
  MfeHandlerMF,
734
1456
  MfeProvider,
1457
+ MfeRegistry,
1458
+ MfeRegistryFactory,
735
1459
  MockEventSource,
736
1460
  MockEvents,
737
- RefContainerProvider,
1461
+ MountStrategy,
1462
+ OptionalMountStrategy,
1463
+ ParentMfeBridge,
1464
+ RestEndpointProtocol,
738
1465
  RestMockPlugin,
739
1466
  RestPlugin,
740
1467
  RestPluginWithConfig,
741
1468
  RestProtocol,
742
1469
  SUPPORTED_LANGUAGES,
743
- ScreensetsRegistry,
744
- ScreensetsRegistryFactory,
745
1470
  SseMockPlugin,
746
1471
  SsePlugin,
747
1472
  SsePluginWithConfig,
748
1473
  SseProtocol,
1474
+ SseStreamProtocol,
749
1475
  TENANT_SLICE_NAME,
750
1476
  TenantEvents,
751
1477
  TextDirection,
1478
+ ThemeAwareReactLifecycle,
752
1479
  apiRegistry,
1480
+ auth,
753
1481
  changeTenant,
754
- clearActiveScreen,
755
1482
  clearTenant,
756
1483
  clearTenantAction,
757
1484
  clearUser,
758
1485
  closeAllPopups,
759
1486
  closePopup,
760
1487
  closeTopPopup,
761
- createHAI3,
762
- createHAI3App2 as createHAI3App,
1488
+ createFrontX,
1489
+ createFrontXApp2 as createFrontXApp,
763
1490
  createI18nRegistry,
764
1491
  createShadowRoot,
765
1492
  createSlice,
766
1493
  createStore,
767
1494
  createThemeRegistry,
768
1495
  effects,
769
- eventBus,
1496
+ eventBus2 as eventBus,
770
1497
  extensionScreenSchema,
771
- extractGtsPackage2 as extractGtsPackage,
1498
+ extractGtsPackage,
772
1499
  footerActions,
773
1500
  footerSlice,
1501
+ frontxApiTransport,
774
1502
  getLanguageMetadata,
775
1503
  getStore,
776
1504
  gtsPlugin,
@@ -783,6 +1511,7 @@ export {
783
1511
  initMockEffects,
784
1512
  initTenantEffects,
785
1513
  injectCssVariables,
1514
+ invalidateQueryCacheForApp,
786
1515
  isMockPlugin,
787
1516
  isRestShortCircuit,
788
1517
  isShortCircuit,
@@ -794,12 +1523,12 @@ export {
794
1523
  loadExtension,
795
1524
  menuActions,
796
1525
  menuSlice,
797
- microfrontends,
1526
+ mfeRegistryFactory,
1527
+ microfrontends2 as microfrontends,
798
1528
  mock,
799
1529
  mockActions,
800
1530
  mockSlice,
801
1531
  mountExtension,
802
- navigateTo,
803
1532
  openPopup,
804
1533
  overlayActions,
805
1534
  overlayDomain,
@@ -808,17 +1537,15 @@ export {
808
1537
  popupDomain,
809
1538
  popupSlice,
810
1539
  presets,
1540
+ queryCache,
1541
+ queryCacheShared,
811
1542
  registerExtension,
812
1543
  registerSlice,
813
- screenActions,
814
1544
  screenDomain,
815
- screenSlice,
816
- screensets,
817
- screensetsRegistryFactory,
818
1545
  selectExtensionError,
819
1546
  selectExtensionState,
1547
+ selectMountedExtensions,
820
1548
  selectRegisteredExtensions,
821
- setActiveScreen,
822
1549
  setFooterConfig,
823
1550
  setFooterVisible,
824
1551
  setHeaderLoading,
@@ -828,7 +1555,6 @@ export {
828
1555
  setMenuVisible,
829
1556
  setMockEnabled,
830
1557
  setOverlayVisible,
831
- setScreenLoading,
832
1558
  setSidebarCollapsed,
833
1559
  setSidebarConfig,
834
1560
  setSidebarContent,
@@ -844,6 +1570,7 @@ export {
844
1570
  sidebarActions,
845
1571
  sidebarDomain,
846
1572
  sidebarSlice,
1573
+ subscribeQueryCacheRuntimeChanged2 as subscribeQueryCacheRuntimeChanged,
847
1574
  tenantActions,
848
1575
  tenantReducer,
849
1576
  tenantSlice,
@@ -854,15 +1581,23 @@ export {
854
1581
  toggleSidebar,
855
1582
  unmountExtension,
856
1583
  unregisterExtension,
857
- useActivePackage,
1584
+ useApiInfiniteQuery,
1585
+ useApiMutation,
1586
+ useApiQuery,
1587
+ useApiStream,
1588
+ useApiSuspenseInfiniteQuery,
1589
+ useApiSuspenseQuery,
858
1590
  useAppDispatch,
859
1591
  useAppSelector,
1592
+ useCanAccess,
860
1593
  useDomainExtensions,
861
1594
  useFormatters,
862
- useHAI3,
1595
+ useFrontX,
863
1596
  useHostAction,
864
1597
  useMfeBridge,
865
1598
  useMfeContext,
1599
+ useMountedExtensions,
1600
+ useQueryCache,
866
1601
  useRegisteredPackages,
867
1602
  useScreenTranslations,
868
1603
  useSharedProperty,