@venizia/ardor-admin 0.4.8-0 → 0.4.8-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/README.md +8 -4
- package/dist/common/locales/en.d.ts +1 -0
- package/dist/common/locales/en.js +1 -0
- package/dist/common/locales/vi.d.ts +1 -0
- package/dist/common/locales/vi.js +1 -0
- package/dist/common/merge.d.ts +11 -0
- package/dist/common/merge.js +25 -0
- package/dist/common/types.d.ts +29 -2
- package/dist/components/application.d.ts +7 -0
- package/dist/components/application.js +7 -0
- package/dist/components/resolve.d.ts +1 -0
- package/dist/components/resolve.js +1 -0
- package/dist/features/feature.d.ts +4 -0
- package/dist/features/feature.js +1 -0
- package/dist/features/messages.js +3 -4
- package/dist/hooks/use-document-title.d.ts +4 -0
- package/dist/hooks/use-features.js +2 -2
- package/dist/hooks/use-notify-error.d.ts +4 -0
- package/dist/hooks/use-notify-error.js +4 -0
- package/dist/hooks/use-refresh-token.d.ts +4 -0
- package/dist/hooks/use-refresh-token.js +4 -0
- package/dist/hooks/use-request-header-locale.d.ts +8 -1
- package/dist/hooks/use-request-header-locale.js +4 -0
- package/dist/hooks/use-translate.d.ts +20 -1
- package/dist/hooks/use-translate.js +5 -10
- package/dist/providers/auth.d.ts +55 -1
- package/dist/providers/auth.js +54 -1
- package/dist/providers/count-rest-data.d.ts +8 -0
- package/dist/providers/count-rest-data.js +8 -0
- package/dist/providers/i18n.d.ts +22 -2
- package/dist/providers/i18n.js +28 -5
- package/dist/providers/list-filter.d.ts +6 -0
- package/dist/providers/list-filter.js +7 -8
- package/dist/providers/rest-data.d.ts +63 -1
- package/dist/providers/rest-data.js +74 -8
- package/package.json +14 -16
- package/dist/common/index.d.ts.map +0 -1
- package/dist/common/index.js.map +0 -1
- package/dist/common/locales/en.d.ts.map +0 -1
- package/dist/common/locales/en.js.map +0 -1
- package/dist/common/locales/index.d.ts.map +0 -1
- package/dist/common/locales/index.js.map +0 -1
- package/dist/common/locales/vi.d.ts.map +0 -1
- package/dist/common/locales/vi.js.map +0 -1
- package/dist/common/types.d.ts.map +0 -1
- package/dist/common/types.js.map +0 -1
- package/dist/components/application.d.ts.map +0 -1
- package/dist/components/application.js.map +0 -1
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js.map +0 -1
- package/dist/components/resolve.d.ts.map +0 -1
- package/dist/components/resolve.js.map +0 -1
- package/dist/features/feature.d.ts.map +0 -1
- package/dist/features/feature.js.map +0 -1
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/index.js.map +0 -1
- package/dist/features/messages.d.ts.map +0 -1
- package/dist/features/messages.js.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/use-document-title.d.ts.map +0 -1
- package/dist/hooks/use-document-title.js.map +0 -1
- package/dist/hooks/use-features.d.ts.map +0 -1
- package/dist/hooks/use-features.js.map +0 -1
- package/dist/hooks/use-html-lang.d.ts.map +0 -1
- package/dist/hooks/use-html-lang.js.map +0 -1
- package/dist/hooks/use-notify-error.d.ts.map +0 -1
- package/dist/hooks/use-notify-error.js.map +0 -1
- package/dist/hooks/use-refresh-token.d.ts.map +0 -1
- package/dist/hooks/use-refresh-token.js.map +0 -1
- package/dist/hooks/use-request-header-locale.d.ts.map +0 -1
- package/dist/hooks/use-request-header-locale.js.map +0 -1
- package/dist/hooks/use-translate.d.ts.map +0 -1
- package/dist/hooks/use-translate.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers/auth.d.ts.map +0 -1
- package/dist/providers/auth.js.map +0 -1
- package/dist/providers/count-rest-data.d.ts.map +0 -1
- package/dist/providers/count-rest-data.js.map +0 -1
- package/dist/providers/i18n-sources.d.ts.map +0 -1
- package/dist/providers/i18n-sources.js.map +0 -1
- package/dist/providers/i18n.d.ts.map +0 -1
- package/dist/providers/i18n.js.map +0 -1
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/list-filter.d.ts.map +0 -1
- package/dist/providers/list-filter.js.map +0 -1
- package/dist/providers/rest-data.d.ts.map +0 -1
- package/dist/providers/rest-data.js.map +0 -1
package/README.md
CHANGED
|
@@ -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
|
-
|
|
|
22
|
-
|
|
|
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 }`.
|
|
@@ -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
|
package/dist/common/types.d.ts
CHANGED
|
@@ -9,26 +9,41 @@ import type { DefaultNetworkRequestService, IDataSourceAuth, ISendParams, ISendR
|
|
|
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
|
-
/**
|
|
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'>;
|
|
27
40
|
export interface IDataProvider<TResource extends string = string> extends IReactAdminDataProvider<TResource> {
|
|
41
|
+
/** A request outside react-admin's methods: the same headers and auth, the method and body of `params`. */
|
|
28
42
|
send: <TResult = unknown>(opts: {
|
|
29
43
|
resource: TResource;
|
|
30
44
|
params: ISendParams;
|
|
31
45
|
}) => Promise<ISendResponse<TResult>>;
|
|
46
|
+
/** The network service every request goes through. */
|
|
32
47
|
getNetworkService(): DefaultNetworkRequestService;
|
|
33
48
|
/** Spread into an `HttpDataSource`: the repository then sends what this provider sends. */
|
|
34
49
|
getDataSourceAuth(): IDataSourceAuth;
|
|
@@ -46,6 +61,7 @@ export interface IAuthRejection {
|
|
|
46
61
|
message?: string | false;
|
|
47
62
|
}
|
|
48
63
|
export interface IAuthProvider extends IReactAdminAuthProvider {
|
|
64
|
+
/** The roles of the signed-in user. */
|
|
49
65
|
getRoles: (params?: unknown) => Promise<Set<string>>;
|
|
50
66
|
/** `true` when the session was refreshed, `false` when there is no refresh to make. */
|
|
51
67
|
refreshToken: () => Promise<boolean>;
|
|
@@ -57,18 +73,29 @@ export interface IAuthProvider extends IReactAdminAuthProvider {
|
|
|
57
73
|
*/
|
|
58
74
|
export type TI18nSource = object | (() => Promise<object>);
|
|
59
75
|
export interface II18nProviderOptions {
|
|
60
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Messages by locale: the application's own over the features', English for a locale with no source or whose loader failed.
|
|
78
|
+
* @default { en: englishMessages }
|
|
79
|
+
*/
|
|
61
80
|
i18nSources?: Record<string, TI18nSource>;
|
|
81
|
+
/**
|
|
82
|
+
* The locales the app offers.
|
|
83
|
+
* @default [{ locale: 'en', name: 'English' }]
|
|
84
|
+
*/
|
|
62
85
|
languages?: Array<Locale>;
|
|
63
86
|
}
|
|
87
|
+
/** The props of `ArdorApplication`: react-admin's `CoreAdmin` props except `children`, plus the ARDOR ones. */
|
|
64
88
|
export interface IApplication extends Omit<CoreAdminProps, 'children'> {
|
|
89
|
+
/** The container the providers and the features are resolved from. */
|
|
65
90
|
container: Container;
|
|
66
91
|
/**
|
|
67
92
|
* The logger `useApplicationLogger()` returns. Absent, a console logger named after the application
|
|
68
93
|
* (IGNIS's, so a provider registered with `LoggerFactory.use` is the one behind it).
|
|
69
94
|
*/
|
|
70
95
|
logger?: ILogger;
|
|
96
|
+
/** The store the Redux provider of the shell serves. */
|
|
71
97
|
reduxStore: Store;
|
|
98
|
+
/** Rendered as the `Suspense` fallback and while feature messages for the locale load. */
|
|
72
99
|
suspense: ReactNode;
|
|
73
100
|
/** The application's own resources; each feature's are mounted after them. */
|
|
74
101
|
resources?: Array<ResourceProps>;
|
|
@@ -1,4 +1,11 @@
|
|
|
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 and `Suspense`.
|
|
7
|
+
* While a feature's lazy messages for the current locale load, it renders `suspense` instead of
|
|
8
|
+
* react-admin; a failed load is thrown from render. Remaining props go to react-admin's `CoreAdmin`.
|
|
9
|
+
*/
|
|
3
10
|
export declare const ArdorApplication: React.FC<IApplication>;
|
|
4
11
|
//# sourceMappingURL=application.d.ts.map
|
|
@@ -49,6 +49,13 @@ const useFeatureMessagesReady = (opts) => {
|
|
|
49
49
|
}
|
|
50
50
|
return state.isReady;
|
|
51
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* The application shell. It resolves the data, auth and i18n providers from `container`, mounts the
|
|
54
|
+
* `resources` and `routes` of the application and of each feature inside react-admin, and wraps it
|
|
55
|
+
* all in `ApplicationContext.Provider` (`container`, `logger`), the Redux provider and `Suspense`.
|
|
56
|
+
* While a feature's lazy messages for the current locale load, it renders `suspense` instead of
|
|
57
|
+
* react-admin; a failed load is thrown from render. Remaining props go to react-admin's `CoreAdmin`.
|
|
58
|
+
*/
|
|
52
59
|
export const ArdorApplication = (props) => {
|
|
53
60
|
const { container, reduxStore, suspense, logger, resources, routes, features: selected, ...reactAdminProps } = props;
|
|
54
61
|
// The list the shell passed, else every feature the application bound.
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
/** Resolves the providers `ArdorApplication` hands to react-admin. */
|
|
4
5
|
export declare class ApplicationProviders {
|
|
5
6
|
/**
|
|
6
7
|
* What `ArdorApplication` resolves from the container when it mounts: the one place, so a test of
|
|
@@ -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
|
}
|
package/dist/features/feature.js
CHANGED
|
@@ -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
|
|
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
|
|
76
|
+
return deepMerge({ sources: bundles.map(({ bundle }) => bundle ?? {}) });
|
|
78
77
|
}
|
|
79
78
|
static leafKeys(opts) {
|
|
80
79
|
const { value, prefix } = opts;
|
|
81
|
-
if (!
|
|
80
|
+
if (!isRecord(value)) {
|
|
82
81
|
return prefix ? [prefix] : [];
|
|
83
82
|
}
|
|
84
83
|
return Object.entries(value).flatMap(([key, child]) => {
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
type TDocumentTitleSegment = string | false | null | undefined;
|
|
2
2
|
export interface IUseDocumentTitleOptions {
|
|
3
|
+
/** The screen title, or a list of segments in order; `false`, `null`, `undefined` and empty segments are dropped. */
|
|
3
4
|
title: TDocumentTitleSegment | Array<TDocumentTitleSegment>;
|
|
5
|
+
/** The last segment of the tab title. */
|
|
4
6
|
appTitle: string;
|
|
7
|
+
/** Joins the segments. @default ' · ' */
|
|
5
8
|
separator?: string;
|
|
9
|
+
/** When `false` the call writes no title. @default true */
|
|
6
10
|
enabled?: boolean;
|
|
7
11
|
/**
|
|
8
12
|
* 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 {
|
|
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 =
|
|
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,13 @@
|
|
|
1
1
|
export interface IUseRequestHeaderLocaleOptions {
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* The header the locale is sent in.
|
|
4
|
+
* @default 'x-locale'
|
|
5
|
+
*/
|
|
3
6
|
key?: string;
|
|
4
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Keeps the current react-admin locale in a header of the default REST data provider's network
|
|
10
|
+
* service, re-set on each locale change. `opts.key` defaults to `x-locale`; an empty key falls back to it.
|
|
11
|
+
*/
|
|
5
12
|
export declare const useRequestHeaderLocale: (opts?: IUseRequestHeaderLocaleOptions) => void;
|
|
6
13
|
//# 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
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
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
|
package/dist/providers/auth.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class SessionClearReasons {
|
|
|
18
18
|
/** `checkAuth` rejected: no token is stored, or the server answered with no data. */
|
|
19
19
|
static readonly REJECTED = "rejected";
|
|
20
20
|
}
|
|
21
|
+
/** One of the `SessionClearReasons` values. */
|
|
21
22
|
export type TSessionClearReason = TStringConstValue<typeof SessionClearReasons>;
|
|
22
23
|
/**
|
|
23
24
|
* The options hold data (`paths`); behaviour is in the protected hooks around `login` and the page
|
|
@@ -28,6 +29,27 @@ export type TSessionClearReason = TStringConstValue<typeof SessionClearReasons>;
|
|
|
28
29
|
* hook that throws, or does not settle in `sessionHookTimeout`, is logged and does not stop the call
|
|
29
30
|
* that fired it. A hook does not call this provider's methods: they wait behind it. Build one provider
|
|
30
31
|
* per auth service: a second one listening to the same service calls every hook again.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { type BaseArdorApplication, configuration, CoreBindings, DefaultAuthProvider, inject, provide } from '@venizia/ardor';
|
|
36
|
+
*
|
|
37
|
+
* class AppAuthProvider extends DefaultAuthProvider {
|
|
38
|
+
* protected override get afterLoginPath() {
|
|
39
|
+
* return '/products';
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* @configuration()
|
|
44
|
+
* export class AuthConfiguration {
|
|
45
|
+
* constructor(@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private readonly application: BaseArdorApplication) {}
|
|
46
|
+
*
|
|
47
|
+
* @provide({ key: CoreBindings.DEFAULT_AUTH_PROVIDER })
|
|
48
|
+
* authProvider() {
|
|
49
|
+
* return this.application.instantiate(AppAuthProvider).value(this.application);
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
31
53
|
*/
|
|
32
54
|
export declare class DefaultAuthProvider<TResource extends string = string> extends BaseProvider<IAuthProvider> {
|
|
33
55
|
protected restDataProvider: IDataProvider<TResource>;
|
|
@@ -149,18 +171,50 @@ export declare class DefaultAuthProvider<TResource extends string = string> exte
|
|
|
149
171
|
private clearSession;
|
|
150
172
|
/** Announces the session in the store, when there is one. */
|
|
151
173
|
private startStoredSession;
|
|
152
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Posts the sign-in body to `signInPath`, stores the session and resolves `{ ...response,
|
|
176
|
+
* redirectTo }`, where `redirectTo` is `afterLoginPath`. Rejects when the request fails, when the
|
|
177
|
+
* response carries no token or user id, or when `beforeSaveSession` throws; no session is stored
|
|
178
|
+
* then. Typed `Promise<unknown>` so that a subclass override of `login` keeps compiling.
|
|
179
|
+
*/
|
|
153
180
|
login(params: ILoginParams): Promise<unknown>;
|
|
181
|
+
/** Clears the session and resolves. */
|
|
154
182
|
logout(_params?: unknown): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Resolves when a token is stored and, if `paths.checkAuth` is set, the server answers with data.
|
|
185
|
+
* Otherwise it ends the session (reason `rejected`) and rejects with `unauthenticatedRejection`;
|
|
186
|
+
* it rejects with the request's error when the check request fails.
|
|
187
|
+
*/
|
|
155
188
|
checkAuth(_params?: unknown): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Resolves for every error but 401 and 403. A 401 clears the session (unless
|
|
191
|
+
* `unauthenticatedRejection` says `logoutUser: false`) and rejects with that rejection; a 403
|
|
192
|
+
* rejects with `{ redirectTo: unauthorizedPagePath, logoutUser: false }`.
|
|
193
|
+
*/
|
|
156
194
|
checkError(error: unknown): Promise<void>;
|
|
195
|
+
/** Resolves the stored user; rejects when there is no `userId`. */
|
|
157
196
|
getIdentity(_params?: unknown): Promise<any>;
|
|
197
|
+
/** Resolves with nothing; the roles come from `getRoles`. */
|
|
158
198
|
getPermissions(_params?: unknown): Promise<void>;
|
|
199
|
+
/** Resolves the roles of the stored session. */
|
|
159
200
|
getRoles(_params?: unknown): Promise<Set<string>>;
|
|
160
201
|
/**
|
|
161
202
|
* Refreshes the session through the network service's `refresh`, the one place a refresh is made:
|
|
162
203
|
* `true` when it was refreshed, `false` when the service has no refresh to make. The `401`
|
|
163
204
|
* recovery calls the same method, so one burst of 401s and this call share one refresh.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```tsx
|
|
208
|
+
* import { useRefreshToken } from '@venizia/ardor';
|
|
209
|
+
*
|
|
210
|
+
* // The data provider options name the refresh endpoint:
|
|
211
|
+
* // { url: '/api', authRecovery: { refreshTokenPath: '/auth/refresh-token' } }
|
|
212
|
+
* export const RefreshButton = () => {
|
|
213
|
+
* const refreshToken = useRefreshToken();
|
|
214
|
+
*
|
|
215
|
+
* return <button onClick={() => refreshToken()}>Refresh</button>;
|
|
216
|
+
* };
|
|
217
|
+
* ```
|
|
164
218
|
*/
|
|
165
219
|
refreshToken(): Promise<boolean>;
|
|
166
220
|
value(container: Container): IAuthProvider;
|
package/dist/providers/auth.js
CHANGED
|
@@ -42,6 +42,27 @@ const readStatus = (error) => {
|
|
|
42
42
|
* hook that throws, or does not settle in `sessionHookTimeout`, is logged and does not stop the call
|
|
43
43
|
* that fired it. A hook does not call this provider's methods: they wait behind it. Build one provider
|
|
44
44
|
* per auth service: a second one listening to the same service calls every hook again.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { type BaseArdorApplication, configuration, CoreBindings, DefaultAuthProvider, inject, provide } from '@venizia/ardor';
|
|
49
|
+
*
|
|
50
|
+
* class AppAuthProvider extends DefaultAuthProvider {
|
|
51
|
+
* protected override get afterLoginPath() {
|
|
52
|
+
* return '/products';
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* @configuration()
|
|
57
|
+
* export class AuthConfiguration {
|
|
58
|
+
* constructor(@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private readonly application: BaseArdorApplication) {}
|
|
59
|
+
*
|
|
60
|
+
* @provide({ key: CoreBindings.DEFAULT_AUTH_PROVIDER })
|
|
61
|
+
* authProvider() {
|
|
62
|
+
* return this.application.instantiate(AppAuthProvider).value(this.application);
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
45
66
|
*/
|
|
46
67
|
let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider extends BaseProvider {
|
|
47
68
|
constructor(restDataProvider, authProviderOptions, authService, permissionProvider) {
|
|
@@ -295,7 +316,12 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
295
316
|
return this.startSession({ token, user: this.authService.getUser() });
|
|
296
317
|
}
|
|
297
318
|
// --- react-admin's auth provider -------------------------------------------------------------
|
|
298
|
-
/**
|
|
319
|
+
/**
|
|
320
|
+
* Posts the sign-in body to `signInPath`, stores the session and resolves `{ ...response,
|
|
321
|
+
* redirectTo }`, where `redirectTo` is `afterLoginPath`. Rejects when the request fails, when the
|
|
322
|
+
* response carries no token or user id, or when `beforeSaveSession` throws; no session is stored
|
|
323
|
+
* then. Typed `Promise<unknown>` so that a subclass override of `login` keeps compiling.
|
|
324
|
+
*/
|
|
299
325
|
async login(params) {
|
|
300
326
|
const response = await this.restDataProvider.send({
|
|
301
327
|
resource: this.signInPath,
|
|
@@ -316,9 +342,15 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
316
342
|
redirectTo: removeDoubleSlashes(`/${this.afterLoginPath}`),
|
|
317
343
|
};
|
|
318
344
|
}
|
|
345
|
+
/** Clears the session and resolves. */
|
|
319
346
|
async logout(_params) {
|
|
320
347
|
await this.clearSession({ reason: SessionClearReasons.LOGOUT });
|
|
321
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Resolves when a token is stored and, if `paths.checkAuth` is set, the server answers with data.
|
|
351
|
+
* Otherwise it ends the session (reason `rejected`) and rejects with `unauthenticatedRejection`;
|
|
352
|
+
* it rejects with the request's error when the check request fails.
|
|
353
|
+
*/
|
|
322
354
|
async checkAuth(_params) {
|
|
323
355
|
const token = this.authService.getAuth();
|
|
324
356
|
if (!token?.value) {
|
|
@@ -344,6 +376,11 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
344
376
|
await this.startStoredSession();
|
|
345
377
|
return Promise.resolve();
|
|
346
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Resolves for every error but 401 and 403. A 401 clears the session (unless
|
|
381
|
+
* `unauthenticatedRejection` says `logoutUser: false`) and rejects with that rejection; a 403
|
|
382
|
+
* rejects with `{ redirectTo: unauthorizedPagePath, logoutUser: false }`.
|
|
383
|
+
*/
|
|
347
384
|
async checkError(error) {
|
|
348
385
|
const status = readStatus(error);
|
|
349
386
|
if (status === 401) {
|
|
@@ -361,6 +398,7 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
361
398
|
}
|
|
362
399
|
return Promise.resolve();
|
|
363
400
|
}
|
|
401
|
+
/** Resolves the stored user; rejects when there is no `userId`. */
|
|
364
402
|
getIdentity(_params) {
|
|
365
403
|
const user = this.authService.getUser();
|
|
366
404
|
if (!user?.userId) {
|
|
@@ -368,9 +406,11 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
368
406
|
}
|
|
369
407
|
return Promise.resolve(user);
|
|
370
408
|
}
|
|
409
|
+
/** Resolves with nothing; the roles come from `getRoles`. */
|
|
371
410
|
getPermissions(_params) {
|
|
372
411
|
return Promise.resolve();
|
|
373
412
|
}
|
|
413
|
+
/** Resolves the roles of the stored session. */
|
|
374
414
|
getRoles(_params) {
|
|
375
415
|
return Promise.resolve(this.authService.getRoles());
|
|
376
416
|
}
|
|
@@ -378,6 +418,19 @@ let DefaultAuthProvider = DefaultAuthProvider_1 = class DefaultAuthProvider exte
|
|
|
378
418
|
* Refreshes the session through the network service's `refresh`, the one place a refresh is made:
|
|
379
419
|
* `true` when it was refreshed, `false` when the service has no refresh to make. The `401`
|
|
380
420
|
* recovery calls the same method, so one burst of 401s and this call share one refresh.
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```tsx
|
|
424
|
+
* import { useRefreshToken } from '@venizia/ardor';
|
|
425
|
+
*
|
|
426
|
+
* // The data provider options name the refresh endpoint:
|
|
427
|
+
* // { url: '/api', authRecovery: { refreshTokenPath: '/auth/refresh-token' } }
|
|
428
|
+
* export const RefreshButton = () => {
|
|
429
|
+
* const refreshToken = useRefreshToken();
|
|
430
|
+
*
|
|
431
|
+
* return <button onClick={() => refreshToken()}>Refresh</button>;
|
|
432
|
+
* };
|
|
433
|
+
* ```
|
|
381
434
|
*/
|
|
382
435
|
async refreshToken() {
|
|
383
436
|
const isRefreshed = await this.restDataProvider.getNetworkService().refresh();
|
|
@@ -3,7 +3,15 @@ import { type TRequestType } from '@venizia/ardor-kernel';
|
|
|
3
3
|
import type { TExtraRequest } from '@venizia/ignis-kernel/repository';
|
|
4
4
|
import type { IListFilter } from './list-filter.js';
|
|
5
5
|
import { DefaultRestDataProvider } from './rest-data.js';
|
|
6
|
+
/**
|
|
7
|
+
* A `DefaultRestDataProvider` whose list reads take their `total` from the count route
|
|
8
|
+
* (`GET /{resource}/count` with the same `where`) instead of the `Content-Range` header.
|
|
9
|
+
*/
|
|
6
10
|
export declare class CountRestDataProvider<TResource extends string = string> extends DefaultRestDataProvider<TResource> {
|
|
11
|
+
/**
|
|
12
|
+
* Sends the list GET and the count GET together; `total` is the count's `count`, or `0` when the
|
|
13
|
+
* count answers none. Rejects when either request rejects.
|
|
14
|
+
*/
|
|
7
15
|
protected requestList<RecordType extends RaRecord = RaRecord>(opts: {
|
|
8
16
|
resource: TResource;
|
|
9
17
|
type: TRequestType;
|