@equinor/fusion-framework-module-context 4.0.0-next.8 → 4.0.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/esm/ContextConfigBuilder.js +2 -2
  3. package/dist/esm/ContextConfigBuilder.js.map +1 -1
  4. package/dist/esm/ContextProvider.js +50 -58
  5. package/dist/esm/ContextProvider.js.map +1 -1
  6. package/dist/esm/client/ContextClient.js +11 -2
  7. package/dist/esm/client/ContextClient.js.map +1 -1
  8. package/dist/esm/configurator.js +4 -2
  9. package/dist/esm/configurator.js.map +1 -1
  10. package/dist/esm/index.js +1 -1
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/module.js +33 -19
  13. package/dist/esm/module.js.map +1 -1
  14. package/dist/esm/{enable-context.js → utils/enable-context.js} +1 -1
  15. package/dist/esm/utils/enable-context.js.map +1 -0
  16. package/dist/esm/utils/index.js +3 -0
  17. package/dist/esm/utils/index.js.map +1 -0
  18. package/dist/esm/utils/resolve-context-from-path.js +17 -0
  19. package/dist/esm/utils/resolve-context-from-path.js.map +1 -0
  20. package/dist/esm/utils/resolve-initial-context.js +16 -0
  21. package/dist/esm/utils/resolve-initial-context.js.map +1 -0
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/dist/types/ContextConfigBuilder.d.ts +1 -1
  24. package/dist/types/ContextProvider.d.ts +20 -20
  25. package/dist/types/client/ContextClient.d.ts +7 -4
  26. package/dist/types/configurator.d.ts +9 -3
  27. package/dist/types/index.d.ts +1 -1
  28. package/dist/types/module.d.ts +3 -1
  29. package/dist/types/{enable-context.d.ts → utils/enable-context.d.ts} +2 -2
  30. package/dist/types/utils/index.d.ts +2 -0
  31. package/dist/types/utils/resolve-context-from-path.d.ts +8 -0
  32. package/dist/types/utils/resolve-initial-context.d.ts +7 -0
  33. package/package.json +12 -7
  34. package/src/ContextConfigBuilder.ts +2 -2
  35. package/src/ContextProvider.ts +59 -55
  36. package/src/client/ContextClient.ts +23 -8
  37. package/src/configurator.ts +22 -3
  38. package/src/index.ts +1 -1
  39. package/src/module.ts +51 -16
  40. package/src/{enable-context.ts → utils/enable-context.ts} +3 -3
  41. package/src/utils/index.ts +2 -0
  42. package/src/utils/resolve-context-from-path.ts +34 -0
  43. package/src/utils/resolve-initial-context.ts +29 -0
  44. package/tsconfig.json +5 -2
  45. package/dist/esm/enable-context.js.map +0 -1
@@ -15,7 +15,7 @@ export declare class ContextConfigBuilder<TModules extends Array<AnyModule> = []
15
15
  setContextParameterFn(fn: ContextModuleConfig['contextParameterFn']): void;
16
16
  setValidateContext(fn: ContextModuleConfig['validateContext']): void;
17
17
  setResolveContext(fn: ContextModuleConfig['resolveContext']): void;
