@equinor/fusion-framework-module-context 7.0.4-next.0 → 8.0.1

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 (79) hide show
  1. package/CHANGELOG.md +36 -15
  2. package/README.md +168 -16
  3. package/dist/esm/ContextConfigBuilder.js +24 -6
  4. package/dist/esm/ContextConfigBuilder.js.map +1 -1
  5. package/dist/esm/ContextProvider.js +71 -15
  6. package/dist/esm/ContextProvider.js.map +1 -1
  7. package/dist/esm/client/ContextClient.js +13 -3
  8. package/dist/esm/client/ContextClient.js.map +1 -1
  9. package/dist/esm/configurator.js +40 -5
  10. package/dist/esm/configurator.js.map +1 -1
  11. package/dist/esm/{errors.js → errors/FusionContextSearchError.js} +3 -1
  12. package/dist/esm/errors/FusionContextSearchError.js.map +1 -0
  13. package/dist/esm/errors/index.js +2 -0
  14. package/dist/esm/errors/index.js.map +1 -0
  15. package/dist/esm/get-context-selector.js +14 -0
  16. package/dist/esm/get-context-selector.js.map +1 -0
  17. package/dist/esm/index.js +12 -0
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/module.js +6 -0
  20. package/dist/esm/module.js.map +1 -1
  21. package/dist/esm/parse-context-item.js +31 -0
  22. package/dist/esm/parse-context-item.js.map +1 -0
  23. package/dist/esm/query-context-selector.js +12 -0
  24. package/dist/esm/query-context-selector.js.map +1 -0
  25. package/dist/esm/related-context-selector.js +12 -0
  26. package/dist/esm/related-context-selector.js.map +1 -0
  27. package/dist/esm/utils/extract-context-id-from-path.js +25 -0
  28. package/dist/esm/utils/extract-context-id-from-path.js.map +1 -0
  29. package/dist/esm/utils/index.js +10 -0
  30. package/dist/esm/utils/index.js.map +1 -1
  31. package/dist/esm/utils/resolve-context-from-path.js +11 -22
  32. package/dist/esm/utils/resolve-context-from-path.js.map +1 -1
  33. package/dist/esm/utils/resolve-initial-context.js +2 -0
  34. package/dist/esm/utils/resolve-initial-context.js.map +1 -1
  35. package/dist/esm/version.js +1 -1
  36. package/dist/esm/version.js.map +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/dist/types/ContextConfigBuilder.d.ts +31 -1
  39. package/dist/types/ContextProvider.d.ts +74 -3
  40. package/dist/types/client/ContextClient.d.ts +8 -1
  41. package/dist/types/configurator.d.ts +134 -4
  42. package/dist/types/{errors.d.ts → errors/FusionContextSearchError.d.ts} +2 -0
  43. package/dist/types/errors/index.d.ts +1 -0
  44. package/dist/types/get-context-selector.d.ts +9 -0
  45. package/dist/types/index.d.ts +12 -0
  46. package/dist/types/module.d.ts +11 -0
  47. package/dist/types/parse-context-item.d.ts +8 -0
  48. package/dist/types/query-context-selector.d.ts +7 -0
  49. package/dist/types/related-context-selector.d.ts +7 -0
  50. package/dist/types/types.d.ts +23 -1
  51. package/dist/types/utils/extract-context-id-from-path.d.ts +16 -0
  52. package/dist/types/utils/index.d.ts +10 -0
  53. package/dist/types/utils/resolve-context-from-path.d.ts +1 -16
  54. package/dist/types/utils/resolve-initial-context.d.ts +1 -1
  55. package/dist/types/version.d.ts +1 -1
  56. package/package.json +12 -12
  57. package/src/ContextConfigBuilder.ts +47 -7
  58. package/src/ContextProvider.ts +115 -28
  59. package/src/client/ContextClient.ts +13 -3
  60. package/src/configurator.ts +152 -10
  61. package/src/{errors.ts → errors/FusionContextSearchError.ts} +2 -0
  62. package/src/errors/index.ts +1 -0
  63. package/src/get-context-selector.ts +18 -0
  64. package/src/index.ts +13 -0
  65. package/src/module.ts +11 -0
  66. package/src/parse-context-item.ts +39 -0
  67. package/src/query-context-selector.ts +15 -0
  68. package/src/related-context-selector.ts +15 -0
  69. package/src/types.ts +23 -1
  70. package/src/utils/extract-context-id-from-path.ts +30 -0
  71. package/src/utils/index.ts +10 -0
  72. package/src/utils/resolve-context-from-path.ts +13 -28
  73. package/src/utils/resolve-initial-context.ts +2 -0
  74. package/src/version.ts +1 -1
  75. package/dist/esm/errors.js.map +0 -1
  76. package/dist/esm/selectors.js +0 -58
  77. package/dist/esm/selectors.js.map +0 -1
  78. package/dist/types/selectors.d.ts +0 -19
  79. package/src/selectors.ts +0 -70
