@gears-frontx/react 0.2.0-alpha.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 +30 -10
- package/dist/index.cjs +148 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -7
- package/dist/index.d.ts +64 -7
- package/dist/index.js +86 -70
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +7 -4
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +5 -2
- package/dist/testing.js.map +1 -1
- package/package.json +3 -4
- package/commands/frontx-duplicate-screenset.md +0 -58
- package/commands/frontx-new-component.md +0 -57
- package/commands/frontx-new-mfe.md +0 -153
- package/commands/frontx-new-screen.md +0 -45
- package/commands/frontx-new-screenset.md +0 -57
package/dist/index.d.cts
CHANGED
|
@@ -366,10 +366,9 @@ declare function useApiSuspenseInfiniteQuery<TPage = unknown, TError = Error>(op
|
|
|
366
366
|
/**
|
|
367
367
|
* useApiStream - Declarative SSE streaming hook
|
|
368
368
|
*
|
|
369
|
-
* Accepts a StreamDescriptor from @gears-frontx/api and
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* a manual disconnect function.
|
|
369
|
+
* Accepts a StreamDescriptor from @gears-frontx/api and returns the latest
|
|
370
|
+
* event, accumulated events, connection status, and a manual disconnect
|
|
371
|
+
* function.
|
|
373
372
|
*
|
|
374
373
|
* @example
|
|
375
374
|
* ```tsx
|
|
@@ -395,17 +394,75 @@ interface ApiStreamOptions {
|
|
|
395
394
|
}
|
|
396
395
|
/** Return type of useApiStream. */
|
|
397
396
|
interface ApiStreamResult<TEvent> {
|
|
398
|
-
/** Latest event payload
|
|
397
|
+
/** Latest event payload, set in both modes — `undefined` until the first event and after each connect attempt resets it. */
|
|
399
398
|
data: TEvent | undefined;
|
|
400
399
|
/** All received events when `mode: 'accumulate'`; empty array in `'latest'` mode. */
|
|
401
400
|
events: TEvent[];
|
|
402
401
|
/** Connection lifecycle status. */
|
|
403
402
|
status: StreamStatus;
|
|
404
|
-
/**
|
|
403
|
+
/** Last connect error — set when a connect attempt rejects, cleared when the next attempt starts. */
|
|
405
404
|
error: Error | null;
|
|
406
405
|
/** Manually close the connection. */
|
|
407
406
|
disconnect: () => void;
|
|
408
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Manages the status lifecycle for a single SSE stream connection.
|
|
410
|
+
*
|
|
411
|
+
* **Status values**
|
|
412
|
+
* - `'idle'` — no connection is being attempted. This is the initial status
|
|
413
|
+
* before the first connect effect runs, and it is ordinarily the status
|
|
414
|
+
* while `enabled` is `false`. Consumers must not assume `'idle'` means "never
|
|
415
|
+
* connected" — a stream that is disabled after having connected returns to
|
|
416
|
+
* `'idle'` too, and `data`/`events` from the earlier connection are not
|
|
417
|
+
* cleared by that transition alone (they are cleared by mode/key changes,
|
|
418
|
+
* see below).
|
|
419
|
+
* - `'connecting'` — `descriptor.connect()` has been called and its promise
|
|
420
|
+
* has not yet settled.
|
|
421
|
+
* - `'connected'` — set both when `connect()` resolves (unless a disconnect
|
|
422
|
+
* was requested while it was pending) and, independently, whenever the
|
|
423
|
+
* `onEvent` callback delivers a new event. Because every event re-asserts
|
|
424
|
+
* `'connected'`, this status also serves as "still receiving events."
|
|
425
|
+
* - `'disconnected'` — reached via any of: the descriptor's `onComplete`
|
|
426
|
+
* callback firing (the stream ended normally), `connect()` rejecting while
|
|
427
|
+
* a disconnect was requested concurrently, or the manual `disconnect()`
|
|
428
|
+
* function being called. `disconnect()` has no `enabled` guard: called
|
|
429
|
+
* while the stream is disabled it still sets `'disconnected'`, which
|
|
430
|
+
* persists until the next connect-effect run.
|
|
431
|
+
* - `'error'` — `connect()` rejected and no disconnect was requested while it
|
|
432
|
+
* was pending. `error` holds the rejection, coerced to an `Error` if it
|
|
433
|
+
* wasn't one already.
|
|
434
|
+
*
|
|
435
|
+
* **Transitions**
|
|
436
|
+
* - Mount / `enabled` becomes `true` / `descriptor.key` or `mode` changes:
|
|
437
|
+
* `data`, `events`, and `error` are reset, then status goes to
|
|
438
|
+
* `'connecting'` and `connect()` is called.
|
|
439
|
+
* - `enabled` becomes `false` (or starts `false`): status is forced to
|
|
440
|
+
* `'idle'` and `connect()` is never called; no other field is reset by
|
|
441
|
+
* this transition alone.
|
|
442
|
+
* - Unmount, `descriptor.key` change, `mode` change, or `enabled` flipping to
|
|
443
|
+
* `false` while connected or connecting: the previous connection is torn
|
|
444
|
+
* down via `descriptor.disconnect()` once its `connect()` promise settles
|
|
445
|
+
* (or immediately skipped if it rejected). All four share the same cleanup
|
|
446
|
+
* path: the effect's cleanup runs on every dependency change and on unmount.
|
|
447
|
+
* - Calling `disconnect()`: if a connection id is already resolved, it is
|
|
448
|
+
* disconnected immediately and status becomes `'disconnected'`. If
|
|
449
|
+
* `connect()` is still pending, the request is recorded and honored when
|
|
450
|
+
* the promise settles — the newly resolved connection is torn down instead
|
|
451
|
+
* of being adopted, and status is `'disconnected'`. One caveat:
|
|
452
|
+
* `disconnect()` does not cancel the event callback, so a buffered or
|
|
453
|
+
* in-flight event landing after the call re-asserts `'connected'` — and in
|
|
454
|
+
* the pending-connect case the settle handler tears the connection down
|
|
455
|
+
* without touching status, so that stray `'connected'` persists.
|
|
456
|
+
* - Stream completion while `connect()` is still pending: the current
|
|
457
|
+
* implementation lets `onComplete` set `'disconnected'`, then the settle
|
|
458
|
+
* handler adopts the connection id and re-asserts `'connected'` anyway.
|
|
459
|
+
* This is known current behavior, not a durable guarantee: a pending
|
|
460
|
+
* upstream derived-status rewrite is expected to remove this transition.
|
|
461
|
+
*
|
|
462
|
+
* `enabled` (default `true`) is the only way to defer connecting past mount;
|
|
463
|
+
* flipping it back to `true` re-runs the same connect sequence as a fresh
|
|
464
|
+
* mount would.
|
|
465
|
+
*/
|
|
409
466
|
declare function useApiStream<TEvent>(descriptor: StreamDescriptor<TEvent>, options?: ApiStreamOptions): ApiStreamResult<TEvent>;
|
|
410
467
|
|
|
411
468
|
/**
|
|
@@ -498,7 +555,7 @@ declare const MfeProvider: React__default.FC<MfeProviderProps>;
|
|
|
498
555
|
* // bridge.executeActionsChain(chain);
|
|
499
556
|
* // bridge.subscribeToProperty(propertyTypeId, callback);
|
|
500
557
|
*
|
|
501
|
-
* return <div>Domain: {bridge.
|
|
558
|
+
* return <div>Domain: {bridge.extDomainId}</div>;
|
|
502
559
|
* }
|
|
503
560
|
* ```
|
|
504
561
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -366,10 +366,9 @@ declare function useApiSuspenseInfiniteQuery<TPage = unknown, TError = Error>(op
|
|
|
366
366
|
/**
|
|
367
367
|
* useApiStream - Declarative SSE streaming hook
|
|
368
368
|
*
|
|
369
|
-
* Accepts a StreamDescriptor from @gears-frontx/api and
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* a manual disconnect function.
|
|
369
|
+
* Accepts a StreamDescriptor from @gears-frontx/api and returns the latest
|
|
370
|
+
* event, accumulated events, connection status, and a manual disconnect
|
|
371
|
+
* function.
|
|
373
372
|
*
|
|
374
373
|
* @example
|
|
375
374
|
* ```tsx
|
|
@@ -395,17 +394,75 @@ interface ApiStreamOptions {
|
|
|
395
394
|
}
|
|
396
395
|
/** Return type of useApiStream. */
|
|
397
396
|
interface ApiStreamResult<TEvent> {
|
|
398
|
-
/** Latest event payload
|
|
397
|
+
/** Latest event payload, set in both modes — `undefined` until the first event and after each connect attempt resets it. */
|
|
399
398
|
data: TEvent | undefined;
|
|
400
399
|
/** All received events when `mode: 'accumulate'`; empty array in `'latest'` mode. */
|
|
401
400
|
events: TEvent[];
|
|
402
401
|
/** Connection lifecycle status. */
|
|
403
402
|
status: StreamStatus;
|
|
404
|
-
/**
|
|
403
|
+
/** Last connect error — set when a connect attempt rejects, cleared when the next attempt starts. */
|
|
405
404
|
error: Error | null;
|
|
406
405
|
/** Manually close the connection. */
|
|
407
406
|
disconnect: () => void;
|
|
408
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Manages the status lifecycle for a single SSE stream connection.
|
|
410
|
+
*
|
|
411
|
+
* **Status values**
|
|
412
|
+
* - `'idle'` — no connection is being attempted. This is the initial status
|
|
413
|
+
* before the first connect effect runs, and it is ordinarily the status
|
|
414
|
+
* while `enabled` is `false`. Consumers must not assume `'idle'` means "never
|
|
415
|
+
* connected" — a stream that is disabled after having connected returns to
|
|
416
|
+
* `'idle'` too, and `data`/`events` from the earlier connection are not
|
|
417
|
+
* cleared by that transition alone (they are cleared by mode/key changes,
|
|
418
|
+
* see below).
|
|
419
|
+
* - `'connecting'` — `descriptor.connect()` has been called and its promise
|
|
420
|
+
* has not yet settled.
|
|
421
|
+
* - `'connected'` — set both when `connect()` resolves (unless a disconnect
|
|
422
|
+
* was requested while it was pending) and, independently, whenever the
|
|
423
|
+
* `onEvent` callback delivers a new event. Because every event re-asserts
|
|
424
|
+
* `'connected'`, this status also serves as "still receiving events."
|
|
425
|
+
* - `'disconnected'` — reached via any of: the descriptor's `onComplete`
|
|
426
|
+
* callback firing (the stream ended normally), `connect()` rejecting while
|
|
427
|
+
* a disconnect was requested concurrently, or the manual `disconnect()`
|
|
428
|
+
* function being called. `disconnect()` has no `enabled` guard: called
|
|
429
|
+
* while the stream is disabled it still sets `'disconnected'`, which
|
|
430
|
+
* persists until the next connect-effect run.
|
|
431
|
+
* - `'error'` — `connect()` rejected and no disconnect was requested while it
|
|
432
|
+
* was pending. `error` holds the rejection, coerced to an `Error` if it
|
|
433
|
+
* wasn't one already.
|
|
434
|
+
*
|
|
435
|
+
* **Transitions**
|
|
436
|
+
* - Mount / `enabled` becomes `true` / `descriptor.key` or `mode` changes:
|
|
437
|
+
* `data`, `events`, and `error` are reset, then status goes to
|
|
438
|
+
* `'connecting'` and `connect()` is called.
|
|
439
|
+
* - `enabled` becomes `false` (or starts `false`): status is forced to
|
|
440
|
+
* `'idle'` and `connect()` is never called; no other field is reset by
|
|
441
|
+
* this transition alone.
|
|
442
|
+
* - Unmount, `descriptor.key` change, `mode` change, or `enabled` flipping to
|
|
443
|
+
* `false` while connected or connecting: the previous connection is torn
|
|
444
|
+
* down via `descriptor.disconnect()` once its `connect()` promise settles
|
|
445
|
+
* (or immediately skipped if it rejected). All four share the same cleanup
|
|
446
|
+
* path: the effect's cleanup runs on every dependency change and on unmount.
|
|
447
|
+
* - Calling `disconnect()`: if a connection id is already resolved, it is
|
|
448
|
+
* disconnected immediately and status becomes `'disconnected'`. If
|
|
449
|
+
* `connect()` is still pending, the request is recorded and honored when
|
|
450
|
+
* the promise settles — the newly resolved connection is torn down instead
|
|
451
|
+
* of being adopted, and status is `'disconnected'`. One caveat:
|
|
452
|
+
* `disconnect()` does not cancel the event callback, so a buffered or
|
|
453
|
+
* in-flight event landing after the call re-asserts `'connected'` — and in
|
|
454
|
+
* the pending-connect case the settle handler tears the connection down
|
|
455
|
+
* without touching status, so that stray `'connected'` persists.
|
|
456
|
+
* - Stream completion while `connect()` is still pending: the current
|
|
457
|
+
* implementation lets `onComplete` set `'disconnected'`, then the settle
|
|
458
|
+
* handler adopts the connection id and re-asserts `'connected'` anyway.
|
|
459
|
+
* This is known current behavior, not a durable guarantee: a pending
|
|
460
|
+
* upstream derived-status rewrite is expected to remove this transition.
|
|
461
|
+
*
|
|
462
|
+
* `enabled` (default `true`) is the only way to defer connecting past mount;
|
|
463
|
+
* flipping it back to `true` re-runs the same connect sequence as a fresh
|
|
464
|
+
* mount would.
|
|
465
|
+
*/
|
|
409
466
|
declare function useApiStream<TEvent>(descriptor: StreamDescriptor<TEvent>, options?: ApiStreamOptions): ApiStreamResult<TEvent>;
|
|
410
467
|
|
|
411
468
|
/**
|
|
@@ -498,7 +555,7 @@ declare const MfeProvider: React__default.FC<MfeProviderProps>;
|
|
|
498
555
|
* // bridge.executeActionsChain(chain);
|
|
499
556
|
* // bridge.subscribeToProperty(propertyTypeId, callback);
|
|
500
557
|
*
|
|
501
|
-
* return <div>Domain: {bridge.
|
|
558
|
+
* return <div>Domain: {bridge.extDomainId}</div>;
|
|
502
559
|
* }
|
|
503
560
|
* ```
|
|
504
561
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/FrontXProvider.tsx
|
|
2
|
-
import { useMemo as useMemo2, useEffect as
|
|
2
|
+
import { useMemo as useMemo2, useEffect as useEffect3, useState as useState2 } from "react";
|
|
3
3
|
import { Provider as ReduxProvider } from "react-redux";
|
|
4
4
|
import {
|
|
5
5
|
createFrontXApp
|
|
@@ -41,7 +41,7 @@ var MfeProvider = ({ value, children }) => {
|
|
|
41
41
|
import React, {
|
|
42
42
|
createContext as createContext3,
|
|
43
43
|
useContext as useContext3,
|
|
44
|
-
useEffect,
|
|
44
|
+
useEffect as useEffect2,
|
|
45
45
|
useLayoutEffect,
|
|
46
46
|
useMemo,
|
|
47
47
|
useState
|
|
@@ -164,13 +164,23 @@ function createQueryCache(queryClient) {
|
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
// src/hooks/useLatest.ts
|
|
168
|
+
import { useEffect, useRef } from "react";
|
|
169
|
+
function useLatest(value) {
|
|
170
|
+
const ref = useRef(value);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
ref.current = value;
|
|
173
|
+
});
|
|
174
|
+
return ref;
|
|
175
|
+
}
|
|
176
|
+
|
|
167
177
|
// src/queryClient.tsx
|
|
168
178
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
169
179
|
var APP_QUERY_CLIENT_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client");
|
|
170
180
|
var APP_QUERY_CLIENT_RESOLVER_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-resolver");
|
|
171
181
|
var APP_QUERY_CLIENT_ACTIVATOR_SYMBOL = /* @__PURE__ */ Symbol.for("frontx:query-cache:app-client-activator");
|
|
172
182
|
var FrontXQueryClientContext = createContext3(void 0);
|
|
173
|
-
var useCommitEffect = typeof globalThis.window === "undefined" ?
|
|
183
|
+
var useCommitEffect = typeof globalThis.window === "undefined" ? useEffect2 : useLayoutEffect;
|
|
174
184
|
function resolveFrontXQueryClient(app) {
|
|
175
185
|
const clientApp = app;
|
|
176
186
|
return clientApp[APP_QUERY_CLIENT_SYMBOL] ?? clientApp[APP_QUERY_CLIENT_RESOLVER_SYMBOL]?.();
|
|
@@ -409,18 +419,19 @@ function useFrontXSuspenseInfiniteQuery(options) {
|
|
|
409
419
|
}
|
|
410
420
|
function useFrontXMutation(options, callbackCtx) {
|
|
411
421
|
useRequiredFrontXQueryClient();
|
|
412
|
-
const latestRef =
|
|
413
|
-
latestRef.current = { options, callbackCtx };
|
|
422
|
+
const latestRef = useLatest({ options, callbackCtx });
|
|
414
423
|
const fetchAbortControllersRef = React.useRef(/* @__PURE__ */ new Set());
|
|
424
|
+
const abortFetchesOnUnmount = React.useEffectEvent(() => {
|
|
425
|
+
if (options.abortOnUnmount) {
|
|
426
|
+
for (const controller of fetchAbortControllersRef.current) {
|
|
427
|
+
controller.abort();
|
|
428
|
+
}
|
|
429
|
+
fetchAbortControllersRef.current.clear();
|
|
430
|
+
}
|
|
431
|
+
});
|
|
415
432
|
React.useEffect(() => {
|
|
416
|
-
const fetchAbortControllers = fetchAbortControllersRef.current;
|
|
417
433
|
return () => {
|
|
418
|
-
|
|
419
|
-
for (const controller of fetchAbortControllers) {
|
|
420
|
-
controller.abort();
|
|
421
|
-
}
|
|
422
|
-
fetchAbortControllers.clear();
|
|
423
|
-
}
|
|
434
|
+
abortFetchesOnUnmount();
|
|
424
435
|
};
|
|
425
436
|
}, []);
|
|
426
437
|
const mutationFn = React.useCallback((variables, context) => {
|
|
@@ -444,22 +455,22 @@ function useFrontXMutation(options, callbackCtx) {
|
|
|
444
455
|
return latestRef.current.options.endpoint.fetch(variables, { signal: controller.signal }).finally(() => {
|
|
445
456
|
fetchAbortControllersRef.current.delete(controller);
|
|
446
457
|
});
|
|
447
|
-
}, []);
|
|
458
|
+
}, [latestRef]);
|
|
448
459
|
const onMutate = React.useCallback((variables, _context) => {
|
|
449
460
|
const current = latestRef.current;
|
|
450
461
|
if (!current.options.onMutate) {
|
|
451
462
|
return void 0;
|
|
452
463
|
}
|
|
453
464
|
return current.options.onMutate(variables, current.callbackCtx);
|
|
454
|
-
}, []);
|
|
465
|
+
}, [latestRef]);
|
|
455
466
|
const onSuccess = React.useCallback((data, variables, context) => {
|
|
456
467
|
const current = latestRef.current;
|
|
457
468
|
return current.options.onSuccess?.(data, variables, context, current.callbackCtx);
|
|
458
|
-
}, []);
|
|
469
|
+
}, [latestRef]);
|
|
459
470
|
const onError = React.useCallback((error, variables, context) => {
|
|
460
471
|
const current = latestRef.current;
|
|
461
472
|
return current.options.onError?.(error, variables, context, current.callbackCtx);
|
|
462
|
-
}, []);
|
|
473
|
+
}, [latestRef]);
|
|
463
474
|
const onSettled = React.useCallback((data, error, variables, context) => {
|
|
464
475
|
const current = latestRef.current;
|
|
465
476
|
return current.options.onSettled?.(
|
|
@@ -469,7 +480,7 @@ function useFrontXMutation(options, callbackCtx) {
|
|
|
469
480
|
context,
|
|
470
481
|
current.callbackCtx
|
|
471
482
|
);
|
|
472
|
-
}, []);
|
|
483
|
+
}, [latestRef]);
|
|
473
484
|
const mutation = useMutation({
|
|
474
485
|
mutationFn,
|
|
475
486
|
onMutate: options.onMutate ? onMutate : void 0,
|
|
@@ -520,7 +531,7 @@ var FrontXProvider = ({
|
|
|
520
531
|
}, [providedApp, stableConfig]);
|
|
521
532
|
const queryClient = useBootstrappedFrontXQueryClient(app);
|
|
522
533
|
const deferQuerySubtree = hasFrontXQueryClientActivator(app) && queryClient === void 0;
|
|
523
|
-
|
|
534
|
+
useEffect3(() => {
|
|
524
535
|
if (queryClient || hasFrontXQueryClientActivator(app) || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test" || process.env.VITEST === "true") {
|
|
525
536
|
return;
|
|
526
537
|
}
|
|
@@ -528,7 +539,7 @@ var FrontXProvider = ({
|
|
|
528
539
|
"[FrontXProvider] No query cache available. Add queryCache() or queryCacheShared() to your plugin composition. useApiQuery/useApiMutation will fail without it."
|
|
529
540
|
);
|
|
530
541
|
}, [app, queryClient]);
|
|
531
|
-
|
|
542
|
+
useEffect3(() => {
|
|
532
543
|
return () => {
|
|
533
544
|
if (!providedApp) {
|
|
534
545
|
app.destroy();
|
|
@@ -543,7 +554,7 @@ var FrontXProvider = ({
|
|
|
543
554
|
};
|
|
544
555
|
|
|
545
556
|
// src/hooks/useCanAccess.ts
|
|
546
|
-
import { useEffect as
|
|
557
|
+
import { useEffect as useEffect4, useState as useState3 } from "react";
|
|
547
558
|
function serializeRecordValue(value) {
|
|
548
559
|
if (value === null) return "n:";
|
|
549
560
|
if (typeof value === "string") return `s:${value}`;
|
|
@@ -560,24 +571,23 @@ function accessQueryKey(query) {
|
|
|
560
571
|
function getAuthRuntime(app) {
|
|
561
572
|
return app.auth ?? null;
|
|
562
573
|
}
|
|
574
|
+
var DENIED_NO_AUTH = { allow: false, isResolving: false };
|
|
563
575
|
function useCanAccess(query) {
|
|
564
576
|
const app = useFrontX();
|
|
577
|
+
const auth2 = getAuthRuntime(app);
|
|
565
578
|
const stableKey = accessQueryKey(query);
|
|
566
579
|
const [stableQuery, setStableQuery] = useState3(() => query);
|
|
567
|
-
const [
|
|
580
|
+
const [runState, setRunState] = useState3({ auth: auth2, stableKey });
|
|
568
581
|
const [result, setResult] = useState3({ allow: false, isResolving: true });
|
|
569
|
-
if (
|
|
570
|
-
|
|
582
|
+
if (runState.auth !== auth2 || runState.stableKey !== stableKey) {
|
|
583
|
+
setRunState({ auth: auth2, stableKey });
|
|
571
584
|
setStableQuery(query);
|
|
572
585
|
setResult({ allow: false, isResolving: true });
|
|
573
586
|
}
|
|
574
|
-
|
|
575
|
-
const auth2 = getAuthRuntime(app);
|
|
587
|
+
useEffect4(() => {
|
|
576
588
|
if (!auth2) {
|
|
577
|
-
setResult({ allow: false, isResolving: false });
|
|
578
589
|
return;
|
|
579
590
|
}
|
|
580
|
-
setResult({ allow: false, isResolving: true });
|
|
581
591
|
let alive = true;
|
|
582
592
|
const controller = new AbortController();
|
|
583
593
|
void auth2.canAccess(stableQuery, { signal: controller.signal }).then((decision) => {
|
|
@@ -593,8 +603,8 @@ function useCanAccess(query) {
|
|
|
593
603
|
alive = false;
|
|
594
604
|
controller.abort();
|
|
595
605
|
};
|
|
596
|
-
}, [
|
|
597
|
-
return result;
|
|
606
|
+
}, [auth2, stableQuery]);
|
|
607
|
+
return auth2 ? result : DENIED_NO_AUTH;
|
|
598
608
|
}
|
|
599
609
|
|
|
600
610
|
// src/components/CanAccess.tsx
|
|
@@ -667,7 +677,7 @@ function useTranslation() {
|
|
|
667
677
|
}
|
|
668
678
|
|
|
669
679
|
// src/hooks/useScreenTranslations.ts
|
|
670
|
-
import { useState as useState4, useEffect as
|
|
680
|
+
import { useState as useState4, useEffect as useEffect5, useMemo as useMemo4, useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
671
681
|
function isTranslationLoader(input) {
|
|
672
682
|
return typeof input === "function";
|
|
673
683
|
}
|
|
@@ -675,7 +685,6 @@ function useScreenTranslations(screensetId, screenId, translations) {
|
|
|
675
685
|
const app = useFrontX();
|
|
676
686
|
const { i18nRegistry: i18nRegistry2 } = app;
|
|
677
687
|
const [loadedLanguage, setLoadedLanguage] = useState4(null);
|
|
678
|
-
const [isLoading, setIsLoading] = useState4(false);
|
|
679
688
|
const [error, setError] = useState4(null);
|
|
680
689
|
const version = useSyncExternalStore2(
|
|
681
690
|
useCallback2(
|
|
@@ -702,12 +711,11 @@ function useScreenTranslations(screensetId, screenId, translations) {
|
|
|
702
711
|
return module.default;
|
|
703
712
|
};
|
|
704
713
|
}, [translations]);
|
|
705
|
-
|
|
714
|
+
useEffect5(() => {
|
|
706
715
|
if (!currentLanguage || currentLanguage === loadedLanguage) {
|
|
707
716
|
return;
|
|
708
717
|
}
|
|
709
718
|
let cancelled = false;
|
|
710
|
-
setIsLoading(true);
|
|
711
719
|
const loadTranslations = async () => {
|
|
712
720
|
try {
|
|
713
721
|
const namespace = `screen.${screensetId}.${screenId}`;
|
|
@@ -716,13 +724,11 @@ function useScreenTranslations(screensetId, screenId, translations) {
|
|
|
716
724
|
i18nRegistry2.register(namespace, currentLanguage, loadedTranslations);
|
|
717
725
|
if (!cancelled) {
|
|
718
726
|
setLoadedLanguage(currentLanguage);
|
|
719
|
-
setIsLoading(false);
|
|
720
727
|
setError(null);
|
|
721
728
|
}
|
|
722
729
|
} catch (err) {
|
|
723
730
|
if (!cancelled) {
|
|
724
731
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
725
|
-
setIsLoading(false);
|
|
726
732
|
}
|
|
727
733
|
}
|
|
728
734
|
};
|
|
@@ -731,7 +737,7 @@ function useScreenTranslations(screensetId, screenId, translations) {
|
|
|
731
737
|
cancelled = true;
|
|
732
738
|
};
|
|
733
739
|
}, [screensetId, screenId, loader, i18nRegistry2, currentLanguage, loadedLanguage]);
|
|
734
|
-
const isLoaded = currentLanguage !== null && currentLanguage === loadedLanguage
|
|
740
|
+
const isLoaded = currentLanguage !== null && currentLanguage === loadedLanguage;
|
|
735
741
|
return { isLoaded, error };
|
|
736
742
|
}
|
|
737
743
|
|
|
@@ -842,25 +848,38 @@ function useApiMutation(options) {
|
|
|
842
848
|
}
|
|
843
849
|
|
|
844
850
|
// src/hooks/useApiStream.ts
|
|
845
|
-
import { useEffect as
|
|
851
|
+
import { useEffect as useEffect6, useMemo as useMemo8, useRef as useRef2, useState as useState5, useCallback as useCallback4 } from "react";
|
|
846
852
|
function useApiStream(descriptor, options) {
|
|
847
853
|
const mode = options?.mode ?? "latest";
|
|
848
854
|
const enabled = options?.enabled ?? true;
|
|
849
855
|
const [data, setData] = useState5(void 0);
|
|
850
856
|
const [events, setEvents] = useState5([]);
|
|
851
|
-
const [status, setStatus] = useState5("idle");
|
|
857
|
+
const [status, setStatus] = useState5(() => enabled ? "connecting" : "idle");
|
|
852
858
|
const [error, setError] = useState5(null);
|
|
853
|
-
const connectPromiseRef =
|
|
854
|
-
const connectionIdRef =
|
|
855
|
-
const disconnectRequestedRef =
|
|
856
|
-
const descriptorRef =
|
|
857
|
-
descriptorRef.current = descriptor;
|
|
859
|
+
const connectPromiseRef = useRef2(null);
|
|
860
|
+
const connectionIdRef = useRef2(null);
|
|
861
|
+
const disconnectRequestedRef = useRef2(false);
|
|
862
|
+
const descriptorRef = useLatest(descriptor);
|
|
858
863
|
const descriptorKey = useMemo8(() => JSON.stringify(descriptor.key), [descriptor.key]);
|
|
859
|
-
|
|
864
|
+
const resetKey = `${descriptorKey}:${mode}`;
|
|
865
|
+
const [prevResetKey, setPrevResetKey] = useState5(resetKey);
|
|
866
|
+
if (resetKey !== prevResetKey) {
|
|
867
|
+
setPrevResetKey(resetKey);
|
|
860
868
|
setData(void 0);
|
|
861
869
|
setEvents([]);
|
|
862
870
|
setError(null);
|
|
863
|
-
}
|
|
871
|
+
}
|
|
872
|
+
const connectKey = `${resetKey}:${enabled}`;
|
|
873
|
+
const [prevConnectKey, setPrevConnectKey] = useState5(connectKey);
|
|
874
|
+
if (connectKey !== prevConnectKey) {
|
|
875
|
+
setPrevConnectKey(connectKey);
|
|
876
|
+
if (enabled) {
|
|
877
|
+
setData(void 0);
|
|
878
|
+
setEvents([]);
|
|
879
|
+
setStatus("connecting");
|
|
880
|
+
setError(null);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
864
883
|
const disconnect = useCallback4(() => {
|
|
865
884
|
disconnectRequestedRef.current = true;
|
|
866
885
|
if (connectionIdRef.current) {
|
|
@@ -869,19 +888,14 @@ function useApiStream(descriptor, options) {
|
|
|
869
888
|
disconnectRequestedRef.current = false;
|
|
870
889
|
}
|
|
871
890
|
setStatus("disconnected");
|
|
872
|
-
}, []);
|
|
873
|
-
|
|
891
|
+
}, [descriptorRef]);
|
|
892
|
+
useEffect6(() => {
|
|
874
893
|
if (!enabled) {
|
|
875
|
-
setStatus("idle");
|
|
876
894
|
return;
|
|
877
895
|
}
|
|
878
896
|
let cancelled = false;
|
|
879
897
|
disconnectRequestedRef.current = false;
|
|
880
898
|
const d = descriptorRef.current;
|
|
881
|
-
setData(void 0);
|
|
882
|
-
setEvents([]);
|
|
883
|
-
setStatus("connecting");
|
|
884
|
-
setError(null);
|
|
885
899
|
const connectPromise = d.connect(
|
|
886
900
|
(event) => {
|
|
887
901
|
if (cancelled) return;
|
|
@@ -927,8 +941,8 @@ function useApiStream(descriptor, options) {
|
|
|
927
941
|
connectPromiseRef.current = null;
|
|
928
942
|
connectionIdRef.current = null;
|
|
929
943
|
};
|
|
930
|
-
}, [descriptorKey, enabled, mode]);
|
|
931
|
-
return { data, events, status, error, disconnect };
|
|
944
|
+
}, [descriptorKey, enabled, mode, descriptorRef]);
|
|
945
|
+
return { data, events, status: enabled ? status : "idle", error, disconnect };
|
|
932
946
|
}
|
|
933
947
|
|
|
934
948
|
// src/hooks/useQueryCache.ts
|
|
@@ -974,7 +988,7 @@ function useHostAction(actionTypeId) {
|
|
|
974
988
|
const chain = {
|
|
975
989
|
action: {
|
|
976
990
|
type: actionTypeId,
|
|
977
|
-
target: bridge.
|
|
991
|
+
target: bridge.extDomainId,
|
|
978
992
|
payload
|
|
979
993
|
}
|
|
980
994
|
};
|
|
@@ -988,7 +1002,7 @@ function useHostAction(actionTypeId) {
|
|
|
988
1002
|
}
|
|
989
1003
|
|
|
990
1004
|
// src/mfe/hooks/useDomainExtensions.ts
|
|
991
|
-
import { useSyncExternalStore as useSyncExternalStore5, useCallback as useCallback7, useRef as
|
|
1005
|
+
import { useSyncExternalStore as useSyncExternalStore5, useCallback as useCallback7, useRef as useRef3 } from "react";
|
|
992
1006
|
function useDomainExtensions(domainId) {
|
|
993
1007
|
const app = useFrontX();
|
|
994
1008
|
const registry = app.mfeRegistry;
|
|
@@ -1003,7 +1017,7 @@ function useDomainExtensions(domainId) {
|
|
|
1003
1017
|
},
|
|
1004
1018
|
[app.store]
|
|
1005
1019
|
);
|
|
1006
|
-
const cacheRef =
|
|
1020
|
+
const cacheRef = useRef3({ ids: "", extensions: [] });
|
|
1007
1021
|
const getSnapshot = useCallback7(() => {
|
|
1008
1022
|
const extensions = registry.getExtensionsForDomain(domainId);
|
|
1009
1023
|
const ids = extensions.map((e) => e.id).join(",");
|
|
@@ -1016,7 +1030,7 @@ function useDomainExtensions(domainId) {
|
|
|
1016
1030
|
}
|
|
1017
1031
|
|
|
1018
1032
|
// src/mfe/hooks/useMountedExtensions.ts
|
|
1019
|
-
import { useSyncExternalStore as useSyncExternalStore6, useCallback as useCallback8, useRef as
|
|
1033
|
+
import { useSyncExternalStore as useSyncExternalStore6, useCallback as useCallback8, useRef as useRef4 } from "react";
|
|
1020
1034
|
function useMountedExtensions(domainId) {
|
|
1021
1035
|
const app = useFrontX();
|
|
1022
1036
|
const registry = app.mfeRegistry;
|
|
@@ -1031,7 +1045,7 @@ function useMountedExtensions(domainId) {
|
|
|
1031
1045
|
},
|
|
1032
1046
|
[app.store]
|
|
1033
1047
|
);
|
|
1034
|
-
const cacheRef =
|
|
1048
|
+
const cacheRef = useRef4({ key: "", extensions: [] });
|
|
1035
1049
|
const getSnapshot = useCallback8(() => {
|
|
1036
1050
|
const ids = registry.getMountedExtensions(domainId);
|
|
1037
1051
|
const resolved = ids.map((id) => registry.getExtension(id)).filter((ext) => ext !== void 0);
|
|
@@ -1045,7 +1059,7 @@ function useMountedExtensions(domainId) {
|
|
|
1045
1059
|
}
|
|
1046
1060
|
|
|
1047
1061
|
// src/mfe/hooks/useRegisteredPackages.ts
|
|
1048
|
-
import { useSyncExternalStore as useSyncExternalStore7, useCallback as useCallback9, useRef as
|
|
1062
|
+
import { useSyncExternalStore as useSyncExternalStore7, useCallback as useCallback9, useRef as useRef5 } from "react";
|
|
1049
1063
|
function useRegisteredPackages() {
|
|
1050
1064
|
const app = useFrontX();
|
|
1051
1065
|
const registry = app.mfeRegistry;
|
|
@@ -1060,7 +1074,7 @@ function useRegisteredPackages() {
|
|
|
1060
1074
|
},
|
|
1061
1075
|
[app.store]
|
|
1062
1076
|
);
|
|
1063
|
-
const cacheRef =
|
|
1077
|
+
const cacheRef = useRef5({ packages: "", list: [] });
|
|
1064
1078
|
const getSnapshot = useCallback9(() => {
|
|
1065
1079
|
const list = registry.getRegisteredPackages();
|
|
1066
1080
|
const packages = list.join(",");
|
|
@@ -1180,7 +1194,7 @@ var ThemeAwareReactLifecycle = class {
|
|
|
1180
1194
|
};
|
|
1181
1195
|
|
|
1182
1196
|
// src/mfe/components/ExtensionDomainSlot.tsx
|
|
1183
|
-
import { useEffect as
|
|
1197
|
+
import { useEffect as useEffect7, useEffectEvent, useRef as useRef6, useState as useState6 } from "react";
|
|
1184
1198
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1185
1199
|
function ExtensionDomainSlot(props) {
|
|
1186
1200
|
const {
|
|
@@ -1191,13 +1205,15 @@ function ExtensionDomainSlot(props) {
|
|
|
1191
1205
|
onDetached,
|
|
1192
1206
|
loadingComponent = null
|
|
1193
1207
|
} = props;
|
|
1194
|
-
const rootRef =
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1208
|
+
const rootRef = useRef6(null);
|
|
1209
|
+
const emitAttached = useEffectEvent((root) => {
|
|
1210
|
+
onAttached?.(root);
|
|
1211
|
+
});
|
|
1212
|
+
const emitDetached = useEffectEvent(() => {
|
|
1213
|
+
onDetached?.();
|
|
1214
|
+
});
|
|
1199
1215
|
const [attached, setAttached] = useState6(false);
|
|
1200
|
-
|
|
1216
|
+
useEffect7(() => {
|
|
1201
1217
|
const root = rootRef.current;
|
|
1202
1218
|
if (!root) {
|
|
1203
1219
|
return;
|
|
@@ -1205,11 +1221,11 @@ function ExtensionDomainSlot(props) {
|
|
|
1205
1221
|
const mounter = registry.getMounter(domainId);
|
|
1206
1222
|
mounter.attach(root);
|
|
1207
1223
|
setAttached(true);
|
|
1208
|
-
|
|
1224
|
+
emitAttached(root);
|
|
1209
1225
|
return () => {
|
|
1210
1226
|
void mounter.detach();
|
|
1211
1227
|
setAttached(false);
|
|
1212
|
-
|
|
1228
|
+
emitDetached();
|
|
1213
1229
|
};
|
|
1214
1230
|
}, [registry, domainId]);
|
|
1215
1231
|
return /* @__PURE__ */ jsx5(
|