18
- setSkipInitialContext(skip: ContextModuleConfig['skipInitialContext']): void;
18
+ setResolveInitialContext(fn: ContextModuleConfig['resolveInitialContext']): void;
19
19
  setContextClient(client: {
20
20
  get: QueryFn<ContextItem, GetContextParameters> | QueryCtorOptions<ContextItem, GetContextParameters>;
21
21
  query: QueryFn<ContextItem[], QueryContextParameters> | QueryCtorOptions<ContextItem[], QueryContextParameters>;
@@ -8,8 +8,8 @@ import Query from '@equinor/fusion-query';
8
8
  export interface IContextProvider {
9
9
  readonly contextClient: ContextClient;
10
10
  readonly queryClient: Query<ContextItem[], QueryContextParameters>;
11
- readonly currentContext$: Observable<ContextItem | undefined>;
12
- currentContext: ContextItem | undefined;
11
+ readonly currentContext$: Observable<ContextItem | null | undefined>;
12
+ currentContext: ContextItem | null | undefined;
13
13
  queryContext(search: string): Observable<Array<ContextItem>>;
14
14
  queryContextAsync(search: string): Promise<Array<ContextItem>>;
15
15
  validateContext(item: ContextItem<Record<string, unknown>>): boolean;
@@ -20,40 +20,40 @@ export interface IContextProvider {
20
20
  clearCurrentContext: VoidFunction;
21
21
  setCurrentContextById(id: string): Observable<ContextItem<Record<string, unknown>>>;
22
22
  setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>;
23
- setCurrentContext(context: ContextItem<Record<string, unknown>>, opt?: {
23
+ setCurrentContext(context: ContextItem<Record<string, unknown>> | null, opt?: {
24
24
  validate?: boolean;
25
25
  resolve?: boolean;
26
- }): Observable<ContextItem<Record<string, unknown>>>;
27
- setCurrentContextAsync(context: ContextItem<Record<string, unknown>>, opt?: {
26
+ }): Observable<ContextItem<Record<string, unknown>> | null>;
27
+ setCurrentContextAsync(context: ContextItem<Record<string, unknown>> | null, opt?: {
28
28
  validate?: boolean;
29
29
  resolve?: boolean;
30
- }): Promise<ContextItem<Record<string, unknown>>>;
30
+ }): Promise<ContextItem<Record<string, unknown>> | null>;
31
31
  }
32
32
  export declare class ContextProvider implements IContextProvider {
33
33
  #private;
34
34
  get contextClient(): ContextClient;
35
35
  get queryClient(): Query<ContextItem<Record<string, unknown>>[], QueryContextParameters>;
36
- get currentContext$(): Observable<ContextItem | undefined>;
37
- get currentContext(): ContextItem | undefined;
38
- set currentContext(context: ContextItem | undefined);
36
+ get currentContext$(): Observable<ContextItem | null | undefined>;
37
+ get currentContext(): ContextItem | undefined | null;
38
+ set currentContext(context: ContextItem | null | undefined);
39
39
  constructor(args: {
40
40
  config: ContextModuleConfig;
41
41
  event?: ModuleType<EventModule>;
42
42
  parentContext?: IContextProvider;
43
43
  });
44
- connectParentContextAsync(provider: IContextProvider, opt?: {
45
- setCurrent?: boolean;
46
- }): Promise<Subscription>;
44
+ connectParentContext(provider: IContextProvider, opt?: {
45
+ skipFirst: boolean;
46
+ }): Subscription;
47
47
  setCurrentContextById(id: string): Observable<ContextItem<Record<string, unknown>>>;
48
48
  setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>;
49
- setCurrentContext(context: ContextItem<Record<string, unknown>>, opt?: {
49
+ setCurrentContext<T extends ContextItem<Record<string, unknown>> | null>(context: T, opt?: {
50
50
  validate?: boolean;
51
51
  resolve?: boolean;
52
- }): Observable<ContextItem<Record<string, unknown>>>;
53
- setCurrentContextAsync(context: ContextItem<Record<string, unknown>>, opt?: {
52
+ }): Observable<T>;
53
+ setCurrentContextAsync<T extends ContextItem<Record<string, unknown>> | null>(context: T, opt?: {
54
54
  validate?: boolean;
55
55
  resolve?: boolean;
56
- }): Promise<ContextItem<Record<string, unknown>>>;
56
+ }): Promise<T>;
57
57
  queryContext(search: string): Observable<Array<ContextItem>>;
58
58
  queryContextAsync(search: string): Promise<Array<ContextItem>>;
59
59
  validateContext(item: ContextItem<Record<string, unknown>>): boolean;
@@ -68,14 +68,14 @@ export default ContextProvider;
68
68
  declare module '@equinor/fusion-framework-module-event' {
69
69
  interface FrameworkEventMap {
70
70
  onCurrentContextChange: FrameworkEvent<FrameworkEventInit<{
71
- context: ContextItem | undefined;
71
+ context: ContextItem | null;
72
72
  }, IContextProvider>>;
73
73
  onCurrentContextChanged: FrameworkEvent<FrameworkEventInit<{
74
- next: ContextItem | undefined;
75
- previous: ContextItem | undefined;
74
+ next: ContextItem | null;
75
+ previous?: ContextItem | null;
76
76
  }, IContextProvider>>;
77
77
  onParentContextChanged: FrameworkEvent<FrameworkEventInit<{
78
- context: ContextItem | undefined;
78
+ context: ContextItem | null;
79
79
  }, IContextProvider>>;
80
80
  onSetContextResolve: FrameworkEvent<FrameworkEventInit<{
81
81
  context: ContextItem;
@@ -4,16 +4,19 @@ import { ContextItem } from '../types';
4
4
  export type GetContextParameters = {
5
5
  id: string;
6
6
  };
7
- export declare class ContextClient extends Observable<ContextItem> {
7
+ export declare class ContextClient extends Observable<ContextItem | null> {
8
8
  #private;
9
- get currentContext(): ContextItem | undefined;
10
- get currentContext$(): Observable<ContextItem | undefined>;
9
+ get currentContext(): ContextItem | null | undefined;
10
+ get currentContext$(): Observable<ContextItem | null | undefined>;
11
11
  get client(): Query<ContextItem, {
12
12
  id: string;
13
13
  }>;
14
14
  constructor(options: QueryCtorOptions<ContextItem, GetContextParameters>);
15
- setCurrentContext(idOrItem?: string | ContextItem): void;
15
+ setCurrentContext(idOrItem?: string | ContextItem | null): void;
16
16
  resolveContext(id: string): Observable<ContextItem>;
17
+ resolveContextAsync(id: string, opt?: {
18
+ awaitResolve: boolean;
19
+ }): Promise<ContextItem>;
17
20
  dispose(): void;
18
21
  }
19
22
  export default ContextClient;
@@ -1,10 +1,12 @@
1
- import { ModuleInitializerArgs } from '@equinor/fusion-framework-module';
1
+ import { ObservableInput } from 'rxjs';
2
+ import { AnyModuleInstance, ModuleInitializerArgs, ModuleInstance } from '@equinor/fusion-framework-module';
2
3
  import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
4
+ import { NavigationModule } from '@equinor/fusion-framework-module-navigation';
3
5
  import { QueryCtorOptions } from '@equinor/fusion-query';
4
6
  import { ContextFilterFn, ContextItem, QueryContextParameters, RelatedContextParameters } from './types';
5
7
  import { GetContextParameters } from './client/ContextClient';
6
8
  import { ContextConfigBuilderCallback } from './ContextConfigBuilder';
7
- import { IContextProvider } from 'ContextProvider';
9
+ import { type IContextProvider } from './ContextProvider';
8
10
  export interface ContextModuleConfig {
9
11
  client: {
10
12
  get: QueryCtorOptions<ContextItem, GetContextParameters>;
@@ -20,6 +22,10 @@ export interface ContextModuleConfig {
20
22
  }) => string | QueryContextParameters;
21
23
  resolveContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['resolveContext']>;
22
24
  validateContext?: (this: IContextProvider, item: ContextItem | null) => ReturnType<IContextProvider['validateContext']>;
25
+ resolveInitialContext?: (args: {
26
+ ref?: AnyModuleInstance | any;
27
+ modules: ModuleInstance;
28
+ }) => ObservableInput<ContextItem | void>;
23
29
  }
24
30
  export interface IContextModuleConfigurator {
25
31
  addConfigBuilder: (init: ContextConfigBuilderCallback) => void;
@@ -29,6 +35,6 @@ export declare class ContextModuleConfigurator implements IContextModuleConfigur
29
35
  defaultExpireTime: number;
30
36
  addConfigBuilder(init: ContextConfigBuilderCallback): void;
31
37
  protected _getServiceProvider(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>): Promise<IApiProvider>;
32
- createConfig(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>): Promise<ContextModuleConfig>;
38
+ createConfig(init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>): Promise<ContextModuleConfig>;
33
39
  }
34
40
  export default ContextModuleConfigurator;
@@ -1,5 +1,5 @@
1
1
  export { ContextModuleConfigurator, IContextModuleConfigurator, ContextModuleConfig, } from './configurator';
2
2
  export { IContextProvider, ContextProvider } from './ContextProvider';
3
3
  export { default, ContextModule, module as contextModule, moduleKey as contextModuleKey, } from './module';
4
- export { enableContext } from './enable-context';
4
+ export { enableContext } from './utils/enable-context';
5
5
  export * from './types';
@@ -1,13 +1,15 @@
1
1
  import { Module } from '@equinor/fusion-framework-module';
2
2
  import { EventModule } from '@equinor/fusion-framework-module-event';
3
3
  import { ServicesModule } from '@equinor/fusion-framework-module-services';
4
+ import { NavigationModule } from '@equinor/fusion-framework-module-navigation';
4
5
  import { IContextModuleConfigurator } from './configurator';
5
6
  import { IContextProvider } from './ContextProvider';
6
7
  export type ContextModuleKey = 'context';
7
8
  export declare const moduleKey: ContextModuleKey;
8
9
  export type ContextModule = Module<ContextModuleKey, IContextProvider, IContextModuleConfigurator, [
9
10
  ServicesModule,
10
- EventModule
11
+ EventModule,
12
+ NavigationModule
11
13
  ]>;
12
14
  export declare const module: ContextModule;
13
15
  declare module '@equinor/fusion-framework-module' {
@@ -1,4 +1,4 @@
1
1
  import type { IModulesConfigurator, AnyModule, ModuleInitializerArgs } from '@equinor/fusion-framework-module';
2
- import type { IContextModuleConfigurator } from './configurator';
3
- import type { ContextConfigBuilder } from './ContextConfigBuilder';
2
+ import type { IContextModuleConfigurator } from '../configurator';
3
+ import type { ContextConfigBuilder } from '../ContextConfigBuilder';
4
4
  export declare const enableContext: (configurator: IModulesConfigurator<any, any>, builder?: (<TDeps extends AnyModule[] = []>(builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>) => void | Promise<void>) | undefined) => void;
@@ -0,0 +1,2 @@
1
+ export { enableContext } from './enable-context';
2
+ export { resolveInitialContext } from './resolve-initial-context';
@@ -0,0 +1,8 @@
1
+ import { ModuleType } from '@equinor/fusion-framework-module';
2
+ import { type ContextModule } from '../module';
3
+ export type ContextPathResolveArgs = {
4
+ extract?: (path: string) => string | undefined;
5
+ validate?: (contextId: string) => boolean;
6
+ };
7
+ export declare const resolveContextFromPath: (context: ModuleType<ContextModule>, args?: ContextPathResolveArgs) => (path: string) => import("rxjs").Observable<import("..").ContextItem<Record<string, unknown>>>;
8
+ export default resolveContextFromPath;
@@ -0,0 +1,7 @@
1
+ import { type ContextModuleConfig } from '../configurator';
2
+ import { ContextPathResolveArgs } from './resolve-context-from-path';
3
+ export declare const resolveContextFromParent: ContextModuleConfig['resolveInitialContext'];
4
+ export declare const resolveInitialContext: (options?: {
5
+ path?: ContextPathResolveArgs;
6
+ }) => Required<ContextModuleConfig>['resolveInitialContext'];
7
+ export default resolveInitialContext;
package/package.json CHANGED
@@ -1,17 +1,21 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-context",
3
- "version": "4.0.0-next.8",
3
+ "version": "4.0.0",
4
4
  "description": "",
5
5
  "main": "./dist/esm/index.js",
6
6
  "exports": {
7
7
  ".": "./dist/esm/index.js",
8
- "./package.json": "./package.json"
8
+ "./package.json": "./package.json",
9
+ "./utils": "./dist/esm/utils/index.js"
9
10
  },
10
11
  "types": "index.d.ts",
11
12
  "typesVersions": {
12
13
  ">=4.2": {
13
14
  "*": [
14
15
  "dist/types/*"
16
+ ],
17
+ "utils": [
18
+ "dist/types/utils/index.d.ts"
15
19
  ]
16
20
  }
17
21
  },
@@ -31,18 +35,19 @@
31
35
  "directory": "packages/modules/context"
32
36
  },
33
37
  "dependencies": {
34
- "@equinor/fusion-query": "^3.0.0",
38
+ "@equinor/fusion-query": "^3.0.1",
35
39
  "fast-deep-equal": "^3.1.3"
36
40
  },
37
41
  "devDependencies": {
38
- "@equinor/fusion-framework-module": "^3.0.0",
39
- "@equinor/fusion-framework-module-event": "^3.0.0",
40
- "@equinor/fusion-framework-module-services": "^3.1.0-next.0",
42
+ "@equinor/fusion-framework-module": "^4.0.0",
43
+ "@equinor/fusion-framework-module-event": "^4.0.0",
44
+ "@equinor/fusion-framework-module-navigation": "^2.1.0",
45
+ "@equinor/fusion-framework-module-services": "^3.1.0",
41
46
  "rxjs": "^7.5.7"
42
47
  },
43
48
  "peerDependencies": {
44
49
  "@equinor/fusion-framework-module": ">=1.2.5",
45
50
  "rxjs": "^7.5.7"
46
51
  },
47
- "gitHead": "3a5344ee1e18a35158681004cb154b2522e659ed"
52
+ "gitHead": "851d70038b9b015663981a9456247f3d5dd0c3a2"
48
53
  }
@@ -65,8 +65,8 @@ export class ContextConfigBuilder<
65
65
  this.config.resolveContext = fn;
66
66
  }
67
67
 
68
- setSkipInitialContext(skip: ContextModuleConfig['skipInitialContext']) {
69
- this.config.skipInitialContext = skip;
68
+ setResolveInitialContext(fn: ContextModuleConfig['resolveInitialContext']) {
69
+ this.config.resolveInitialContext = fn;
70
70
  }
71
71
 
72
72
  setContextClient(
@@ -31,8 +31,8 @@ export interface IContextProvider {
31
31
  /** DANGER */
32
32
  readonly queryClient: Query<ContextItem[], QueryContextParameters>;
33
33
 
34
- readonly currentContext$: Observable<ContextItem | undefined>;
35
- currentContext: ContextItem | undefined;
34
+ readonly currentContext$: Observable<ContextItem | null | undefined>;
35
+ currentContext: ContextItem | null | undefined;
36
36
  queryContext(search: string): Observable<Array<ContextItem>>;
37
37
  queryContextAsync(search: string): Promise<Array<ContextItem>>;
38
38
  validateContext(item: ContextItem<Record<string, unknown>>): boolean;
@@ -50,14 +50,14 @@ export interface IContextProvider {
50
50
  setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>;
51
51
 
52
52
  setCurrentContext(
53
- context: ContextItem<Record<string, unknown>>,
53
+ context: ContextItem<Record<string, unknown>> | null,
54
54
  opt?: { validate?: boolean; resolve?: boolean }
55
- ): Observable<ContextItem<Record<string, unknown>>>;
55
+ ): Observable<ContextItem<Record<string, unknown>> | null>;
56
56
 
57
57
  setCurrentContextAsync(
58
- context: ContextItem<Record<string, unknown>>,
58
+ context: ContextItem<Record<string, unknown>> | null,
59
59
  opt?: { validate?: boolean; resolve?: boolean }
60
- ): Promise<ContextItem<Record<string, unknown>>>;
60
+ ): Promise<ContextItem<Record<string, unknown>> | null>;
61
61
  }
62
62
 
63
63
  export class ContextProvider implements IContextProvider {
@@ -81,22 +81,25 @@ export class ContextProvider implements IContextProvider {
81
81
  return this.#contextQuery;
82
82
  }
83
83
 
84
- get currentContext$(): Observable<ContextItem | undefined> {
84
+ get currentContext$(): Observable<ContextItem | null | undefined> {
85
85
  return this.#contextClient.currentContext$;
86
86
  }
87
87
 
88
- get currentContext(): ContextItem | undefined {
88
+ get currentContext(): ContextItem | undefined | null {
89
89
  return this.#contextClient.currentContext;
90
90
  }
91
91
 
92
92
  /** @deprecated do not use, will be removed */
93
- set currentContext(context: ContextItem | undefined) {
93
+ set currentContext(context: ContextItem | null | undefined) {
94
94
  console.warn(
95
95
  '@deprecated',
96
96
  'ContextProvider.currentContext',
97
97
  'use setCurrentContextById|setCurrentContext|clearCurrentContext'
98
98
  );
99
- context ? this.setCurrentContextAsync(context) : this.clearCurrentContext();
99
+ if (context === undefined) {
100
+ throw Error('not allowed to set current context as undefined undefined!');
101
+ }
102
+ this.setCurrentContextAsync(context);
100
103
  }
101
104
 
102
105
  constructor(args: {
@@ -150,20 +153,30 @@ export class ContextProvider implements IContextProvider {
150
153
  /** observe event from child modules */
151
154
  this.#event.addEventListener('onCurrentContextChanged', (e) => {
152
155
  /** loop prevention */
153
- if (e.source !== this) {
154
- this.currentContext = e.detail.next;
156
+ if (e.source !== this && e.detail.next !== undefined) {
157
+ this.setCurrentContext(e.detail.next);
155
158
  }
156
159
  })
157
160
  );
158
161
  }
159
162
  }
160
163
 
161
- public async connectParentContextAsync(
164
+ public connectParentContext(
162
165
  provider: IContextProvider,
163
- opt?: { setCurrent?: boolean }
164
- ): Promise<Subscription> {
166
+ opt?: { skipFirst: boolean }
167
+ ): Subscription {
165
168
  const parentContext$ = provider.currentContext$.pipe(
166
- filter((next) => this.currentContext !== next),
169
+ filter((next, index) => {
170
+ if (opt?.skipFirst && index <= 1) {
171
+ console.debug(
172
+ 'ContextProvider::connectParentContext',
173
+ 'skipping first item',
174
+ next
175
+ );
176
+ return false;
177
+ }
178
+ return this.currentContext !== next;
179
+ }),
167
180
  switchMap(async (next) => {
168
181
  if (next) {
169
182
  const onParentContextChanged = await this.#event?.dispatchEvent(
@@ -206,29 +219,7 @@ export class ContextProvider implements IContextProvider {
206
219
 
207
220
  const subscription = parentContext$.subscribe();
208
221
  this.#subscriptions.add(subscription);
209
-
210
- return new Promise((resolve, reject) => {
211
- if (opt?.setCurrent && provider.currentContext) {
212
- subscription.add(reject);
213
- subscription.add(
214
- this.setCurrentContext(provider.currentContext, {
215
- validate: true,
216
- resolve: true,
217
- }).subscribe({
218
- error: (err) => {
219
- subscription.remove(reject);
220
- reject(err);
221
- },
222
- complete: () => {
223
- subscription.remove(reject);
224
- resolve(subscription);
225
- },
226
- })
227
- );
228
- } else {
229
- return resolve(subscription);
230
- }
231
- });
222
+ return subscription;
232
223
  }
233
224
 
234
225
  public setCurrentContextById(id: string): Observable<ContextItem<Record<string, unknown>>> {
@@ -236,7 +227,10 @@ export class ContextProvider implements IContextProvider {
236
227
  try {
237
228
  this.#contextClient
238
229
  .resolveContext(id)
239
- .pipe(switchMap((item) => this.setCurrentContext(item)))
230
+ .pipe(
231
+ filter((item): item is ContextItem => !!item),
232
+ switchMap((item) => this.setCurrentContext(item))
233
+ )
240
234
  .subscribe(subscriber);
241
235
  } catch (err) {
242
236
  subscriber.error(err);
@@ -248,16 +242,16 @@ export class ContextProvider implements IContextProvider {
248
242
  return firstValueFrom(this.setCurrentContextById(id));
249
243
  }
250
244
 
251
- public setCurrentContext(
252
- context: ContextItem<Record<string, unknown>>,
245
+ public setCurrentContext<T extends ContextItem<Record<string, unknown>> | null>(
246
+ context: T,
253
247
  opt?: { validate?: boolean; resolve?: boolean }
254
- ): Observable<ContextItem<Record<string, unknown>>> {
248
+ ): Observable<T> {
255
249
  return new Observable((subscriber) => {
256
250
  if (context === this.currentContext) {
257
251
  subscriber.next(context);
258
252
  return subscriber.complete();
259
253
  }
260
- if (opt?.validate && !this.validateContext(context)) {
254
+ if (context && opt?.validate && !this.validateContext(context)) {
261
255
  if (!opt.resolve) {
262
256
  /** cannot resolve context, and provided is invalid */
263
257
  this.#event?.dispatchEvent('onSetContextValidationFailed', {
@@ -309,7 +303,9 @@ export class ContextProvider implements IContextProvider {
309
303
  return resolved;
310
304
  }),
311
305
  /** recursive set current context without validation and resolve */
312
- switchMap((resolved) => this.setCurrentContext(resolved))
306
+ switchMap((resolved) =>
307
+ this.setCurrentContext(resolved as unknown as T)
308
+ )
313
309
  )
314
310
  .subscribe(subscriber);
315
311
  }
@@ -333,17 +329,17 @@ export class ContextProvider implements IContextProvider {
333
329
  })
334
330
  )
335
331
  .subscribe((context) => {
336
- this.#contextClient.setCurrentContext(context);
332
+ this.#contextClient.setCurrentContext(context ?? null);
337
333
  subscriber.next(context);
338
334
  subscriber.complete();
339
335
  });
340
336
  });
341
337
  }
342
338
 
343
- public async setCurrentContextAsync(
344
- context: ContextItem<Record<string, unknown>>,
339
+ public async setCurrentContextAsync<T extends ContextItem<Record<string, unknown>> | null>(
340
+ context: T,
345
341
  opt?: { validate?: boolean; resolve?: boolean }
346
- ): Promise<ContextItem<Record<string, unknown>>> {
342
+ ): Promise<T> {
347
343
  return firstValueFrom(this.setCurrentContext(context, opt));
348
344
  }
349
345
 
@@ -407,7 +403,15 @@ export class ContextProvider implements IContextProvider {
407
403
  )
408
404
  );
409
405
  }
410
- return this.#contextRelated.query(args).pipe(map(({ value }) => value));
406
+ return this.#contextRelated.query(args).pipe(
407
+ map(({ value }) => value),
408
+ catchError((err) => {
409
+ if (err.cause) {
410
+ throw err.cause;
411
+ }
412
+ throw err;
413
+ })
414
+ );
411
415
  }
412
416
 
413
417
  public relatedContextsAsync(
@@ -417,7 +421,7 @@ export class ContextProvider implements IContextProvider {
417
421
  }
418
422
 
419
423
  public clearCurrentContext() {
420
- this.#contextClient.setCurrentContext(undefined);
424
+ this.setCurrentContextAsync(null);
421
425
  }
422
426
 
423
427
  dispose() {
@@ -434,7 +438,7 @@ declare module '@equinor/fusion-framework-module-event' {
434
438
  onCurrentContextChange: FrameworkEvent<
435
439
  FrameworkEventInit<
436
440
  {
437
- context: ContextItem | undefined;
441
+ context: ContextItem | null;
438
442
  },
439
443
  IContextProvider
440
444
  >
@@ -443,8 +447,8 @@ declare module '@equinor/fusion-framework-module-event' {
443
447
  onCurrentContextChanged: FrameworkEvent<
444
448
  FrameworkEventInit<
445
449
  {
446
- next: ContextItem | undefined;
447
- previous: ContextItem | undefined;
450
+ next: ContextItem | null;
451
+ previous?: ContextItem | null;
448
452
  },
449
453
  IContextProvider
450
454
  >
@@ -452,7 +456,7 @@ declare module '@equinor/fusion-framework-module-event' {
452
456
  onParentContextChanged: FrameworkEvent<
453
457
  FrameworkEventInit<
454
458
  {
455
- context: ContextItem | undefined;
459
+ context: ContextItem | null;
456
460
  },
457
461
  IContextProvider
458
462
  >
@@ -1,4 +1,4 @@
1
- import { Observable, BehaviorSubject, EMPTY } from 'rxjs';
1
+ import { Observable, BehaviorSubject, EMPTY, lastValueFrom, firstValueFrom } from 'rxjs';
2
2
  import { catchError, map } from 'rxjs/operators';
3
3
 
4
4
  import equal from 'fast-deep-equal';
@@ -9,16 +9,16 @@ import { ContextItem } from '../types';
9
9
 
10
10
  export type GetContextParameters = { id: string };
11
11
 
12
- export class ContextClient extends Observable<ContextItem> {
12
+ export class ContextClient extends Observable<ContextItem | null> {
13
13
  #client: Query<ContextItem, { id: string }>;
14
14
  /** might change to reactive state, for comparing state with reducer */
15
- #currentContext$: BehaviorSubject<ContextItem | undefined>;
15
+ #currentContext$: BehaviorSubject<ContextItem | null | undefined>;
16
16
 
17
- get currentContext(): ContextItem | undefined {
17
+ get currentContext(): ContextItem | null | undefined {
18
18
  return this.#currentContext$.value;
19
19
  }
20
20
 
21
- get currentContext$(): Observable<ContextItem | undefined> {
21
+ get currentContext$(): Observable<ContextItem | null | undefined> {
22
22
  return this.#currentContext$.asObservable();
23
23
  }
24
24
 
@@ -29,13 +29,14 @@ export class ContextClient extends Observable<ContextItem> {
29
29
  constructor(options: QueryCtorOptions<ContextItem, GetContextParameters>) {
30
30
  super((observer) => this.#currentContext$.subscribe(observer));
31
31
  this.#client = new Query(options);
32
- this.#currentContext$ = new BehaviorSubject<ContextItem | undefined>(undefined);
32
+ this.#currentContext$ = new BehaviorSubject<ContextItem | null | undefined>(undefined);
33
33
  }
34
34
 
35
- public setCurrentContext(idOrItem?: string | ContextItem) {
35
+ public setCurrentContext(idOrItem?: string | ContextItem | null) {
36
36
  if (typeof idOrItem === 'string') {
37
37
  // TODO - compare context
38
38
  this.resolveContext(idOrItem)
39
+ // TODO should this catch error?
39
40
  .pipe(catchError(() => EMPTY))
40
41
  .subscribe((value) => this.setCurrentContext(value));
41
42
  /** only add context if not match */
@@ -45,7 +46,21 @@ export class ContextClient extends Observable<ContextItem> {
45
46
  }
46
47
 
47
48
  public resolveContext(id: string): Observable<ContextItem> {
48
- return this.#client.query({ id }).pipe(map((x) => x.value));
49
+ return this.#client.query({ id }).pipe(
50
+ map((x) => x.value),
51
+ // unwrap error
52
+ catchError((err) => {
53
+ if (err.cause) {
54
+ throw err.cause;
55
+ }
56
+ throw err;
57
+ })
58
+ );
59
+ }
60
+
61
+ public resolveContextAsync(id: string, opt?: { awaitResolve: boolean }): Promise<ContextItem> {
62
+ const fn = opt?.awaitResolve ? lastValueFrom : firstValueFrom;
63
+ return fn(this.resolveContext(id));
49
64
  }
50
65
 
51
66
  public dispose(): void {
@@ -1,5 +1,13 @@
1
- import { ModuleInitializerArgs, ModulesInstanceType } from '@equinor/fusion-framework-module';
1
+ import { ObservableInput } from 'rxjs';
2
+
3
+ import {
4
+ AnyModuleInstance,
5
+ ModuleInitializerArgs,
6
+ ModuleInstance,
7
+ ModulesInstanceType,
8
+ } from '@equinor/fusion-framework-module';
2
9
  import { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
10
+ import { NavigationModule } from '@equinor/fusion-framework-module-navigation';
3
11
  import { getContextSelector, queryContextSelector, relatedContextSelector } from './selectors';
4
12
  import { QueryCtorOptions } from '@equinor/fusion-query';
5
13
  import {
@@ -10,7 +18,8 @@ import {
10
18
  } from './types';
11
19
  import { GetContextParameters } from './client/ContextClient';
12
20
  import { ContextConfigBuilder, ContextConfigBuilderCallback } from './ContextConfigBuilder';
13
- import { IContextProvider } from 'ContextProvider';
21
+ import { type IContextProvider } from './ContextProvider';
22
+ import resolveInitialContext from './utils/resolve-initial-context';
14
23
 
15
24
  export interface ContextModuleConfig {
16
25
  client: {
@@ -41,6 +50,14 @@ export interface ContextModuleConfig {
41
50
  this: IContextProvider,
42
51
  item: ContextItem | null
43
52
  ) => ReturnType<IContextProvider['validateContext']>;
53
+
54
+ resolveInitialContext?: (
55
+ args: {
56
+ ref?: AnyModuleInstance | any;
57
+ modules: ModuleInstance;
58
+ }
59
+ // ...args: Parameters<ContextModule['postInitialize']>
60
+ ) => ObservableInput<ContextItem | void>;
44
61
  }
45
62
 
46
63
  export interface IContextModuleConfigurator {
@@ -72,7 +89,7 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
72
89
  }
73
90
 
74
91
  public async createConfig(
75
- init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>
92
+ init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>
76
93
  ): Promise<ContextModuleConfig> {
77
94
  const config = await this.#configBuilders.reduce(async (cur, cb) => {
78
95
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -81,6 +98,8 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
81
98
  return Object.assign(cur, builder.config);
82
99
  }, Promise.resolve({} as Partial<ContextModuleConfig>));
83
100
 
101
+ config.resolveInitialContext ??= resolveInitialContext();
102
+
84
103
  // TODO - make less lazy
85
104
  config.client ??= await (async (): Promise<ContextModuleConfig['client']> => {
86
105
  const apiProvider = await this._getServiceProvider(init);
package/src/index.ts CHANGED
@@ -13,6 +13,6 @@ export {
13
13
  moduleKey as contextModuleKey,
14
14
  } from './module';
15
15
 
16
- export { enableContext } from './enable-context';
16
+ export { enableContext } from './utils/enable-context';
17
17
 
18
18
  export * from './types';