@@ -8,7 +8,11 @@ import type {
8
8
  } from '@equinor/fusion-framework-module';
9
9
  import type { ServicesModule, IApiProvider } from '@equinor/fusion-framework-module-services';
10
10
  import type { NavigationModule } from '@equinor/fusion-framework-module-navigation';
11
- import { getContextSelector, queryContextSelector, relatedContextSelector } from './selectors';
11
+ import {
12
+ getContextSelector,
13
+ queryContextSelector,
14
+ relatedContextSelector,
15
+ } from './get-context-selector';
12
16
  import type { QueryCtorOptions } from '@equinor/fusion-query';
13
17
  import type {
14
18
  ContextFilterFn,
@@ -21,87 +25,225 @@ import { ContextConfigBuilder, type ContextConfigBuilderCallback } from './Conte
21
25
  import type { IContextProvider } from './ContextProvider';
22
26
  import resolveInitialContext from './utils/resolve-initial-context';
23
27
 
28
+ /**
29
+ * Resolved configuration for the context module.
30
+ *
31
+ * Holds query clients, type filters, parent-connection settings, and
32
+ * optional callbacks for validation, resolution, and path integration.
33
+ * Produced by {@link ContextModuleConfigurator.createConfig} after all
34
+ * {@link ContextConfigBuilder} callbacks have run.
35
+ *
36
+ * @see ContextConfigBuilder — fluent API for populating this config.
37
+ * @see ContextProvider — runtime consumer of this config.
38
+ */
24
39
  export interface ContextModuleConfig {
40
+ /**
41
+ * Query client options used to fetch, search, and resolve related context items.
42
+ *
43
+ * - `get` — retrieves a single context item by ID.
44
+ * - `query` — searches context items by text and optional type filter.
45
+ * - `related` — fetches context items related to a given item (used during resolution).
46
+ */
25
47
  client: {
26
48
  get: QueryCtorOptions<ContextItem, GetContextParameters>;
27
49
  query: QueryCtorOptions<ContextItem[], QueryContextParameters>;
28
50
  related?: QueryCtorOptions<ContextItem[], RelatedContextParameters>;
29
51
  };
52
+
53
+ /**
54
+ * Allowed context type IDs (e.g. `['ProjectMaster', 'Facility']`).
55
+ *
56
+ * When set, {@link ContextProvider.validateContext} only accepts items
57
+ * whose `type.id` matches one of these values (case-insensitive).
58
+ */
30
59
  contextType?: string[];
60
+
61
+ /**
62
+ * Optional post-query filter applied to the result set returned by
63
+ * {@link ContextProvider.queryContext}.
64
+ */
31
65
  contextFilter?: ContextFilterFn;
32
66
 
33
67
  /**
34
- * connect context module to paren context module.
68
+ * Whether to connect the context module to a parent context module.
69
+ *
70
+ * When `true` (the default), the provider subscribes to the parent's
71
+ * `currentContext$` and mirrors changes into its own state.
35
72
  *
36
- * _default: `true`_
73
+ * @defaultValue `true`
37
74
  */
38
75
  connectParentContext?: boolean;
39
76
 
40
- /** set initial context from parent, will await resolve */
77
+ /**
78
+ * When `true`, skips resolving an initial context from the path or parent
79
+ * during module post-initialization.
80
+ */
41
81
  skipInitialContext?: boolean;
42
82
 
83
+ /**
84
+ * Extracts a context ID from a URL path segment.
85
+ *
86
+ * Used during initial context resolution and deep-link support.
87
+ * If not provided, the default GUID-based extractor is used.
88
+ *
89
+ * @param path - The URL path to inspect.
90
+ * @returns The extracted context ID, or `undefined` if none is found.
91
+ */
43
92
  extractContextIdFromPath?: (path: string) => string | undefined;
93
+
94
+ /**
95
+ * Generates a URL path that embeds the given context item's ID.
96
+ *
97
+ * Used by navigation integrations to update the browser URL when
98
+ * the context changes.
99
+ *
100
+ * @param context - The active context item.
101
+ * @param path - The current URL path.
102
+ * @returns The updated path, or `undefined` to leave it unchanged.
103
+ */
44
104
  generatePathFromContext?: (context: ContextItem, path: string) => string | undefined;
45
105
 
46
106
  /**
47
- * Method for generating context query parameters.
107
+ * Transforms a user search string and the configured context type into
108
+ * the query parameters sent to the context API.
109
+ *
110
+ * Override this to customise how free-text searches are mapped to the
111
+ * backend query contract.
48
112
  */
49
113
  contextParameterFn?: (args: {
50
114
  search: string;
51
115
  type: ContextModuleConfig['contextType'];
52
116
  }) => string | QueryContextParameters;
53
117
 
118
+ /**
119
+ * Custom context resolution strategy.
120
+ *
121
+ * Called with `this` bound to the {@link IContextProvider} when a context
122
+ * item fails validation and the caller requests resolution.
123
+ *
124
+ * @param item - The context item to resolve, or `null`.
125
+ * @returns An observable emitting the resolved context item.
126
+ */
54
127
  resolveContext?: (
55
128
  this: IContextProvider,
56
129
  item: ContextItem | null,
57
130
  ) => ReturnType<IContextProvider['resolveContext']>;
58
131
 
132
+ /**
133
+ * Custom context validation strategy.
134
+ *
135
+ * Called with `this` bound to the {@link IContextProvider} to decide
136
+ * whether a candidate context item is acceptable.
137
+ *
138
+ * @param item - The context item to validate, or `null`.
139
+ * @returns `true` if the item is valid.
140
+ */
59
141
  validateContext?: (
60
142
  this: IContextProvider,
61
143
  item: ContextItem | null,
62
144
  ) => ReturnType<IContextProvider['validateContext']>;
63
145
 
146
+ /**
147
+ * Resolves the initial context during module post-initialization.
148
+ *
149
+ * The default implementation tries to extract a context ID from the
150
+ * current navigation path, falling back to the parent provider's context.
151
+ *
152
+ * @param args - Module reference and instance map.
153
+ * @returns An observable input emitting the initial context item, or void.
154
+ */
64
155
  resolveInitialContext?: (args: {
65
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ // biome-ignore lint/suspicious/noExplicitAny: `AnyModuleInstance | any` intentionally widens to accept any module instance shape for `ref`
66
157
  ref?: AnyModuleInstance | any;
67
158
  modules: ModuleInstance;
159
+ // biome-ignore lint/suspicious/noConfusingVoidType: `void` here relies on TypeScript's special-cased "void-returning callback accepts any return value" behavior — `undefined` would break assignability of resolver functions that only conditionally emit a `ContextItem`
68
160
  }) => ObservableInput<ContextItem | void>;
69
161
  }
70
162
 
163
+ /**
164
+ * Public configurator contract for the context module.
165
+ *
166
+ * Consumers call {@link addConfigBuilder} to register one or more
167
+ * {@link ContextConfigBuilderCallback} functions that will run during
168
+ * module initialization to populate the {@link ContextModuleConfig}.
169
+ */
71
170
  export interface IContextModuleConfigurator {
171
+ /**
172
+ * Registers a configuration callback that receives a {@link ContextConfigBuilder}.
173
+ *
174
+ * Multiple builders can be added; they execute sequentially and merge
175
+ * their results into a single {@link ContextModuleConfig}.
176
+ *
177
+ * @param init - Builder callback invoked during module initialization.
178
+ */
72
179
  addConfigBuilder: (init: ContextConfigBuilderCallback) => void;
73
180
  }
74
181
 
182
+ /**
183
+ * Default implementation of {@link IContextModuleConfigurator}.
184
+ *
185
+ * Collects {@link ContextConfigBuilderCallback} registrations and, when
186
+ * {@link createConfig} is called, runs them in order against a
187
+ * {@link ContextConfigBuilder} to produce the final {@link ContextModuleConfig}.
188
+ *
189
+ * If no custom client is configured, the configurator falls back to
190
+ * creating one from the {@link ServicesModule} API provider.
191
+ */
75
192
  export class ContextModuleConfigurator implements IContextModuleConfigurator {
193
+ /** Default cache TTL (in ms) for context query results. */
76
194
  defaultExpireTime = 1 * 60 * 1000;
77
195
 
78
196
  #configBuilders: Array<ContextConfigBuilderCallback> = [];
79
197
 
198
+ /** @inheritdoc */
80
199
  addConfigBuilder(init: ContextConfigBuilderCallback): void {
81
200
  this.#configBuilders.push(init);
82
201
  }
83
202
 
203
+ /**
204
+ * Resolves the services API provider, preferring the local module
205
+ * instance and falling back to the parent module.
206
+ *
207
+ * @param init - Module initializer arguments.
208
+ * @returns The resolved API provider.
209
+ * @throws Error if no services module is available.
210
+ */
84
211
  protected async _getServiceProvider(
85
212
  init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule]>,
86
213
  ): Promise<IApiProvider> {
214
+ // prefer the local services module instance if available
87
215
  if (init.hasModule('services')) {
88
216
  return init.requireInstance('services');
89
217
  }
90
218
  const parentServiceModule = (init.ref as ModulesInstanceType<[ServicesModule]>)?.services;
219
+ // fall back to the parent module's services instance
91
220
  if (!parentServiceModule) {
92
221
  throw Error('no service services provider configures [ServicesModule]');
93
222
  }
94
223
  return parentServiceModule;
95
224
  }
96
225
 
226
+ /**
227
+ * Runs all registered config builders and produces the final
228
+ * {@link ContextModuleConfig}.
229
+ *
230
+ * If no `resolveInitialContext` was set, the default path + parent
231
+ * resolver is used. If no `client` was set, one is created from the
232
+ * {@link ServicesModule} API provider.
233
+ *
234
+ * @param init - Module initializer arguments including dependency instances.
235
+ * @returns The fully resolved context module configuration.
236
+ */
97
237
  public async createConfig(
98
238
  init: ModuleInitializerArgs<IContextModuleConfigurator, [ServicesModule, NavigationModule]>,
99
239
  ): Promise<ContextModuleConfig> {
240
+ // run each registered config builder in sequence, merging results into the accumulated config
100
241
  const config = await this.#configBuilders.reduce(
101
242
  async (cur, cb) => {
102
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
243
+ // biome-ignore lint/suspicious/noExplicitAny: builder generics are erased here — `unknown` breaks assignability of the concrete `ContextConfigBuilder` instance passed to consumer callbacks
103
244
  const builder = new ContextConfigBuilder<any, any>(init, await cur);
104
245
  await Promise.resolve(cb(builder));
246
+ // Merge this builder's config into the accumulator for the next iteration.
105
247
  return Object.assign(cur, builder.config);
106
248
  },
107
249
  Promise.resolve({} as Partial<ContextModuleConfig>),
@@ -114,7 +256,7 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
114
256
  },
115
257
  });
