diff --git a/ActionController.md b/ActionController.md new file mode 100644 index 0000000..2867db7 --- /dev/null +++ b/ActionController.md @@ -0,0 +1,51 @@ +# ActionController +This was the original inspiration for the STRX Principle concept. Except here we decomposed and recomposed the concept into two parts. Here the ActionController in contrast to its original is an subject that fires once and has an expiration. This streamlines the difficulty of working with this concept, as originally to make it wholly responsible for itself, it would require an additional subscription to determine whether it should be open or closed. But now with the additional refinement of each action having an expiration, we can define the expiration as the limit to the lifespan of each action. And would be the second point of failure that an ActionStrategy would move into a failure state. + +The purpose of the ActionController is to perform singular actions that effect one thing asynchronously. For example in a FileSystem concept, it would be the literal Method, that would allow for manipulation of files. As each operation can have a failure state that is outside of the control of STRX. Such as a file being open by a user, or the lack of permission allotted to the run time. + +In order to have the ActionController work within STRX we must set up the Method in a specific manner to ensure type safety. Note the usage of the "switchMap," as we are changing the context of the stream from an Action to a specific subject that will later return an Action. +## Example +``` typescript +const createSomeMethodCreator: MethodCreator = () => { + const logSubject = new Subject(); + const logMethod: Method = logSubject.pipe( + switchMap((act: Action) => { + return createActionController$(act, (controller, action) => { + const payload = selectPayload(action); + if (action.strategy) { + someAsyncFunction.then(value => { + const strategy = action.strategy as ActionStrategy; + if (value) { + const newStrategy = + strategySuccess( + strategy, + strategyData_unifyData(strategy, {some: value}) + ); + controller.fire(newStrategy); + } else { + const newStrategy = + strategyFailed( + strategy, + strategyData_appendFailure(strategy, 'Some Async function failed.') + ); + controller.fire(newStrategy); + } + } + ); + } else { + controller.fire(axiumConclude()); + } + }); + }), + ); + return [ + logMethod, + logSubject + ]; +}; +``` +In the above we are likewise paying attention to the possibility of failure within the Asynchronous Function. Then informing the type of failure that occurred. It is advisable if you know these failures modes in advance. To create an enum that can store such. As the failureCondition is just some enum holding a set of strings. Then use this enum to inform your next decision from your FailureNode. Note here that in classical game ai systems that they would ordinarily restrict the amount of successive actions that an ai might make to 2. This pattern of design of informing some failure demonstrates the inherit higher order reasoning within STRX's design. Complexity classically has been seen as a poor design choice, but the goal within STRX is to fully decompose intelligent systems as the unfortunate truth is that higher order logic is complex by default. But may be made less complex via logical determinism, wherein we decision with failure modes in mind. Versus leaving that decision to some probability. + +The easiest example of such is living with room mates and wanting to have a bowl of cereal before going to work. The entire strategy involves the confirmation that the room mate did not drink the rest of the milk the previous night. Therefore the strategy that we impose accounts for this possibility via changing the meal, or forgoing it entirely to get to work on time. But likewise if we forgo our breakfast and get to work. We should take into account the possibility of grabbing a snack from the break room, but if all the snacks are gone. Then in the last ditch effort can have some coworker or delivery service bring in some food. If none of these options are available, then we will have to wait till lunch. + +Note here that while we are doing so seamlessly. In a video game context this would be considered to be too complex to design. As classically in order to create this behavior it would be the generation of smaller steps to achieve some short sighted goal or interaction. But in the scope of planning, in order to make it through to the end of the day. We are having to make decisions in line with our overall goals to successfully make it back to bed at a reasonable hour. As we not only take into account that day, but know that if we go out and play. We likewise can hamper our own next work the next day, if we don't get home on time. This demonstrates the purpose of identifying "Time" and "Space" as a "Universal Concepts." Where time is the limiter to our ability to perform some action, depending on other strategies we are required to perform. And space the locality and availability of some concept to successfully perform some action. That you may sleep at the office if working late, but then you would need some clothes in your car. And if your work has a clean shave policy, then likewise you would need a method of shaving available to match the requirements that your workspace has set you for you to meet each day. \ No newline at end of file diff --git a/README.md b/README.md index a128110..4b79706 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,13 @@ The inspiration for STRX was that of Redux and its origin via the FLUX design pa ![Ghostbusters - "We'll Cross the Streams" - (HD) - Scenes from the 80s - (1984)](https://github.com/Phuire-Research/STRX/blob/main/CrossTheStreams.gif?raw=true) ### Concept Index -* [ActionStrategy](https://github.com/Phuire-Research/STRX/blob/main/ActionStrategy.md) - Created in 2018, this is the governing concept that allows for the Unified Turing Machine to have a strong halting quality. Likewise the direct analog of higher order logic and universal transformer. +* [Action Strategy](https://github.com/Phuire-Research/STRX/blob/main/ActionStrategy.md) - Created in 2018, this is the governing concept that allows for the Unified Turing Machine to have a strong halting quality. Likewise the direct analog of higher order logic and universal transformer. * [Axium](https://github.com/Phuire-Research/STRX/blob/main/Axium.md) - Governing concept that contains the set of concepts that formalizes each axium. * [Concept](https://github.com/Phuire-Research/STRX/blob/main/Concept.md) - The programming abstraction of a concept that is decomposable to the sum of its parts via: state, qualities, principles, and mode. -* [Stage Planner](https://github.com/Phuire-Research/STRX/blob/main/StagePlanner.md) - Introducing the stage planner paradigm. A specialized helper function to prevent action overflow when dispatching actions in subscriptions. +* [Stage Planner](https://github.com/Phuire-Research/STRX/blob/main/StagePlanner.md) - Introducing the stage planner paradigm. A specialized helper function to prevent action overflow when dispatching actions in subscriptions. +* [Action Controller](https://github.com/Phuire-Research/STRX/blob/main/ActionController.md) - Allows methods to be performed asynchronously. * [Spatial Ownership](https://github.com/Phuire-Research/STRX/blob/main/SpatialOwnership.md) - Streamlines the complex nature of the ActionStrategy as it relates to itself and other axiums. This is what allows STRX to be a graph computation paradigm. +* [Strategy Data](https://github.com/Phuire-Research/STRX/blob/main/StrategyData.md) - Allows for the ActionStrategy pattern to act as a "Universal Transformer." Likewise decorates strategies with the necessary information to inform "ActionNodes," of possible failure conditions. * [Unified Turing Machine](https://github.com/Phuire-Research/STRX/blob/main/The-Unified-Turing-Machine.md) - The governing concept for this entire framework. ## The Halting Problem diff --git a/StrategyData.md b/StrategyData.md new file mode 100644 index 0000000..956b857 --- /dev/null +++ b/StrategyData.md @@ -0,0 +1,47 @@ +# Strategy Data +This is the true power of the ActionStrategy design pattern as a Universal Transformer. As this system is informed by "Data Oriented Design." We are focusing explicitly on the transforming of some data over a period of time. How are doing so, is via the creation of some Record that would be unified over the lifetime of that ActionStrategy. As the act of unifying some data. Is merely decomposing those records and recomposing such into a new record with all previous parameters with the newest taking president. + +What allows for the ActionStrategy paradigm to be a "Universal Transformer." Is the recognition of the most fundamental truth in computer programming. Is that via the utilization of plain text with transpilation to some native binary. Allows for all files to be modified within a plain text environment. As such this could also include the vertices within some graphical model that would be used to inform the appearance of some geometry within a simulation. + +The ActionStrategy's goal would be how to transform that data over a series of steps to match the goal set out by its topic. The difficulty within this approach is that it requires specific naming conventions to handle this transformation over time. As we are matching "Universal Properties." Therefore if a parameter is not being opted in to this unifying data approach. That parameter should have some preposition that stops this process from happening. For example, "uiDivStyle," versus "style." + +## Consumer Functions +``` typescript +export const strategyData_appendFailure = (strategy: ActionStrategy, condition: failureConditions | string): Record => {}; +export const strategyData_selectFailureCondition = (strategy: ActionStrategy): failureConditions | string | undefined => {}; +export const strategyData_clearFailureCondition = (strategy: ActionStrategy): Record | undefined => {}; +export const strategyData_select = (strategy: ActionStrategy): T | undefined => {}; +export const strategyData_unifyData = (strategy: ActionStrategy, data: Record): Record => {}; +``` +* strategyData_appendFailure - This will append a "failureCondition," property that is informed either by the failureConditions enum or your string. This allows for your failureNodes to make intelligent decisions. Or if there is no data field present, it will create a new record with this property. +* strategyData_selectFailureCondition - This will return the failure condition or undefined if not set. +* strategyData_clearFailureCondition - This will clear such from the current record, otherwise if there are no additional properties on the record, or if no record is present at the time of clear, it will return undefined. As this forces data validation to ensure there is some data to unify given some success. +* strategyData_select - This will simply return and set the current type of the returned data. But forces type safety via the possibility of an undefined type. Note here that the strength of TypeScript allows for Records to be return or casted as a Slice of the given type, even if other Actions have added properties that would not be accounted for within your transformation context. +* strategyData_unifyData - The reason for all the undefined returns. This will take the current records, decomposed, and recompose such together with the newest data taking precedent. + +## The Reason for this Approach +We are using within the context your concepts the creation of consumer functions to ensure type safety even within the context of data that would be unified with other concepts. That these records might have the failureCondition property. But your logic would only care for the parameters that it is attempting to transform. This in effect allows for the creation of new emergent data if the "crossing of the streams." And in contrast to previous methods of programming, this would be similar to the creation of the assembly line within the context of programming. As classically the assembly line is generalized within the context of classic via the utilization of factories. As factories have assembly lines, but you are traditionally only caring for the output based on some input. + +Therefore this approach decomposes the concept of the factory into its parts. And allows for different concepts to inform the unified output of your overall factory as the "Axium." As even if we examine the properties of some file type binary. When in the context of its informing program, that file would just be some set of properties. The decision to turn such into a binary does save on space and allows such to be condensed and represents a construct that purposefully resists decomposition from other programs. We could take advantage of this approach while having a shared standard for the binary transformation of plain text and its decomposition. Point being, it is a relative thing and what matters in the context of programming and entities is that everything is merely a list of properties. The difficulty is gaining access to those that you would otherwise transform. + +## Failure Conditions and Higher Order Logic +``` typescript +export enum failureConditions { + ownershipExpired = 'ownershipExpired', + ownershipBlocked = 'ownershipBlocked', + controllerExpired = 'controllerExpired', + axiumExpired = 'axiumExpired', + axiumBadGeneration = 'axiumBadGeneration' +} +``` +Note with the above, due to there being by default 3 different types of failure within an ActionStrategy within STRX. That being "Expiration," "Blocked," and "Bad Generation." Expiration, which we treat as an absolute failure with no recovery. If a strategy must be recovery if it expires, please use a subscription or plan to pay attention to the badActions list on the "Axium." From there you can determine either you action's type or strategy topic to reissue such if it expired. Blocked signifies that the "Ownership" concept is currently loaded at the target transformation is temporarily being blocked, but may still expire. And finally "Bad Generation," this denotes whether the quality or governing concept is currently loaded within the Axium. As the axium is allowed to transform its functionality over time, the generation signifies the current iteration of its configuration. This is effected via the removal or concepts or the addition of such. If one wants to effect the composition of qualities on a concept, it is advised to still use these functions while providing a means to transfer the current state onto the new concept. + +We have likewise introduced additional data handling functions. Specially here we are using "strategyData_appendFailure." To denote, specially what type of failure has happened within the ActionStrategy. Therefore the FailureNode, may utilize "strategyData_selectFailureCondition," to determine some decision to handle the returned property. This is where this system becomes complex. Noting that within traditional game Ai systems that would use some behavior tree or action planner. This is why those systems limit the amount of actions to just 2, and call anything more than that as overtly complex. Therefore this likewise reinforces that this system of programming is the bluntest form of higher order logic. + +As the fundamental difficulty of higher order logic, is having to take into account complex state arrangements to make decisions. And the longer the strategy, or the more successive used in unison, the higher chance there will be some failure. Therefore this is higher order via the exponential factor of decision making that must be taken into account per ActionStrategy. + +The advantage that STRX has over these classical game Ai systems. Is we know in advance some "Universal Concepts," that would inform the failure mode. That being you cannot have two sets of feet standing in the same position, would be "Spatial Ownership." Or in the case of computer systems, is whether you are modifying some file that is already open by another program. + +The other would be "Time," or expiration paradigm within STRX that limits of lifetimes of Strategies within this System. As the createActionController function assigns a timer to the life time of that specific ActionController. That is cleared upon success or will fire the next FailureNode or conclusion if present, while appending the failure context to the data property. + +*There is much more to be expanded in addition to working examples. Internally this is being used to prototype a "UserInterface" concept and will release those examples once that concept is worthy of being in a mvp state.* \ No newline at end of file diff --git a/package.json b/package.json index 6bc2ad9..934f18b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@phuire/strx", - "version": "0.0.33", + "version": "0.0.34", "description": "Unified Turing Machine", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/concepts/ownership/ownership.mode.ts b/src/concepts/ownership/ownership.mode.ts index 112cf44..78c1e57 100644 --- a/src/concepts/ownership/ownership.mode.ts +++ b/src/concepts/ownership/ownership.mode.ts @@ -10,6 +10,7 @@ import { AppendActionListToDialogPayload, axiumAppendActionListToDialogType } fr import { selectState } from '../../model/selector'; import { OwnershipState, ownershipName } from './ownership.concept'; import { AxiumState } from '../axium/axium.concept'; +import { failureConditions, strategyData_appendFailure } from '../../model/actionStrategyData'; export const ownershipMode: Mode = ( [_action, _concepts, action$, concepts$] : [Action, Concept[], Subject, UnifiedSubject] @@ -33,9 +34,13 @@ export const ownershipMode: Mode = ( const shouldBlock = ownershipShouldBlock(concepts, action); if (shouldBlock) { if (action.strategy) { + const strategy = action.strategy; if (action.strategy.currentNode.failureNode === null) { // This assumes that the Strategy does not account for the Block - let nextAction = strategyFailed(action.strategy); + let nextAction = strategyFailed( + strategy, + strategyData_appendFailure(strategy, failureConditions.ownershipBlocked) + ); // Logical Determination: axiumConcludeType if (nextAction.semaphore[3] === 3) { concepts = clearStubs(concepts, nextAction.strategy as ActionStrategy); @@ -50,7 +55,10 @@ export const ownershipMode: Mode = ( // This assumes that the Strategy is accounting for the Block // console.log('Check Action Failed1', action); [concepts, action] = checkIn(concepts, action); - const nextAction = strategyFailed(action.strategy as ActionStrategy); + const nextAction = strategyFailed( + strategy, + strategyData_appendFailure(strategy, failureConditions.ownershipBlocked) + ); concepts = updateAddToPendingActions(concepts, nextAction); concepts$.next(concepts); } diff --git a/src/concepts/ownership/ownership.principle.ts b/src/concepts/ownership/ownership.principle.ts index b368035..4a48006 100644 --- a/src/concepts/ownership/ownership.principle.ts +++ b/src/concepts/ownership/ownership.principle.ts @@ -11,6 +11,15 @@ import { UnifiedSubject } from '../../model/stagePlanner'; import { BadActionPayload, axiumBadActionType } from '../axium/qualities/badAction.quality'; import { axiumRegisterStagePlanner } from '../axium/qualities/registerStagePlanner.quality'; import { axiumSelectOpen } from '../axium/axium.selector'; +import { failureConditions, strategyData_appendFailure } from '../../model/actionStrategyData'; + +function denoteExpiredPending(action: Action): Action { + if (action.strategy) { + const strategy = action.strategy; + action.strategy.data = strategyData_appendFailure(strategy, failureConditions.ownershipExpired); + } + return action; +} export const ownershipPrinciple: PrincipleFunction = ( observer: Subscriber, @@ -63,7 +72,7 @@ export const ownershipPrinciple: PrincipleFunction = ( const newPending: Action[] = []; for (const pending of ownershipState.pendingActions) { if (pending.expiration < Date.now()) { - badActions.push(pending); + badActions.push(denoteExpiredPending(pending)); } else { newPending.push(pending); } diff --git a/src/index.ts b/src/index.ts index 74fcef9..d90060d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,14 @@ export { puntStrategy, createActionNode } from './model/actionStrategy'; +export { + failureConditions, + strategyData_appendFailure, + strategyData_selectFailureCondition, + strategyData_clearFailureCondition, + strategyData_select, + strategyData_unifyData +} from './model/actionStrategyData'; export type { Action, ActionType } from './model/action'; export { primeAction, createAction, getSemaphore, prepareActionCreator, prepareActionWithPayloadCreator } from './model/action'; export { createConcept, createQuality, defaultReducer, defaultMethodCreator } from './model/concept'; @@ -29,6 +37,7 @@ export type { KeyedSelector } from './model/selector'; export { selectState, selectConcept, selectSlice, selectPayload } from './model/selector'; export { PrincipleFunction } from './model/principle'; export type { dispatchOptions, Staging, UnifiedSubject, StagePlanner, NamedStagePlanner } from './model/stagePlanner'; +export { createActionController$ } from './model/actionController'; export { stageWaitForOpenThenIterate, stageWaitForOwnershipThenIterate } from './model/stagePlanner'; export type { OwnershipTicket, OwnershipTicketStub, OwnershipLedger } from './model/ownership'; diff --git a/src/model/action.ts b/src/model/action.ts index 04d723b..2534612 100644 --- a/src/model/action.ts +++ b/src/model/action.ts @@ -2,6 +2,8 @@ import { Concept } from './concept'; import { ActionStrategy } from './actionStrategy'; import { KeyedSelector } from './selector'; import { AxiumState } from '../concepts/axium/axium.concept'; +import { BadActionPayload, axiumBadAction } from '../concepts/axium/qualities/badAction.quality'; +import { failureConditions, strategyData_appendFailure } from './actionStrategyData'; export const nullActionType: ActionType = 'null'; // These need to be logical determined ahead of time. @@ -22,35 +24,46 @@ export type Action = { axium?: string; }; +const createPayload = (payload: T) => payload; + export function primeAction(concepts: Concept[], action: Action): Action { - for (const concept of concepts) { - const semaphore = getSemaphore(concepts, concept.name, action.type); - if (semaphore[2] !== -1) { - let axium; - if (action.axium) { - axium = action.axium; - } else { - axium = (concepts[0].state as AxiumState).name; - } - const newAction = { - ...action, - semaphore: semaphore, - axium, - }; - if (newAction.strategy) { - newAction.strategy.currentNode.action = newAction; + const expired = action.expiration < Date.now(); + if (!expired) { + for (const concept of concepts) { + const semaphore = getSemaphore(concepts, concept.name, action.type); + if (semaphore[2] !== -1 && action.expiration) { + let axium; + if (action.axium) { + axium = action.axium; + } else { + axium = (concepts[0].state as AxiumState).name; + } + const newAction = { + ...action, + semaphore: semaphore, + axium, + }; + if (newAction.strategy) { + newAction.strategy.currentNode.action = newAction; + } + return newAction; } - return newAction; } } const badAction: Action = { type: axiumBadActionType, - semaphore: getSemaphore(concepts, concepts[0].name, axiumBadActionType), + payload: createPayload([action]), expiration: Date.now() + 5000, + semaphore: getSemaphore(concepts, concepts[0].name, axiumBadActionType) }; if (action.strategy) { badAction.strategy = action.strategy; badAction.strategy.currentNode.action = badAction; + if (expired) { + badAction.strategy.data = strategyData_appendFailure(badAction.strategy, failureConditions.axiumExpired); + } else { + badAction.strategy.data = strategyData_appendFailure(badAction.strategy, failureConditions.axiumBadGeneration); + } } return badAction; } diff --git a/src/model/actionController.ts b/src/model/actionController.ts new file mode 100644 index 0000000..abbc38a --- /dev/null +++ b/src/model/actionController.ts @@ -0,0 +1,60 @@ +import { Action, axiumBadAction, strategyFailed } from '../index'; +import { Subject } from 'rxjs'; +import { failureConditions, strategyData_appendFailure } from './actionStrategyData'; + +export class ActionController extends Subject { + expiration: number; + expired: boolean; + timer: NodeJS.Timeout; + action: Action; + constructor(action: Action) { + super(); + this.expiration = action.expiration; + this.expired = false; + this.action = action; + this.timer = setTimeout(() => { + this.expired = true; + if (this.action.strategy) { + this.next(strategyFailed( + this.action.strategy, + strategyData_appendFailure(this.action.strategy, failureConditions.controllerExpired) + )); + } else { + this.next(axiumBadAction([this.action])); + } + }, this.expiration - Date.now()); + } + /** + * Next fires once and then completes. + * In case someone uses next over fire. + */ + next(action: Action) { + this.fire(action); + } + /** + * Fires once and then completes. + */ + fire(action: Action) { + if (!this.closed) { + if (!this.expired) { + clearTimeout(this.timer); + this.timer.unref(); + } + const { observers } = this; + const len = observers.length; + for (let i = 0; i < len; i++) { + observers[i].next(action); + } + this.complete(); + } + } +} + +export const createActionController$ = (act: Action, controlling: (controller: ActionController, action: Action) => void) => { + const ctrl = new ActionController(act); + // Needs to have timeout so that subscribers have time to attach in case the controller fires synchronously. + setTimeout(() => { + controlling(ctrl, act); + }, 0); + return ctrl; +}; diff --git a/src/model/actionStrategyData.ts b/src/model/actionStrategyData.ts new file mode 100644 index 0000000..4cf85a4 --- /dev/null +++ b/src/model/actionStrategyData.ts @@ -0,0 +1,71 @@ +// eslint-disable-next-line no-shadow +import { ActionStrategy } from './actionStrategy'; + +// eslint-disable-next-line no-shadow +export enum failureConditions { + ownershipExpired = 'ownershipExpired', + ownershipBlocked = 'ownershipBlocked', + controllerExpired = 'controllerExpired', + axiumExpired = 'axiumExpired', + axiumBadGeneration = 'axiumBadGeneration' +} + +export const strategyData_appendFailure = (strategy: ActionStrategy, condition: failureConditions | string): Record => { + if (strategy.data) { + return { + ...strategy.data, + failureCondition: condition + }; + } else { + return { + failureCondition: condition + }; + } +}; + +export const strategyData_selectFailureCondition = (strategy: ActionStrategy): failureConditions | string | undefined => { + if (strategy.data) { + if (Object.keys(strategy.data).includes('failureCondition')) { + const data = strategy.data as Record; + return data['failureCondition'] as failureConditions | string; + } + } + return undefined; +}; + +export const strategyData_clearFailureCondition = (strategy: ActionStrategy): Record | undefined => { + const condition = strategyData_selectFailureCondition(strategy); + const data = strategyData_select>(strategy); + if (condition && data) { + const newData: Record = {}; + for (const entry of Object.keys(data)) { + if (entry !== 'failureCondition') { + newData[entry] = data[entry]; + } + } + return newData; + } else if (data && Object.keys(data).length > 0) { + return data; + } else { + return undefined; + } +}; + +export const strategyData_select = (strategy: ActionStrategy): T | undefined => { + if (strategy.data) { + return strategy.data as T; + } else { + return undefined; + } +}; + +export const strategyData_unifyData = (strategy: ActionStrategy, data: Record): Record => { + if (strategy.data) { + return { + ...strategy.data, + ...data + }; + } else { + return {...data}; + } +}; \ No newline at end of file diff --git a/src/test/actionController.test.ts b/src/test/actionController.test.ts new file mode 100644 index 0000000..dd56552 --- /dev/null +++ b/src/test/actionController.test.ts @@ -0,0 +1,33 @@ +import { ActionController, createActionController$ } from '../model/actionController'; +import { axiumBadActionType } from '../concepts/axium/qualities/badAction.quality'; +import { axiumLog, axiumLogType } from '../concepts/axium/qualities/log.quality'; + +test('ActionController Expired Test', (done) => { + const act = axiumLog(undefined, 200); + const cont = new ActionController(act); + cont.subscribe(action => { + expect(action.type).toBe(axiumBadActionType); + done(); + }); +}); + +test('ActionController Next Test', (done) => { + const act = axiumLog(undefined, 200); + const cont = new ActionController(act); + cont.subscribe(action => { + expect(action.type).toBe(axiumLogType); + done(); + }); + cont.fire(act); +}); + +test('ActionController createActionController$ Test', (done) => { + const act = axiumLog(undefined, 200); + const cont = createActionController$(act, (controller, action) => { + controller.fire(action); + }); + cont.subscribe(action => { + expect(action.type).toBe(axiumLogType); + done(); + }); +}); \ No newline at end of file