@venizia/ardor-admin 0.4.8-0 → 0.4.8-10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +9 -5
  2. package/dist/common/locales/en.d.ts +1 -0
  3. package/dist/common/locales/en.js +1 -0
  4. package/dist/common/locales/vi.d.ts +1 -0
  5. package/dist/common/locales/vi.js +1 -0
  6. package/dist/common/merge.d.ts +11 -0
  7. package/dist/common/merge.js +25 -0
  8. package/dist/common/types.d.ts +39 -6
  9. package/dist/components/application.d.ts +8 -0
  10. package/dist/components/application.js +37 -2
  11. package/dist/components/resolve.d.ts +16 -5
  12. package/dist/components/resolve.js +3 -6
  13. package/dist/features/feature.d.ts +4 -0
  14. package/dist/features/feature.js +1 -0
  15. package/dist/features/messages.js +3 -4
  16. package/dist/hooks/use-document-title.d.ts +5 -0
  17. package/dist/hooks/use-features.js +2 -2
  18. package/dist/hooks/use-notify-error.d.ts +4 -0
  19. package/dist/hooks/use-notify-error.js +4 -0
  20. package/dist/hooks/use-refresh-token.d.ts +4 -0
  21. package/dist/hooks/use-refresh-token.js +4 -0
  22. package/dist/hooks/use-request-header-locale.d.ts +9 -1
  23. package/dist/hooks/use-request-header-locale.js +4 -0
  24. package/dist/hooks/use-translate.d.ts +20 -1
  25. package/dist/hooks/use-translate.js +5 -10
  26. package/dist/providers/auth.d.ts +67 -3
  27. package/dist/providers/auth.js +95 -7
  28. package/dist/providers/count-rest-data.d.ts +8 -0
  29. package/dist/providers/count-rest-data.js +8 -0
  30. package/dist/providers/i18n.d.ts +22 -2
  31. package/dist/providers/i18n.js +28 -5
  32. package/dist/providers/list-filter.d.ts +5 -1
  33. package/dist/providers/list-filter.js +8 -9
  34. package/dist/providers/rest-data.d.ts +66 -3
  35. package/dist/providers/rest-data.js +96 -11
  36. package/package.json +14 -17
  37. package/dist/common/index.d.ts.map +0 -1
  38. package/dist/common/index.js.map +0 -1
  39. package/dist/common/locales/en.d.ts.map +0 -1
  40. package/dist/common/locales/en.js.map +0 -1
  41. package/dist/common/locales/index.d.ts.map +0 -1
  42. package/dist/common/locales/index.js.map +0 -1
  43. package/dist/common/locales/vi.d.ts.map +0 -1
  44. package/dist/common/locales/vi.js.map +0 -1
  45. package/dist/common/types.d.ts.map +0 -1
  46. package/dist/common/types.js.map +0 -1
  47. package/dist/components/application.d.ts.map +0 -1
  48. package/dist/components/application.js.map +0 -1
  49. package/dist/components/index.d.ts.map +0 -1
  50. package/dist/components/index.js.map +0 -1
  51. package/dist/components/resolve.d.ts.map +0 -1
  52. package/dist/components/resolve.js.map +0 -1
  53. package/dist/features/feature.d.ts.map +0 -1
  54. package/dist/features/feature.js.map +0 -1
  55. package/dist/features/index.d.ts.map +0 -1
  56. package/dist/features/index.js.map +0 -1
  57. package/dist/features/messages.d.ts.map +0 -1
  58. package/dist/features/messages.js.map +0 -1
  59. package/dist/hooks/index.d.ts.map +0 -1
  60. package/dist/hooks/index.js.map +0 -1
  61. package/dist/hooks/use-document-title.d.ts.map +0 -1
  62. package/dist/hooks/use-document-title.js.map +0 -1
  63. package/dist/hooks/use-features.d.ts.map +0 -1
  64. package/dist/hooks/use-features.js.map +0 -1
  65. package/dist/hooks/use-html-lang.d.ts.map +0 -1
  66. package/dist/hooks/use-html-lang.js.map +0 -1
  67. package/dist/hooks/use-notify-error.d.ts.map +0 -1
  68. package/dist/hooks/use-notify-error.js.map +0 -1
  69. package/dist/hooks/use-refresh-token.d.ts.map +0 -1
  70. package/dist/hooks/use-refresh-token.js.map +0 -1
  71. package/dist/hooks/use-request-header-locale.d.ts.map +0 -1
  72. package/dist/hooks/use-request-header-locale.js.map +0 -1
  73. package/dist/hooks/use-translate.d.ts.map +0 -1
  74. package/dist/hooks/use-translate.js.map +0 -1
  75. package/dist/index.d.ts.map +0 -1
  76. package/dist/index.js.map +0 -1
  77. package/dist/providers/auth.d.ts.map +0 -1
  78. package/dist/providers/auth.js.map +0 -1
  79. package/dist/providers/count-rest-data.d.ts.map +0 -1
  80. package/dist/providers/count-rest-data.js.map +0 -1
  81. package/dist/providers/i18n-sources.d.ts.map +0 -1
  82. package/dist/providers/i18n-sources.js.map +0 -1
  83. package/dist/providers/i18n.d.ts.map +0 -1
  84. package/dist/providers/i18n.js.map +0 -1
  85. package/dist/providers/index.d.ts.map +0 -1
  86. package/dist/providers/index.js.map +0 -1
  87. package/dist/providers/list-filter.d.ts.map +0 -1
  88. package/dist/providers/list-filter.js.map +0 -1
  89. package/dist/providers/rest-data.d.ts.map +0 -1
  90. package/dist/providers/rest-data.js.map +0 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@ The [react-admin](https://marmelab.com/react-admin/) adapter of
4
4
  [ARDOR](https://github.com/VENIZIA-AI/ardor): every place the framework touches `ra-core`.
5
5
 
6
6
  ```bash
7
- bun add @venizia/ardor-admin @venizia/ardor-kernel @venizia/ardor-react @venizia/ignis-inversion @venizia/ignis-filter @venizia/ignis-kernel @venizia/ignis-helpers reflect-metadata ra-core ra-i18n-polyglot
7
+ bun add @venizia/ardor-admin @venizia/ardor-kernel @venizia/ardor-react @venizia/ignis-inversion @venizia/ignis-filter @venizia/ignis-kernel @venizia/ignis-helpers reflect-metadata ra-core ra-i18n-polyglot react react-dom react-redux react-router-dom @reduxjs/toolkit @tanstack/react-query
8
8
  ```
9
9
 
10
10
  Most applications install [`@venizia/ardor`](https://www.npmjs.com/package/@venizia/ardor) instead,
@@ -15,11 +15,15 @@ which re-exports this package.
15
15
  | Area | Exports |
16
16
  |---|---|
17
17
  | Data | `DefaultRestDataProvider`, `CountRestDataProvider` |
18
- | Auth | `DefaultAuthProvider` |
18
+ | Auth | `DefaultAuthProvider`, `SessionClearReasons`, `IAuthRejection` |
19
19
  | i18n | `DefaultI18nProvider`, `englishMessages`, `vietnameseMessages` |
20
- | Component | `ArdorApplication` - the `CoreAdmin` root, wired from the container |
21
- | Hooks | `useTranslate`, `useNotifyError`, `useRefreshToken`, `useRequestHeaderLocale` |
22
- | Types | `IDataProvider`, `IAuthProvider`, `II18nProviderOptions`, `IApplication`, `TUseTranslateKeys` |
20
+ | Component | `ArdorApplication` - the `CoreAdmin` root, wired from the container; `ApplicationProviders` |
21
+ | Features | `Features`, `TFeature`, `useFeatures` |
22
+ | Lists | `ListFilterBuilder` |
23
+ | Hooks | `useTranslate`, `useUntypedTranslate`, `useNotifyError`, `useRefreshToken`, `useRequestHeaderLocale`, `useFeatures`, `useDocumentTitle`, `useHtmlLang` |
24
+ | Types | `IDataProvider`, `IAuthProvider`, `II18nProviderOptions`, `IApplication`, `TUseTranslateKeys`, `TI18nSource` |
25
+
26
+ Every export, with its signature, is in the [references](https://ardor.venizia.ai/references/).
23
27
 
24
28
  The data provider speaks the `@venizia/ignis-filter` vocabulary - react-admin pagination, sort and
25
29
  filter are mapped onto `{ where, order, limit, skip, fields, include }`.
@@ -1,3 +1,4 @@
1
+ /** The built-in English messages: react-admin's `ra` catalogue and ARDOR's own keys. */
1
2
  export declare const englishMessages: {
2
3
  ra: {
3
4
  action: {
@@ -1,3 +1,4 @@
1
+ /** The built-in English messages: react-admin's `ra` catalogue and ARDOR's own keys. */
1
2
  export const englishMessages = {
2
3
  ra: {
3
4
  action: {
@@ -1,3 +1,4 @@
1
1
  import type { TranslationMessages } from 'ra-core';
2
+ /** The built-in Vietnamese messages, the same shape as `englishMessages`. */
2
3
  export declare const vietnameseMessages: TranslationMessages;
3
4
  //# sourceMappingURL=vi.d.ts.map
@@ -1,3 +1,4 @@
1
+ /** The built-in Vietnamese messages, the same shape as `englishMessages`. */
1
2
  export const vietnameseMessages = {
2
3
  ra: {
3
4
  action: {
@@ -0,0 +1,11 @@
1
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
2
+ /**
3
+ * A new object with the sources merged in order: a later source wins, plain objects merge
4
+ * recursively, anything else (an array, a string) replaces, and an `undefined` never overwrites. The
5
+ * sources are not changed. For JSON-like values only (message bundles): a `Date` becomes `{}`, an
6
+ * array is shared with its source by reference, and `__proto__` is dropped.
7
+ */
8
+ export declare const deepMerge: (opts: {
9
+ sources: ReadonlyArray<object>;
10
+ }) => Record<string, unknown>;
11
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1,25 @@
1
+ export const isRecord = (value) => {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ };
4
+ /**
5
+ * A new object with the sources merged in order: a later source wins, plain objects merge
6
+ * recursively, anything else (an array, a string) replaces, and an `undefined` never overwrites. The
7
+ * sources are not changed. For JSON-like values only (message bundles): a `Date` becomes `{}`, an
8
+ * array is shared with its source by reference, and `__proto__` is dropped.
9
+ */
10
+ export const deepMerge = (opts) => {
11
+ const merged = {};
12
+ for (const source of opts.sources) {
13
+ for (const [key, value] of Object.entries(source)) {
14
+ if (key === '__proto__' || value === undefined) {
15
+ continue;
16
+ }
17
+ const current = merged[key];
18
+ merged[key] = isRecord(value)
19
+ ? deepMerge({ sources: [isRecord(current) ? current : {}, value] })
20
+ : value;
21
+ }
22
+ }
23
+ return merged;
24
+ };
25
+ //# sourceMappingURL=merge.js.map
@@ -5,30 +5,46 @@ import type { TExtraRequest } from '@venizia/ignis-kernel/repository';
5
5
  import type { AuthProvider, CoreAdminProps, DataProvider, Locale, ResourceProps } from 'ra-core';
6
6
  import type { ReactNode } from 'react';
7
7
  import type { RouteProps } from 'react-router-dom';
8
- import type { DefaultNetworkRequestService, IDataSourceAuth, ISendParams, ISendResponse, RequestCountData, TConstValue, TRequestMethod, TRequestType } from '@venizia/ardor-kernel';
8
+ import type { DefaultNetworkRequestService, IDataSourceAuth, INetworkResponse, ISendParams, RequestCountData, TConstValue, TRequestMethod, TRequestType } from '@venizia/ardor-kernel';
9
9
  import type { TFeature } from '../features/feature.js';
10
10
  /** What `DefaultRestDataProvider.request` sends: every react-admin method goes through it. */
11
11
  export interface IRestRequestOptions<TResource extends string = string> {
12
+ /** The first path segment, unless `paths` is set. */
12
13
  resource: TResource;
14
+ /** What the response is read as, and what the request is logged as. */
13
15
  type: TRequestType;
16
+ /** The HTTP method. */
14
17
  method: TRequestMethod;
15
- /** Default `[resource]`. */
18
+ /**
19
+ * The path segments of the request.
20
+ * @default [resource]
21
+ */
16
22
  paths?: Array<string>;
23
+ /** The request body; encoded by `bodyType`. */
17
24
  body?: unknown;
18
25
  bodyType?: ISendParams['bodyType'];
26
+ /** The query string. */
19
27
  query?: Record<string, unknown>;
28
+ /** The list extras, sent in a header. */
20
29
  extra?: TExtraRequest;
21
30
  /** Win over the computed headers: a caller that names a header means it. */
22
31
  headers?: HeadersInit;
32
+ /**
33
+ * What the request asks the server to count.
34
+ * @default RequestCountData.DATA_ONLY
35
+ */
23
36
  requestCountData?: TConstValue<typeof RequestCountData>;
24
37
  }
25
38
  /** The nine data methods of react-admin, as ra-core declares them. */
26
39
  export type IReactAdminDataProvider<TResource extends string = string> = Pick<DataProvider<TResource>, 'getList' | 'getOne' | 'getMany' | 'getManyReference' | 'update' | 'updateMany' | 'create' | 'delete' | 'deleteMany'>;
40
+ /** react-admin's data provider with a free-form `send` and access to the network service and its auth. */
27
41
  export interface IDataProvider<TResource extends string = string> extends IReactAdminDataProvider<TResource> {
42
+ /** A request outside react-admin's methods: the same headers and auth, the method and body of `params`. */
28
43
  send: <TResult = unknown>(opts: {
29
44
  resource: TResource;
30
45
  params: ISendParams;
31
- }) => Promise<ISendResponse<TResult>>;
46
+ }) => Promise<INetworkResponse<TResult>>;
47
+ /** The network service every request goes through. */
32
48
  getNetworkService(): DefaultNetworkRequestService;
33
49
  /** Spread into an `HttpDataSource`: the repository then sends what this provider sends. */
34
50
  getDataSourceAuth(): IDataSourceAuth;
@@ -45,7 +61,12 @@ export interface IAuthRejection {
45
61
  logoutUser?: boolean;
46
62
  message?: string | false;
47
63
  }
64
+ /** The auth provider of an ARDOR application: react-admin's methods, the user's roles and a token refresh. */
48
65
  export interface IAuthProvider extends IReactAdminAuthProvider {
66
+ /**
67
+ * The roles of the signed-in user, as the browser holds them: they only show or hide interface, and
68
+ * the backend must check every permission on every request.
69
+ */
49
70
  getRoles: (params?: unknown) => Promise<Set<string>>;
50
71
  /** `true` when the session was refreshed, `false` when there is no refresh to make. */
51
72
  refreshToken: () => Promise<boolean>;
@@ -56,19 +77,31 @@ export interface IAuthProvider extends IReactAdminAuthProvider {
56
77
  * is the messages). A loader that fails leaves that locale on the English messages.
57
78
  */
58
79
  export type TI18nSource = object | (() => Promise<object>);
80
+ /** What `DefaultI18nProvider` is built from: the message sources and the languages on offer. */
59
81
  export interface II18nProviderOptions {
60
- /** Messages by locale: the application's own over the features', English for a locale with no source or whose loader failed. */
82
+ /**
83
+ * Messages by locale: the application's own over the features', English for a locale with no source or whose loader failed.
84
+ * @default { en: englishMessages }
85
+ */
61
86
  i18nSources?: Record<string, TI18nSource>;
87
+ /**
88
+ * The locales the app offers.
89
+ * @default [{ locale: 'en', name: 'English' }]
90
+ */
62
91
  languages?: Array<Locale>;
63
92
  }
93
+ /** The props of `ArdorApplication`: react-admin's `CoreAdmin` props except `children`, plus the ARDOR ones. */
64
94
  export interface IApplication extends Omit<CoreAdminProps, 'children'> {
95
+ /** The container the providers and the features are resolved from. */
65
96
  container: Container;
66
97
  /**
67
- * The logger `useApplicationLogger()` returns. Absent, a console logger named after the application
98
+ * The logger `useApplicationLogger()` returns. Absent, a console logger named `ArdorApplication`
68
99
  * (IGNIS's, so a provider registered with `LoggerFactory.use` is the one behind it).
69
100
  */
70
101
  logger?: ILogger;
71
- reduxStore: Store;
102
+ /** The store the Redux provider of the shell serves. Absent, the shell mounts no Redux provider. */
103
+ reduxStore?: Store;
104
+ /** Rendered as the `Suspense` fallback and while feature messages for the locale load. */
72
105
  suspense: ReactNode;
73
106
  /** The application's own resources; each feature's are mounted after them. */
74
107
  resources?: Array<ResourceProps>;
@@ -1,4 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { IApplication } from '../common/types.js';
3
+ /**
4
+ * The application shell. It resolves the data, auth and i18n providers from `container`, mounts the
5
+ * `resources` and `routes` of the application and of each feature inside react-admin, and wraps it
6
+ * all in `ApplicationContext.Provider` (`container`, `logger`), the Redux provider (only when
7
+ * `reduxStore` is passed) and `Suspense`. While a feature's lazy messages for the current locale
8
+ * load, it renders `suspense` instead of react-admin; a failed load is thrown from render. Remaining
9
+ * props go to react-admin's `CoreAdmin`.
10
+ */
3
11
  export declare const ArdorApplication: React.FC<IApplication>;
4
12
  //# sourceMappingURL=application.d.ts.map
@@ -1,10 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { LoggerFactory } from '@venizia/ignis-helpers/core';
4
+ import { QueryClient } from '@tanstack/react-query';
4
5
  import { CoreAdmin, CustomRoutes, Resource } from 'ra-core';
5
6
  import { Provider as ReduxProvider } from 'react-redux';
6
7
  import { Route } from 'react-router-dom';
7
8
  import { Features } from '../features/feature.js';
9
+ import { CoreBindings } from '@venizia/ardor-kernel';
8
10
  import { ApplicationContext } from '@venizia/ardor-react';
9
11
  import { ApplicationProviders } from './resolve.js';
10
12
  const Wrapper = ({ applicationName = 'ArdorApplication', container, reduxStore, suspense, logger, children, }) => {
@@ -12,7 +14,8 @@ const Wrapper = ({ applicationName = 'ArdorApplication', container, reduxStore,
12
14
  const applicationLogger = React.useMemo(() => {
13
15
  return logger ?? LoggerFactory.getLogger([applicationName]);
14
16
  }, [logger, applicationName]);
15
- return (_jsx(ApplicationContext.Provider, { value: { container, logger: applicationLogger }, children: _jsx(ReduxProvider, { store: reduxStore, children: _jsx(React.Suspense, { fallback: suspense, children: children }) }) }));
17
+ const content = _jsx(React.Suspense, { fallback: suspense, children: children });
18
+ return (_jsx(ApplicationContext.Provider, { value: { container, logger: applicationLogger }, children: reduxStore ? _jsx(ReduxProvider, { store: reduxStore, children: content }) : content }));
16
19
  };
17
20
  /**
18
21
  * The i18n provider is built with the initial locale's eager messages only; when a feature loads
@@ -49,6 +52,14 @@ const useFeatureMessagesReady = (opts) => {
49
52
  }
50
53
  return state.isReady;
51
54
  };
55
+ /**
56
+ * The application shell. It resolves the data, auth and i18n providers from `container`, mounts the
57
+ * `resources` and `routes` of the application and of each feature inside react-admin, and wraps it
58
+ * all in `ApplicationContext.Provider` (`container`, `logger`), the Redux provider (only when
59
+ * `reduxStore` is passed) and `Suspense`. While a feature's lazy messages for the current locale
60
+ * load, it renders `suspense` instead of react-admin; a failed load is thrown from render. Remaining
61
+ * props go to react-admin's `CoreAdmin`.
62
+ */
52
63
  export const ArdorApplication = (props) => {
53
64
  const { container, reduxStore, suspense, logger, resources, routes, features: selected, ...reactAdminProps } = props;
54
65
  // The list the shell passed, else every feature the application bound.
@@ -63,7 +74,31 @@ export const ArdorApplication = (props) => {
63
74
  const providers = React.useMemo(() => {
64
75
  return ApplicationProviders.resolve({ container });
65
76
  }, [container]);
66
- const adminProps = { ...providers, ...reactAdminProps };
77
+ // The cache is owned here, so it can be emptied when the session ends by any route (a sign-out
78
+ // button empties it itself; a failed refresh, or code that calls `cleanUp`, does not).
79
+ const queryClient = React.useMemo(() => {
80
+ return reactAdminProps.queryClient ?? new QueryClient();
81
+ }, [reactAdminProps.queryClient]);
82
+ React.useEffect(() => {
83
+ const authService = container.get({
84
+ key: CoreBindings.DEFAULT_AUTH_SERVICE,
85
+ isOptional: true,
86
+ });
87
+ return authService?.subscribe({
88
+ listener: () => {
89
+ if (!authService.getAuth()?.value) {
90
+ // After the redirect to the sign-in page, as react-admin's own sign-out does.
91
+ setTimeout(() => {
92
+ // Not when the next user signed in meanwhile.
93
+ if (!authService.getAuth()?.value) {
94
+ queryClient.clear();
95
+ }
96
+ }, 0);
97
+ }
98
+ },
99
+ });
100
+ }, [container, queryClient]);
101
+ const adminProps = { ...providers, ...reactAdminProps, queryClient };
67
102
  const isMessagesReady = useFeatureMessagesReady({
68
103
  features,
69
104
  i18nProvider: providers.i18nProvider,
@@ -1,17 +1,28 @@
1
1
  import type { Container } from '@venizia/ignis-inversion';
2
2
  import type { I18nProvider } from 'ra-core';
3
3
  import type { IAuthProvider, IDataProvider } from '../common/index.js';
4
+ interface IResolvedProviders {
5
+ dataProvider: IDataProvider;
6
+ authProvider?: IAuthProvider;
7
+ i18nProvider: I18nProvider;
8
+ }
9
+ /** Resolves the providers `ArdorApplication` hands to react-admin. */
4
10
  export declare class ApplicationProviders {
5
11
  /**
6
12
  * What `ArdorApplication` resolves from the container when it mounts: the one place, so a test of
7
- * the boot resolves exactly what the app does.
13
+ * the boot resolves exactly what the app does. With `isAuthRequired: true`, for an application that
14
+ * always binds a sign-in, `authProvider` is typed as present and an unbound one throws.
8
15
  */
9
16
  static resolve(opts: {
10
17
  container: Container;
11
- }): {
12
- dataProvider: IDataProvider;
13
- authProvider?: IAuthProvider;
14
- i18nProvider: I18nProvider;
18
+ isAuthRequired: true;
19
+ }): IResolvedProviders & {
20
+ authProvider: IAuthProvider;
15
21
  };
22
+ static resolve(opts: {
23
+ container: Container;
24
+ isAuthRequired?: boolean;
25
+ }): IResolvedProviders;
16
26
  }
27
+ export {};
17
28
  //# sourceMappingURL=resolve.d.ts.map
@@ -1,11 +1,8 @@
1
1
  import { CoreBindings } from '@venizia/ardor-kernel';
2
+ /** Resolves the providers `ArdorApplication` hands to react-admin. */
2
3
  export class ApplicationProviders {
3
- /**
4
- * What `ArdorApplication` resolves from the container when it mounts: the one place, so a test of
5
- * the boot resolves exactly what the app does.
6
- */
7
4
  static resolve(opts) {
8
- const { container } = opts;
5
+ const { container, isAuthRequired } = opts;
9
6
  const dataProvider = container.get({
10
7
  key: CoreBindings.DEFAULT_REST_DATA_PROVIDER,
11
8
  });
@@ -13,7 +10,7 @@ export class ApplicationProviders {
13
10
  // without an auth provider - every page is public.
14
11
  const authProvider = container.get({
15
12
  key: CoreBindings.DEFAULT_AUTH_PROVIDER,
16
- isOptional: true,
13
+ isOptional: !isAuthRequired,
17
14
  });
18
15
  const i18nProvider = container.get({
19
16
  key: CoreBindings.DEFAULT_I18N_PROVIDER,
@@ -15,6 +15,7 @@ export type TFeature<TExtension extends object = {}> = IFeatureBase & TExtension
15
15
  /** The application's own `i18nSources` override these. */
16
16
  messages?: TFeatureMessages;
17
17
  };
18
+ /** Defines, reads, mounts and loads the messages of the features an application binds. */
18
19
  export declare class Features {
19
20
  /** Types a feature, keeping the shell's extension. */
20
21
  static define<TExtension extends object = {}>(feature: TFeature<TExtension>): TFeature<TExtension>;
@@ -28,7 +29,9 @@ export declare class Features {
28
29
  */
29
30
  static mount(opts: {
30
31
  features: Array<TFeature>;
32
+ /** @default [] */
31
33
  resources?: Array<ResourceProps>;
34
+ /** @default [] */
32
35
  routes?: Array<RouteProps>;
33
36
  }): {
34
37
  resources: Array<ResourceProps>;
@@ -46,6 +49,7 @@ export declare class Features {
46
49
  static loadMessages(opts: {
47
50
  features: Array<TFeature>;
48
51
  i18nProvider: I18nProvider;
52
+ /** The locale to load. @default i18nProvider.getLocale() */
49
53
  locale?: string;
50
54
  }): Promise<void>;
51
55
  }
@@ -2,6 +2,7 @@ import { getError } from '@venizia/ignis-inversion';
2
2
  import { CoreBindings } from '@venizia/ardor-kernel';
3
3
  import { LazyLocales } from '../providers/i18n-sources.js';
4
4
  import { FeatureMessages } from './messages.js';
5
+ /** Defines, reads, mounts and loads the messages of the features an application binds. */
5
6
  export class Features {
6
7
  /** Types a feature, keeping the shell's extension. */
7
8
  static define(feature) {
@@ -1,6 +1,5 @@
1
1
  import { getError } from '@venizia/ignis-inversion';
2
- import isPlainObject from 'lodash/isPlainObject.js';
3
- import merge from 'lodash/merge.js';
2
+ import { deepMerge, isRecord } from '../common/merge.js';
4
3
  /**
5
4
  * The features' messages for one locale, merged. Keys stay flat - no namespace is added - so a key
6
5
  * two features both define throws, naming both: a silent "later wins" is a wrong label nobody
@@ -74,11 +73,11 @@ export class FeatureMessages {
74
73
  owners.set(key, feature);
75
74
  }
76
75
  }
77
- return merge({}, ...bundles.map(({ bundle }) => bundle ?? {}));
76
+ return deepMerge({ sources: bundles.map(({ bundle }) => bundle ?? {}) });
78
77
  }
79
78
  static leafKeys(opts) {
80
79
  const { value, prefix } = opts;
81
- if (!isPlainObject(value)) {
80
+ if (!isRecord(value)) {
82
81
  return prefix ? [prefix] : [];
83
82
  }
84
83
  return Object.entries(value).flatMap(([key, child]) => {
@@ -1,8 +1,13 @@
1
1
  type TDocumentTitleSegment = string | false | null | undefined;
2
+ /** What `useDocumentTitle` writes into the tab title, and when. */
2
3
  export interface IUseDocumentTitleOptions {
4
+ /** The screen title, or a list of segments in order; `false`, `null`, `undefined` and empty segments are dropped. */
3
5
  title: TDocumentTitleSegment | Array<TDocumentTitleSegment>;
6
+ /** The last segment of the tab title. */
4
7
  appTitle: string;
8
+ /** Joins the segments. @default ' · ' */
5
9
  separator?: string;
10
+ /** When `false` the call writes no title. @default true */
6
11
  enabled?: boolean;
7
12
  /**
8
13
  * A base title for a layout. It shows only while no other call is mounted, so a screen's title
@@ -1,8 +1,8 @@
1
- import { useInjectableContainer } from '@venizia/ardor-react';
1
+ import { useApplicationContainer } from '@venizia/ardor-react';
2
2
  import { Features } from '../features/feature.js';
3
3
  /** The features the application mounts - for a menu, or a role editor listing their `permissions`. */
4
4
  export const useFeatures = () => {
5
- const container = useInjectableContainer();
5
+ const container = useApplicationContainer();
6
6
  return Features.read({ container });
7
7
  };
8
8
  //# sourceMappingURL=use-features.js.map
@@ -1,5 +1,9 @@
1
1
  import type { ApplicationError } from '@venizia/ignis-inversion';
2
2
  import type { NotificationOptions, NotificationType } from 'ra-core';
3
+ /**
4
+ * Returns a stable function that shows an error notification for an `ApplicationError`: the message
5
+ * is its normalized `code`, with its normalized `args` as message arguments. `options` override both.
6
+ */
3
7
  export declare const useNotifyError: () => (error: ApplicationError, options?: NotificationOptions & {
4
8
  type?: NotificationType;
5
9
  }) => void;
@@ -1,5 +1,9 @@
1
1
  import React from 'react';
2
2
  import { useNotify } from 'ra-core';
3
+ /**
4
+ * Returns a stable function that shows an error notification for an `ApplicationError`: the message
5
+ * is its normalized `code`, with its normalized `args` as message arguments. `options` override both.
6
+ */
3
7
  export const useNotifyError = () => {
4
8
  const notify = useNotify();
5
9
  return React.useCallback((error, options) => {
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Returns a function that refreshes the session through the auth provider's `refreshToken` and, once
3
+ * that resolves, invalidates the cached permissions. It rejects when `refreshToken` rejects.
4
+ */
1
5
  export declare const useRefreshToken: () => TRefreshToken;
2
6
  /** `true` when the session was refreshed, `false` when there is no auth provider or no refresh to make. */
3
7
  type TRefreshToken = () => Promise<boolean>;
@@ -3,6 +3,10 @@ import { useQueryClient } from '@tanstack/react-query';
3
3
  import { useAuthProvider } from 'ra-core';
4
4
  import { LoggerFactory } from '@venizia/ignis-helpers/core';
5
5
  const logger = LoggerFactory.getLogger(['useRefreshToken']);
6
+ /**
7
+ * Returns a function that refreshes the session through the auth provider's `refreshToken` and, once
8
+ * that resolves, invalidates the cached permissions. It rejects when `refreshToken` rejects.
9
+ */
6
10
  export const useRefreshToken = () => {
7
11
  const authProvider = useAuthProvider();
8
12
  const queryClient = useQueryClient();
@@ -1,6 +1,14 @@
1
+ /** The header `useRequestHeaderLocale` sends the locale in. */
1
2
  export interface IUseRequestHeaderLocaleOptions {
2
- /** The header the locale is sent in. Default `x-locale`. */
3
+ /**
4
+ * The header the locale is sent in.
5
+ * @default 'x-locale'
6
+ */
3
7
  key?: string;
4
8
  }
9
+ /**
10
+ * Keeps the current react-admin locale in a header of the default REST data provider's network
11
+ * service, re-set on each locale change. `opts.key` defaults to `x-locale`; an empty key falls back to it.
12
+ */
5
13
  export declare const useRequestHeaderLocale: (opts?: IUseRequestHeaderLocaleOptions) => void;
6
14
  //# sourceMappingURL=use-request-header-locale.d.ts.map
@@ -2,6 +2,10 @@ import React from 'react';
2
2
  import { useLocaleState } from 'ra-core';
3
3
  import { CoreBindings, HeaderConstants } from '@venizia/ardor-kernel';
4
4
  import { useInjectable } from '@venizia/ardor-react';
5
+ /**
6
+ * Keeps the current react-admin locale in a header of the default REST data provider's network
7
+ * service, re-set on each locale change. `opts.key` defaults to `x-locale`; an empty key falls back to it.
8
+ */
5
9
  export const useRequestHeaderLocale = (opts) => {
6
10
  const key = opts?.key?.length ? opts.key : HeaderConstants.X_LOCALE;
7
11
  const [locale] = useLocaleState();
@@ -1,12 +1,31 @@
1
- import { type I18nProvider } from 'ra-core';
1
+ import { type I18nProvider, type TranslateFunction } from 'ra-core';
2
2
  import type { TFullPaths } from '@venizia/ardor-kernel';
3
3
  import type { englishMessages } from '../common/locales/index.js';
4
+ /**
5
+ * Augment this interface to add keys to `TUseTranslateKeys` (messages an app adds to the defaults):
6
+ *
7
+ * ```ts
8
+ * declare module '@venizia/ardor' {
9
+ * interface IUseTranslateKeysOverrides {
10
+ * 'app.orders.title': unknown;
11
+ * }
12
+ * }
13
+ * ```
14
+ */
4
15
  export interface IUseTranslateKeysOverrides {
5
16
  }
17
+ /** Every dotted path to a message in the built-in English messages. */
6
18
  export type TUseTranslateKeysDefault = TFullPaths<typeof englishMessages>;
19
+ /** The keys `useTranslate` accepts: the built-in message paths plus the keys added through `IUseTranslateKeysOverrides`. */
7
20
  export type TUseTranslateKeys = TUseTranslateKeysDefault | keyof IUseTranslateKeysOverrides;
8
21
  /** The options react-admin's `translate` takes (`smart_count`, `_` for a default text, ...). */
9
22
  export type TUseTranslateOptions = Parameters<I18nProvider['translate']>[1];
23
+ /** `translate` with the key narrowed to `TUseTranslateKeys`. */
10
24
  export type TUseTranslateFn = (key: TUseTranslateKeys, options?: TUseTranslateOptions) => string;
25
+ /** `translate` as react-admin types it, taking any string key. */
26
+ export type TUseUntypedTranslateFn = TranslateFunction;
27
+ /** For a key that exists only at run time (`errors.${code}`); a key written in code belongs to `useTranslate`. */
28
+ export declare const useUntypedTranslate: () => TUseUntypedTranslateFn;
29
+ /** The same function as `useUntypedTranslate`, with the key narrowed to `TUseTranslateKeys`. */
11
30
  export declare const useTranslate: () => TUseTranslateFn;
12
31
  //# sourceMappingURL=use-translate.d.ts.map
@@ -1,11 +1,6 @@
1
- import React from 'react';
2
- import { useI18nProvider } from 'ra-core';
3
- const identity = (key) => key;
4
- export const useTranslate = () => {
5
- const i18nProvider = useI18nProvider();
6
- const translate = React.useCallback((key, options) => {
7
- return i18nProvider.translate(key, options);
8
- }, [i18nProvider]);
9
- return i18nProvider ? translate : identity;
10
- };
1
+ import { useTranslate as useRaTranslate } from 'ra-core';
2
+ /** For a key that exists only at run time (`errors.${code}`); a key written in code belongs to `useTranslate`. */
3
+ export const useUntypedTranslate = useRaTranslate;
4
+ /** The same function as `useUntypedTranslate`, with the key narrowed to `TUseTranslateKeys`. */
5
+ export const useTranslate = useUntypedTranslate;
11
6
  //# sourceMappingURL=use-translate.js.map