@equinor/fusion-framework-module-context 8.0.3 → 9.0.0-next.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 +76 -0
- package/README.md +78 -2
- package/dist/esm/ContextModuleConfig.js +2 -0
- package/dist/esm/ContextModuleConfig.js.map +1 -0
- package/dist/esm/ContextModuleConfigurator.interface.js +2 -0
- package/dist/esm/ContextModuleConfigurator.interface.js.map +1 -0
- package/dist/esm/ContextModuleConfigurator.js +211 -0
- package/dist/esm/ContextModuleConfigurator.js.map +1 -0
- package/dist/esm/ContextProvider.js +1 -1
- package/dist/esm/ContextProvider.js.map +1 -1
- package/dist/esm/__tests__/ContextModuleConfigurator.test.js +282 -0
- package/dist/esm/__tests__/ContextModuleConfigurator.test.js.map +1 -0
- package/dist/esm/__tests__/mock/context-mock.test.js +101 -0
- package/dist/esm/__tests__/mock/context-mock.test.js.map +1 -0
- package/dist/esm/__tests__/mock/create-context-item-factory.test.js +45 -0
- package/dist/esm/__tests__/mock/create-context-item-factory.test.js.map +1 -0
- package/dist/esm/__tests__/mock/create-context-items.test.js +46 -0
- package/dist/esm/__tests__/mock/create-context-items.test.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mock/ContextMockConfigurator.js +206 -0
- package/dist/esm/mock/ContextMockConfigurator.js.map +1 -0
- package/dist/esm/mock/fixtures/create-context-item-factory.js +50 -0
- package/dist/esm/mock/fixtures/create-context-item-factory.js.map +1 -0
- package/dist/esm/mock/fixtures/create-context-items.js +53 -0
- package/dist/esm/mock/fixtures/create-context-items.js.map +1 -0
- package/dist/esm/mock/fixtures/index.js +22 -0
- package/dist/esm/mock/fixtures/index.js.map +1 -0
- package/dist/esm/mock/fixtures/string-to-seed.js +15 -0
- package/dist/esm/mock/fixtures/string-to-seed.js.map +1 -0
- package/dist/esm/mock/index.js +31 -0
- package/dist/esm/mock/index.js.map +1 -0
- package/dist/esm/mock/module.js +41 -0
- package/dist/esm/mock/module.js.map +1 -0
- package/dist/esm/module.js +35 -6
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/{get-context-selector.js → selectors/get-context-selector.js} +1 -4
- package/dist/esm/selectors/get-context-selector.js.map +1 -0
- package/dist/esm/selectors/index.js +13 -0
- package/dist/esm/selectors/index.js.map +1 -0
- package/dist/esm/{query-context-selector.js → selectors/query-context-selector.js} +1 -1
- package/dist/esm/selectors/query-context-selector.js.map +1 -0
- package/dist/esm/{related-context-selector.js → selectors/related-context-selector.js} +1 -1
- package/dist/esm/selectors/related-context-selector.js.map +1 -0
- package/dist/esm/utils/enable-context.js +1 -1
- package/dist/esm/utils/enable-context.js.map +1 -1
- package/dist/esm/utils/index.js +2 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/parse-context-item.js.map +1 -0
- 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/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/{configurator.d.ts → ContextModuleConfig.d.ts} +4 -64
- package/dist/types/ContextModuleConfigurator.d.ts +97 -0
- package/dist/types/{ContextConfigBuilder.d.ts → ContextModuleConfigurator.interface.d.ts} +27 -47
- package/dist/types/ContextProvider.d.ts +1 -1
- package/dist/types/__tests__/ContextModuleConfigurator.test.d.ts +1 -0
- package/dist/types/__tests__/mock/context-mock.test.d.ts +1 -0
- package/dist/types/__tests__/mock/create-context-item-factory.test.d.ts +1 -0
- package/dist/types/__tests__/mock/create-context-items.test.d.ts +1 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/mock/ContextMockConfigurator.d.ts +155 -0
- package/dist/types/mock/fixtures/create-context-item-factory.d.ts +40 -0
- package/dist/types/mock/fixtures/create-context-items.d.ts +59 -0
- package/dist/types/mock/fixtures/index.d.ts +21 -0
- package/dist/types/mock/fixtures/string-to-seed.d.ts +12 -0
- package/dist/types/mock/index.d.ts +30 -0
- package/dist/types/mock/module.d.ts +38 -0
- package/dist/types/module.d.ts +5 -3
- package/dist/types/{get-context-selector.d.ts → selectors/get-context-selector.d.ts} +1 -3
- package/dist/types/selectors/index.d.ts +12 -0
- package/dist/types/{query-context-selector.d.ts → selectors/query-context-selector.d.ts} +1 -1
- package/dist/types/{related-context-selector.d.ts → selectors/related-context-selector.d.ts} +1 -1
- package/dist/types/utils/enable-context.d.ts +4 -5
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/{parse-context-item.d.ts → utils/parse-context-item.d.ts} +1 -1
- package/dist/types/utils/resolve-initial-context.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/docs/data-model.md +134 -0
- package/docs/lifecycle.md +163 -0
- package/docs/recipes.md +89 -0
- package/package.json +36 -8
- package/src/ContextModuleConfig.ts +147 -0
- package/src/ContextModuleConfigurator.interface.ts +145 -0
- package/src/ContextModuleConfigurator.ts +295 -0
- package/src/ContextProvider.ts +1 -2
- package/src/__tests__/ContextModuleConfigurator.test.ts +412 -0
- package/src/__tests__/mock/context-mock.test.ts +137 -0
- package/src/__tests__/mock/create-context-item-factory.test.ts +60 -0
- package/src/__tests__/mock/create-context-items.test.ts +58 -0
- package/src/index.ts +3 -5
- package/src/mock/ContextMockConfigurator.ts +244 -0
- package/src/mock/fixtures/create-context-item-factory.ts +62 -0
- package/src/mock/fixtures/create-context-items.ts +80 -0
- package/src/mock/fixtures/index.ts +28 -0
- package/src/mock/fixtures/string-to-seed.ts +18 -0
- package/src/mock/index.ts +33 -0
- package/src/mock/module.ts +54 -0
- package/src/module.ts +45 -17
- package/src/{get-context-selector.ts → selectors/get-context-selector.ts} +2 -6
- package/src/selectors/index.ts +12 -0
- package/src/{query-context-selector.ts → selectors/query-context-selector.ts} +2 -2
- package/src/{related-context-selector.ts → selectors/related-context-selector.ts} +2 -2
- package/src/utils/enable-context.ts +6 -10
- package/src/utils/index.ts +2 -0
- package/src/{parse-context-item.ts → utils/parse-context-item.ts} +1 -1
- package/src/utils/resolve-initial-context.ts +5 -2
- package/src/version.ts +1 -1
- package/tsconfig.json +6 -0
- package/vitest.config.ts +11 -0
- package/dist/esm/ContextConfigBuilder.js +0 -175
- package/dist/esm/ContextConfigBuilder.js.map +0 -1
- package/dist/esm/configurator.js +0 -104
- package/dist/esm/configurator.js.map +0 -1
- package/dist/esm/get-context-selector.js.map +0 -1
- package/dist/esm/parse-context-item.js.map +0 -1
- package/dist/esm/query-context-selector.js.map +0 -1
- package/dist/esm/related-context-selector.js.map +0 -1
- package/src/ContextConfigBuilder.ts +0 -258
- package/src/configurator.ts +0 -300
- /package/dist/esm/{parse-context-item.js → utils/parse-context-item.js} +0 -0
|
@@ -1,21 +1,18 @@
|
|
|
1
|
+
import type { AnyModuleInstance, ModuleInstance } from '@equinor/fusion-framework-module';
|
|
1
2
|
import type { ObservableInput } from 'rxjs';
|
|
2
|
-
import type { AnyModuleInstance, ModuleInitializerArgs, ModuleInstance } from '@equinor/fusion-framework-module';
|
|
3
|
-
import type { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
|
|
4
|
-
import type { NavigationModule } from '@equinor/fusion-framework-module-navigation';
|
|
5
3
|
import type { QueryCtorOptions } from '@equinor/fusion-query';
|
|
6
4
|
import type { ContextFilterFn, ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
7
5
|
import type { GetContextParameters } from './client/ContextClient';
|
|
8
|
-
import { type ContextConfigBuilderCallback } from './ContextConfigBuilder';
|
|
9
6
|
import type { IContextProvider } from './ContextProvider';
|
|
10
7
|
/**
|
|
11
8
|
* Resolved configuration for the context module.
|
|
12
9
|
*
|
|
13
10
|
* Holds query clients, type filters, parent-connection settings, and
|
|
14
11
|
* optional callbacks for validation, resolution, and path integration.
|
|
15
|
-
* Produced by {@link ContextModuleConfigurator.
|
|
16
|
-
*
|
|
12
|
+
* Produced by {@link ContextModuleConfigurator.createConfigAsync} after all
|
|
13
|
+
* registered config builders have run.
|
|
17
14
|
*
|
|
18
|
-
* @see
|
|
15
|
+
* @see IContextModuleConfigurator — fluent API for populating this config.
|
|
19
16
|
* @see ContextProvider — runtime consumer of this config.
|
|
20
17
|
*/
|
|
21
18
|
export interface ContextModuleConfig {
|
|
@@ -123,60 +120,3 @@ export interface ContextModuleConfig {
|
|
|
123
120
|
modules: ModuleInstance;
|
|
124
121
|
}) => ObservableInput<ContextItem | void>;
|
|
125
122
|
}
|
|
126
|
-
/**
|
|
127
|
-
* Public configurator contract for the context module.
|
|
128
|
-
*
|
|
129
|
-
* Consumers call {@link addConfigBuilder} to register one or more
|
|
130
|
-
* {@link ContextConfigBuilderCallback} functions that will run during
|
|
131
|
-
* module initialization to populate the {@link ContextModuleConfig}.
|
|
132
|
-
*/
|
|
133
|
-
export interface IContextModuleConfigurator {
|
|
134
|
-
/**
|
|
135
|
-
* Registers a configuration callback that receives a {@link ContextConfigBuilder}.
|
|
136
|
-
*
|
|
137
|
-
* Multiple builders can be added; they execute sequentially and merge
|
|
138
|
-
* their results into a single {@link ContextModuleConfig}.
|
|
139
|
-
*
|
|
140
|
-
* @param init - Builder callback invoked during module initialization.
|
|
141
|
-
*/
|
|
142
|
-
addConfigBuilder: (init: ContextConfigBuilderCallback) => void;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Default implementation of {@link IContextModuleConfigurator}.
|
|
146
|
-
*
|
|
147
|
-
* Collects {@link ContextConfigBuilderCallback} registrations and, when
|
|
148
|
-
* {@link createConfig} is called, runs them in order against a
|
|
149
|
-
* {@link ContextConfigBuilder} to produce the final {@link ContextModuleConfig}.
|
|
150
|
-
*
|
|
151
|
-
* If no custom client is configured, the configurator falls back to
|
|
152
|
-
* creating one from the {@link ServicesModule} API provider.
|
|
153
|
-
*/
|
|
154
|
-
export declare class ContextModuleConfigurator implements IContextModuleConfigurator {
|
|
155
|
-
#private;
|
|
156
|
-
/** Default cache TTL (in ms) for context query results. */
|
|
157
|
-
defaultExpireTime: number;
|
|
158
|
-
/** @inheritdoc */
|
|
159
|
-
addConfigBuilder(init: ContextConfigBuilderCallback): void;
|
|
160
|
-
/**
|
|
161
|
-
* Resolves the services API provider, preferring the local module
|
|
162
|
-
* instance and falling back to the parent module.
|
|
163
|
-
*
|
|
164
|
-
* @param init - Module initializer arguments.
|
|
165
|
-
* @returns The resolved API provider.
|
|
166
|
-
* @throws Error if no services module is available.
|
|
167
|
-
*/
|
|
168
|
-
protected _getServiceProvider(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>): Promise<IApiProvider>;
|
|
169
|
-
/**
|
|
170
|
-
* Runs all registered config builders and produces the final
|
|
171
|
-
* {@link ContextModuleConfig}.
|
|
172
|
-
*
|
|
173
|
-
* If no `resolveInitialContext` was set, the default path + parent
|
|
174
|
-
* resolver is used. If no `client` was set, one is created from the
|
|
175
|
-
* {@link ServicesModule} API provider.
|
|
176
|
-
*
|
|
177
|
-
* @param init - Module initializer arguments including dependency instances.
|
|
178
|
-
* @returns The fully resolved context module configuration.
|
|
179
|
-
*/
|
|
180
|
-
createConfig(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>): Promise<ContextModuleConfig>;
|
|
181
|
-
}
|
|
182
|
-
export default ContextModuleConfigurator;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { BaseConfigBuilder, type ModuleInitializerArgs, type Modules, type ModuleType } from '@equinor/fusion-framework-module';
|
|
2
|
+
import type { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
|
|
3
|
+
import type { NavigationModule } from '@equinor/fusion-framework-module-navigation';
|
|
4
|
+
import type { QueryCtorOptions, QueryFn } from '@equinor/fusion-query';
|
|
5
|
+
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
6
|
+
import type { GetContextParameters } from './client/ContextClient';
|
|
7
|
+
import type { ContextModuleConfig } from './ContextModuleConfig';
|
|
8
|
+
import type { ContextConfigBuilderCallback, IContextModuleConfigurator } from './ContextModuleConfigurator.interface';
|
|
9
|
+
export type { ContextModuleConfig } from './ContextModuleConfig';
|
|
10
|
+
export type { ContextConfigBuilderCallback, IContextModuleConfigurator, } from './ContextModuleConfigurator.interface';
|
|
11
|
+
/**
|
|
12
|
+
* Default implementation of {@link IContextModuleConfigurator}.
|
|
13
|
+
*
|
|
14
|
+
* Collects {@link ContextConfigBuilderCallback} registrations and, when
|
|
15
|
+
* {@link createConfigAsync} is called, runs them in order — each callback
|
|
16
|
+
* receives this configurator itself and populates the config through its
|
|
17
|
+
* setter methods, which register into {@link BaseConfigBuilder._set} — to
|
|
18
|
+
* produce the final {@link ContextModuleConfig}.
|
|
19
|
+
*
|
|
20
|
+
* If no custom client is configured, the configurator falls back to
|
|
21
|
+
* creating one from the {@link ServicesModule} API provider.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* Extends {@link BaseConfigBuilder} and fully reuses its `_set`/`_buildConfig`
|
|
25
|
+
* machinery. Setters always pass an `async () => value` callback to `_set`,
|
|
26
|
+
* never the raw value directly — `_set` decides whether it received a value
|
|
27
|
+
* or a deferred callback by checking `typeof value_or_cb === 'function'`, so
|
|
28
|
+
* fields that are themselves functions (e.g. `validateContext`) would
|
|
29
|
+
* otherwise be misread as callbacks and invoked instead of stored.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ContextModuleConfigurator extends BaseConfigBuilder<ContextModuleConfig> implements IContextModuleConfigurator {
|
|
32
|
+
#private;
|
|
33
|
+
/** Default cache TTL (in ms) for context query results. */
|
|
34
|
+
defaultExpireTime: number;
|
|
35
|
+
/** @inheritdoc */
|
|
36
|
+
addConfigBuilder(init: ContextConfigBuilderCallback): void;
|
|
37
|
+
/** @inheritdoc */
|
|
38
|
+
requireInstance<TKey extends string = Extract<keyof Modules, string>>(module: TKey): Promise<ModuleType<Modules[TKey]>>;
|
|
39
|
+
/** @inheritdoc */
|
|
40
|
+
requireInstance<T>(module: string): Promise<T>;
|
|
41
|
+
/** @inheritdoc */
|
|
42
|
+
setContextType(type: ContextModuleConfig['contextType']): void;
|
|
43
|
+
/** @inheritdoc */
|
|
44
|
+
setContextFilter(filter: ContextModuleConfig['contextFilter']): void;
|
|
45
|
+
/** @inheritdoc */
|
|
46
|
+
connectParentContext(connect: ContextModuleConfig['connectParentContext']): void;
|
|
47
|
+
/** @inheritdoc */
|
|
48
|
+
setContextParameterFn(fn: ContextModuleConfig['contextParameterFn']): void;
|
|
49
|
+
/** @inheritdoc */
|
|
50
|
+
setValidateContext(fn: ContextModuleConfig['validateContext']): void;
|
|
51
|
+
/** @inheritdoc */
|
|
52
|
+
setResolveContext(fn: ContextModuleConfig['resolveContext']): void;
|
|
53
|
+
/** @inheritdoc */
|
|
54
|
+
setContextPathExtractor(fn: ContextModuleConfig['extractContextIdFromPath']): void;
|
|
55
|
+
/** @inheritdoc */
|
|
56
|
+
setContextPathGenerator(fn: ContextModuleConfig['generatePathFromContext']): void;
|
|
57
|
+
/** @inheritdoc */
|
|
58
|
+
setResolveInitialContext(fn: ContextModuleConfig['resolveInitialContext']): void;
|
|
59
|
+
/** @inheritdoc */
|
|
60
|
+
setContextClient(client: {
|
|
61
|
+
get: QueryFn<ContextItem, GetContextParameters> | QueryCtorOptions<ContextItem, GetContextParameters>;
|
|
62
|
+
query: QueryFn<ContextItem[], QueryContextParameters> | QueryCtorOptions<ContextItem[], QueryContextParameters>;
|
|
63
|
+
related?: QueryFn<ContextItem[], RelatedContextParameters> | QueryCtorOptions<ContextItem[], RelatedContextParameters>;
|
|
64
|
+
}, expire?: number): void;
|
|
65
|
+
/**
|
|
66
|
+
* Resolves the services API provider, preferring the local module
|
|
67
|
+
* instance and falling back to the parent module.
|
|
68
|
+
*
|
|
69
|
+
* @param init - Module initializer arguments.
|
|
70
|
+
* @returns The resolved API provider.
|
|
71
|
+
* @throws Error if no services module is available.
|
|
72
|
+
*/
|
|
73
|
+
protected _getServiceProvider(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>): Promise<IApiProvider>;
|
|
74
|
+
/**
|
|
75
|
+
* Runs all registered config builders, then resolves the accumulated
|
|
76
|
+
* `_set` registrations into the final {@link ContextModuleConfig}.
|
|
77
|
+
*
|
|
78
|
+
* @param init - Module initializer arguments including dependency instances.
|
|
79
|
+
* @param initial - Optional partial config inherited from a parent module.
|
|
80
|
+
* @returns The fully resolved context module configuration.
|
|
81
|
+
*/
|
|
82
|
+
protected _createConfig(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>, initial?: Partial<ContextModuleConfig>): Promise<ContextModuleConfig>;
|
|
83
|
+
/**
|
|
84
|
+
* Defaults `resolveInitialContext` and `client` when a builder callback
|
|
85
|
+
* didn't set them.
|
|
86
|
+
*
|
|
87
|
+
* If no `resolveInitialContext` was set, the default path + parent
|
|
88
|
+
* resolver is used. If no `client` was set, one is created from the
|
|
89
|
+
* {@link ServicesModule} API provider.
|
|
90
|
+
*
|
|
91
|
+
* @param config - The config accumulated from registered builder callbacks.
|
|
92
|
+
* @param init - Module initializer arguments including dependency instances.
|
|
93
|
+
* @returns The fully resolved context module configuration.
|
|
94
|
+
*/
|
|
95
|
+
protected _processConfig(config: Partial<ContextModuleConfig>, init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>): Promise<ContextModuleConfig>;
|
|
96
|
+
}
|
|
97
|
+
export default ContextModuleConfigurator;
|
|
@@ -1,65 +1,45 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Modules, ModuleType } from '@equinor/fusion-framework-module';
|
|
2
2
|
import type { QueryCtorOptions, QueryFn } from '@equinor/fusion-query';
|
|
3
|
-
import type {
|
|
4
|
-
import type { ContextModuleConfig, ContextModuleConfigurator, IContextModuleConfigurator } from './configurator';
|
|
3
|
+
import type { ContextModuleConfig } from './ContextModuleConfig';
|
|
5
4
|
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
5
|
+
import type { GetContextParameters } from './client/ContextClient';
|
|
6
6
|
/**
|
|
7
7
|
* Callback passed to {@link IContextModuleConfigurator.addConfigBuilder}.
|
|
8
8
|
*
|
|
9
|
-
* Receives
|
|
10
|
-
* to populate the context module configuration. The
|
|
11
|
-
*
|
|
12
|
-
* @typeParam TDeps - Module dependency array inferred from the configurator.
|
|
9
|
+
* Receives the {@link IContextModuleConfigurator} itself and may use its
|
|
10
|
+
* setter methods to populate the context module configuration. The
|
|
11
|
+
* callback may be async.
|
|
13
12
|
*/
|
|
14
|
-
export type ContextConfigBuilderCallback =
|
|
13
|
+
export type ContextConfigBuilderCallback = (builder: IContextModuleConfigurator) => void | Promise<void>;
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `ContextConfigBuilder` provides a fluent API for setting up various aspects of context management,
|
|
19
|
-
* including context type, filtering, parent context connection, parameter resolution, validation,
|
|
20
|
-
* path extraction/generation, and client configuration for fetching context items.
|
|
21
|
-
*
|
|
22
|
-
* @typeParam TModules - An array of modules that extend `AnyModule`. Defaults to an empty array.
|
|
23
|
-
* @typeParam TInit - The initializer arguments for the module, extending `ModuleInitializerArgs`.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* const builder = new ContextConfigBuilder(init);
|
|
28
|
-
* builder.setContextType(['ProjectMaster']);
|
|
29
|
-
* builder.setContextFilter(items => items.filter(ctx => ctx.isActive));
|
|
30
|
-
* builder.setContextClient({ get: fetchContextItem, query: fetchContextItems });
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* @remarks
|
|
34
|
-
* - Use the provided setter methods to customize context behavior as needed.
|
|
35
|
-
* - The builder pattern allows chaining configuration methods for clarity and convenience.
|
|
36
|
-
* - The `requireInstance` method enables asynchronous retrieval of module instances by name.
|
|
37
|
-
*
|
|
38
|
-
* @todo(#5120) - this should extend the BaseConfigBuilder
|
|
15
|
+
* Public configurator contract for the context module.
|
|
39
16
|
*
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
17
|
+
* Consumers call {@link addConfigBuilder} to register one or more
|
|
18
|
+
* {@link ContextConfigBuilderCallback} functions that will run during
|
|
19
|
+
* module initialization to populate the {@link ContextModuleConfig}, using
|
|
20
|
+
* the fluent setter methods declared below.
|
|
42
21
|
*/
|
|
43
|
-
export
|
|
44
|
-
#private;
|
|
45
|
-
config: Partial<ContextModuleConfig>;
|
|
22
|
+
export interface IContextModuleConfigurator {
|
|
46
23
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
24
|
+
* Registers a configuration callback that receives this configurator.
|
|
25
|
+
*
|
|
26
|
+
* Multiple builders can be added; they execute sequentially against the
|
|
27
|
+
* same configurator instance, so later calls win when they touch the
|
|
28
|
+
* same field.
|
|
29
|
+
*
|
|
30
|
+
* @param init - Builder callback invoked during module initialization.
|
|
50
31
|
*/
|
|
51
|
-
|
|
32
|
+
addConfigBuilder: (init: ContextConfigBuilderCallback) => void;
|
|
52
33
|
/**
|
|
53
|
-
* Requires a module instance by its registered key.
|
|
54
|
-
*
|
|
34
|
+
* Requires a module instance by its registered key or name.
|
|
35
|
+
*
|
|
36
|
+
* Only resolvable from within a {@link ContextConfigBuilderCallback} —
|
|
37
|
+
* throws if called before module initialization has started.
|
|
38
|
+
*
|
|
39
|
+
* @param module - The key or name of the module to resolve.
|
|
55
40
|
* @returns A promise that resolves to the requested module instance.
|
|
56
41
|
*/
|
|
57
42
|
requireInstance<TKey extends string = Extract<keyof Modules, string>>(module: TKey): Promise<ModuleType<Modules[TKey]>>;
|
|
58
|
-
/**
|
|
59
|
-
* Requires a module instance by name.
|
|
60
|
-
* @param module - The name of the module to resolve.
|
|
61
|
-
* @returns A promise that resolves to the requested module instance.
|
|
62
|
-
*/
|
|
63
43
|
requireInstance<T>(module: string): Promise<T>;
|
|
64
44
|
/**
|
|
65
45
|
* Sets the context type for the current configuration.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable, Subscription } from 'rxjs';
|
|
2
|
-
import type { ContextModuleConfig } from './
|
|
2
|
+
import type { ContextModuleConfig } from './ContextModuleConfig';
|
|
3
3
|
import { BaseModuleProvider } from '@equinor/fusion-framework-module/provider';
|
|
4
4
|
import { ContextClient } from './client/ContextClient';
|
|
5
5
|
import type { ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @packageDocumentation
|
|
12
12
|
*/
|
|
13
|
-
export { ContextModuleConfigurator
|
|
13
|
+
export { ContextModuleConfigurator } from './ContextModuleConfigurator';
|
|
14
|
+
export type { IContextModuleConfigurator } from './ContextModuleConfigurator.interface';
|
|
15
|
+
export type { ContextModuleConfig } from './ContextModuleConfig';
|
|
14
16
|
export { IContextProvider, ContextProvider } from './ContextProvider';
|
|
15
17
|
export { default, ContextModule, module as contextModule, moduleKey as contextModuleKey, } from './module';
|
|
16
18
|
export { enableContext } from './utils/enable-context';
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { ModuleInitializerArgs } from '@equinor/fusion-framework-module';
|
|
2
|
+
import type { NavigationModule } from '@equinor/fusion-framework-module-navigation';
|
|
3
|
+
import type { ServicesModule } from '@equinor/fusion-framework-module-services';
|
|
4
|
+
import { ContextModuleConfigurator } from '../ContextModuleConfigurator';
|
|
5
|
+
import type { ContextModuleConfig } from '../ContextModuleConfig';
|
|
6
|
+
import type { IContextModuleConfigurator } from '../ContextModuleConfigurator.interface';
|
|
7
|
+
import type { ContextItem } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a context item by id, or `undefined` if none matches.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* The escape hatch for {@link ContextMockConfigurator.setResolver} — anything
|
|
13
|
+
* the friendly seeding methods did not anticipate can be expressed here instead.
|
|
14
|
+
*/
|
|
15
|
+
export type ContextResolverFn = (id: string) => ContextItem | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* A {@link ContextModuleConfigurator} backed by in-memory context items instead
|
|
18
|
+
* of a real context API, for seeding context in tests.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* Two layers cover different needs, both running through the real
|
|
22
|
+
* `ContextProvider` logic underneath — only the data source is substituted,
|
|
23
|
+
* never `validateContext`, `resolveContext`, or parent-context propagation:
|
|
24
|
+
*
|
|
25
|
+
* - **Friendly layer** — {@link setCurrentContext}, {@link setContexts},
|
|
26
|
+
* {@link addContext}, {@link setRelatedContexts} — a small, context-domain
|
|
27
|
+
* vocabulary for the common case: seed a known item, get it back.
|
|
28
|
+
* - **Escape hatch** — {@link setResolver} — a raw id-lookup function for a
|
|
29
|
+
* custom id-based resolution strategy or a shape the friendly layer did not
|
|
30
|
+
* cover. Only replaces id-based lookup (used by `setCurrentContextById` and
|
|
31
|
+
* the initial context); it does not affect related-context resolution by
|
|
32
|
+
* item, which always goes through {@link setRelatedContexts} instead.
|
|
33
|
+
*
|
|
34
|
+
* Seeding the initial context (via {@link setCurrentContext}) overrides
|
|
35
|
+
* `resolveInitialContext` directly with the seeded item, so a test never needs
|
|
36
|
+
* to construct a fake navigation module or parent framework instance to make an
|
|
37
|
+
* app start up with a known context selected. Resolving a context id from a URL
|
|
38
|
+
* path on startup is not covered — that needs a fake router, which is separate,
|
|
39
|
+
* not-yet-built work.
|
|
40
|
+
*
|
|
41
|
+
* Seeded ids are never generated for you — every item is looked up and
|
|
42
|
+
* returned exactly by the id it was seeded with, so a test never sees a
|
|
43
|
+
* different id than the one it wrote. Use `createContextItemFactory` or
|
|
44
|
+
* `createContextItems` for fixtures needing ids of their own, rather than a
|
|
45
|
+
* random generator that would change on every run.
|
|
46
|
+
*
|
|
47
|
+
* Related-context resolution defaults to the same seeded pool, filtered to
|
|
48
|
+
* whichever type(s) the query asked for — related context is the same
|
|
49
|
+
* context, just a different type, exactly as `ContextProvider.resolveContext`
|
|
50
|
+
* uses it to resolve an item of an unexpected type into one of the configured
|
|
51
|
+
* type. Seed items of both types and resolution works with no extra wiring;
|
|
52
|
+
* {@link setRelatedContexts} overrides that default for one specific item.
|
|
53
|
+
*
|
|
54
|
+
* @example Seed and select a known context item
|
|
55
|
+
* ```ts
|
|
56
|
+
* enableContextMock(configurator, (mock) => {
|
|
57
|
+
* mock.setCurrentContext({ id: 'my-ctx', type: { id: 'ProjectMaster' }, value: {} });
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Resolve a child-typed item into its parent type
|
|
62
|
+
* ```ts
|
|
63
|
+
* enableContextMock(configurator, (mock) => {
|
|
64
|
+
* // no explicit wiring needed — relatedContexts filters this same pool by type
|
|
65
|
+
* mock.setContexts([project, facility]);
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example Override related contexts for one specific item
|
|
70
|
+
* ```ts
|
|
71
|
+
* enableContextMock(configurator, (mock) => {
|
|
72
|
+
* mock.addContext(project);
|
|
73
|
+
* mock.setRelatedContexts(project.id, [facilityA, facilityB]);
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example Escape hatch for a custom resolution need
|
|
78
|
+
* ```ts
|
|
79
|
+
* enableContextMock(configurator, (mock) => {
|
|
80
|
+
* mock.setResolver((id) => (id === 'special' ? specialContextItem : undefined));
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare class ContextMockConfigurator extends ContextModuleConfigurator {
|
|
85
|
+
#private;
|
|
86
|
+
/**
|
|
87
|
+
* Registers the in-memory client up front, so `createConfig` never falls
|
|
88
|
+
* back to building one from a real `ServicesModule` — a mock needs neither
|
|
89
|
+
* an API provider nor a network to answer `get`/`query`/`related`.
|
|
90
|
+
*/
|
|
91
|
+
constructor();
|
|
92
|
+
/**
|
|
93
|
+
* Seeds a context item and selects it as the context the app resolves on startup.
|
|
94
|
+
*
|
|
95
|
+
* @param item - The context item to seed and select.
|
|
96
|
+
* @returns This configurator, for chaining.
|
|
97
|
+
*/
|
|
98
|
+
setCurrentContext(item: ContextItem): this;
|
|
99
|
+
/**
|
|
100
|
+
* Seeds multiple context items, making each resolvable by id.
|
|
101
|
+
*
|
|
102
|
+
* @remarks
|
|
103
|
+
* Does not select any of them as current — pair with {@link setCurrentContext}
|
|
104
|
+
* for that.
|
|
105
|
+
*
|
|
106
|
+
* @param items - The context items to seed.
|
|
107
|
+
* @returns This configurator, for chaining.
|
|
108
|
+
*/
|
|
109
|
+
setContexts(items: ContextItem[]): this;
|
|
110
|
+
/**
|
|
111
|
+
* Seeds a single context item, making it resolvable by id.
|
|
112
|
+
*
|
|
113
|
+
* @param item - The context item to seed.
|
|
114
|
+
* @returns This configurator, for chaining.
|
|
115
|
+
*/
|
|
116
|
+
addContext(item: ContextItem): this;
|
|
117
|
+
/**
|
|
118
|
+
* Overrides related-context resolution for one specific source item.
|
|
119
|
+
*
|
|
120
|
+
* @remarks
|
|
121
|
+
* Without this, `relatedContexts` filters the seeded pool by the requested
|
|
122
|
+
* type(s) — this is only needed when a test wants a specific item to resolve
|
|
123
|
+
* something other than that default (e.g. no related items at all).
|
|
124
|
+
*
|
|
125
|
+
* @param itemId - The id of the item relations are being overridden for.
|
|
126
|
+
* @param items - The context items to return from `relatedContexts` for that item.
|
|
127
|
+
* @returns This configurator, for chaining.
|
|
128
|
+
*/
|
|
129
|
+
setRelatedContexts(itemId: string, items: ContextItem[]): this;
|
|
130
|
+
/**
|
|
131
|
+
* Escape hatch: overrides id-based context resolution directly.
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
134
|
+
* For a custom id-lookup strategy or a shape the friendly seeding methods
|
|
135
|
+
* above did not cover — reaching for this means they were already tried
|
|
136
|
+
* and did not fit. Only replaces lookup by id (used by
|
|
137
|
+
* `setCurrentContextById` and the seeded initial context) — related-context
|
|
138
|
+
* resolution by item always goes through {@link setRelatedContexts} instead,
|
|
139
|
+
* regardless of this setting.
|
|
140
|
+
*
|
|
141
|
+
* @param fn - Resolves a context item by id, or returns `undefined` if none matches.
|
|
142
|
+
* @returns This configurator, for chaining.
|
|
143
|
+
*/
|
|
144
|
+
setResolver(fn: ContextResolverFn): this;
|
|
145
|
+
/**
|
|
146
|
+
* Defaults `resolveInitialContext` from the seeded/resolved state, once the
|
|
147
|
+
* real config has been assembled.
|
|
148
|
+
*
|
|
149
|
+
* @param config - The context module config assembled by the base configurator.
|
|
150
|
+
* @param init - Module initializer arguments.
|
|
151
|
+
* @returns The context module configuration, backed by seeded/resolved data.
|
|
152
|
+
*/
|
|
153
|
+
protected _processConfig(config: Partial<ContextModuleConfig>, init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>): Promise<ContextModuleConfig>;
|
|
154
|
+
}
|
|
155
|
+
export default ContextMockConfigurator;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ContextItem } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates one {@link ContextItem} fixture per call, for a single factory instance.
|
|
4
|
+
*
|
|
5
|
+
* @param overrides - Fields to override on the generated item. Supplying `id`
|
|
6
|
+
* skips the sequential generator for that one item.
|
|
7
|
+
* @returns A fully-formed {@link ContextItem}.
|
|
8
|
+
*/
|
|
9
|
+
export type MockContextItemFactory = (overrides?: Partial<ContextItem>) => ContextItem;
|
|
10
|
+
/**
|
|
11
|
+
* Builds a {@link MockContextItemFactory} producing deterministic, sequential
|
|
12
|
+
* ids of the form `<prefix>-<n>`, with a realistic `title` filled in by
|
|
13
|
+
* {@link https://fakerjs.dev/ | faker} (`@faker-js/faker`, a peer dependency —
|
|
14
|
+
* only required if this factory is imported).
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Ids are scoped to the returned factory, not shared globally, so two tests
|
|
18
|
+
* creating their own factory each start counting from `1` — a random id (e.g.
|
|
19
|
+
* `crypto.randomUUID()`) would change on every run and break any assertion
|
|
20
|
+
* that seeds one item and expects to see that same id again. The faker seed
|
|
21
|
+
* is derived from that same id, so the generated `title` is just as
|
|
22
|
+
* deterministic. For fixtures spanning several context types (e.g. a
|
|
23
|
+
* parent/child type hierarchy), use `createContextItems` instead — it
|
|
24
|
+
* assigns ids per type and wires the type hierarchy for you.
|
|
25
|
+
*
|
|
26
|
+
* @param prefix - The id prefix. Defaults to `'ctx'`.
|
|
27
|
+
* @returns A function creating one {@link ContextItem} fixture per call.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const createContextItem = createContextItemFactory();
|
|
32
|
+
*
|
|
33
|
+
* const project = createContextItem(); // id: 'ctx-1', title: a deterministic faker company name
|
|
34
|
+
* const facility = createContextItem({ title: 'Facility A' }); // id: 'ctx-2', title overridden
|
|
35
|
+
*
|
|
36
|
+
* mock.setContexts([project, facility]);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const createContextItemFactory: (prefix?: string) => MockContextItemFactory;
|
|
40
|
+
export default createContextItemFactory;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ContextItem } from '../../types';
|
|
2
|
+
/** Overridable {@link ContextItem} fields. `id` and `type` are always assigned by the type seed itself. */
|
|
3
|
+
export type ContextItemOverrides = Partial<Omit<ContextItem, 'id' | 'type'>>;
|
|
4
|
+
/**
|
|
5
|
+
* Describes one context type to generate fixtures for.
|
|
6
|
+
*
|
|
7
|
+
* @param type - The context type id (e.g. `'ProjectMaster'`, `'Contract'`).
|
|
8
|
+
* @param count - Number of items to generate for this type. Defaults to `1`.
|
|
9
|
+
* @param parentTypeIds - Parent type ids, when this type is a child in the
|
|
10
|
+
* type hierarchy — sets `type.isChildType`/`type.parentTypeIds` on every
|
|
11
|
+
* generated item of this type.
|
|
12
|
+
* @param item - Per-item overrides, called with the item's 1-based index
|
|
13
|
+
* within this type.
|
|
14
|
+
*/
|
|
15
|
+
export interface ContextTypeSeed {
|
|
16
|
+
type: string;
|
|
17
|
+
count?: number;
|
|
18
|
+
parentTypeIds?: string[];
|
|
19
|
+
item?: (index: number) => ContextItemOverrides;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generates a batch of {@link ContextItem} fixtures across one or more
|
|
23
|
+
* context types, with deterministic ids, a realistic `title` from
|
|
24
|
+
* {@link https://fakerjs.dev/ | faker} (`@faker-js/faker`, a peer dependency —
|
|
25
|
+
* only required if this factory is imported), and a consistent type hierarchy.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Ids are `<type>-<n>` (lowercased type, 1-based per type), so results are
|
|
29
|
+
* stable across runs and readable in test failures. `title` is filled in from
|
|
30
|
+
* a faker instance seeded by that same id, so it's just as deterministic.
|
|
31
|
+
* Feeding the result to `ContextMockConfigurator.setContexts` is enough to
|
|
32
|
+
* make related-context resolution work across types — the mock's
|
|
33
|
+
* `relatedContexts` filters this same seeded pool by type, so a child-typed
|
|
34
|
+
* item resolves into a parent of its `parentTypeIds` without any per-item
|
|
35
|
+
* wiring. That default is a simplification: the real context API resolves
|
|
36
|
+
* relations per specific instance (a `Contract` belongs to one particular
|
|
37
|
+
* `ProjectMaster`, not every `ProjectMaster` in the system) — seed only one
|
|
38
|
+
* instance per type when that distinction doesn't matter to the test, or use
|
|
39
|
+
* `ContextMockConfigurator.setRelatedContexts` to pin a specific item's
|
|
40
|
+
* relations when it does.
|
|
41
|
+
*
|
|
42
|
+
* @param types - The context types, and how many items to generate for each.
|
|
43
|
+
* @returns The generated context items, in the order the type seeds were given.
|
|
44
|
+
*
|
|
45
|
+
* @example Seed a project type and a child contract type
|
|
46
|
+
* ```ts
|
|
47
|
+
* const [project] = createContextItems([{ type: 'ProjectMaster' }]);
|
|
48
|
+
* const [contract] = createContextItems([
|
|
49
|
+
* { type: 'Contract', parentTypeIds: ['ProjectMaster'] },
|
|
50
|
+
* ]);
|
|
51
|
+
*
|
|
52
|
+
* enableContextMock(configurator, (mock) => {
|
|
53
|
+
* mock.setContexts([project, contract]);
|
|
54
|
+
* // relatedContexts({ item: contract, filter: { type: ['ProjectMaster'] } }) now resolves `project`
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare const createContextItems: (types: ContextTypeSeed[]) => ContextItem[];
|
|
59
|
+
export default createContextItems;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixture generators for {@link ContextMockConfigurator} test data.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* A separate entry point from `@equinor/fusion-framework-module-context/mock`
|
|
6
|
+
* because these factories use {@link https://fakerjs.dev/ | faker}
|
|
7
|
+
* (`@faker-js/faker`) for realistic values — an optional peer dependency, only
|
|
8
|
+
* required if this entry point is imported. `enableContextMock` and
|
|
9
|
+
* `ContextMockConfigurator` on the main `/mock` entry point never require it.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { createContextItems } from '@equinor/fusion-framework-module-context/mock/fixtures';
|
|
14
|
+
*
|
|
15
|
+
* const [project] = createContextItems([{ type: 'ProjectMaster' }]);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
export { createContextItemFactory, type MockContextItemFactory, } from './create-context-item-factory';
|
|
21
|
+
export { createContextItems, type ContextTypeSeed, type ContextItemOverrides, } from './create-context-items';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derives a numeric seed from a string, for a deterministic {@link https://fakerjs.dev/ | faker} instance.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* A pure-JS hash (no `node:crypto`) so fixtures stay usable in browser-like test
|
|
6
|
+
* environments, not just Node. Same id in, same seed out, every run.
|
|
7
|
+
*
|
|
8
|
+
* @param value - The string to derive a seed from (typically a generated item id).
|
|
9
|
+
* @returns A 32-bit unsigned integer seed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const stringToSeed: (value: string) => number;
|
|
12
|
+
export default stringToSeed;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock context module for tests: real provider, real configurator, in-memory data.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Substituting the data source is the smallest change that removes the context
|
|
6
|
+
* API, HTTP mocking, and service-discovery mocking from a test. Everything
|
|
7
|
+
* above it — validation, resolution, parent-context propagation, initial-context
|
|
8
|
+
* selection — is the production code path.
|
|
9
|
+
*
|
|
10
|
+
* This is one of two ways to mock context data in tests: a small, static,
|
|
11
|
+
* in-memory pool (this module — no HTTP layer involved at all). The other is
|
|
12
|
+
* mocking the context API's HTTP responses directly (e.g. with MSW), which
|
|
13
|
+
* exercises the real `ContextModuleConfigurator`/services/HTTP pipeline —
|
|
14
|
+
* reach for that instead when the test needs to cover that pipeline itself.
|
|
15
|
+
* For fixture generators with realistic fake data, see
|
|
16
|
+
* `@equinor/fusion-framework-module-context/mock/fixtures`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
|
|
21
|
+
*
|
|
22
|
+
* enableContextMock(configurator, (mock) => {
|
|
23
|
+
* mock.setCurrentContext({ id: 'my-ctx', type: { id: 'ProjectMaster' }, value: {} });
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @packageDocumentation
|
|
28
|
+
*/
|
|
29
|
+
export { ContextMockConfigurator, type ContextResolverFn, } from './ContextMockConfigurator';
|
|
30
|
+
export { enableContextMock, contextMockModule, type ContextMockConfigFn } from './module';
|