@equinor/fusion-framework-module-context 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +128 -110
- package/dist/esm/ContextConfigBuilder.js.map +1 -1
- package/dist/esm/ContextProvider.js +18 -11
- package/dist/esm/ContextProvider.js.map +1 -1
- package/dist/esm/client/ContextClient.js.map +1 -1
- package/dist/esm/configurator.js.map +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/selectors.js.map +1 -1
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/resolve-context-from-path.js +17 -12
- package/dist/esm/utils/resolve-context-from-path.js.map +1 -1
- package/dist/esm/utils/resolve-initial-context.js +3 -1
- package/dist/esm/utils/resolve-initial-context.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/resolve-context-from-path.d.ts +11 -2
- package/dist/types/utils/resolve-initial-context.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/ContextConfigBuilder.ts +2 -0
- package/src/ContextProvider.ts +192 -43
- package/src/errors.ts +20 -0
- package/src/module.ts +18 -1
- package/src/selectors.ts +26 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/resolve-context-from-path.ts +99 -6
- package/src/utils/resolve-initial-context.ts +27 -4
- package/src/version.ts +1 -1
package/src/errors.ts
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents an error that occurs during a search in the Fusion Context.
|
|
3
|
+
*/
|
|
1
4
|
export class FusionContextSearchError extends Error {
|
|
2
5
|
#details;
|
|
3
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The title of the error.
|
|
9
|
+
*/
|
|
4
10
|
get title(): string {
|
|
5
11
|
return this.#details.title;
|
|
6
12
|
}
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The description of the error, if available.
|
|
16
|
+
*/
|
|
8
17
|
get description(): string | undefined {
|
|
9
18
|
return this.#details.description;
|
|
10
19
|
}
|
|
11
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new instance of FusionContextSearchError.
|
|
23
|
+
* @param details - The details of the error.
|
|
24
|
+
* @param options - Optional parameters for the error.
|
|
25
|
+
*/
|
|
12
26
|
constructor(
|
|
13
27
|
details: {
|
|
28
|
+
/**
|
|
29
|
+
* The title of the error.
|
|
30
|
+
*/
|
|
14
31
|
title: string;
|
|
32
|
+
/**
|
|
33
|
+
* The description of the error, if available.
|
|
34
|
+
*/
|
|
15
35
|
description?: string;
|
|
16
36
|
},
|
|
17
37
|
options?: ErrorOptions,
|
package/src/module.ts
CHANGED
|
@@ -25,27 +25,41 @@ export const module: ContextModule = {
|
|
|
25
25
|
name: moduleKey,
|
|
26
26
|
configure: () => new ContextModuleConfigurator(),
|
|
27
27
|
initialize: async function (args) {
|
|
28
|
+
// create config from configurator
|
|
28
29
|
const config = await (args.config as ContextModuleConfigurator).createConfig(args);
|
|
30
|
+
|
|
31
|
+
// get event module if available
|
|
29
32
|
const event = args.hasModule('event') ? await args.requireInstance('event') : undefined;
|
|
33
|
+
|
|
34
|
+
// get parent context provider if available
|
|
30
35
|
const parentProvider = (args.ref as ModulesInstance<[ContextModule]>)?.context;
|
|
36
|
+
|
|
37
|
+
// create context provider
|
|
31
38
|
const provider = new ContextProvider({ config, event, parentContext: parentProvider });
|
|
32
39
|
|
|
40
|
+
// create subscription for disposing the provider
|
|
33
41
|
const subscription = new Subscription(() => provider.dispose());
|
|
34
42
|
|
|
43
|
+
// setup post initialize to module
|
|
35
44
|
this.postInitialize = (args) =>
|
|
45
|
+
// create observable for resolving initial context
|
|
36
46
|
new Observable((subscriber) => {
|
|
47
|
+
// resolve initial context if available from config if available
|
|
37
48
|
const resolveInitialContext$ = config.resolveInitialContext
|
|
38
49
|
? from(config.resolveInitialContext(args)).pipe(
|
|
50
|
+
// filter out invalid context items
|
|
39
51
|
filter((item): item is ContextItem => !!item),
|
|
40
52
|
switchMap((item) =>
|
|
53
|
+
// set current context with validation and resolution
|
|
41
54
|
args.modules.context.setCurrentContext(item, {
|
|
42
55
|
validate: true,
|
|
43
56
|
resolve: true,
|
|
44
57
|
}),
|
|
45
58
|
),
|
|
46
59
|
)
|
|
47
|
-
: EMPTY;
|
|
60
|
+
: EMPTY; // if no initial context is available, complete immediately
|
|
48
61
|
|
|
62
|
+
// add teardown to resolve initial context
|
|
49
63
|
subscriber.add(
|
|
50
64
|
resolveInitialContext$
|
|
51
65
|
.pipe(
|
|
@@ -55,6 +69,7 @@ export const module: ContextModule = {
|
|
|
55
69
|
'failed to resolve initial context',
|
|
56
70
|
err,
|
|
57
71
|
);
|
|
72
|
+
// failed to resolve initial context, complete immediately
|
|
58
73
|
return EMPTY;
|
|
59
74
|
}),
|
|
60
75
|
)
|
|
@@ -67,6 +82,7 @@ export const module: ContextModule = {
|
|
|
67
82
|
);
|
|
68
83
|
},
|
|
69
84
|
complete: () => {
|
|
85
|
+
// connect parent context if available when stream completes
|
|
70
86
|
if (config.connectParentContext !== false && parentProvider) {
|
|
71
87
|
provider.connectParentContext(parentProvider);
|
|
72
88
|
}
|
|
@@ -76,6 +92,7 @@ export const module: ContextModule = {
|
|
|
76
92
|
);
|
|
77
93
|
});
|
|
78
94
|
|
|
95
|
+
// add teardown to module
|
|
79
96
|
this.dispose = () => subscription.unsubscribe();
|
|
80
97
|
|
|
81
98
|
return provider;
|
package/src/selectors.ts
CHANGED
|
@@ -4,12 +4,23 @@ import type { QueryContextResponse } from '@equinor/fusion-framework-module-serv
|
|
|
4
4
|
import type { RelatedContextResponse } from '@equinor/fusion-framework-module-services/context/related';
|
|
5
5
|
import type { ContextItem, ContextItemType } from './types';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Parses the context type from the response of the GetContext API.
|
|
9
|
+
*
|
|
10
|
+
* @param type The type property from the GetContext response.
|
|
11
|
+
* @returns The parsed context item type.
|
|
12
|
+
*/
|
|
7
13
|
const parseContextType = (type: GetContextResponse<'v1'>['type']): ContextItemType => ({
|
|
8
14
|
id: type.id,
|
|
9
15
|
isChildType: type.isChildType,
|
|
10
16
|
parentTypeIds: type.parentTypeIds ?? [],
|
|
11
17
|
});
|
|
12
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Parses an ApiContextEntity object into a ContextItem object.
|
|
21
|
+
* @param item The ApiContextEntity object to parse.
|
|
22
|
+
* @returns The parsed ContextItem object.
|
|
23
|
+
*/
|
|
13
24
|
const parseContextItem = (item: ApiContextEntity<ApiVersion.v1>): ContextItem => {
|
|
14
25
|
return {
|
|
15
26
|
id: item.id,
|
|
@@ -25,16 +36,31 @@ const parseContextItem = (item: ApiContextEntity<ApiVersion.v1>): ContextItem =>
|
|
|
25
36
|
};
|
|
26
37
|
};
|
|
27
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Parse the response from the GetContext API into a context item.
|
|
41
|
+
* @param response The response object containing the context item.
|
|
42
|
+
* @returns A promise that resolves to the context item.
|
|
43
|
+
*/
|
|
28
44
|
export const getContextSelector = async (response: Response): Promise<ContextItem> => {
|
|
29
45
|
const result = (await response.json()) as GetContextResponse<'v1'>;
|
|
30
46
|
return parseContextItem(result);
|
|
31
47
|
};
|
|
32
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Parse the response from the QueryContext API into an array of context items.
|
|
51
|
+
* @param response The response object.
|
|
52
|
+
* @returns A promise that resolves to an array of context items.
|
|
53
|
+
*/
|
|
33
54
|
export const queryContextSelector = async (response: Response): Promise<ContextItem[]> => {
|
|
34
55
|
const result = (await response.json()) as QueryContextResponse<'v1'>;
|
|
35
56
|
return result.map(parseContextItem);
|
|
36
57
|
};
|
|
37
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Parse the response from the RelatedContext API into an array of context items.
|
|
61
|
+
* @param response The response object containing the related context items.
|
|
62
|
+
* @returns A promise that resolves to an array of ContextItem objects.
|
|
63
|
+
*/
|
|
38
64
|
export const relatedContextSelector = async (response: Response): Promise<ContextItem[]> => {
|
|
39
65
|
const result = (await response.json()) as RelatedContextResponse<'v1'>;
|
|
40
66
|
return result.map(parseContextItem);
|
package/src/utils/index.ts
CHANGED
|
@@ -1,24 +1,116 @@
|
|
|
1
|
-
import { EMPTY } from 'rxjs';
|
|
1
|
+
import { EMPTY, Observable } from 'rxjs';
|
|
2
2
|
|
|
3
|
-
import { ModuleType } from '@equinor/fusion-framework-module';
|
|
3
|
+
import { type ModuleType } from '@equinor/fusion-framework-module';
|
|
4
4
|
|
|
5
5
|
import { type ContextModule } from '../module';
|
|
6
|
+
import { type ContextItem } from '../types';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Arguments for resolving a context from a path.
|
|
10
|
+
*/
|
|
7
11
|
export type ContextPathResolveArgs = {
|
|
12
|
+
/**
|
|
13
|
+
* Callback to extract a context id from a path.
|
|
14
|
+
* @param path string - the path to extract the context id from
|
|
15
|
+
* @returns string | undefined - the context id or undefined
|
|
16
|
+
*/
|
|
8
17
|
extract?: (path: string) => string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Callback to validate a context id.
|
|
20
|
+
* @param contextId string - the context id to validate
|
|
21
|
+
* @returns boolean - true if the context id is valid
|
|
22
|
+
*/
|
|
9
23
|
validate?: (contextId: string) => boolean;
|
|
10
24
|
};
|
|
11
25
|
|
|
26
|
+
// GUID pattern
|
|
12
27
|
const matchGUID =
|
|
13
28
|
/^(?:(?:[0-9a-fA-F]){8}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){12})$/;
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Method will try to extract a context id from a path.
|
|
32
|
+
* The default matcher is a GUID pattern.
|
|
33
|
+
* Will iterate over the path and return the first match.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const path = '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?dsadasdas#example';
|
|
38
|
+
* const contextId = extractContextIdFromPath(path); // '7fd97952-7fe6-409b-a6dc-292dbf0e50d7'
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @param path string - the path to extract the context id from
|
|
42
|
+
* @param matcher RegExp - the pattern to match against
|
|
43
|
+
* @returns string | undefined - the context id or undefined
|
|
44
|
+
*/
|
|
45
|
+
export const extractContextIdFromPath = (
|
|
46
|
+
path: string,
|
|
47
|
+
matcher: RegExp = matchGUID,
|
|
48
|
+
): string | undefined =>
|
|
49
|
+
path
|
|
50
|
+
// remove leading slashes
|
|
51
|
+
.replace(/^\/+/, '')
|
|
52
|
+
// split path by slashes
|
|
53
|
+
.split('/')
|
|
54
|
+
// find the first path fragment that matches the matcher
|
|
55
|
+
.find((x) => x.match(matcher));
|
|
17
56
|
|
|
18
57
|
const validateContextId = (contextId: string): boolean => !!contextId.match(matchGUID);
|
|
19
58
|
|
|
20
|
-
|
|
21
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Method will try to resolve a context from a path.
|
|
61
|
+
* The method will return a function that takes a path and returns an observable of the resolved context.
|
|
62
|
+
* The method will use the context module to resolve the context.
|
|
63
|
+
* The method will use the extract and validate methods from the args to extract and validate the context id.
|
|
64
|
+
* If the context id is not valid, the method will throw an error.
|
|
65
|
+
* If the context id is valid, the method will return an observable of the resolved context.
|
|
66
|
+
* If the context id is not found, the method will return an empty observable.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const resolve = resolveContextFromPath(modules.context);
|
|
71
|
+
* resolve(
|
|
72
|
+
* '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?foobar#example'
|
|
73
|
+
* ).subscribe(console.log);
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @param context The context module.
|
|
77
|
+
* @returns A function that takes a path and returns an Observable of the resolved context item.
|
|
78
|
+
*/
|
|
79
|
+
export interface resolveContextFromPath {
|
|
80
|
+
(context: ModuleType<ContextModule>): (path: string) => Observable<ContextItem>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* const resolve = resolveContextFromPath(
|
|
88
|
+
* modules.context,
|
|
89
|
+
* {
|
|
90
|
+
* extract: (path) => path.find(extractingContextFromPath),
|
|
91
|
+
* validate: (id) => isValidContextId(id)
|
|
92
|
+
* });
|
|
93
|
+
* resolve(
|
|
94
|
+
* '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?foobar#example'
|
|
95
|
+
* ).subscribe(console.log);
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @param context The context module.
|
|
99
|
+
* @param args The arguments for resolving the path.
|
|
100
|
+
* @returns A function that takes a path and returns an Observable of the resolved context item.
|
|
101
|
+
*/
|
|
102
|
+
export interface resolveContextFromPath {
|
|
103
|
+
(
|
|
104
|
+
context: ModuleType<ContextModule>,
|
|
105
|
+
args: ContextPathResolveArgs,
|
|
106
|
+
): (path: string) => Observable<ContextItem>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resolveContextFromPath(
|
|
110
|
+
context: ModuleType<ContextModule>,
|
|
111
|
+
args?: ContextPathResolveArgs,
|
|
112
|
+
) {
|
|
113
|
+
return (path: string) => {
|
|
22
114
|
const { extract = extractContextIdFromPath, validate = validateContextId } = args ?? {};
|
|
23
115
|
const contextId = extract(path);
|
|
24
116
|
if (!contextId) {
|
|
@@ -30,5 +122,6 @@ export const resolveContextFromPath =
|
|
|
30
122
|
|
|
31
123
|
throw Error(`Failed to validate context [${contextId}] from path [${path}]`);
|
|
32
124
|
};
|
|
125
|
+
}
|
|
33
126
|
|
|
34
127
|
export default resolveContextFromPath;
|
|
@@ -1,27 +1,50 @@
|
|
|
1
|
-
import { ModulesInstance } from '@equinor/fusion-framework-module';
|
|
2
|
-
import { ContextModule } from '../module';
|
|
1
|
+
import { type ModulesInstance } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { type ContextModule } from '../module';
|
|
3
3
|
import { type ContextModuleConfig } from '../configurator';
|
|
4
4
|
import { concat, EMPTY, first, of } from 'rxjs';
|
|
5
5
|
|
|
6
|
-
import { ContextPathResolveArgs, resolveContextFromPath } from './resolve-context-from-path';
|
|
6
|
+
import { type ContextPathResolveArgs, resolveContextFromPath } from './resolve-context-from-path';
|
|
7
|
+
import { type NavigationModule } from '@equinor/fusion-framework-module-navigation';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the initial context from the parent module.
|
|
11
|
+
*
|
|
12
|
+
* @param ref - parent modules.
|
|
13
|
+
* @returns An Observable of the resolved initial context.
|
|
14
|
+
*/
|
|
8
15
|
export const resolveContextFromParent: ContextModuleConfig['resolveInitialContext'] = ({ ref }) => {
|
|
9
16
|
const parentContext = (ref as ModulesInstance<[ContextModule]>)?.context;
|
|
17
|
+
// check if the parent has context module
|
|
10
18
|
if (!parentContext) {
|
|
11
19
|
throw Error(['resolveContextFromNavigation', 'ref does not support context!'].join('\n'));
|
|
12
20
|
}
|
|
21
|
+
// return the current context from the parent or empty if the parent does not have a context
|
|
13
22
|
return parentContext.currentContext ? of(parentContext.currentContext) : EMPTY;
|
|
14
23
|
};
|
|
15
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the initial context for a Fusion Framework context module.
|
|
27
|
+
*
|
|
28
|
+
* will try to resolve the initial context from the path, and if that fails, it will try to resolve the context from the parent.
|
|
29
|
+
*
|
|
30
|
+
* @param options - Optional configuration for resolving the context path.
|
|
31
|
+
* @returns A function that accepts the module's reference and modules, and returns an Observable of the resolved initial context.
|
|
32
|
+
*/
|
|
16
33
|
export const resolveInitialContext =
|
|
17
34
|
(options?: {
|
|
18
35
|
path?: ContextPathResolveArgs;
|
|
19
36
|
}): Required<ContextModuleConfig>['resolveInitialContext'] =>
|
|
20
37
|
({ ref, modules }) => {
|
|
21
38
|
const { context, navigation } = modules;
|
|
39
|
+
// create a path resolver from the context module
|
|
22
40
|
const pathResolver = resolveContextFromPath(context, options?.path);
|
|
41
|
+
// use the path from the navigation module, or the path from the parent navigation module
|
|
42
|
+
const pathname =
|
|
43
|
+
navigation?.path.pathname ??
|
|
44
|
+
(ref as Partial<ModulesInstance<[NavigationModule]>>).navigation?.path.pathname;
|
|
45
|
+
// try to resolve the context from the path, and if that fails, try to resolve the context from the parent
|
|
23
46
|
return concat(
|
|
24
|
-
|
|
47
|
+
pathname ? pathResolver(pathname) : EMPTY,
|
|
25
48
|
resolveContextFromParent({ ref, modules }),
|
|
26
49
|
).pipe(first());
|
|
27
50
|
};
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '4.
|
|
2
|
+
export const version = '4.2.0';
|