@equinor/fusion-framework-module-context
Advanced tools
Comparing version 5.0.7 to 5.0.8
# Change Log | ||
## 5.0.8 | ||
### Patch Changes | ||
- [#2333](https://github.com/equinor/fusion-framework/pull/2333) [`86d55b8`](https://github.com/equinor/fusion-framework/commit/86d55b8d27a572f3f62170b1e72aceda54f955e1) Thanks [@odinr](https://github.com/odinr)! - Updated `TypeScript` to 5.5.3 | ||
- [#2320](https://github.com/equinor/fusion-framework/pull/2320) [`1dd85f3`](https://github.com/equinor/fusion-framework/commit/1dd85f3a408a73df556d1812a5f280945cc100ee) Thanks [@odinr](https://github.com/odinr)! - Removed the `removeComments` option from the `tsconfig.base.json` file. | ||
Removing the `removeComments` option allows TypeScript to preserve comments in the compiled JavaScript output. This can be beneficial for several reasons: | ||
1. Improved debugging: Preserved comments can help developers understand the code better during debugging sessions. | ||
2. Documentation: JSDoc comments and other important code documentation will be retained in the compiled output. | ||
3. Source map accuracy: Keeping comments can lead to more accurate source maps, which is crucial for debugging and error tracking. | ||
No action is required from consumers of the library. This change affects the build process and doesn't introduce any breaking changes or new features. | ||
Before: | ||
```json | ||
{ | ||
"compilerOptions": { | ||
"module": "ES2022", | ||
"target": "ES6", | ||
"incremental": true, | ||
"removeComments": true, | ||
"preserveConstEnums": true, | ||
"sourceMap": true, | ||
"moduleResolution": "node" | ||
} | ||
} | ||
``` | ||
After: | ||
```json | ||
{ | ||
"compilerOptions": { | ||
"module": "ES2022", | ||
"target": "ES6", | ||
"incremental": true, | ||
"preserveConstEnums": true, | ||
"sourceMap": true, | ||
"moduleResolution": "node" | ||
} | ||
} | ||
``` | ||
This change ensures that comments are preserved in the compiled output, potentially improving the development and debugging experience for users of the Fusion Framework. | ||
- Updated dependencies [[`2f74edc`](https://github.com/equinor/fusion-framework/commit/2f74edcd4a3ea2b87d69f0fd63492145c3c01663), [`5e20ce1`](https://github.com/equinor/fusion-framework/commit/5e20ce17af709f0443b7110bfc952ff8d8d81dee), [`86d55b8`](https://github.com/equinor/fusion-framework/commit/86d55b8d27a572f3f62170b1e72aceda54f955e1), [`29ff796`](https://github.com/equinor/fusion-framework/commit/29ff796ebb3a643c604e4153b6798bde5992363c), [`1dd85f3`](https://github.com/equinor/fusion-framework/commit/1dd85f3a408a73df556d1812a5f280945cc100ee), [`5e20ce1`](https://github.com/equinor/fusion-framework/commit/5e20ce17af709f0443b7110bfc952ff8d8d81dee)]: | ||
- @equinor/fusion-framework-module@4.3.2 | ||
- @equinor/fusion-query@5.1.0 | ||
## 5.0.7 | ||
@@ -4,0 +57,0 @@ |
@@ -30,2 +30,3 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
_ContextClient_client.set(this, void 0); | ||
/** might change to reactive state, for comparing state with reducer */ | ||
_ContextClient_currentContext$.set(this, void 0); | ||
@@ -37,5 +38,8 @@ __classPrivateFieldSet(this, _ContextClient_client, new Query(options), "f"); | ||
if (typeof idOrItem === 'string') { | ||
// TODO - compare context | ||
this.resolveContext(idOrItem) | ||
// TODO should this catch error? | ||
.pipe(catchError(() => EMPTY)) | ||
.subscribe((value) => this.setCurrentContext(value)); | ||
/** only add context if not match */ | ||
} | ||
@@ -47,3 +51,5 @@ else if (!equal(idOrItem, __classPrivateFieldGet(this, _ContextClient_currentContext$, "f").value)) { | ||
resolveContext(id) { | ||
return __classPrivateFieldGet(this, _ContextClient_client, "f").query({ id }).pipe(map((x) => x.value), catchError((err) => { | ||
return __classPrivateFieldGet(this, _ContextClient_client, "f").query({ id }).pipe(map((x) => x.value), | ||
// unwrap error | ||
catchError((err) => { | ||
if (err.cause) { | ||
@@ -50,0 +56,0 @@ throw err.cause; |
@@ -46,2 +46,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
const config = yield __classPrivateFieldGet(this, _ContextModuleConfigurator_configBuilders, "f").reduce((cur, cb) => __awaiter(this, void 0, void 0, function* () { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const builder = new ContextConfigBuilder(init, yield cur); | ||
@@ -52,2 +53,3 @@ yield Promise.resolve(cb(builder)); | ||
(_a = config.resolveInitialContext) !== null && _a !== void 0 ? _a : (config.resolveInitialContext = resolveInitialContext()); | ||
// TODO - make less lazy | ||
(_b = config.client) !== null && _b !== void 0 ? _b : (config.client = yield (() => __awaiter(this, void 0, void 0, function* () { | ||
@@ -68,2 +70,3 @@ const apiProvider = yield this._getServiceProvider(init); | ||
}, | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
@@ -78,2 +81,3 @@ expire: this.defaultExpireTime, | ||
}, | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
@@ -80,0 +84,0 @@ expire: this.defaultExpireTime, |
@@ -13,2 +13,3 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
var _ContextConfigBuilder_init; | ||
// TODO - this should extend the BaseConfigBuilder | ||
export class ContextConfigBuilder { | ||
@@ -20,2 +21,3 @@ constructor(init, config = {}) { | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
requireInstance(module) { | ||
@@ -58,2 +60,3 @@ return __classPrivateFieldGet(this, _ContextConfigBuilder_init, "f").requireInstance(module); | ||
? { | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
@@ -68,5 +71,7 @@ client: { | ||
if (client.related) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
this.config.client.related = | ||
typeof client.related === 'function' | ||
? { | ||
// TODO - might cast to checksum | ||
key: (args) => JSON.stringify(args), | ||
@@ -73,0 +78,0 @@ client: { |
@@ -39,2 +39,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
} | ||
/** @deprecated do not use, will be removed */ | ||
set currentContext(context) { | ||
@@ -63,2 +64,3 @@ console.warn('@deprecated', 'ContextProvider.currentContext', 'use setCurrentContextById|setCurrentContext|clearCurrentContext'); | ||
__classPrivateFieldSet(this, _ContextProvider_event, event, "f"); | ||
// set the resolve and validate context functions | ||
config.resolveContext && (this.resolveContext = (_a = config.resolveContext) === null || _a === void 0 ? void 0 : _a.bind(this)); | ||
@@ -68,2 +70,3 @@ config.validateContext && (this.validateContext = (_b = config.validateContext) === null || _b === void 0 ? void 0 : _b.bind(this)); | ||
__classPrivateFieldSet(this, _ContextProvider_contextFilter, config.contextFilter, "f"); | ||
// create clients | ||
__classPrivateFieldSet(this, _ContextProvider_contextClient, new ContextClient(config.client.get), "f"); | ||
@@ -74,9 +77,17 @@ __classPrivateFieldSet(this, _ContextProvider_contextQuery, new Query(config.client.query), "f"); | ||
} | ||
__classPrivateFieldSet(this, _ContextProvider_contextParameterFn, (_c = config.contextParameterFn) !== null && _c !== void 0 ? _c : ((args) => ({ | ||
// set the context parameter function | ||
__classPrivateFieldSet(this, _ContextProvider_contextParameterFn, (_c = config.contextParameterFn) !== null && _c !== void 0 ? _c : | ||
// fallback to default | ||
((args) => ({ | ||
search: args.search, | ||
filter: { type: args.type }, | ||
})), "f"); | ||
// if event module is available, setup event listeners | ||
if (__classPrivateFieldGet(this, _ContextProvider_event, "f")) { | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(this.currentContext$ | ||
.pipe(pairwise()) | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add( | ||
// observe current context changes | ||
this.currentContext$ | ||
.pipe( | ||
// emit previous and next context | ||
pairwise()) | ||
.subscribe(([previous, next]) => { | ||
@@ -90,3 +101,6 @@ var _a; | ||
})); | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(__classPrivateFieldGet(this, _ContextProvider_event, "f").addEventListener('onCurrentContextChanged', (e) => { | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add( | ||
// observe current context changes from child modules | ||
__classPrivateFieldGet(this, _ContextProvider_event, "f").addEventListener('onCurrentContextChanged', (e) => { | ||
// prevent infinite loop, only set context if source is not this | ||
if (e.source !== this && e.detail.next !== undefined) { | ||
@@ -97,5 +111,9 @@ this.setCurrentContext(e.detail.next); | ||
} | ||
// wire up context queue | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(__classPrivateFieldGet(this, _ContextProvider_contextQueue, "f") | ||
.pipe(switchMap((next) => next), tap((x) => console.debug('ContextProvider::#contextQueue', x))) | ||
.pipe( | ||
// resolve context item from queue | ||
switchMap((next) => next), tap((x) => console.debug('ContextProvider::#contextQueue', x))) | ||
.subscribe((context) => { | ||
// set context from resolved context item from queue | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f").setCurrentContext(context !== null && context !== void 0 ? context : null); | ||
@@ -105,4 +123,8 @@ })); | ||
connectParentContext(provider, opt) { | ||
const parentContext$ = provider.currentContext$.pipe(filter((x) => x !== undefined), filter((next, index) => { | ||
const parentContext$ = provider.currentContext$.pipe( | ||
// do not set context if parent has not initialized | ||
filter((x) => x !== undefined), filter((next, index) => { | ||
var _a; | ||
// skip first item if opt.skipFirst is true | ||
// TODO: this is a bit hacky, should be handled in a better way | ||
if ((opt === null || opt === void 0 ? void 0 : opt.skipFirst) && index <= 1) { | ||
@@ -112,2 +134,3 @@ console.debug('ContextProvider::connectParentContext', 'skipping first item', next); | ||
} | ||
// only set context if it has changed | ||
return ((_a = this.currentContext) === null || _a === void 0 ? void 0 : _a.id) !== (next === null || next === void 0 ? void 0 : next.id); | ||
@@ -117,4 +140,6 @@ }), switchMap((next) => __awaiter(this, void 0, void 0, function* () { | ||
if (!next) { | ||
// if parent context is null, just return | ||
return { next }; | ||
} | ||
// notify event observers that parent context is about to change and await for cancelation | ||
const onParentContextChanged = yield ((_a = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _a === void 0 ? void 0 : _a.dispatchEvent('onParentContextChanged', { | ||
@@ -126,3 +151,6 @@ source: this, | ||
return { next, canceled: onParentContextChanged === null || onParentContextChanged === void 0 ? void 0 : onParentContextChanged.canceled }; | ||
})), filter((x) => !x.canceled), switchMap(({ next }) => { | ||
})), | ||
// filter out canceled context changes | ||
filter((x) => !x.canceled), switchMap(({ next }) => { | ||
// set current context with validation and resolution | ||
return this.setCurrentContext(next, { | ||
@@ -133,2 +161,3 @@ validate: true, | ||
console.warn('ContextProvider::onParentContextChanged', 'setCurrentContext', err); | ||
// do not emit any value if an error occurs | ||
return EMPTY; | ||
@@ -138,5 +167,8 @@ })); | ||
console.warn('ContextProvider::onParentContextChanged', 'unhandled exception', err); | ||
// do not emit any value if an error occurs | ||
return EMPTY; | ||
})); | ||
// subscribe to parent context changes | ||
const subscription = parentContext$.subscribe(); | ||
// add subscription to internal teardown | ||
__classPrivateFieldGet(this, _ContextProvider_subscriptions, "f").add(subscription); | ||
@@ -149,7 +181,13 @@ return subscription; | ||
__classPrivateFieldGet(this, _ContextProvider_contextClient, "f") | ||
// resolve context item by id | ||
.resolveContext(id) | ||
.pipe(filter((item) => !!item), switchMap((item) => this.setCurrentContext(item))) | ||
.pipe( | ||
// filter out invalid context items | ||
filter((item) => !!item), | ||
// set current context with validation and resolution | ||
switchMap((item) => this.setCurrentContext(item))) | ||
.subscribe(subscriber); | ||
} | ||
catch (err) { | ||
// catch any unhandled exceptions and emit error | ||
subscriber.error(err); | ||
@@ -160,13 +198,40 @@ } | ||
setCurrentContextByIdAsync(id) { | ||
// return last value from observable | ||
return lastValueFrom(this.setCurrentContextById(id)); | ||
} | ||
/** | ||
* Setting context is a complex operation, and might not happen immediately. | ||
* When setting the context, a task is created and added to the queue. | ||
* Once the task is completed, the returned observable will emit the value which will be the next state. | ||
* | ||
* Even tho this function returns a `Observable`, the task will be queued even tho nobody subscribes. | ||
* | ||
* If the observable is subscribe, unsubscribing __WILL__ abort the task and remove it from queue | ||
* | ||
* @param context context item which would be queue to set as current | ||
*/ | ||
setCurrentContext(context, opt) { | ||
// signal for aborting the queue entry | ||
const abort$ = new Subject(); | ||
// wrapper for returning an observable to the caller | ||
const subject$ = new Subject(); | ||
const task$ = this._setCurrentContext(context, opt).pipe(tap((x) => subject$.next(x)), takeUntil(abort$), finalize(() => subject$.complete()), catchError((err) => { | ||
const task$ = this._setCurrentContext(context, opt).pipe( | ||
// send context item which was set to the caller | ||
tap((x) => subject$.next(x)), | ||
// abort task on signal | ||
takeUntil(abort$), | ||
// close the observable sent to the caller | ||
finalize(() => subject$.complete()), | ||
// catch any unhandled exceptions to not stall the queue | ||
catchError((err) => { | ||
// emit error to caller | ||
subject$.error(err); | ||
// skip setting any context | ||
return EMPTY; | ||
})); | ||
// add task to internal queue | ||
__classPrivateFieldGet(this, _ContextProvider_contextQueue, "f").next(task$); | ||
return subject$.pipe(finalize(() => abort$.next(true))); | ||
return subject$.pipe( | ||
// if caller subscribes, unsubscribe should abort queue entry | ||
finalize(() => abort$.next(true))); | ||
} | ||
@@ -176,2 +241,3 @@ _setCurrentContext(context, opt) { | ||
var _a; | ||
// if context is the same as current, just emit and complete | ||
if (context === this.currentContext) { | ||
@@ -181,4 +247,7 @@ subscriber.next(context); | ||
} | ||
// check if context is provided and should be validated | ||
if (context && (opt === null || opt === void 0 ? void 0 : opt.validate) && !this.validateContext(context)) { | ||
// check if the resolve context is provided | ||
if (!opt.resolve) { | ||
// notify event observers that context validation failed since resolve is not provided | ||
(_a = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _a === void 0 ? void 0 : _a.dispatchEvent('onSetContextValidationFailed', { | ||
@@ -188,2 +257,3 @@ source: this, | ||
}); | ||
// emit error and complete | ||
return subscriber.error(Error('failed to validate provided context')); | ||
@@ -193,5 +263,8 @@ } | ||
return of(context) | ||
.pipe(switchMap((context) => __awaiter(this, void 0, void 0, function* () { | ||
var _b; | ||
const event = yield ((_b = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _b === void 0 ? void 0 : _b.dispatchEvent('onSetContextResolve', { | ||
.pipe( | ||
// notify event observers that context is about to get resolved | ||
switchMap((context) => __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
// wait for event listeners to handle the event | ||
const event = yield ((_a = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _a === void 0 ? void 0 : _a.dispatchEvent('onSetContextResolve', { | ||
source: this, | ||
@@ -201,2 +274,3 @@ cancelable: true, | ||
})); | ||
// check if event was canceled and abort if so | ||
if (event === null || event === void 0 ? void 0 : event.canceled) { | ||
@@ -206,8 +280,13 @@ throw Error('resolving of context was canceled'); | ||
return context; | ||
})), switchMap((context) => this.resolveContext(context).pipe(map((resolved) => ({ | ||
})), | ||
// resolve context | ||
switchMap((context) => this.resolveContext(context).pipe(map((resolved) => ({ | ||
context, | ||
resolved, | ||
})))), switchMap((_c) => __awaiter(this, [_c], void 0, function* ({ context, resolved }) { | ||
var _d; | ||
const event = yield ((_d = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _d === void 0 ? void 0 : _d.dispatchEvent('onSetContextResolved', { | ||
})))), | ||
// notify event listeners that context was resolved | ||
switchMap((_a) => __awaiter(this, [_a], void 0, function* ({ context, resolved }) { | ||
var _b; | ||
// wait for event listeners to handle the event | ||
const event = yield ((_b = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _b === void 0 ? void 0 : _b.dispatchEvent('onSetContextResolved', { | ||
source: this, | ||
@@ -217,2 +296,3 @@ cancelable: true, | ||
})); | ||
// check if event was canceled and abort if so | ||
if (event === null || event === void 0 ? void 0 : event.canceled) { | ||
@@ -222,10 +302,15 @@ throw Error('resolving of context was canceled'); | ||
return resolved; | ||
})), switchMap((resolved) => this._setCurrentContext(resolved))) | ||
})), | ||
// recursive call to set current context without validation and resolution | ||
switchMap((resolved) => this._setCurrentContext(resolved))) | ||
.subscribe(subscriber); | ||
} | ||
} | ||
// make the context an observable | ||
return of(context) | ||
.pipe(switchMap((context) => __awaiter(this, void 0, void 0, function* () { | ||
var _e; | ||
const event = yield ((_e = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _e === void 0 ? void 0 : _e.dispatchEvent('onCurrentContextChange', { | ||
.pipe( | ||
// alert event listeners that context is about to change | ||
switchMap((context) => __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
const event = yield ((_a = __classPrivateFieldGet(this, _ContextProvider_event, "f")) === null || _a === void 0 ? void 0 : _a.dispatchEvent('onCurrentContextChange', { | ||
source: this, | ||
@@ -236,2 +321,3 @@ canBubble: true, | ||
})); | ||
// check if event was canceled and abort if so | ||
if (event === null || event === void 0 ? void 0 : event.canceled) { | ||
@@ -243,3 +329,5 @@ throw Error('change of context was aborted'); | ||
.subscribe((context) => { | ||
// emit context to the caller | ||
subscriber.next(context); | ||
// only take the first value and complete | ||
subscriber.complete(); | ||
@@ -256,3 +344,5 @@ }); | ||
const query$ = this.queryClient | ||
.query(__classPrivateFieldGet(this, _ContextProvider_contextParameterFn, "f").call(this, { | ||
.query( | ||
// generate query parameters | ||
__classPrivateFieldGet(this, _ContextProvider_contextParameterFn, "f").call(this, { | ||
search, | ||
@@ -262,2 +352,3 @@ type: __classPrivateFieldGet(this, _ContextProvider_contextType, "f"), | ||
.pipe(catchError((err) => { | ||
// if query client throws a QueryClientError, extract the cause and throw it | ||
if (err.name === 'QueryClientError') { | ||
@@ -268,2 +359,3 @@ throw err.cause; | ||
}), map((x) => x.value)); | ||
// apply context filter if available | ||
return __classPrivateFieldGet(this, _ContextProvider_contextFilter, "f") ? query$.pipe(map(__classPrivateFieldGet(this, _ContextProvider_contextFilter, "f"))) : query$; | ||
@@ -280,10 +372,17 @@ } | ||
resolveContext(item) { | ||
return this.relatedContexts({ item, filter: { type: __classPrivateFieldGet(this, _ContextProvider_contextType, "f") } }).pipe(map((x) => x.filter((item) => this.validateContext(item))), map((values) => { | ||
// request related context items for the given context item with the same context type which the provider is configured with | ||
return this.relatedContexts({ item, filter: { type: __classPrivateFieldGet(this, _ContextProvider_contextType, "f") } }).pipe( | ||
// filter out invalid context items | ||
map((x) => x.filter((item) => this.validateContext(item))), map((values) => { | ||
// related context should be resolved to a single context item | ||
const value = values.shift(); | ||
// if no value is found, throw an error | ||
if (!value) { | ||
throw Error('failed to resolve context'); | ||
} | ||
// if multiple items are found, log a warning | ||
if (values.length) { | ||
console.warn('ContextProvider::relatedContext', 'multiple items found 🤣', values); | ||
} | ||
// return the resolved context item | ||
return value; | ||
@@ -296,5 +395,7 @@ })); | ||
relatedContexts(args) { | ||
// check if related context client is available | ||
if (!__classPrivateFieldGet(this, _ContextProvider_contextRelated, "f")) { | ||
return throwError(() => Error('ContextProvider::relatedContexts - no client defined for resolving related context')); | ||
} | ||
// request related context items | ||
return __classPrivateFieldGet(this, _ContextProvider_contextRelated, "f").query(args).pipe(map(({ value }) => value), catchError((err) => { | ||
@@ -301,0 +402,0 @@ if (err.cause) { |
@@ -13,9 +13,23 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
var _FusionContextSearchError_details; | ||
/** | ||
* Represents an error that occurs during a search in the Fusion Context. | ||
*/ | ||
export class FusionContextSearchError extends Error { | ||
/** | ||
* The title of the error. | ||
*/ | ||
get title() { | ||
return __classPrivateFieldGet(this, _FusionContextSearchError_details, "f").title; | ||
} | ||
/** | ||
* The description of the error, if available. | ||
*/ | ||
get description() { | ||
return __classPrivateFieldGet(this, _FusionContextSearchError_details, "f").description; | ||
} | ||
/** | ||
* Creates a new instance of FusionContextSearchError. | ||
* @param details - The details of the error. | ||
* @param options - Optional parameters for the error. | ||
*/ | ||
constructor(details, options) { | ||
@@ -22,0 +36,0 @@ var _a; |
@@ -20,17 +20,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
var _a; | ||
// create config from configurator | ||
const config = yield args.config.createConfig(args); | ||
// get event module if available | ||
const event = args.hasModule('event') ? yield args.requireInstance('event') : undefined; | ||
// get parent context provider if available | ||
const parentProvider = (_a = args.ref) === null || _a === void 0 ? void 0 : _a.context; | ||
// create context provider | ||
const provider = new ContextProvider({ config, event, parentContext: parentProvider }); | ||
// create subscription for disposing the provider | ||
const subscription = new Subscription(() => provider.dispose()); | ||
this.postInitialize = (args) => new Observable((subscriber) => { | ||
// setup post initialize to module | ||
this.postInitialize = (args) => | ||
// create observable for resolving initial context | ||
new Observable((subscriber) => { | ||
// resolve initial context if available from config if available | ||
const resolveInitialContext$ = config.resolveInitialContext | ||
? from(config.resolveInitialContext(args)).pipe(filter((item) => !!item), switchMap((item) => args.modules.context.setCurrentContext(item, { | ||
? from(config.resolveInitialContext(args)).pipe( | ||
// filter out invalid context items | ||
filter((item) => !!item), switchMap((item) => | ||
// set current context with validation and resolution | ||
args.modules.context.setCurrentContext(item, { | ||
validate: true, | ||
resolve: true, | ||
}))) | ||
: EMPTY; | ||
: EMPTY; // if no initial context is available, complete immediately | ||
// add teardown to resolve initial context | ||
subscriber.add(resolveInitialContext$ | ||
.pipe(catchError((err) => { | ||
console.warn('ContextModule.postInitialize', 'failed to resolve initial context', err); | ||
// failed to resolve initial context, complete immediately | ||
return EMPTY; | ||
@@ -43,2 +58,3 @@ })) | ||
complete: () => { | ||
// connect parent context if available when stream completes | ||
if (config.connectParentContext !== false && parentProvider) { | ||
@@ -51,2 +67,3 @@ provider.connectParentContext(parentProvider); | ||
}); | ||
// add teardown to module | ||
this.dispose = () => subscription.unsubscribe(); | ||
@@ -53,0 +70,0 @@ return provider; |
@@ -10,2 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}; | ||
/** | ||
* Parses the context type from the response of the GetContext API. | ||
* | ||
* @param type The type property from the GetContext response. | ||
* @returns The parsed context item type. | ||
*/ | ||
const parseContextType = (type) => { | ||
@@ -19,2 +25,7 @@ var _a; | ||
}; | ||
/** | ||
* Parses an ApiContextEntity object into a ContextItem object. | ||
* @param item The ApiContextEntity object to parse. | ||
* @returns The parsed ContextItem object. | ||
*/ | ||
const parseContextItem = (item) => { | ||
@@ -31,5 +42,11 @@ var _a, _b, _c, _d; | ||
type: parseContextType(item.type), | ||
// TODO | ||
value: (_d = item.value) !== null && _d !== void 0 ? _d : {}, | ||
}; | ||
}; | ||
/** | ||
* Parse the response from the GetContext API into a context item. | ||
* @param response The response object containing the context item. | ||
* @returns A promise that resolves to the context item. | ||
*/ | ||
export const getContextSelector = (response) => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -39,2 +56,7 @@ const result = (yield response.json()); | ||
}); | ||
/** | ||
* Parse the response from the QueryContext API into an array of context items. | ||
* @param response The response object. | ||
* @returns A promise that resolves to an array of context items. | ||
*/ | ||
export const queryContextSelector = (response) => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -44,2 +66,7 @@ const result = (yield response.json()); | ||
}); | ||
/** | ||
* Parse the response from the RelatedContext API into an array of context items. | ||
* @param response The response object containing the related context items. | ||
* @returns A promise that resolves to an array of ContextItem objects. | ||
*/ | ||
export const relatedContextSelector = (response) => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -46,0 +73,0 @@ const result = (yield response.json()); |
import { module } from '../module'; | ||
export const enableContext = (configurator, builder) => { | ||
/** | ||
* Method for enabling the Service module | ||
* @param configurator - configuration object | ||
*/ | ||
export const enableContext = ( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
configurator, builder) => { | ||
configurator.addConfig({ | ||
@@ -4,0 +10,0 @@ module, |
import { EMPTY } from 'rxjs'; | ||
// GUID pattern | ||
const matchGUID = /^(?:(?:[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})$/; | ||
/** | ||
* Method will try to extract a context id from a path. | ||
* The default matcher is a GUID pattern. | ||
* Will iterate over the path and return the first match. | ||
* | ||
* @example | ||
* ```ts | ||
* const path = '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?dsadasdas#example'; | ||
* const contextId = extractContextIdFromPath(path); // '7fd97952-7fe6-409b-a6dc-292dbf0e50d7' | ||
* ``` | ||
* | ||
* @param path string - the path to extract the context id from | ||
* @param matcher RegExp - the pattern to match against | ||
* @returns string | undefined - the context id or undefined | ||
*/ | ||
export const extractContextIdFromPath = (path, matcher = matchGUID) => path | ||
// remove leading slashes | ||
.replace(/^\/+/, '') | ||
// split path by slashes | ||
.split('/') | ||
// find the first path fragment that matches the matcher | ||
.find((x) => x.match(matcher)); | ||
@@ -7,0 +26,0 @@ const validateContextId = (contextId) => !!contextId.match(matchGUID); |
import { concat, EMPTY, first, of } from 'rxjs'; | ||
import { resolveContextFromPath } from './resolve-context-from-path'; | ||
/** | ||
* Resolves the initial context from the parent module. | ||
* | ||
* @param ref - parent modules. | ||
* @returns An Observable of the resolved initial context. | ||
*/ | ||
export const resolveContextFromParent = ({ ref }) => { | ||
const parentContext = ref === null || ref === void 0 ? void 0 : ref.context; | ||
// check if the parent has context module | ||
if (!parentContext) { | ||
throw Error(['resolveContextFromNavigation', 'ref does not support context!'].join('\n')); | ||
} | ||
// return the current context from the parent or empty if the parent does not have a context | ||
return parentContext.currentContext ? of(parentContext.currentContext) : EMPTY; | ||
}; | ||
/** | ||
* Resolves the initial context for a Fusion Framework context module. | ||
* | ||
* will try to resolve the initial context from the path, and if that fails, it will try to resolve the context from the parent. | ||
* | ||
* @param options - Optional configuration for resolving the context path. | ||
* @returns A function that accepts the module's reference and modules, and returns an Observable of the resolved initial context. | ||
*/ | ||
export const resolveInitialContext = (options) => ({ ref, modules }) => { | ||
var _a, _b; | ||
const { context, navigation } = modules; | ||
// create a path resolver from the context module | ||
const pathResolver = resolveContextFromPath(context, options === null || options === void 0 ? void 0 : options.path); | ||
// use the path from the navigation module, or the path from the parent navigation module | ||
const pathname = (_a = navigation === null || navigation === void 0 ? void 0 : navigation.path.pathname) !== null && _a !== void 0 ? _a : (_b = ref.navigation) === null || _b === void 0 ? void 0 : _b.path.pathname; | ||
// try to resolve the context from the path, and if that fails, try to resolve the context from the parent | ||
return concat(pathname ? pathResolver(pathname) : EMPTY, resolveContextFromParent({ ref, modules })).pipe(first()); | ||
@@ -16,0 +35,0 @@ }; |
@@ -1,2 +0,3 @@ | ||
export const version = '5.0.7'; | ||
// Generated by genversion. | ||
export const version = '5.0.8'; | ||
//# sourceMappingURL=version.js.map |
@@ -18,4 +18,13 @@ import { ObservableInput } from 'rxjs'; | ||
contextFilter?: ContextFilterFn; | ||
/** | ||
* connect context module to paren context module. | ||
* | ||
* _default: `true`_ | ||
*/ | ||
connectParentContext?: boolean; | ||
/** set initial context from parent, will await resolve */ | ||
skipInitialContext?: boolean; | ||
/** | ||
* Method for generating context query parameters. | ||
*/ | ||
contextParameterFn?: (args: { | ||
@@ -22,0 +31,0 @@ search: string; |
@@ -8,17 +8,82 @@ import { Observable, Subscription } from 'rxjs'; | ||
import Query from '@equinor/fusion-query'; | ||
/** | ||
* WARNING: this is an initial out cast. | ||
* api clients will most probably not be exposed in future! | ||
*/ | ||
/** | ||
* Represents a context provider that manages the current context and provides methods for querying and manipulating context items. | ||
*/ | ||
export interface IContextProvider { | ||
/** DANGER */ | ||
readonly contextClient: ContextClient; | ||
/** DANGER */ | ||
readonly queryClient: Query<ContextItem[], QueryContextParameters>; | ||
readonly currentContext$: Observable<ContextItem | null | undefined>; | ||
currentContext: ContextItem | null | undefined; | ||
/** | ||
* Queries the context items based on the provided search string. | ||
* @param search The search string. | ||
* @returns An observable that emits an array of context items. | ||
*/ | ||
queryContext(search: string): Observable<Array<ContextItem>>; | ||
/** | ||
* Queries the context items asynchronously based on the provided search string. | ||
* @param search The search string. | ||
* @returns A promise that resolves to an array of context items. | ||
*/ | ||
queryContextAsync(search: string): Promise<Array<ContextItem>>; | ||
/** | ||
* Validates the given context item. | ||
* @param item The context item to validate. | ||
* @returns A boolean indicating whether the context item is valid or not. | ||
*/ | ||
validateContext(item: ContextItem<Record<string, unknown>>): boolean; | ||
/** | ||
* Resolves the context item as a stream. | ||
* @param current The current context item. | ||
* @returns An observable that emits the resolved context item. | ||
*/ | ||
resolveContext: (current: ContextItem) => Observable<ContextItem>; | ||
/** | ||
* Resolves the context item asynchronously. | ||
* @param current The current context item. | ||
* @returns A promise that resolves to the resolved context item. | ||
*/ | ||
resolveContextAsync: (current: ContextItem) => Promise<ContextItem>; | ||
/** | ||
* Retrieves the related context items based on the provided parameters. | ||
* @param args The parameters for retrieving related context items. | ||
* @returns An observable that emits an array of related context items. | ||
*/ | ||
relatedContexts: (args: RelatedContextParameters) => Observable<Array<ContextItem<Record<string, unknown>>>>; | ||
/** | ||
* Retrieves the related context items asynchronously based on the provided parameters. | ||
* @param args The parameters for retrieving related context items. | ||
* @returns A promise that resolves to an array of related context items. | ||
*/ | ||
relatedContextsAsync: (args: RelatedContextParameters) => Promise<Array<ContextItem<Record<string, unknown>>>>; | ||
/** | ||
* Clears the current context. | ||
*/ | ||
clearCurrentContext: VoidFunction; | ||
/** | ||
* Sets the current context item by its ID. | ||
* @param id The ID of the context item. | ||
* @returns An observable that emits the current context item. | ||
*/ | ||
setCurrentContextById(id: string): Observable<ContextItem<Record<string, unknown>>>; | ||
/** | ||
* Sets the current context item by its ID asynchronously. | ||
* @param id The ID of the context item. | ||
* @returns A promise that resolves to the current context item. | ||
*/ | ||
setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>; | ||
/** | ||
* Sets the current context item. | ||
* @param context The context item to set as the current context. | ||
* @param opt Optional settings for the operation. | ||
* @param opt.validate Specifies whether to validate the context item. Default is `true`. | ||
* @param opt.resolve Specifies whether to resolve the context item. Default is `true`. | ||
* @returns An observable that emits the current context item or `null`. | ||
*/ | ||
setCurrentContext(context: ContextItem<Record<string, unknown>> | null, opt?: { | ||
@@ -28,2 +93,10 @@ validate?: boolean; | ||
}): Observable<ContextItem<Record<string, unknown>> | null>; | ||
/** | ||
* Sets the current context item asynchronously. | ||
* @param context The context item to set as the current context. | ||
* @param opt Optional settings for the operation. | ||
* @param opt.validate Specifies whether to validate the context item. Default is `true`. | ||
* @param opt.resolve Specifies whether to resolve the context item. Default is `true`. | ||
* @returns A promise that resolves to the current context item or `null`. | ||
*/ | ||
setCurrentContextAsync(context: ContextItem<Record<string, unknown>> | null, opt?: { | ||
@@ -40,2 +113,3 @@ validate?: boolean; | ||
get currentContext(): ContextItem | undefined | null; | ||
/** @deprecated do not use, will be removed */ | ||
set currentContext(context: ContextItem | null | undefined); | ||
@@ -45,2 +119,3 @@ constructor(args: { | ||
event?: ModuleType<EventModule>; | ||
/** @deprecated use ContextProvider.connectParentContext */ | ||
parentContext?: IContextProvider; | ||
@@ -53,2 +128,13 @@ }); | ||
setCurrentContextByIdAsync(id: string): Promise<ContextItem<Record<string, unknown>>>; | ||
/** | ||
* Setting context is a complex operation, and might not happen immediately. | ||
* When setting the context, a task is created and added to the queue. | ||
* Once the task is completed, the returned observable will emit the value which will be the next state. | ||
* | ||
* Even tho this function returns a `Observable`, the task will be queued even tho nobody subscribes. | ||
* | ||
* If the observable is subscribe, unsubscribing __WILL__ abort the task and remove it from queue | ||
* | ||
* @param context context item which would be queue to set as current | ||
*/ | ||
setCurrentContext<T extends ContextItem<Record<string, unknown>> | null>(context: T, opt?: { | ||
@@ -55,0 +141,0 @@ validate?: boolean; |
@@ -0,9 +1,29 @@ | ||
/** | ||
* Represents an error that occurs during a search in the Fusion Context. | ||
*/ | ||
export declare class FusionContextSearchError extends Error { | ||
#private; | ||
/** | ||
* The title of the error. | ||
*/ | ||
get title(): string; | ||
/** | ||
* The description of the error, if available. | ||
*/ | ||
get description(): string | undefined; | ||
/** | ||
* Creates a new instance of FusionContextSearchError. | ||
* @param details - The details of the error. | ||
* @param options - Optional parameters for the error. | ||
*/ | ||
constructor(details: { | ||
/** | ||
* The title of the error. | ||
*/ | ||
title: string; | ||
/** | ||
* The description of the error, if available. | ||
*/ | ||
description?: string; | ||
}, options?: ErrorOptions); | ||
} |
import type { ContextItem } from './types'; | ||
/** | ||
* Parse the response from the GetContext API into a context item. | ||
* @param response The response object containing the context item. | ||
* @returns A promise that resolves to the context item. | ||
*/ | ||
export declare const getContextSelector: (response: Response) => Promise<ContextItem>; | ||
/** | ||
* Parse the response from the QueryContext API into an array of context items. | ||
* @param response The response object. | ||
* @returns A promise that resolves to an array of context items. | ||
*/ | ||
export declare const queryContextSelector: (response: Response) => Promise<ContextItem[]>; | ||
/** | ||
* Parse the response from the RelatedContext API into an array of context items. | ||
* @param response The response object containing the related context items. | ||
* @returns A promise that resolves to an array of ContextItem objects. | ||
*/ | ||
export declare const relatedContextSelector: (response: Response) => Promise<ContextItem[]>; |
import type { IModulesConfigurator, AnyModule, ModuleInitializerArgs } from '@equinor/fusion-framework-module'; | ||
import type { IContextModuleConfigurator } from '../configurator'; | ||
import type { ContextConfigBuilder } from '../ContextConfigBuilder'; | ||
export declare const enableContext: (configurator: IModulesConfigurator<any, any>, builder?: (<TDeps extends AnyModule[] = []>(builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>) => void | Promise<void>) | undefined) => void; | ||
/** | ||
* Method for enabling the Service module | ||
* @param configurator - configuration object | ||
*/ | ||
export declare const enableContext: (configurator: IModulesConfigurator<any, any>, builder?: <TDeps extends Array<AnyModule> = []>(builder: ContextConfigBuilder<TDeps, ModuleInitializerArgs<IContextModuleConfigurator, TDeps>>) => void | Promise<void>) => void; |
@@ -5,10 +5,77 @@ import { Observable } from 'rxjs'; | ||
import { type ContextItem } from '../types'; | ||
/** | ||
* Arguments for resolving a context from a path. | ||
*/ | ||
export type ContextPathResolveArgs = { | ||
/** | ||
* Callback to extract a context id from a path. | ||
* @param path string - the path to extract the context id from | ||
* @returns string | undefined - the context id or undefined | ||
*/ | ||
extract?: (path: string) => string | undefined; | ||
/** | ||
* Callback to validate a context id. | ||
* @param contextId string - the context id to validate | ||
* @returns boolean - true if the context id is valid | ||
*/ | ||
validate?: (contextId: string) => boolean; | ||
}; | ||
/** | ||
* Method will try to extract a context id from a path. | ||
* The default matcher is a GUID pattern. | ||
* Will iterate over the path and return the first match. | ||
* | ||
* @example | ||
* ```ts | ||
* const path = '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?dsadasdas#example'; | ||
* const contextId = extractContextIdFromPath(path); // '7fd97952-7fe6-409b-a6dc-292dbf0e50d7' | ||
* ``` | ||
* | ||
* @param path string - the path to extract the context id from | ||
* @param matcher RegExp - the pattern to match against | ||
* @returns string | undefined - the context id or undefined | ||
*/ | ||
export declare const extractContextIdFromPath: (path: string, matcher?: RegExp) => string | undefined; | ||
/** | ||
* Method will try to resolve a context from a path. | ||
* The method will return a function that takes a path and returns an observable of the resolved context. | ||
* The method will use the context module to resolve the context. | ||
* The method will use the extract and validate methods from the args to extract and validate the context id. | ||
* If the context id is not valid, the method will throw an error. | ||
* If the context id is valid, the method will return an observable of the resolved context. | ||
* If the context id is not found, the method will return an empty observable. | ||
* | ||
* @example | ||
* ```ts | ||
* const resolve = resolveContextFromPath(modules.context); | ||
* resolve( | ||
* '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?foobar#example' | ||
* ).subscribe(console.log); | ||
* ``` | ||
* | ||
* @param context The context module. | ||
* @returns A function that takes a path and returns an Observable of the resolved context item. | ||
*/ | ||
export interface resolveContextFromPath { | ||
(context: ModuleType<ContextModule>): (path: string) => Observable<ContextItem>; | ||
} | ||
/** | ||
* | ||
* @example | ||
* ```ts | ||
* const resolve = resolveContextFromPath( | ||
* modules.context, | ||
* { | ||
* extract: (path) => path.find(extractingContextFromPath), | ||
* validate: (id) => isValidContextId(id) | ||
* }); | ||
* resolve( | ||
* '/apps/context/7fd97952-7fe6-409b-a6dc-292dbf0e50d7?foobar#example' | ||
* ).subscribe(console.log); | ||
* ``` | ||
* | ||
* @param context The context module. | ||
* @param args The arguments for resolving the path. | ||
* @returns A function that takes a path and returns an Observable of the resolved context item. | ||
*/ | ||
export interface resolveContextFromPath { | ||
@@ -15,0 +82,0 @@ (context: ModuleType<ContextModule>, args: ContextPathResolveArgs): (path: string) => Observable<ContextItem>; |
import { type ContextModuleConfig } from '../configurator'; | ||
import { type ContextPathResolveArgs } from './resolve-context-from-path'; | ||
/** | ||
* Resolves the initial context from the parent module. | ||
* | ||
* @param ref - parent modules. | ||
* @returns An Observable of the resolved initial context. | ||
*/ | ||
export declare const resolveContextFromParent: ContextModuleConfig['resolveInitialContext']; | ||
/** | ||
* Resolves the initial context for a Fusion Framework context module. | ||
* | ||
* will try to resolve the initial context from the path, and if that fails, it will try to resolve the context from the parent. | ||
* | ||
* @param options - Optional configuration for resolving the context path. | ||
* @returns A function that accepts the module's reference and modules, and returns an Observable of the resolved initial context. | ||
*/ | ||
export declare const resolveInitialContext: (options?: { | ||
path?: ContextPathResolveArgs; | ||
}) => Required<ContextModuleConfig>['resolveInitialContext']; | ||
}) => Required<ContextModuleConfig>["resolveInitialContext"]; | ||
export default resolveInitialContext; |
@@ -1,1 +0,1 @@ | ||
export declare const version = "5.0.7"; | ||
export declare const version = "5.0.8"; |
{ | ||
"name": "@equinor/fusion-framework-module-context", | ||
"version": "5.0.7", | ||
"version": "5.0.8", | ||
"description": "", | ||
@@ -48,15 +48,15 @@ "main": "./dist/esm/index.js", | ||
"fast-deep-equal": "^3.1.3", | ||
"@equinor/fusion-query": "^5.0.5" | ||
"@equinor/fusion-query": "^5.1.0" | ||
}, | ||
"devDependencies": { | ||
"rxjs": "^7.8.1", | ||
"typescript": "^5.4.2", | ||
"@equinor/fusion-framework-module-navigation": "^4.0.2", | ||
"@equinor/fusion-framework-module": "^4.3.1", | ||
"@equinor/fusion-framework-module-event": "^4.1.2", | ||
"@equinor/fusion-framework-module-services": "^4.1.0" | ||
"typescript": "^5.5.3", | ||
"@equinor/fusion-framework-module": "^4.3.2", | ||
"@equinor/fusion-framework-module-navigation": "^4.0.3", | ||
"@equinor/fusion-framework-module-event": "^4.2.0", | ||
"@equinor/fusion-framework-module-services": "^4.1.1" | ||
}, | ||
"peerDependencies": { | ||
"rxjs": "^7.8.1", | ||
"@equinor/fusion-framework-module": "^4.3.1" | ||
"@equinor/fusion-framework-module": "^4.3.2" | ||
}, | ||
@@ -63,0 +63,0 @@ "scripts": { |
// Generated by genversion. | ||
export const version = '5.0.7'; | ||
export const version = '5.0.8'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
339597
2839
Updated@equinor/fusion-query@^5.1.0