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

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