@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/CLAUDE.md +191 -45
- package/dist/index.cjs +1131 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +418 -119
- package/dist/index.d.ts +418 -119
- package/dist/index.js +977 -242
- package/dist/index.js.map +1 -1
- package/dist/testing-Dlmfu6tE.d.cts +56 -0
- package/dist/testing-Dlmfu6tE.d.ts +56 -0
- package/dist/testing.cjs +77 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +4 -0
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +54 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-Cjd20AT-.d.cts +231 -0
- package/dist/types-Cjd20AT-.d.ts +231 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.ts +2 -2
- package/llms.txt +20 -20
- package/package.json +29 -18
- package/commands/hai3-duplicate-screenset.md +0 -58
- package/commands/hai3-new-component.md +0 -48
- package/commands/hai3-new-mfe.md +0 -166
- package/commands/hai3-new-screen.md +0 -39
- package/commands/hai3-new-screenset.md +0 -47
- package/dist/types-Cp8Zm5s0.d.cts +0 -136
- package/dist/types-Cp8Zm5s0.d.ts +0 -136
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { EndpointDescriptor, FrontXApp } from '@gears-frontx/framework';
|
|
3
|
+
import '@gears-frontx/framework/testing';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* QueryCache — restricted interface for interacting with the shared QueryClient.
|
|
7
|
+
*
|
|
8
|
+
* MFEs and screen-set components access the cache only through this sanctioned
|
|
9
|
+
* public interface. It is injected as { queryCache } into useApiMutation
|
|
10
|
+
* callbacks and also returned by useQueryCache() for controlled imperative
|
|
11
|
+
* cache access.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type QueryCacheKey = readonly unknown[];
|
|
15
|
+
type QueryCacheRefetchMode = 'active' | 'inactive' | 'all' | 'none';
|
|
16
|
+
type QueryCacheInvalidateTarget = EndpointDescriptor<unknown> | QueryCacheKey | null | undefined;
|
|
17
|
+
interface QueryCacheState<TData = unknown, TError = Error> {
|
|
18
|
+
data: TData | undefined;
|
|
19
|
+
dataUpdatedAt: number;
|
|
20
|
+
error: TError | null;
|
|
21
|
+
errorUpdatedAt: number;
|
|
22
|
+
fetchFailureCount: number;
|
|
23
|
+
fetchFailureReason: TError | null;
|
|
24
|
+
fetchStatus: 'fetching' | 'paused' | 'idle';
|
|
25
|
+
isInvalidated: boolean;
|
|
26
|
+
status: 'pending' | 'error' | 'success';
|
|
27
|
+
}
|
|
28
|
+
type QueryCacheInvalidateFilters = {
|
|
29
|
+
queryKey?: QueryCacheInvalidateTarget;
|
|
30
|
+
exact?: boolean;
|
|
31
|
+
refetchType?: QueryCacheRefetchMode;
|
|
32
|
+
};
|
|
33
|
+
interface QueryCache {
|
|
34
|
+
get<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined;
|
|
35
|
+
getState<TData = unknown, TError = Error>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): QueryCacheState<TData, TError> | undefined;
|
|
36
|
+
set<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey, dataOrUpdater: T | ((old: T | undefined) => T | undefined)): void;
|
|
37
|
+
cancel(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
38
|
+
invalidate(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
39
|
+
invalidateMany(filters?: QueryCacheInvalidateFilters | null): Promise<void>;
|
|
40
|
+
remove(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): void;
|
|
41
|
+
}
|
|
42
|
+
interface MutationCallbackContext {
|
|
43
|
+
queryCache: QueryCache;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare function resolveFrontXQueryClient(app: FrontXApp): QueryClient | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Imperatively invalidates cache entries for `target` on the QueryClient bound to `app`.
|
|
49
|
+
* Use from non-React code (e.g. MFE actions); in components prefer `useQueryCache().invalidate`.
|
|
50
|
+
*/
|
|
51
|
+
declare function invalidateQueryCacheForApp(app: FrontXApp, target: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
52
|
+
/** Resolves the app-bound QueryClient without triggering plugin activation during render. */
|
|
53
|
+
declare function bootstrapFrontXQueryClient(app: FrontXApp): QueryClient | undefined;
|
|
54
|
+
declare function useOptionalFrontXQueryClient(): QueryClient | undefined;
|
|
55
|
+
|
|
56
|
+
export { type MutationCallbackContext as M, type QueryCache as Q, type QueryCacheInvalidateFilters as a, type QueryCacheState as b, bootstrapFrontXQueryClient as c, invalidateQueryCacheForApp as i, resolveFrontXQueryClient as r, useOptionalFrontXQueryClient as u };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { EndpointDescriptor, FrontXApp } from '@gears-frontx/framework';
|
|
3
|
+
import '@gears-frontx/framework/testing';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* QueryCache — restricted interface for interacting with the shared QueryClient.
|
|
7
|
+
*
|
|
8
|
+
* MFEs and screen-set components access the cache only through this sanctioned
|
|
9
|
+
* public interface. It is injected as { queryCache } into useApiMutation
|
|
10
|
+
* callbacks and also returned by useQueryCache() for controlled imperative
|
|
11
|
+
* cache access.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type QueryCacheKey = readonly unknown[];
|
|
15
|
+
type QueryCacheRefetchMode = 'active' | 'inactive' | 'all' | 'none';
|
|
16
|
+
type QueryCacheInvalidateTarget = EndpointDescriptor<unknown> | QueryCacheKey | null | undefined;
|
|
17
|
+
interface QueryCacheState<TData = unknown, TError = Error> {
|
|
18
|
+
data: TData | undefined;
|
|
19
|
+
dataUpdatedAt: number;
|
|
20
|
+
error: TError | null;
|
|
21
|
+
errorUpdatedAt: number;
|
|
22
|
+
fetchFailureCount: number;
|
|
23
|
+
fetchFailureReason: TError | null;
|
|
24
|
+
fetchStatus: 'fetching' | 'paused' | 'idle';
|
|
25
|
+
isInvalidated: boolean;
|
|
26
|
+
status: 'pending' | 'error' | 'success';
|
|
27
|
+
}
|
|
28
|
+
type QueryCacheInvalidateFilters = {
|
|
29
|
+
queryKey?: QueryCacheInvalidateTarget;
|
|
30
|
+
exact?: boolean;
|
|
31
|
+
refetchType?: QueryCacheRefetchMode;
|
|
32
|
+
};
|
|
33
|
+
interface QueryCache {
|
|
34
|
+
get<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined;
|
|
35
|
+
getState<TData = unknown, TError = Error>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): QueryCacheState<TData, TError> | undefined;
|
|
36
|
+
set<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey, dataOrUpdater: T | ((old: T | undefined) => T | undefined)): void;
|
|
37
|
+
cancel(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
38
|
+
invalidate(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
39
|
+
invalidateMany(filters?: QueryCacheInvalidateFilters | null): Promise<void>;
|
|
40
|
+
remove(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): void;
|
|
41
|
+
}
|
|
42
|
+
interface MutationCallbackContext {
|
|
43
|
+
queryCache: QueryCache;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare function resolveFrontXQueryClient(app: FrontXApp): QueryClient | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Imperatively invalidates cache entries for `target` on the QueryClient bound to `app`.
|
|
49
|
+
* Use from non-React code (e.g. MFE actions); in components prefer `useQueryCache().invalidate`.
|
|
50
|
+
*/
|
|
51
|
+
declare function invalidateQueryCacheForApp(app: FrontXApp, target: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;
|
|
52
|
+
/** Resolves the app-bound QueryClient without triggering plugin activation during render. */
|
|
53
|
+
declare function bootstrapFrontXQueryClient(app: FrontXApp): QueryClient | undefined;
|
|
54
|
+
declare function useOptionalFrontXQueryClient(): QueryClient | undefined;
|
|
55
|
+
|
|
56
|
+
export { type MutationCallbackContext as M, type QueryCache as Q, type QueryCacheInvalidateFilters as a, type QueryCacheState as b, bootstrapFrontXQueryClient as c, invalidateQueryCacheForApp as i, resolveFrontXQueryClient as r, useOptionalFrontXQueryClient as u };
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/testing.ts
|
|
31
|
+
var testing_exports = {};
|
|
32
|
+
__export(testing_exports, {
|
|
33
|
+
bootstrapFrontXQueryClient: () => bootstrapFrontXQueryClient,
|
|
34
|
+
describeBootstrapMfeContract: () => import_testing.describeBootstrapMfeContract,
|
|
35
|
+
resolveFrontXQueryClient: () => resolveFrontXQueryClient,
|
|
36
|
+
useOptionalFrontXQueryClient: () => useOptionalFrontXQueryClient
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(testing_exports);
|
|
39
|
+
|
|
40
|
+
// src/queryClient.tsx
|
|
41
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
42
|
+
var import_react_query = require("@tanstack/react-query");
|
|
43
|
+
var import_framework2 = require("@gears-frontx/framework");
|
|
44
|
+
|
|
45
|
+
// src/hooks/QueryCache.ts
|
|
46
|
+
var import_framework = require("@gears-frontx/framework");
|
|
47
|
+
|
|
48
|
+
// src/hooks/useLatest.ts
|
|
49
|
+
var import_react = require("react");
|
|
50
|
+
|
|
51
|
+
// src/queryClient.tsx
|
|
52
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
53
|
+
var APP_QUERY_CLIENT_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client");
|
|
54
|
+
var APP_QUERY_CLIENT_RESOLVER_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-resolver");
|
|
55
|
+
var FrontXQueryClientContext = (0, import_react2.createContext)(void 0);
|
|
56
|
+
var useCommitEffect = typeof globalThis.window === "undefined" ? import_react2.useEffect : import_react2.useLayoutEffect;
|
|
57
|
+
function resolveFrontXQueryClient(app) {
|
|
58
|
+
const clientApp = app;
|
|
59
|
+
return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();
|
|
60
|
+
}
|
|
61
|
+
function bootstrapFrontXQueryClient(app) {
|
|
62
|
+
return resolveFrontXQueryClient(app);
|
|
63
|
+
}
|
|
64
|
+
function useOptionalFrontXQueryClient() {
|
|
65
|
+
return (0, import_react2.useContext)(FrontXQueryClientContext);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/testing.ts
|
|
69
|
+
var import_testing = require("@gears-frontx/framework/testing");
|
|
70
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
71
|
+
0 && (module.exports = {
|
|
72
|
+
bootstrapFrontXQueryClient,
|
|
73
|
+
describeBootstrapMfeContract,
|
|
74
|
+
resolveFrontXQueryClient,
|
|
75
|
+
useOptionalFrontXQueryClient
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts","../src/queryClient.tsx","../src/hooks/QueryCache.ts","../src/hooks/useLatest.ts"],"sourcesContent":["/**\n * Test-only entry for @gears-frontx/react.\n *\n * Utilities here are intended for Vitest (or similar) suites that need access\n * to query-client wiring without importing package internals directly.\n */\n\n// @cpt-dod:cpt-frontx-dod-react-bindings-provider:p1\n\n// @cpt-begin:cpt-frontx-dod-react-bindings-provider:p1:inst-react-testing-reexports\nexport {\n bootstrapFrontXQueryClient,\n resolveFrontXQueryClient,\n useOptionalFrontXQueryClient,\n} from './queryClient';\n\n/** App-layer tests must import this from `@gears-frontx/react/testing`, not `@gears-frontx/framework/testing`. */\nexport { describeBootstrapMfeContract } from '@gears-frontx/framework/testing';\nexport type {\n BootstrapMfeResolveArgs,\n BootstrapMfeTestSpecOptions,\n} from '@gears-frontx/framework/testing';\n// @cpt-end:cpt-frontx-dod-react-bindings-provider:p1:inst-react-testing-reexports\n","import React, {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n QueryClientContext,\n QueryClientProvider,\n type QueryClient,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseInfiniteQuery,\n useSuspenseQuery,\n type MutationFunctionContext,\n} from '@tanstack/react-query';\nimport {\n subscribeQueryCacheRuntimeChanged,\n type EndpointDescriptor,\n type FrontXApp,\n} from '@gears-frontx/framework';\nimport type { ApiQueryOverrides } from './hooks/useApiQuery';\nimport type {\n ApiInfiniteQueryOptions,\n ApiInfiniteQueryPageContext,\n} from './hooks/useApiInfiniteQuery';\nimport type { UseApiMutationOptions } from './hooks/useApiMutation';\nimport { createQueryCache, type MutationCallbackContext, type QueryCacheKey } from './hooks/QueryCache';\nimport { useLatest } from './hooks/useLatest';\n\n// @cpt-FEATURE:implement-endpoint-descriptors:p3\n// @cpt-dod:cpt-frontx-dod-request-lifecycle-query-provider:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-query:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-react-query-client-symbols\nconst APP_QUERY_CLIENT_SYMBOL = Symbol.for('frontx:query-cache:app-client');\nconst APP_QUERY_CLIENT_RESOLVER_SYMBOL = Symbol.for('frontx:query-cache:app-client-resolver');\nconst APP_QUERY_CLIENT_ACTIVATOR_SYMBOL = Symbol.for('frontx:query-cache:app-client-activator');\n\ntype QueryClientApp = FrontXApp & {\n [APP_QUERY_CLIENT_SYMBOL]?: QueryClient;\n [APP_QUERY_CLIENT_RESOLVER_SYMBOL]?: () => QueryClient | undefined;\n [APP_QUERY_CLIENT_ACTIVATOR_SYMBOL]?: () => QueryClient | undefined;\n};\n\nconst FrontXQueryClientContext = createContext<QueryClient | undefined>(undefined);\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-react-query-client-symbols\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-resolve-app-query-client\ntype RuntimeAwareEndpointDescriptor<TData> = EndpointDescriptor<TData> & {\n fetch(options?: { signal?: AbortSignal; staleTime?: number }): Promise<TData>;\n};\n\ntype AppResolvedQueryClient = {\n app: FrontXApp;\n queryClient: QueryClient;\n};\n\nconst useCommitEffect = typeof globalThis.window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function resolveFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const clientApp = app as QueryClientApp;\n return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();\n}\n\nexport function hasFrontXQueryClientActivator(app: FrontXApp): boolean {\n return typeof (app as QueryClientApp)[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL] === 'function';\n}\n\nexport function activateFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const clientApp = app as QueryClientApp;\n return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL]?.();\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-resolve-app-query-client\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-imperative-cache-bootstrap\n/**\n * Imperatively invalidates cache entries for `target` on the QueryClient bound to `app`.\n * Use from non-React code (e.g. MFE actions); in components prefer `useQueryCache().invalidate`.\n */\nexport async function invalidateQueryCacheForApp(\n app: FrontXApp,\n target: EndpointDescriptor<unknown> | QueryCacheKey\n): Promise<void> {\n const queryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);\n if (!queryClient) {\n throw new Error(\n '[invalidateQueryCacheForApp] No QueryClient on app. Ensure queryCache() or queryCacheShared() is in the plugin chain.'\n );\n }\n await createQueryCache(queryClient).invalidate(target);\n}\n\n/** Resolves the app-bound QueryClient without triggering plugin activation during render. */\nexport function bootstrapFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n return resolveFrontXQueryClient(app);\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-imperative-cache-bootstrap\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-use-bootstrapped-query-client\nexport function useBootstrappedFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const fromApp = useMemo(() => bootstrapFrontXQueryClient(app), [app]);\n const [eventResolvedClient, setEventResolvedClient] = useState<AppResolvedQueryClient | undefined>(\n undefined\n );\n const queryClient =\n fromApp ?? (eventResolvedClient?.app === app ? eventResolvedClient.queryClient : undefined);\n\n useCommitEffect(() => {\n if (fromApp || !hasFrontXQueryClientActivator(app)) {\n return;\n }\n\n const tryResolveQueryClient = () => {\n const nextQueryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);\n if (nextQueryClient) {\n setEventResolvedClient((current) => {\n if (current?.app === app && current.queryClient === nextQueryClient) {\n return current;\n }\n\n return {\n app,\n queryClient: nextQueryClient,\n };\n });\n }\n };\n\n // Only activate after commit so abandoned renders cannot leak retainers/listeners.\n tryResolveQueryClient();\n\n const subscription = subscribeQueryCacheRuntimeChanged(() => {\n tryResolveQueryClient();\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [app, fromApp]);\n\n return queryClient;\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-use-bootstrapped-query-client\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-frontx-query-client-provider\nexport function FrontXQueryClientProvider({\n queryClient,\n children,\n}: Readonly<{\n queryClient: QueryClient | undefined;\n children: React.ReactNode;\n}>) {\n if (!queryClient) {\n return (\n <FrontXQueryClientContext.Provider value={undefined}>\n <QueryClientContext.Provider value={undefined}>{children}</QueryClientContext.Provider>\n </FrontXQueryClientContext.Provider>\n );\n }\n\n return (\n <FrontXQueryClientContext.Provider value={queryClient}>\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n </FrontXQueryClientContext.Provider>\n );\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-frontx-query-client-provider\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-query-client-context-hooks\nexport function useOptionalFrontXQueryClient(): QueryClient | undefined {\n return useContext(FrontXQueryClientContext);\n}\n\nexport function useRequiredFrontXQueryClient(): QueryClient {\n const queryClient = useOptionalFrontXQueryClient();\n if (!queryClient) {\n throw new Error(\n '[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition.'\n );\n }\n\n return queryClient;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-query-client-context-hooks\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-descriptor-fetch-stale-helpers\nfunction buildPageContext<TPage>(\n page: TPage,\n pages: readonly TPage[],\n descriptor: EndpointDescriptor<TPage>,\n descriptors: readonly EndpointDescriptor<TPage>[]\n): ApiInfiniteQueryPageContext<TPage> {\n return {\n page,\n pages,\n descriptor,\n descriptors,\n };\n}\n\nfunction normalizeFetchStaleTime(staleTime: unknown): number | undefined {\n if (staleTime === 'static') {\n return Number.POSITIVE_INFINITY;\n }\n\n return typeof staleTime === 'number' ? staleTime : undefined;\n}\n\nfunction resolveFetchStaleTime<TData>(\n queryClient: QueryClient,\n descriptor: EndpointDescriptor<TData>,\n overrideStaleTime?: number\n): number | undefined {\n return normalizeFetchStaleTime(\n overrideStaleTime ??\n descriptor.staleTime ??\n queryClient.getDefaultOptions().queries?.staleTime\n );\n}\n\nfunction fetchDescriptor<TData>(\n descriptor: EndpointDescriptor<TData>,\n options?: { signal?: AbortSignal; staleTime?: number }\n): Promise<TData> {\n return (descriptor as RuntimeAwareEndpointDescriptor<TData>).fetch(options);\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-descriptor-fetch-stale-helpers\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-query\nexport function useFrontXQuery<TData = unknown, TError = Error>(\n descriptor: EndpointDescriptor<TData>,\n overrides?: ApiQueryOverrides\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);\n const result = useQuery<TData, TError>({\n queryKey: descriptor.key as unknown[],\n queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),\n staleTime: overrides?.staleTime ?? descriptor.staleTime,\n gcTime: overrides?.gcTime ?? descriptor.gcTime,\n });\n\n return {\n data: result.data,\n error: result.error,\n isLoading: result.isLoading,\n isFetching: result.isFetching,\n isError: result.isError,\n refetch: result.refetch,\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-query\nexport function useFrontXSuspenseQuery<TData = unknown, TError = Error>(\n descriptor: EndpointDescriptor<TData>,\n overrides?: ApiQueryOverrides\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);\n const result = useSuspenseQuery<TData, TError>({\n queryKey: descriptor.key as unknown[],\n queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),\n staleTime: overrides?.staleTime ?? descriptor.staleTime,\n gcTime: overrides?.gcTime ?? descriptor.gcTime,\n });\n\n return {\n data: result.data,\n error: result.error,\n isFetching: result.isFetching,\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-infinite-query\nexport function useFrontXInfiniteQuery<TPage = unknown, TError = Error>(\n options: ApiInfiniteQueryOptions<TPage>\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const result = useInfiniteQuery<\n TPage,\n TError,\n readonly TPage[],\n readonly unknown[],\n EndpointDescriptor<TPage>\n >({\n queryKey: options.initialPage.key,\n initialPageParam: options.initialPage,\n queryFn: ({ pageParam, signal }) =>\n fetchDescriptor(pageParam, {\n signal,\n staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime),\n }),\n getNextPageParam: (\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n ) =>\n options.getNextPage(\n buildPageContext(\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n )\n ),\n getPreviousPageParam: options.getPreviousPage\n ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) =>\n options.getPreviousPage?.(\n buildPageContext(\n firstPage,\n allPages,\n firstPageDescriptor,\n allPageDescriptors\n )\n )\n : undefined,\n select: (data) => data.pages,\n staleTime: options.staleTime ?? options.initialPage.staleTime,\n gcTime: options.gcTime ?? options.initialPage.gcTime,\n maxPages: options.maxPages,\n });\n\n return {\n data: result.data,\n error: result.error,\n isLoading: result.isLoading,\n isFetching: result.isFetching,\n isError: result.isError,\n hasNextPage: result.hasNextPage ?? false,\n hasPreviousPage: result.hasPreviousPage ?? false,\n isFetchingNextPage: result.isFetchingNextPage,\n isFetchingPreviousPage: result.isFetchingPreviousPage,\n fetchNextPage: async () => {\n await result.fetchNextPage();\n },\n fetchPreviousPage: async () => {\n await result.fetchPreviousPage();\n },\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-infinite-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-infinite-query\nexport function useFrontXSuspenseInfiniteQuery<TPage = unknown, TError = Error>(\n options: ApiInfiniteQueryOptions<TPage>\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const result = useSuspenseInfiniteQuery<\n TPage,\n TError,\n readonly TPage[],\n readonly unknown[],\n EndpointDescriptor<TPage>\n >({\n queryKey: options.initialPage.key,\n initialPageParam: options.initialPage,\n queryFn: ({ pageParam, signal }) =>\n fetchDescriptor(pageParam, {\n signal,\n staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime),\n }),\n getNextPageParam: (\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n ) =>\n options.getNextPage(\n buildPageContext(\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n )\n ),\n getPreviousPageParam: options.getPreviousPage\n ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) =>\n options.getPreviousPage?.(\n buildPageContext(\n firstPage,\n allPages,\n firstPageDescriptor,\n allPageDescriptors\n )\n )\n : undefined,\n select: (data) => data.pages,\n staleTime: options.staleTime ?? options.initialPage.staleTime,\n gcTime: options.gcTime ?? options.initialPage.gcTime,\n maxPages: options.maxPages,\n });\n\n return {\n data: result.data,\n error: result.error,\n isFetching: result.isFetching,\n hasNextPage: result.hasNextPage ?? false,\n hasPreviousPage: result.hasPreviousPage ?? false,\n isFetchingNextPage: result.isFetchingNextPage,\n isFetchingPreviousPage: result.isFetchingPreviousPage,\n fetchNextPage: async () => {\n await result.fetchNextPage();\n },\n fetchPreviousPage: async () => {\n await result.fetchPreviousPage();\n },\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-infinite-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-delegate-frontx-use-mutation\nexport function useFrontXMutation<\n TData = unknown,\n TError = Error,\n TVariables = void,\n TContext = unknown,\n>(\n options: UseApiMutationOptions<TData, TError, TVariables, TContext>,\n callbackCtx: MutationCallbackContext\n) {\n useRequiredFrontXQueryClient();\n\n const latestRef = useLatest({ options, callbackCtx });\n const fetchAbortControllersRef = React.useRef<Set<AbortController>>(new Set());\n\n // Effect event: reads abortOnUnmount from the most recent render, not\n // whatever option value was current when the unmount effect was set up.\n const abortFetchesOnUnmount = React.useEffectEvent(() => {\n if (options.abortOnUnmount) {\n for (const controller of fetchAbortControllersRef.current) {\n controller.abort();\n }\n fetchAbortControllersRef.current.clear();\n }\n });\n\n React.useEffect(() => {\n return () => {\n abortFetchesOnUnmount();\n };\n }, []);\n\n const mutationFn = React.useCallback((variables: TVariables, context: MutationFunctionContext) => {\n const cancelOnSupersede = latestRef.current.options.cancelOnSupersede;\n if (cancelOnSupersede) {\n for (const controller of fetchAbortControllersRef.current) {\n controller.abort();\n }\n fetchAbortControllersRef.current.clear();\n }\n\n const controller = new AbortController();\n fetchAbortControllersRef.current.add(controller);\n\n const librarySignal = (context as MutationFunctionContext & { signal?: AbortSignal }).signal;\n if (cancelOnSupersede && librarySignal) {\n if (librarySignal.aborted) {\n controller.abort();\n } else {\n librarySignal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n }\n\n return latestRef.current.options.endpoint\n .fetch(variables, { signal: controller.signal })\n .finally(() => {\n fetchAbortControllersRef.current.delete(controller);\n });\n // latestRef's identity never changes (useLatest returns a stable ref\n // object) — listing it satisfies exhaustive-deps without recreating this\n // callback on every render.\n }, [latestRef]);\n\n const onMutate = React.useCallback((variables: TVariables, _context: MutationFunctionContext) => {\n const current = latestRef.current;\n if (!current.options.onMutate) {\n return undefined as TContext;\n }\n\n return current.options.onMutate(variables, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onSuccess = React.useCallback((data: TData, variables: TVariables, context: TContext | undefined) => {\n const current = latestRef.current;\n return current.options.onSuccess?.(data, variables, context, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onError = React.useCallback((error: TError, variables: TVariables, context: TContext | undefined) => {\n const current = latestRef.current;\n return current.options.onError?.(error, variables, context, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onSettled = React.useCallback((\n data: TData | undefined,\n error: TError | null,\n variables: TVariables,\n context: TContext | undefined\n ) => {\n const current = latestRef.current;\n return current.options.onSettled?.(\n data,\n error,\n variables,\n context,\n current.callbackCtx\n );\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const mutation = useMutation<TData, TError, TVariables, TContext>({\n mutationFn,\n onMutate: options.onMutate ? onMutate : undefined,\n onSuccess: options.onSuccess ? onSuccess : undefined,\n onError: options.onError ? onError : undefined,\n onSettled: options.onSettled ? onSettled : undefined,\n });\n\n return {\n mutate: mutation.mutate as (variables: TVariables) => void,\n mutateAsync: mutation.mutateAsync as (variables: TVariables) => Promise<TData>,\n isPending: mutation.isPending,\n error: mutation.error,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-delegate-frontx-use-mutation\n","/**\n * QueryCache — restricted interface for interacting with the shared QueryClient.\n *\n * MFEs and screen-set components access the cache only through this sanctioned\n * public interface. It is injected as { queryCache } into useApiMutation\n * callbacks and also returned by useQueryCache() for controlled imperative\n * cache access.\n */\n// @cpt-dod:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-create-query-cache\n// @cpt-FEATURE:implement-endpoint-descriptors:p3\n\nimport type { EndpointDescriptor } from '@gears-frontx/framework';\nimport { eventBus } from '@gears-frontx/framework';\nimport { type QueryClient, type QueryState } from '@tanstack/react-query';\n\nexport type QueryCacheKey = readonly unknown[];\nexport type QueryCacheRefetchMode = 'active' | 'inactive' | 'all' | 'none';\nexport type QueryCacheInvalidateTarget =\n | EndpointDescriptor<unknown>\n | QueryCacheKey\n | null\n | undefined;\n\nexport interface QueryCacheState<TData = unknown, TError = Error> {\n data: TData | undefined;\n dataUpdatedAt: number;\n error: TError | null;\n errorUpdatedAt: number;\n fetchFailureCount: number;\n fetchFailureReason: TError | null;\n fetchStatus: 'fetching' | 'paused' | 'idle';\n isInvalidated: boolean;\n status: 'pending' | 'error' | 'success';\n}\n\nexport type QueryCacheInvalidateFilters = {\n queryKey?: QueryCacheInvalidateTarget;\n exact?: boolean;\n refetchType?: QueryCacheRefetchMode;\n};\n\ntype QueryCacheUpdater<T> = T | ((old: T | undefined) => T | undefined);\n\ntype CacheInvalidateEvent = {\n queryKey: QueryCacheKey;\n exact?: boolean;\n refetchType?: QueryCacheRefetchMode;\n source?: string;\n};\n\ntype CacheSetEvent = {\n queryKey: QueryCacheKey;\n dataOrUpdater: QueryCacheUpdater<unknown>;\n source?: string;\n};\n\ntype CacheRemoveEvent = {\n queryKey: QueryCacheKey;\n source?: string;\n};\n\nconst QUERY_CLIENT_BROADCAST_TARGET = Symbol.for('frontx:query-cache:broadcast-target');\nconst QUERY_CLIENT_BROADCAST_COUNTER = Symbol.for('frontx:query-cache:broadcast-counter');\n\ntype QueryClientWithMetadata = QueryClient & {\n [QUERY_CLIENT_BROADCAST_TARGET]?: string;\n};\n\ntype QueryClientCounterHost = typeof globalThis & {\n [QUERY_CLIENT_BROADCAST_COUNTER]?: number;\n};\n\nfunction emitCacheEvent(event: string, payload: unknown): void {\n (eventBus.emit as (eventType: string, payload: unknown) => void)(event, payload);\n}\n\nfunction ensureBroadcastSource(queryClient: QueryClient): string {\n const clientWithMetadata = queryClient as QueryClientWithMetadata;\n const existingSource = clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET];\n if (typeof existingSource === 'string' && existingSource.length > 0) {\n return existingSource;\n }\n\n const host = globalThis as QueryClientCounterHost;\n const nextCounter = (host[QUERY_CLIENT_BROADCAST_COUNTER] ?? 0) + 1;\n host[QUERY_CLIENT_BROADCAST_COUNTER] = nextCounter;\n\n const source = `query-cache-${nextCounter}`;\n clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET] = source;\n return source;\n}\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-resolve-key\nexport function resolveKey(target: EndpointDescriptor<unknown> | QueryCacheKey): readonly unknown[] {\n if (\n target !== null &&\n typeof target === 'object' &&\n !Array.isArray(target) &&\n 'key' in target &&\n 'fetch' in target &&\n Array.isArray(target.key) &&\n typeof target.fetch === 'function'\n ) {\n return target.key;\n }\n\n return target as readonly unknown[];\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-resolve-key\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-query-cache-interface\nexport interface QueryCache {\n get<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined;\n getState<TData = unknown, TError = Error>(\n queryKey: EndpointDescriptor<unknown> | QueryCacheKey\n ): QueryCacheState<TData, TError> | undefined;\n set<T>(\n queryKey: EndpointDescriptor<unknown> | QueryCacheKey,\n dataOrUpdater: T | ((old: T | undefined) => T | undefined)\n ): void;\n cancel(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;\n invalidate(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;\n invalidateMany(filters?: QueryCacheInvalidateFilters | null): Promise<void>;\n remove(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): void;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-query-cache-interface\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-mutation-callback-context\nexport interface MutationCallbackContext {\n queryCache: QueryCache;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-mutation-callback-context\n\nfunction toQueryUpdater<T>(\n dataOrUpdater: QueryCacheUpdater<T>\n): (old: T | undefined) => T | undefined {\n if (typeof dataOrUpdater === 'function') {\n return dataOrUpdater as (old: T | undefined) => T | undefined;\n }\n\n return () => dataOrUpdater;\n}\n\nfunction mapQueryState<TData, TError = Error>(\n state: QueryState<TData, TError>\n): QueryCacheState<TData, TError> {\n return {\n data: state.data,\n dataUpdatedAt: state.dataUpdatedAt,\n error: state.error,\n errorUpdatedAt: state.errorUpdatedAt,\n fetchFailureCount: state.fetchFailureCount,\n fetchFailureReason: state.fetchFailureReason,\n fetchStatus: state.fetchStatus,\n isInvalidated: state.isInvalidated,\n status: state.status,\n };\n}\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-create-query-cache\nexport function createQueryCache(queryClient: QueryClient): QueryCache {\n return {\n get: <T,>(key: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined => {\n return queryClient.getQueryData<T>(resolveKey(key));\n },\n getState: <TData = unknown, TError = Error>(key: EndpointDescriptor<unknown> | QueryCacheKey) => {\n const state = queryClient.getQueryState<TData, TError>(resolveKey(key));\n return state ? mapQueryState(state) : undefined;\n },\n set: <T,>(key: EndpointDescriptor<unknown> | QueryCacheKey, dataOrUpdater: QueryCacheUpdater<T>): void => {\n const resolvedKey = resolveKey(key);\n queryClient.setQueryData<T>(resolvedKey, toQueryUpdater(dataOrUpdater));\n emitCacheEvent('cache/set', {\n queryKey: resolvedKey,\n dataOrUpdater: dataOrUpdater as QueryCacheUpdater<unknown>,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheSetEvent);\n },\n cancel: (key: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void> => {\n return queryClient.cancelQueries({ queryKey: resolveKey(key) });\n },\n invalidate: (key: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void> => {\n const resolvedKey = resolveKey(key);\n const result = queryClient.invalidateQueries({ queryKey: resolvedKey });\n emitCacheEvent('cache/invalidate', {\n queryKey: resolvedKey,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheInvalidateEvent);\n return result;\n },\n invalidateMany: (filters): Promise<void> => {\n if (filters?.queryKey == null) {\n return Promise.resolve();\n }\n\n const resolvedKey = resolveKey(filters.queryKey);\n if (!Array.isArray(resolvedKey) || resolvedKey.length === 0) {\n return Promise.resolve();\n }\n\n const payload = {\n ...filters,\n queryKey: resolvedKey,\n };\n const result = queryClient.invalidateQueries(payload);\n emitCacheEvent('cache/invalidate', {\n ...payload,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheInvalidateEvent);\n return result;\n },\n remove: (key: EndpointDescriptor<unknown> | QueryCacheKey): void => {\n const resolvedKey = resolveKey(key);\n queryClient.removeQueries({ queryKey: resolvedKey });\n emitCacheEvent('cache/remove', {\n queryKey: resolvedKey,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheRemoveEvent);\n },\n };\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-create-query-cache\n","/**\n * useLatest - keeps a ref pinned to the most recently rendered value\n *\n * A handful of hooks/components in this package need to read the current\n * value of a prop or option from inside a callback or effect-cleanup closure\n * that must NOT re-run every time that value changes (e.g. a stable\n * useCallback with `[]` deps, or a mount/unmount effect keyed on identity\n * rather than on every option). Stashing the value in a ref sidesteps the\n * closure staleness without pulling the value into the dependency array.\n *\n * The write happens inside a deps-less effect (runs after every commit)\n * rather than during render, because refs must not be mutated while\n * rendering: render can be invoked more than once per commit (React\n * StrictMode's double-invoke, and future concurrent-rendering paths that\n * may discard a render), so a render-time write can leave the ref out of\n * sync with what was actually committed to the DOM. A deps-less effect\n * runs exactly once per real commit, so the ref always reflects the value\n * that was last rendered.\n */\n\nimport { useEffect, useRef, type RefObject } from 'react';\n\n/**\n * Returns a ref that always holds the most recently rendered `value`.\n *\n * The ref identity is stable across renders (same object returned every\n * time), so it is safe to add to a `useCallback`/`useEffect` dependency\n * array without causing the callback/effect to be recreated.\n */\nexport function useLatest<T>(value: T): RefObject<T> {\n const ref = useRef(value);\n\n // Deps-less: must run after every render so `ref.current` never lags\n // behind the value that was just committed. See module doc for why this\n // write cannot happen during render itself.\n useEffect(() => {\n ref.current = value;\n });\n\n return ref;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAOO;AACP,yBAUO;AACP,IAAAC,oBAIO;;;ACVP,uBAAyB;;;ACOzB,mBAAkD;;;AF6I1C;AAzHR,IAAM,0BAA0B,uBAAO,IAAI,+BAA+B;AAC1E,IAAM,mCAAmC,uBAAO,IAAI,wCAAwC;AAS5F,IAAM,+BAA2B,6BAAuC,MAAS;AAajF,IAAM,kBAAkB,OAAO,WAAW,WAAW,cAAc,0BAAY;AAExE,SAAS,yBAAyB,KAAyC;AAChF,QAAM,YAAY;AAClB,SAAO,UAAU,uBAAuB,KAAK,UAAU,gCAAgC,IAAI;AAC7F;AA+BO,SAAS,2BAA2B,KAAyC;AAClF,SAAO,yBAAyB,GAAG;AACrC;AA0EO,SAAS,+BAAwD;AACtE,aAAO,0BAAW,wBAAwB;AAC5C;;;ADhKA,qBAA6C;","names":["import_react","import_framework"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { c as bootstrapFrontXQueryClient, r as resolveFrontXQueryClient, u as useOptionalFrontXQueryClient } from './testing-Dlmfu6tE.cjs';
|
|
2
|
+
export { BootstrapMfeResolveArgs, BootstrapMfeTestSpecOptions, describeBootstrapMfeContract } from '@gears-frontx/framework/testing';
|
|
3
|
+
import '@tanstack/react-query';
|
|
4
|
+
import '@gears-frontx/framework';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { c as bootstrapFrontXQueryClient, r as resolveFrontXQueryClient, u as useOptionalFrontXQueryClient } from './testing-Dlmfu6tE.js';
|
|
2
|
+
export { BootstrapMfeResolveArgs, BootstrapMfeTestSpecOptions, describeBootstrapMfeContract } from '@gears-frontx/framework/testing';
|
|
3
|
+
import '@tanstack/react-query';
|
|
4
|
+
import '@gears-frontx/framework';
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/queryClient.tsx
|
|
2
|
+
import React, {
|
|
3
|
+
createContext,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect as useEffect2,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import {
|
|
11
|
+
QueryClientContext,
|
|
12
|
+
QueryClientProvider,
|
|
13
|
+
useInfiniteQuery,
|
|
14
|
+
useMutation,
|
|
15
|
+
useQuery,
|
|
16
|
+
useSuspenseInfiniteQuery,
|
|
17
|
+
useSuspenseQuery
|
|
18
|
+
} from "@tanstack/react-query";
|
|
19
|
+
import {
|
|
20
|
+
subscribeQueryCacheRuntimeChanged
|
|
21
|
+
} from "@gears-frontx/framework";
|
|
22
|
+
|
|
23
|
+
// src/hooks/QueryCache.ts
|
|
24
|
+
import { eventBus } from "@gears-frontx/framework";
|
|
25
|
+
|
|
26
|
+
// src/hooks/useLatest.ts
|
|
27
|
+
import { useEffect, useRef } from "react";
|
|
28
|
+
|
|
29
|
+
// src/queryClient.tsx
|
|
30
|
+
import { jsx } from "react/jsx-runtime";
|
|
31
|
+
var APP_QUERY_CLIENT_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client");
|
|
32
|
+
var APP_QUERY_CLIENT_RESOLVER_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-resolver");
|
|
33
|
+
var FrontXQueryClientContext = createContext(void 0);
|
|
34
|
+
var useCommitEffect = typeof globalThis.window === "undefined" ? useEffect2 : useLayoutEffect;
|
|
35
|
+
function resolveFrontXQueryClient(app) {
|
|
36
|
+
const clientApp = app;
|
|
37
|
+
return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();
|
|
38
|
+
}
|
|
39
|
+
function bootstrapFrontXQueryClient(app) {
|
|
40
|
+
return resolveFrontXQueryClient(app);
|
|
41
|
+
}
|
|
42
|
+
function useOptionalFrontXQueryClient() {
|
|
43
|
+
return useContext(FrontXQueryClientContext);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/testing.ts
|
|
47
|
+
import { describeBootstrapMfeContract } from "@gears-frontx/framework/testing";
|
|
48
|
+
export {
|
|
49
|
+
bootstrapFrontXQueryClient,
|
|
50
|
+
describeBootstrapMfeContract,
|
|
51
|
+
resolveFrontXQueryClient,
|
|
52
|
+
useOptionalFrontXQueryClient
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queryClient.tsx","../src/hooks/QueryCache.ts","../src/hooks/useLatest.ts","../src/testing.ts"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n QueryClientContext,\n QueryClientProvider,\n type QueryClient,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseInfiniteQuery,\n useSuspenseQuery,\n type MutationFunctionContext,\n} from '@tanstack/react-query';\nimport {\n subscribeQueryCacheRuntimeChanged,\n type EndpointDescriptor,\n type FrontXApp,\n} from '@gears-frontx/framework';\nimport type { ApiQueryOverrides } from './hooks/useApiQuery';\nimport type {\n ApiInfiniteQueryOptions,\n ApiInfiniteQueryPageContext,\n} from './hooks/useApiInfiniteQuery';\nimport type { UseApiMutationOptions } from './hooks/useApiMutation';\nimport { createQueryCache, type MutationCallbackContext, type QueryCacheKey } from './hooks/QueryCache';\nimport { useLatest } from './hooks/useLatest';\n\n// @cpt-FEATURE:implement-endpoint-descriptors:p3\n// @cpt-dod:cpt-frontx-dod-request-lifecycle-query-provider:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-query:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-react-query-client-symbols\nconst APP_QUERY_CLIENT_SYMBOL = Symbol.for('frontx:query-cache:app-client');\nconst APP_QUERY_CLIENT_RESOLVER_SYMBOL = Symbol.for('frontx:query-cache:app-client-resolver');\nconst APP_QUERY_CLIENT_ACTIVATOR_SYMBOL = Symbol.for('frontx:query-cache:app-client-activator');\n\ntype QueryClientApp = FrontXApp & {\n [APP_QUERY_CLIENT_SYMBOL]?: QueryClient;\n [APP_QUERY_CLIENT_RESOLVER_SYMBOL]?: () => QueryClient | undefined;\n [APP_QUERY_CLIENT_ACTIVATOR_SYMBOL]?: () => QueryClient | undefined;\n};\n\nconst FrontXQueryClientContext = createContext<QueryClient | undefined>(undefined);\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-react-query-client-symbols\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-resolve-app-query-client\ntype RuntimeAwareEndpointDescriptor<TData> = EndpointDescriptor<TData> & {\n fetch(options?: { signal?: AbortSignal; staleTime?: number }): Promise<TData>;\n};\n\ntype AppResolvedQueryClient = {\n app: FrontXApp;\n queryClient: QueryClient;\n};\n\nconst useCommitEffect = typeof globalThis.window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function resolveFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const clientApp = app as QueryClientApp;\n return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();\n}\n\nexport function hasFrontXQueryClientActivator(app: FrontXApp): boolean {\n return typeof (app as QueryClientApp)[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL] === 'function';\n}\n\nexport function activateFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const clientApp = app as QueryClientApp;\n return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_ACTIVATOR_SYMBOL]?.();\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-resolve-app-query-client\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-imperative-cache-bootstrap\n/**\n * Imperatively invalidates cache entries for `target` on the QueryClient bound to `app`.\n * Use from non-React code (e.g. MFE actions); in components prefer `useQueryCache().invalidate`.\n */\nexport async function invalidateQueryCacheForApp(\n app: FrontXApp,\n target: EndpointDescriptor<unknown> | QueryCacheKey\n): Promise<void> {\n const queryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);\n if (!queryClient) {\n throw new Error(\n '[invalidateQueryCacheForApp] No QueryClient on app. Ensure queryCache() or queryCacheShared() is in the plugin chain.'\n );\n }\n await createQueryCache(queryClient).invalidate(target);\n}\n\n/** Resolves the app-bound QueryClient without triggering plugin activation during render. */\nexport function bootstrapFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n return resolveFrontXQueryClient(app);\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-imperative-cache-bootstrap\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-use-bootstrapped-query-client\nexport function useBootstrappedFrontXQueryClient(app: FrontXApp): QueryClient | undefined {\n const fromApp = useMemo(() => bootstrapFrontXQueryClient(app), [app]);\n const [eventResolvedClient, setEventResolvedClient] = useState<AppResolvedQueryClient | undefined>(\n undefined\n );\n const queryClient =\n fromApp ?? (eventResolvedClient?.app === app ? eventResolvedClient.queryClient : undefined);\n\n useCommitEffect(() => {\n if (fromApp || !hasFrontXQueryClientActivator(app)) {\n return;\n }\n\n const tryResolveQueryClient = () => {\n const nextQueryClient = resolveFrontXQueryClient(app) ?? activateFrontXQueryClient(app);\n if (nextQueryClient) {\n setEventResolvedClient((current) => {\n if (current?.app === app && current.queryClient === nextQueryClient) {\n return current;\n }\n\n return {\n app,\n queryClient: nextQueryClient,\n };\n });\n }\n };\n\n // Only activate after commit so abandoned renders cannot leak retainers/listeners.\n tryResolveQueryClient();\n\n const subscription = subscribeQueryCacheRuntimeChanged(() => {\n tryResolveQueryClient();\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [app, fromApp]);\n\n return queryClient;\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-query-client-lifecycle:p2:inst-use-bootstrapped-query-client\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-frontx-query-client-provider\nexport function FrontXQueryClientProvider({\n queryClient,\n children,\n}: Readonly<{\n queryClient: QueryClient | undefined;\n children: React.ReactNode;\n}>) {\n if (!queryClient) {\n return (\n <FrontXQueryClientContext.Provider value={undefined}>\n <QueryClientContext.Provider value={undefined}>{children}</QueryClientContext.Provider>\n </FrontXQueryClientContext.Provider>\n );\n }\n\n return (\n <FrontXQueryClientContext.Provider value={queryClient}>\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n </FrontXQueryClientContext.Provider>\n );\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-frontx-query-client-provider\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-query-client-context-hooks\nexport function useOptionalFrontXQueryClient(): QueryClient | undefined {\n return useContext(FrontXQueryClientContext);\n}\n\nexport function useRequiredFrontXQueryClient(): QueryClient {\n const queryClient = useOptionalFrontXQueryClient();\n if (!queryClient) {\n throw new Error(\n '[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition.'\n );\n }\n\n return queryClient;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-query-provider:p2:inst-query-client-context-hooks\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-descriptor-fetch-stale-helpers\nfunction buildPageContext<TPage>(\n page: TPage,\n pages: readonly TPage[],\n descriptor: EndpointDescriptor<TPage>,\n descriptors: readonly EndpointDescriptor<TPage>[]\n): ApiInfiniteQueryPageContext<TPage> {\n return {\n page,\n pages,\n descriptor,\n descriptors,\n };\n}\n\nfunction normalizeFetchStaleTime(staleTime: unknown): number | undefined {\n if (staleTime === 'static') {\n return Number.POSITIVE_INFINITY;\n }\n\n return typeof staleTime === 'number' ? staleTime : undefined;\n}\n\nfunction resolveFetchStaleTime<TData>(\n queryClient: QueryClient,\n descriptor: EndpointDescriptor<TData>,\n overrideStaleTime?: number\n): number | undefined {\n return normalizeFetchStaleTime(\n overrideStaleTime ??\n descriptor.staleTime ??\n queryClient.getDefaultOptions().queries?.staleTime\n );\n}\n\nfunction fetchDescriptor<TData>(\n descriptor: EndpointDescriptor<TData>,\n options?: { signal?: AbortSignal; staleTime?: number }\n): Promise<TData> {\n return (descriptor as RuntimeAwareEndpointDescriptor<TData>).fetch(options);\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-descriptor-fetch-stale-helpers\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-query\nexport function useFrontXQuery<TData = unknown, TError = Error>(\n descriptor: EndpointDescriptor<TData>,\n overrides?: ApiQueryOverrides\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);\n const result = useQuery<TData, TError>({\n queryKey: descriptor.key as unknown[],\n queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),\n staleTime: overrides?.staleTime ?? descriptor.staleTime,\n gcTime: overrides?.gcTime ?? descriptor.gcTime,\n });\n\n return {\n data: result.data,\n error: result.error,\n isLoading: result.isLoading,\n isFetching: result.isFetching,\n isError: result.isError,\n refetch: result.refetch,\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-query\nexport function useFrontXSuspenseQuery<TData = unknown, TError = Error>(\n descriptor: EndpointDescriptor<TData>,\n overrides?: ApiQueryOverrides\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const staleTime = resolveFetchStaleTime(queryClient, descriptor, overrides?.staleTime);\n const result = useSuspenseQuery<TData, TError>({\n queryKey: descriptor.key as unknown[],\n queryFn: ({ signal }) => fetchDescriptor(descriptor, { signal, staleTime }),\n staleTime: overrides?.staleTime ?? descriptor.staleTime,\n gcTime: overrides?.gcTime ?? descriptor.gcTime,\n });\n\n return {\n data: result.data,\n error: result.error,\n isFetching: result.isFetching,\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-infinite-query\nexport function useFrontXInfiniteQuery<TPage = unknown, TError = Error>(\n options: ApiInfiniteQueryOptions<TPage>\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const result = useInfiniteQuery<\n TPage,\n TError,\n readonly TPage[],\n readonly unknown[],\n EndpointDescriptor<TPage>\n >({\n queryKey: options.initialPage.key,\n initialPageParam: options.initialPage,\n queryFn: ({ pageParam, signal }) =>\n fetchDescriptor(pageParam, {\n signal,\n staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime),\n }),\n getNextPageParam: (\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n ) =>\n options.getNextPage(\n buildPageContext(\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n )\n ),\n getPreviousPageParam: options.getPreviousPage\n ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) =>\n options.getPreviousPage?.(\n buildPageContext(\n firstPage,\n allPages,\n firstPageDescriptor,\n allPageDescriptors\n )\n )\n : undefined,\n select: (data) => data.pages,\n staleTime: options.staleTime ?? options.initialPage.staleTime,\n gcTime: options.gcTime ?? options.initialPage.gcTime,\n maxPages: options.maxPages,\n });\n\n return {\n data: result.data,\n error: result.error,\n isLoading: result.isLoading,\n isFetching: result.isFetching,\n isError: result.isError,\n hasNextPage: result.hasNextPage ?? false,\n hasPreviousPage: result.hasPreviousPage ?? false,\n isFetchingNextPage: result.isFetchingNextPage,\n isFetchingPreviousPage: result.isFetchingPreviousPage,\n fetchNextPage: async () => {\n await result.fetchNextPage();\n },\n fetchPreviousPage: async () => {\n await result.fetchPreviousPage();\n },\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-infinite-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-infinite-query\nexport function useFrontXSuspenseInfiniteQuery<TPage = unknown, TError = Error>(\n options: ApiInfiniteQueryOptions<TPage>\n) {\n const queryClient = useRequiredFrontXQueryClient();\n const result = useSuspenseInfiniteQuery<\n TPage,\n TError,\n readonly TPage[],\n readonly unknown[],\n EndpointDescriptor<TPage>\n >({\n queryKey: options.initialPage.key,\n initialPageParam: options.initialPage,\n queryFn: ({ pageParam, signal }) =>\n fetchDescriptor(pageParam, {\n signal,\n staleTime: resolveFetchStaleTime(queryClient, pageParam, options.staleTime),\n }),\n getNextPageParam: (\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n ) =>\n options.getNextPage(\n buildPageContext(\n lastPage,\n allPages,\n lastPageDescriptor,\n allPageDescriptors\n )\n ),\n getPreviousPageParam: options.getPreviousPage\n ? (firstPage, allPages, firstPageDescriptor, allPageDescriptors) =>\n options.getPreviousPage?.(\n buildPageContext(\n firstPage,\n allPages,\n firstPageDescriptor,\n allPageDescriptors\n )\n )\n : undefined,\n select: (data) => data.pages,\n staleTime: options.staleTime ?? options.initialPage.staleTime,\n gcTime: options.gcTime ?? options.initialPage.gcTime,\n maxPages: options.maxPages,\n });\n\n return {\n data: result.data,\n error: result.error,\n isFetching: result.isFetching,\n hasNextPage: result.hasNextPage ?? false,\n hasPreviousPage: result.hasPreviousPage ?? false,\n isFetchingNextPage: result.isFetchingNextPage,\n isFetchingPreviousPage: result.isFetchingPreviousPage,\n fetchNextPage: async () => {\n await result.fetchNextPage();\n },\n fetchPreviousPage: async () => {\n await result.fetchPreviousPage();\n },\n refetch: async () => {\n await result.refetch();\n },\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-query:p2:inst-delegate-frontx-use-suspense-infinite-query\n\n// @cpt-begin:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-delegate-frontx-use-mutation\nexport function useFrontXMutation<\n TData = unknown,\n TError = Error,\n TVariables = void,\n TContext = unknown,\n>(\n options: UseApiMutationOptions<TData, TError, TVariables, TContext>,\n callbackCtx: MutationCallbackContext\n) {\n useRequiredFrontXQueryClient();\n\n const latestRef = useLatest({ options, callbackCtx });\n const fetchAbortControllersRef = React.useRef<Set<AbortController>>(new Set());\n\n // Effect event: reads abortOnUnmount from the most recent render, not\n // whatever option value was current when the unmount effect was set up.\n const abortFetchesOnUnmount = React.useEffectEvent(() => {\n if (options.abortOnUnmount) {\n for (const controller of fetchAbortControllersRef.current) {\n controller.abort();\n }\n fetchAbortControllersRef.current.clear();\n }\n });\n\n React.useEffect(() => {\n return () => {\n abortFetchesOnUnmount();\n };\n }, []);\n\n const mutationFn = React.useCallback((variables: TVariables, context: MutationFunctionContext) => {\n const cancelOnSupersede = latestRef.current.options.cancelOnSupersede;\n if (cancelOnSupersede) {\n for (const controller of fetchAbortControllersRef.current) {\n controller.abort();\n }\n fetchAbortControllersRef.current.clear();\n }\n\n const controller = new AbortController();\n fetchAbortControllersRef.current.add(controller);\n\n const librarySignal = (context as MutationFunctionContext & { signal?: AbortSignal }).signal;\n if (cancelOnSupersede && librarySignal) {\n if (librarySignal.aborted) {\n controller.abort();\n } else {\n librarySignal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n }\n\n return latestRef.current.options.endpoint\n .fetch(variables, { signal: controller.signal })\n .finally(() => {\n fetchAbortControllersRef.current.delete(controller);\n });\n // latestRef's identity never changes (useLatest returns a stable ref\n // object) — listing it satisfies exhaustive-deps without recreating this\n // callback on every render.\n }, [latestRef]);\n\n const onMutate = React.useCallback((variables: TVariables, _context: MutationFunctionContext) => {\n const current = latestRef.current;\n if (!current.options.onMutate) {\n return undefined as TContext;\n }\n\n return current.options.onMutate(variables, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onSuccess = React.useCallback((data: TData, variables: TVariables, context: TContext | undefined) => {\n const current = latestRef.current;\n return current.options.onSuccess?.(data, variables, context, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onError = React.useCallback((error: TError, variables: TVariables, context: TContext | undefined) => {\n const current = latestRef.current;\n return current.options.onError?.(error, variables, context, current.callbackCtx);\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const onSettled = React.useCallback((\n data: TData | undefined,\n error: TError | null,\n variables: TVariables,\n context: TContext | undefined\n ) => {\n const current = latestRef.current;\n return current.options.onSettled?.(\n data,\n error,\n variables,\n context,\n current.callbackCtx\n );\n // latestRef's identity never changes — see mutationFn above.\n }, [latestRef]);\n\n const mutation = useMutation<TData, TError, TVariables, TContext>({\n mutationFn,\n onMutate: options.onMutate ? onMutate : undefined,\n onSuccess: options.onSuccess ? onSuccess : undefined,\n onError: options.onError ? onError : undefined,\n onSettled: options.onSettled ? onSettled : undefined,\n });\n\n return {\n mutate: mutation.mutate as (variables: TVariables) => void,\n mutateAsync: mutation.mutateAsync as (variables: TVariables) => Promise<TData>,\n isPending: mutation.isPending,\n error: mutation.error,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n// @cpt-end:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-delegate-frontx-use-mutation\n","/**\n * QueryCache — restricted interface for interacting with the shared QueryClient.\n *\n * MFEs and screen-set components access the cache only through this sanctioned\n * public interface. It is injected as { queryCache } into useApiMutation\n * callbacks and also returned by useQueryCache() for controlled imperative\n * cache access.\n */\n// @cpt-dod:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2\n// @cpt-flow:cpt-frontx-flow-request-lifecycle-use-api-mutation:p2:inst-create-query-cache\n// @cpt-FEATURE:implement-endpoint-descriptors:p3\n\nimport type { EndpointDescriptor } from '@gears-frontx/framework';\nimport { eventBus } from '@gears-frontx/framework';\nimport { type QueryClient, type QueryState } from '@tanstack/react-query';\n\nexport type QueryCacheKey = readonly unknown[];\nexport type QueryCacheRefetchMode = 'active' | 'inactive' | 'all' | 'none';\nexport type QueryCacheInvalidateTarget =\n | EndpointDescriptor<unknown>\n | QueryCacheKey\n | null\n | undefined;\n\nexport interface QueryCacheState<TData = unknown, TError = Error> {\n data: TData | undefined;\n dataUpdatedAt: number;\n error: TError | null;\n errorUpdatedAt: number;\n fetchFailureCount: number;\n fetchFailureReason: TError | null;\n fetchStatus: 'fetching' | 'paused' | 'idle';\n isInvalidated: boolean;\n status: 'pending' | 'error' | 'success';\n}\n\nexport type QueryCacheInvalidateFilters = {\n queryKey?: QueryCacheInvalidateTarget;\n exact?: boolean;\n refetchType?: QueryCacheRefetchMode;\n};\n\ntype QueryCacheUpdater<T> = T | ((old: T | undefined) => T | undefined);\n\ntype CacheInvalidateEvent = {\n queryKey: QueryCacheKey;\n exact?: boolean;\n refetchType?: QueryCacheRefetchMode;\n source?: string;\n};\n\ntype CacheSetEvent = {\n queryKey: QueryCacheKey;\n dataOrUpdater: QueryCacheUpdater<unknown>;\n source?: string;\n};\n\ntype CacheRemoveEvent = {\n queryKey: QueryCacheKey;\n source?: string;\n};\n\nconst QUERY_CLIENT_BROADCAST_TARGET = Symbol.for('frontx:query-cache:broadcast-target');\nconst QUERY_CLIENT_BROADCAST_COUNTER = Symbol.for('frontx:query-cache:broadcast-counter');\n\ntype QueryClientWithMetadata = QueryClient & {\n [QUERY_CLIENT_BROADCAST_TARGET]?: string;\n};\n\ntype QueryClientCounterHost = typeof globalThis & {\n [QUERY_CLIENT_BROADCAST_COUNTER]?: number;\n};\n\nfunction emitCacheEvent(event: string, payload: unknown): void {\n (eventBus.emit as (eventType: string, payload: unknown) => void)(event, payload);\n}\n\nfunction ensureBroadcastSource(queryClient: QueryClient): string {\n const clientWithMetadata = queryClient as QueryClientWithMetadata;\n const existingSource = clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET];\n if (typeof existingSource === 'string' && existingSource.length > 0) {\n return existingSource;\n }\n\n const host = globalThis as QueryClientCounterHost;\n const nextCounter = (host[QUERY_CLIENT_BROADCAST_COUNTER] ?? 0) + 1;\n host[QUERY_CLIENT_BROADCAST_COUNTER] = nextCounter;\n\n const source = `query-cache-${nextCounter}`;\n clientWithMetadata[QUERY_CLIENT_BROADCAST_TARGET] = source;\n return source;\n}\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-resolve-key\nexport function resolveKey(target: EndpointDescriptor<unknown> | QueryCacheKey): readonly unknown[] {\n if (\n target !== null &&\n typeof target === 'object' &&\n !Array.isArray(target) &&\n 'key' in target &&\n 'fetch' in target &&\n Array.isArray(target.key) &&\n typeof target.fetch === 'function'\n ) {\n return target.key;\n }\n\n return target as readonly unknown[];\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-resolve-key\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-query-cache-interface\nexport interface QueryCache {\n get<T>(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined;\n getState<TData = unknown, TError = Error>(\n queryKey: EndpointDescriptor<unknown> | QueryCacheKey\n ): QueryCacheState<TData, TError> | undefined;\n set<T>(\n queryKey: EndpointDescriptor<unknown> | QueryCacheKey,\n dataOrUpdater: T | ((old: T | undefined) => T | undefined)\n ): void;\n cancel(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;\n invalidate(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void>;\n invalidateMany(filters?: QueryCacheInvalidateFilters | null): Promise<void>;\n remove(queryKey: EndpointDescriptor<unknown> | QueryCacheKey): void;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-query-cache-interface\n\n// @cpt-begin:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-mutation-callback-context\nexport interface MutationCallbackContext {\n queryCache: QueryCache;\n}\n// @cpt-end:cpt-frontx-dod-request-lifecycle-use-api-mutation:p2:inst-mutation-callback-context\n\nfunction toQueryUpdater<T>(\n dataOrUpdater: QueryCacheUpdater<T>\n): (old: T | undefined) => T | undefined {\n if (typeof dataOrUpdater === 'function') {\n return dataOrUpdater as (old: T | undefined) => T | undefined;\n }\n\n return () => dataOrUpdater;\n}\n\nfunction mapQueryState<TData, TError = Error>(\n state: QueryState<TData, TError>\n): QueryCacheState<TData, TError> {\n return {\n data: state.data,\n dataUpdatedAt: state.dataUpdatedAt,\n error: state.error,\n errorUpdatedAt: state.errorUpdatedAt,\n fetchFailureCount: state.fetchFailureCount,\n fetchFailureReason: state.fetchFailureReason,\n fetchStatus: state.fetchStatus,\n isInvalidated: state.isInvalidated,\n status: state.status,\n };\n}\n\n// @cpt-begin:implement-endpoint-descriptors:p3:inst-create-query-cache\nexport function createQueryCache(queryClient: QueryClient): QueryCache {\n return {\n get: <T,>(key: EndpointDescriptor<unknown> | QueryCacheKey): T | undefined => {\n return queryClient.getQueryData<T>(resolveKey(key));\n },\n getState: <TData = unknown, TError = Error>(key: EndpointDescriptor<unknown> | QueryCacheKey) => {\n const state = queryClient.getQueryState<TData, TError>(resolveKey(key));\n return state ? mapQueryState(state) : undefined;\n },\n set: <T,>(key: EndpointDescriptor<unknown> | QueryCacheKey, dataOrUpdater: QueryCacheUpdater<T>): void => {\n const resolvedKey = resolveKey(key);\n queryClient.setQueryData<T>(resolvedKey, toQueryUpdater(dataOrUpdater));\n emitCacheEvent('cache/set', {\n queryKey: resolvedKey,\n dataOrUpdater: dataOrUpdater as QueryCacheUpdater<unknown>,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheSetEvent);\n },\n cancel: (key: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void> => {\n return queryClient.cancelQueries({ queryKey: resolveKey(key) });\n },\n invalidate: (key: EndpointDescriptor<unknown> | QueryCacheKey): Promise<void> => {\n const resolvedKey = resolveKey(key);\n const result = queryClient.invalidateQueries({ queryKey: resolvedKey });\n emitCacheEvent('cache/invalidate', {\n queryKey: resolvedKey,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheInvalidateEvent);\n return result;\n },\n invalidateMany: (filters): Promise<void> => {\n if (filters?.queryKey == null) {\n return Promise.resolve();\n }\n\n const resolvedKey = resolveKey(filters.queryKey);\n if (!Array.isArray(resolvedKey) || resolvedKey.length === 0) {\n return Promise.resolve();\n }\n\n const payload = {\n ...filters,\n queryKey: resolvedKey,\n };\n const result = queryClient.invalidateQueries(payload);\n emitCacheEvent('cache/invalidate', {\n ...payload,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheInvalidateEvent);\n return result;\n },\n remove: (key: EndpointDescriptor<unknown> | QueryCacheKey): void => {\n const resolvedKey = resolveKey(key);\n queryClient.removeQueries({ queryKey: resolvedKey });\n emitCacheEvent('cache/remove', {\n queryKey: resolvedKey,\n source: ensureBroadcastSource(queryClient),\n } satisfies CacheRemoveEvent);\n },\n };\n}\n// @cpt-end:implement-endpoint-descriptors:p3:inst-create-query-cache\n","/**\n * useLatest - keeps a ref pinned to the most recently rendered value\n *\n * A handful of hooks/components in this package need to read the current\n * value of a prop or option from inside a callback or effect-cleanup closure\n * that must NOT re-run every time that value changes (e.g. a stable\n * useCallback with `[]` deps, or a mount/unmount effect keyed on identity\n * rather than on every option). Stashing the value in a ref sidesteps the\n * closure staleness without pulling the value into the dependency array.\n *\n * The write happens inside a deps-less effect (runs after every commit)\n * rather than during render, because refs must not be mutated while\n * rendering: render can be invoked more than once per commit (React\n * StrictMode's double-invoke, and future concurrent-rendering paths that\n * may discard a render), so a render-time write can leave the ref out of\n * sync with what was actually committed to the DOM. A deps-less effect\n * runs exactly once per real commit, so the ref always reflects the value\n * that was last rendered.\n */\n\nimport { useEffect, useRef, type RefObject } from 'react';\n\n/**\n * Returns a ref that always holds the most recently rendered `value`.\n *\n * The ref identity is stable across renders (same object returned every\n * time), so it is safe to add to a `useCallback`/`useEffect` dependency\n * array without causing the callback/effect to be recreated.\n */\nexport function useLatest<T>(value: T): RefObject<T> {\n const ref = useRef(value);\n\n // Deps-less: must run after every render so `ref.current` never lags\n // behind the value that was just committed. See module doc for why this\n // write cannot happen during render itself.\n useEffect(() => {\n ref.current = value;\n });\n\n return ref;\n}\n","/**\n * Test-only entry for @gears-frontx/react.\n *\n * Utilities here are intended for Vitest (or similar) suites that need access\n * to query-client wiring without importing package internals directly.\n */\n\n// @cpt-dod:cpt-frontx-dod-react-bindings-provider:p1\n\n// @cpt-begin:cpt-frontx-dod-react-bindings-provider:p1:inst-react-testing-reexports\nexport {\n bootstrapFrontXQueryClient,\n resolveFrontXQueryClient,\n useOptionalFrontXQueryClient,\n} from './queryClient';\n\n/** App-layer tests must import this from `@gears-frontx/react/testing`, not `@gears-frontx/framework/testing`. */\nexport { describeBootstrapMfeContract } from '@gears-frontx/framework/testing';\nexport type {\n BootstrapMfeResolveArgs,\n BootstrapMfeTestSpecOptions,\n} from '@gears-frontx/framework/testing';\n// @cpt-end:cpt-frontx-dod-react-bindings-provider:p1:inst-react-testing-reexports\n"],"mappings":";AAAA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA,aAAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAGK;;;ACVP,SAAS,gBAAgB;;;ACOzB,SAAS,WAAW,cAA8B;;;AF6I1C;AAzHR,IAAM,0BAA0B,uBAAO,IAAI,+BAA+B;AAC1E,IAAM,mCAAmC,uBAAO,IAAI,wCAAwC;AAS5F,IAAM,2BAA2B,cAAuC,MAAS;AAajF,IAAM,kBAAkB,OAAO,WAAW,WAAW,cAAcC,aAAY;AAExE,SAAS,yBAAyB,KAAyC;AAChF,QAAM,YAAY;AAClB,SAAO,UAAU,uBAAuB,KAAK,UAAU,gCAAgC,IAAI;AAC7F;AA+BO,SAAS,2BAA2B,KAAyC;AAClF,SAAO,yBAAyB,GAAG;AACrC;AA0EO,SAAS,+BAAwD;AACtE,SAAO,WAAW,wBAAwB;AAC5C;;;AGhKA,SAAS,oCAAoC;","names":["useEffect","useEffect"]}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ChildMfeBridge, AccessRecord, AccessQuery, FrontXConfig, FrontXApp, RootState, Formatters, Language } from '@gears-frontx/framework';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MFE Context Value
|
|
7
|
+
* Contains bridge and metadata about the MFE instance.
|
|
8
|
+
*/
|
|
9
|
+
interface MfeContextValue {
|
|
10
|
+
/** Child bridge for communication with host */
|
|
11
|
+
bridge: ChildMfeBridge;
|
|
12
|
+
/** Extension ID */
|
|
13
|
+
extensionId: string;
|
|
14
|
+
/** Domain ID where MFE is mounted */
|
|
15
|
+
domainId: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* MFE Context
|
|
19
|
+
* Holds the MFE bridge and metadata for child components.
|
|
20
|
+
*/
|
|
21
|
+
declare const MfeContext: React.Context<MfeContextValue | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Use the MFE context.
|
|
24
|
+
* Throws if used outside of MFE context.
|
|
25
|
+
*
|
|
26
|
+
* @returns The MFE context value
|
|
27
|
+
*/
|
|
28
|
+
declare function useMfeContext(): MfeContextValue;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @gears-frontx/react - Type Definitions
|
|
32
|
+
*
|
|
33
|
+
* Core types for FrontX React bindings.
|
|
34
|
+
* Provides type-safe hooks and components.
|
|
35
|
+
*
|
|
36
|
+
* Now using real imports from @gears-frontx/framework since packages are built together.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gears FrontX-owned query result type.
|
|
41
|
+
* Returned by useApiQuery — callers depend on this contract, not on TanStack internals.
|
|
42
|
+
*/
|
|
43
|
+
interface ApiQueryResult<TData, TError = Error> {
|
|
44
|
+
data: TData | undefined;
|
|
45
|
+
error: TError | null;
|
|
46
|
+
isLoading: boolean;
|
|
47
|
+
isFetching: boolean;
|
|
48
|
+
isError: boolean;
|
|
49
|
+
refetch: () => Promise<unknown>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gears FrontX-owned suspense query result type.
|
|
53
|
+
* Returned by useApiSuspenseQuery — callers depend on this contract, not on TanStack internals.
|
|
54
|
+
*/
|
|
55
|
+
interface ApiSuspenseQueryResult<TData, TError = Error> {
|
|
56
|
+
data: TData;
|
|
57
|
+
error: TError | null;
|
|
58
|
+
isFetching: boolean;
|
|
59
|
+
refetch: () => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Gears FrontX-owned infinite query result type.
|
|
63
|
+
* Returned by useApiInfiniteQuery for descriptor-driven pagination.
|
|
64
|
+
*/
|
|
65
|
+
interface ApiInfiniteQueryResult<TPage, TError = Error> {
|
|
66
|
+
data: readonly TPage[] | undefined;
|
|
67
|
+
error: TError | null;
|
|
68
|
+
isLoading: boolean;
|
|
69
|
+
isFetching: boolean;
|
|
70
|
+
isError: boolean;
|
|
71
|
+
hasNextPage: boolean;
|
|
72
|
+
hasPreviousPage: boolean;
|
|
73
|
+
isFetchingNextPage: boolean;
|
|
74
|
+
isFetchingPreviousPage: boolean;
|
|
75
|
+
fetchNextPage: () => Promise<void>;
|
|
76
|
+
fetchPreviousPage: () => Promise<void>;
|
|
77
|
+
refetch: () => Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Gears FrontX-owned suspense infinite query result type.
|
|
81
|
+
* Returned by useApiSuspenseInfiniteQuery for descriptor-driven pagination.
|
|
82
|
+
*/
|
|
83
|
+
interface ApiSuspenseInfiniteQueryResult<TPage, TError = Error> {
|
|
84
|
+
data: readonly TPage[];
|
|
85
|
+
error: TError | null;
|
|
86
|
+
isFetching: boolean;
|
|
87
|
+
hasNextPage: boolean;
|
|
88
|
+
hasPreviousPage: boolean;
|
|
89
|
+
isFetchingNextPage: boolean;
|
|
90
|
+
isFetchingPreviousPage: boolean;
|
|
91
|
+
fetchNextPage: () => Promise<void>;
|
|
92
|
+
fetchPreviousPage: () => Promise<void>;
|
|
93
|
+
refetch: () => Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Gears FrontX-owned mutation result type.
|
|
97
|
+
* Returned by useApiMutation — callers depend on this contract, not on TanStack internals.
|
|
98
|
+
*/
|
|
99
|
+
interface ApiMutationResult<TData, TError = Error, TVariables = void> {
|
|
100
|
+
mutate: (variables: TVariables) => void;
|
|
101
|
+
mutateAsync: (variables: TVariables) => Promise<TData>;
|
|
102
|
+
isPending: boolean;
|
|
103
|
+
error: TError | null;
|
|
104
|
+
data: TData | undefined;
|
|
105
|
+
reset: () => void;
|
|
106
|
+
}
|
|
107
|
+
type Selector<TResult, TState = RootState> = (state: TState) => TResult;
|
|
108
|
+
type TranslationParams = Record<string, string | number | boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* FrontX Provider Props
|
|
111
|
+
* Props for the main FrontXProvider component.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* <FrontXProvider config={{ devMode: true }}>
|
|
116
|
+
* <App />
|
|
117
|
+
* </FrontXProvider>
|
|
118
|
+
*
|
|
119
|
+
* // With pre-built app
|
|
120
|
+
* const app = createFrontX().use(microfrontends()).build();
|
|
121
|
+
* <FrontXProvider app={app}>
|
|
122
|
+
* <App />
|
|
123
|
+
* </FrontXProvider>
|
|
124
|
+
*
|
|
125
|
+
* // With MFE bridge (for MFE components)
|
|
126
|
+
* <FrontXProvider mfeBridge={{ bridge, extensionId, domainId }}>
|
|
127
|
+
* <MyMfeApp />
|
|
128
|
+
* </FrontXProvider>
|
|
129
|
+
*
|
|
130
|
+
* // QueryCache is resolved from the app's plugin composition
|
|
131
|
+
* <FrontXProvider app={app}>
|
|
132
|
+
* <MyMfeApp />
|
|
133
|
+
* </FrontXProvider>
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
interface FrontXProviderProps {
|
|
137
|
+
/** Child components */
|
|
138
|
+
children: ReactNode;
|
|
139
|
+
/** FrontX configuration */
|
|
140
|
+
config?: FrontXConfig;
|
|
141
|
+
/** Pre-built FrontX app instance (optional) */
|
|
142
|
+
app?: FrontXApp;
|
|
143
|
+
/** MFE bridge context (for MFE components) */
|
|
144
|
+
mfeBridge?: MfeContextValue;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* useFrontX Hook Return Type
|
|
148
|
+
* Returns the FrontX app instance from context.
|
|
149
|
+
*/
|
|
150
|
+
type UseFrontXReturn = FrontXApp;
|
|
151
|
+
/**
|
|
152
|
+
* useAppSelector Hook
|
|
153
|
+
* Type-safe selector hook for Redux state.
|
|
154
|
+
*
|
|
155
|
+
* @template TResult - The result type of the selector
|
|
156
|
+
*/
|
|
157
|
+
type UseAppSelector = <TResult>(selector: Selector<TResult>) => TResult;
|
|
158
|
+
/**
|
|
159
|
+
* useAppDispatch Hook Return Type
|
|
160
|
+
* Returns the typed dispatch function.
|
|
161
|
+
*/
|
|
162
|
+
type UseAppDispatchReturn = (action: unknown) => unknown;
|
|
163
|
+
/**
|
|
164
|
+
* useTranslation Hook Return Type
|
|
165
|
+
* Translation utilities.
|
|
166
|
+
*/
|
|
167
|
+
interface UseTranslationReturn {
|
|
168
|
+
/** Translate a key */
|
|
169
|
+
t: (key: string, params?: TranslationParams) => string;
|
|
170
|
+
/** Current language */
|
|
171
|
+
language: Language | null;
|
|
172
|
+
/** Change language */
|
|
173
|
+
setLanguage: (language: Language) => void;
|
|
174
|
+
/** Check if current language is RTL */
|
|
175
|
+
isRTL: boolean;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* useScreenTranslations Hook Return Type
|
|
179
|
+
* Screen-level translation loading state.
|
|
180
|
+
*/
|
|
181
|
+
interface UseScreenTranslationsReturn {
|
|
182
|
+
/** Whether translations are loaded */
|
|
183
|
+
isLoaded: boolean;
|
|
184
|
+
/** Loading error (if any) */
|
|
185
|
+
error: Error | null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* useTheme Hook Return Type
|
|
189
|
+
* Theme utilities.
|
|
190
|
+
*/
|
|
191
|
+
interface UseThemeReturn {
|
|
192
|
+
/** Current theme ID */
|
|
193
|
+
currentTheme: string | undefined;
|
|
194
|
+
/** All available themes */
|
|
195
|
+
themes: Array<{
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
}>;
|
|
199
|
+
/** Change theme */
|
|
200
|
+
setTheme: (themeId: string) => void;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* useFormatters Hook Return Type
|
|
204
|
+
* Locale-aware formatters (locale from i18nRegistry.getLanguage()).
|
|
205
|
+
* References @gears-frontx/i18n Formatters so signatures stay in sync.
|
|
206
|
+
*/
|
|
207
|
+
type UseFormattersReturn = Formatters;
|
|
208
|
+
/**
|
|
209
|
+
* useCanAccess Hook Return Type
|
|
210
|
+
* Result of an RBAC access check.
|
|
211
|
+
* allow is false until an explicit 'allow' decision arrives (pessimistic default).
|
|
212
|
+
*/
|
|
213
|
+
interface UseCanAccessResult {
|
|
214
|
+
allow: boolean;
|
|
215
|
+
isResolving: boolean;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* CanAccess Component Props.
|
|
219
|
+
* Pessimistic: renders `denied` (or `loading`) until an authoritative 'allow' arrives.
|
|
220
|
+
*/
|
|
221
|
+
interface CanAccessProps<TRecord extends AccessRecord = AccessRecord> {
|
|
222
|
+
query: AccessQuery<TRecord>;
|
|
223
|
+
/** Content rendered when access is explicitly allowed. */
|
|
224
|
+
allowed: ReactNode;
|
|
225
|
+
/** Content rendered on deny or error. Defaults to null. */
|
|
226
|
+
denied?: ReactNode;
|
|
227
|
+
/** Optional slot shown while the decision is resolving. Falls back to `denied`. */
|
|
228
|
+
loading?: ReactNode;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { type ApiQueryResult as A, type CanAccessProps as C, type FrontXProviderProps as F, type MfeContextValue as M, type UseTranslationReturn as U, type ApiInfiniteQueryResult as a, type ApiMutationResult as b, type UseScreenTranslationsReturn as c, type UseFormattersReturn as d, type UseThemeReturn as e, type ApiSuspenseQueryResult as f, type ApiSuspenseInfiniteQueryResult as g, type UseCanAccessResult as h, MfeContext as i, type UseAppDispatchReturn as j, type UseAppSelector as k, type UseFrontXReturn as l, useMfeContext as u };
|