116
258
 
117
- // TODO - make less lazy
259
+ // TODO(#5119) - make less lazy
118
260
  config.client ??= await (async (): Promise<ContextModuleConfig['client']> => {
119
261
  const apiProvider = await this._getServiceProvider(init);
120
262
  const contextClient = await apiProvider.createContextClient('json$');
@@ -130,7 +272,7 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
130
272
  client: {
131
273
  fn: (query) => contextClient.query('v1', { query }, { selector: queryContextSelector }),
132
274
  },
133
- // TODO - might cast to checksum
275
+ // TODO(#5118) - might cast to checksum
134
276
  key: (args) => JSON.stringify(args),
135
277
  expire: this.defaultExpireTime,
136
278
  },
@@ -144,7 +286,7 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
144
286
  );
145
287
  },
146
288
  },
147
- // TODO - might cast to checksum
289
+ // TODO(#5118) - might cast to checksum
148
290
  key: (args) => JSON.stringify(args),
149
291
  expire: this.defaultExpireTime,
150
292
  },
@@ -17,6 +17,7 @@ export class FusionContextSearchError extends Error {
17
17
 
18
18
  /**
19
19
  * The title of the error.
20
+ * @returns The error title.
20
21
  */
21
22
  get title(): string {
22
23
  return this.#details.title;
@@ -24,6 +25,7 @@ export class FusionContextSearchError extends Error {
24
25
 
25
26
  /**
26
27
  * The description of the error, if available.
28
+ * @returns The error description, or `undefined` if none was provided.
27
29
  */
28
30
  get description(): string | undefined {
29
31
  return this.#details.description;
@@ -0,0 +1 @@
1
+ export * from './FusionContextSearchError';
@@ -0,0 +1,18 @@
1
+ import type { GetContextResponse } from '@equinor/fusion-framework-module-services/context/get';
2
+
3
+ import { parseContextItem } from './parse-context-item';
4
+ import type { ContextItem } from './types';
5
+
6
+ /**
7
+ * Parse the response from the GetContext API into a context item.
8
+ * @param response The response object containing the context item.
9
+ * @returns A promise that resolves to the context item.
10
+ */
11
+ export const getContextSelector = async (response: Response): Promise<ContextItem> => {
12
+ const result = (await response.json()) as GetContextResponse<'v1'>;
13
+ return parseContextItem(result);
14
+ };
15
+
16
+ // Deliberately re-exported from here to preserve the package's existing selectors entry point
17
+ export { queryContextSelector } from './query-context-selector';
18
+ export { relatedContextSelector } from './related-context-selector';
package/src/index.ts CHANGED
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Context module for the Fusion Framework.
3
+ *
4
+ * Provides context management for Fusion-based applications and portals,
5
+ * including setting, querying, validating, and resolving context items.
6
+ *
7
+ * Use {@link enableContext} to register the module in a configurator,
8
+ * then access the {@link IContextProvider} from the module instance
9
+ * to interact with context state.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+
1
14
  export {
2
15
  ContextModuleConfigurator,
3
16
  IContextModuleConfigurator,
package/src/module.ts CHANGED
@@ -10,8 +10,19 @@ import { type IContextModuleConfigurator, ContextModuleConfigurator } from './co
10
10
  import { type IContextProvider, ContextProvider } from './ContextProvider';
11
11
  import type { ContextItem } from './types';
12
12
 
13
+ /**
14
+ * Literal type identifying the context module within the Fusion Framework module system.
15
+ *
16
+ * Used as the key when registering or looking up the module in a `Modules` map.
17
+ */
13
18
  export type ContextModuleKey = 'context';
14
19
 
20
+ /**
21
+ * Module registration key for the context module.
22
+ *
23
+ * Pass this value—or reference it as `contextModuleKey`—when you need to
24
+ * identify the context module by name at runtime (e.g., `hasModule(contextModuleKey)`).
25
+ */
15
26
  export const moduleKey: ContextModuleKey = 'context';
16
27
 
17
28
  /**
@@ -0,0 +1,39 @@
1
+ import type {
2
+ ApiVersion,
3
+ ApiContextEntity,
4
+ } from '@equinor/fusion-framework-module-services/context';
5
+ import type { GetContextResponse } from '@equinor/fusion-framework-module-services/context/get';
6
+
7
+ import type { ContextItem, ContextItemType } from './types';
8
+
9
+ /**
10
+ * Parses the context type from the response of the GetContext API.
11
+ *
12
+ * @param type The type property from the GetContext response.
13
+ * @returns The parsed context item type.
14
+ */
15
+ const parseContextType = (type: GetContextResponse<'v1'>['type']): ContextItemType => ({
16
+ id: type.id,
17
+ isChildType: type.isChildType,
18
+ parentTypeIds: type.parentTypeIds ?? [],
19
+ });
20
+
21
+ /**
22
+ * Parses an ApiContextEntity object into a ContextItem object.
23
+ * @param item The ApiContextEntity object to parse.
24
+ * @returns The parsed ContextItem object.
25
+ */
26
+ export const parseContextItem = (item: ApiContextEntity<ApiVersion.v1>): ContextItem => {
27
+ return {
28
+ id: item.id,
29
+ externalId: item.externalId ?? undefined,
30
+ isActive: item.isActive,
31
+ isDeleted: item.isDeleted,
32
+ created: new Date(item.created),
33
+ source: item.source ?? undefined,
34
+ title: item.title ?? undefined,
35
+ type: parseContextType(item.type),
36
+ // TODO(#5115): parse and map the raw `value` payload into a typed context item value
37
+ value: item.value ?? {},
38
+ };
39
+ };
@@ -0,0 +1,15 @@
1
+ import type { QueryContextResponse } from '@equinor/fusion-framework-module-services/context/query';
2
+
3
+ import { parseContextItem } from './parse-context-item';
4
+ import type { ContextItem } from './types';
5
+
6
+ /**
7
+ * Parse the response from the QueryContext API into an array of context items.
8
+ * @param response The response object.
9
+ * @returns A promise that resolves to an array of context items.
10
+ */
11
+ export const queryContextSelector = async (response: Response): Promise<ContextItem[]> => {
12
+ const result = (await response.json()) as QueryContextResponse<'v1'>;
13
+ // parse each raw API entry into a ContextItem
14
+ return result.map(parseContextItem);
15
+ };
@@ -0,0 +1,15 @@
1
+ import type { RelatedContextResponse } from '@equinor/fusion-framework-module-services/context/related';
2
+
3
+ import { parseContextItem } from './parse-context-item';
4
+ import type { ContextItem } from './types';
5
+
6
+ /**
7
+ * Parse the response from the RelatedContext API into an array of context items.
8
+ * @param response The response object containing the related context items.
9
+ * @returns A promise that resolves to an array of ContextItem objects.
10
+ */
11
+ export const relatedContextSelector = async (response: Response): Promise<ContextItem[]> => {
12
+ const result = (await response.json()) as RelatedContextResponse<'v1'>;
13
+ // parse each raw API entry into a ContextItem
14
+ return result.map(parseContextItem);
15
+ };
package/src/types.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * @property graphic - Optional graphical representation, either as a string or an object containing type and content.
17
17
  * @property meta - Optional meta information, either as a string or an object containing type and content.
18
18
  *
19
- * @todo - convert to Zod schema for validation and type safety.
19
+ * @todo(#5122) - convert to Zod schema for validation and type safety.
20
20
  */
21
21
  export type ContextItem<TType extends Record<string, unknown> = Record<string, unknown>> = {
22
22
  id: string;
@@ -44,12 +44,34 @@ export type ContextItem<TType extends Record<string, unknown> = Record<string, u
44
44
  };
45
45
  };
46
46
 
47
+ /**
48
+ * Describes the type classification of a {@link ContextItem}.
49
+ *
50
+ * Every context item carries a `type` that identifies what kind of
51
+ * entity it represents (e.g. `ProjectMaster`, `Facility`, `Contract`).
52
+ * The optional hierarchy fields indicate parent–child relationships
53
+ * between context types.
54
+ */
47
55
  export interface ContextItemType {
56
+ /** Unique identifier for the context type (e.g. `'ProjectMaster'`). */
48
57
  id: string;
58
+ /** Whether this type is a child of another context type. */
49
59
  isChildType?: boolean;
60
+ /** IDs of parent context types, when `isChildType` is `true`. */
50
61
  parentTypeIds?: string[];
51
62
  }
52
63
 
64
+ /**
65
+ * Parameters for querying context items from the context API.
66
+ *
67
+ * Used by {@link ContextProvider.queryContext} and the underlying
68
+ * query client to search and filter context results.
69
+ *
70
+ * @property search - Free-text search term.
71
+ * @property filter - Optional structured filters.
72
+ * @property filter.type - Restrict results to specific context type IDs.
73
+ * @property filter.externalId - Filter by an external system identifier.
74
+ */
53
75
  export type QueryContextParameters = {
54
76
  search?: string;
55
77
  filter?: {
@@ -0,0 +1,30 @@
1
+ // GUID pattern
2
+ const matchGUID =
3
+ /^(?:(?:[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})$/;
4
+
5
+ /**
6
+ * Method will try to extract a context id from a path.
7
+ * The default matcher is a GUID pattern.
8
+ * Will iterate over the path and return the first match.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const path = '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?dsadasdas#example';
13
+ * const contextId = extractContextIdFromPath(path); // '7fd97952-7fe6-409b-a6dc-292dbf0e50d7'
14
+ * ```
15
+ *
16
+ * @param path string - the path to extract the context id from
17
+ * @param matcher RegExp - the pattern to match against
18
+ * @returns string | undefined - the context id or undefined
19
+ */
20
+ export const extractContextIdFromPath = (
21
+ path: string,
22
+ matcher: RegExp = matchGUID,
23
+ ): string | undefined =>
24
+ path
25
+ // remove leading slashes
26
+ .replace(/^\/+/, '')
27
+ // split path by slashes
28
+ .split('/')
29
+ // find the first path fragment that matches the matcher
30
+ .find((x) => x.match(matcher));
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Utility functions for context module configuration and initialization.
3
+ *
4
+ * - {@link enableContext} — register the context module on a configurator.
5
+ * - {@link resolveInitialContext} — default initial-context resolver (path → parent fallback).
6
+ * - {@link extractContextIdFromPath} — extract a GUID context ID from a URL path.
7
+ * - {@link resolveContextFromPath} — resolve a context item from a URL path.
8
+ *
9
+ * @packageDocumentation
10
+ */
1
11
  export { enableContext } from './enable-context';
2
12
  export { resolveInitialContext } from './resolve-initial-context';
3
13
  export { extractContextIdFromPath, resolveContextFromPath } from './resolve-context-from-path';
@@ -4,6 +4,9 @@ import type { ModuleType } from '@equinor/fusion-framework-module';
4
4
 
5
5
  import type { ContextModule } from '../module';
6
6
  import type { ContextItem } from '../types';
7
+ import { extractContextIdFromPath } from './extract-context-id-from-path';
8
+
9
+ export { extractContextIdFromPath } from './extract-context-id-from-path';
7
10
 
8
11
  /**
9
12
  * Arguments for resolving a context from a path.
@@ -23,37 +26,10 @@ export type ContextPathResolveArgs = {
23
26
  validate?: (contextId: string) => boolean;
24
27
  };
25
28
 
26
- // GUID pattern
29
+ // GUID pattern, used as the default context id validator
27
30
  const matchGUID =
28
31
  /^(?:(?:[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})$/;
29
32
 
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));
56
-
57
33
  const validateContextId = (contextId: string): boolean => !!contextId.match(matchGUID);
58
34
 
59
35
  /**
@@ -104,6 +80,13 @@ export function resolveContextFromPath(
104
80
  args?: ContextPathResolveArgs,
105
81
  ): (path: string) => Observable<ContextItem>;
106
82
 
83
+ /**
84
+ * Resolves a context item from a path, using the provided extract/validate callbacks or the defaults.
85
+ * @param context The context module.
86
+ * @param args The arguments for resolving the path.
87
+ * @returns A function that takes a path and returns an Observable of the resolved context item.
88
+ * @throws Error if the extracted context id fails validation.
89
+ */
107
90
  export function resolveContextFromPath(
108
91
  context: ModuleType<ContextModule>,
109
92
  args?: ContextPathResolveArgs,
@@ -111,9 +94,11 @@ export function resolveContextFromPath(
111
94
  return (path: string) => {
112
95
  const { extract = extractContextIdFromPath, validate = validateContextId } = args ?? {};
113
96
  const contextId = extract(path);
97
+ // no context id found in the path, nothing to resolve
114
98
  if (!contextId) {
115
99
  return EMPTY;
116
100
  }
101
+ // only resolve the context if the extracted id passes validation
117
102
  if (validate(contextId)) {
118
103
  return context.contextClient.resolveContext(contextId);
119
104
  }
@@ -30,6 +30,8 @@ export const resolveContextFromParent: ContextModuleConfig['resolveInitialContex
30
30
  * @param options - Optional configuration for resolving the context path.
31
31
  * @returns A function that accepts the module's reference and modules, and returns an Observable of the resolved initial context.
32
32
  */
33
+ // Deliberately co-located with resolveContextFromParent, which it composes with
34
+ // fusion-lint-disable-next-line single-export-per-file
33
35
  export const resolveInitialContext =
34
36
  (options?: {
35
37
  path?: ContextPathResolveArgs;
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '7.0.4-next.0';
2
+ export const version = '8.0.1';
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC;IAET;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,YACE,OASC,EACD,OAAsB;QAEtB,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF"}
@@ -1,58 +0,0 @@
1
- /**
2
- * Parses the context type from the response of the GetContext API.
3
- *
4
- * @param type The type property from the GetContext response.
5
- * @returns The parsed context item type.
6
- */
7
- const parseContextType = (type) => ({
8
- id: type.id,
9
- isChildType: type.isChildType,
10
- parentTypeIds: type.parentTypeIds ?? [],
11
- });
12
- /**
13
- * Parses an ApiContextEntity object into a ContextItem object.
14
- * @param item The ApiContextEntity object to parse.
15
- * @returns The parsed ContextItem object.
16
- */
17
- const parseContextItem = (item) => {
18
- return {
19
- id: item.id,
20
- externalId: item.externalId ?? undefined,
21
- isActive: item.isActive,
22
- isDeleted: item.isDeleted,
23
- created: new Date(item.created),
24
- source: item.source ?? undefined,
25
- title: item.title ?? undefined,
26
- type: parseContextType(item.type),
27
- // TODO
28
- value: item.value ?? {},
29
- };
30
- };
31
- /**
32
- * Parse the response from the GetContext API into a context item.
33
- * @param response The response object containing the context item.
34
- * @returns A promise that resolves to the context item.
35
- */
36
- export const getContextSelector = async (response) => {
37
- const result = (await response.json());
38
- return parseContextItem(result);
39
- };
40
- /**
41
- * Parse the response from the QueryContext API into an array of context items.
42
- * @param response The response object.
43
- * @returns A promise that resolves to an array of context items.
44
- */
45
- export const queryContextSelector = async (response) => {
46
- const result = (await response.json());
47
- return result.map(parseContextItem);
48
- };
49
- /**
50
- * Parse the response from the RelatedContext API into an array of context items.
51
- * @param response The response object containing the related context items.
52
- * @returns A promise that resolves to an array of ContextItem objects.
53
- */
54
- export const relatedContextSelector = async (response) => {
55
- const result = (await response.json());
56
- return result.map(parseContextItem);
57
- };
58
- //# sourceMappingURL=selectors.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../src/selectors.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,CAAC,IAAsC,EAAmB,EAAE,CAAC,CAAC;IACrF,EAAE,EAAE,IAAI,CAAC,EAAE;IACX,WAAW,EAAE,IAAI,CAAC,WAAW;IAC7B,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;CACxC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,IAAqC,EAAe,EAAE;IAC9E,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;QACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;QAChC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;QAC9B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QACjC,OAAO;QACP,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;KACxB,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,QAAkB,EAAwB,EAAE;IACnF,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA6B,CAAC;IACnE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EAAE,QAAkB,EAA0B,EAAE;IACvF,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA+B,CAAC;IACrE,OAAO,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EAAE,QAAkB,EAA0B,EAAE;IACzF,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAiC,CAAC;IACvE,OAAO,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC,CAAC"}