Created
August 24, 2024 02:09
-
-
Save dovranJorayev/74ce5005d11764248009f3b9ccbdfd74 to your computer and use it in GitHub Desktop.
Effector operator to create Effector Effect from @farfetched/core remote operations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// shared/lib/farfetched/to-effect.ts | |
import { Mutation, Query } from '@farfetched/core'; | |
import { attach, createStore, sample } from 'effector'; | |
import { nanoid } from 'nanoid'; | |
import { DeferredPromise, makeDeferredPromise } from '../../promise'; | |
const paramsKeySymbol = Symbol.for('operationEffectKey'); | |
const setParamsId = (params: Record<string, unknown>) => { | |
const id = nanoid(); | |
Object.defineProperty(params, paramsKeySymbol, { | |
value: id, | |
enumerable: false, | |
configurable: false, | |
writable: false | |
}); | |
return id; | |
}; | |
const getParamsId = (params: Record<string, unknown>): string | null => { | |
return ( | |
Object.getOwnPropertyDescriptor(params, paramsKeySymbol)?.value ?? null | |
); | |
}; | |
/** @private */ | |
export function createEffectFromOperation< | |
Params extends Record<string, unknown>, | |
D, | |
E | |
>(operation: Query<Params, D, E> | Mutation<Params, D, E>) { | |
const $deferrsMap = createStore({ | |
ref: new Map<string, DeferredPromise<D>>() | |
}); | |
const effect = attach({ | |
source: $deferrsMap, | |
effect: async (deffersMap, params: Params) => { | |
if (!(params !== null && typeof params === 'object')) { | |
throw new Error( | |
'Params should be an Record<string, unknown> constrains' | |
); | |
} | |
let id = getParamsId(params); | |
let promise = makeDeferredPromise<D>(); | |
if (!id) { | |
id = setParamsId(params); | |
const deffer = makeDeferredPromise<D>(); | |
deffersMap.ref.set(id, deffer); | |
promise = deffer; | |
} else { | |
const deffer = deffersMap.ref.get(id); | |
if (deffer) { | |
promise = deffer; | |
} else { | |
const deffer = makeDeferredPromise<D>(); | |
deffersMap.ref.set(id, deffer); | |
promise = deffer; | |
} | |
} | |
operation.start(params); | |
return await promise; | |
} | |
}); | |
sample({ | |
clock: operation.finished.success, | |
target: attach({ | |
source: $deferrsMap, | |
effect: async ( | |
deferrsMap, | |
successData: { params: Params; result: D } | |
) => { | |
const id = getParamsId(successData.params); | |
if (id) { | |
const defer = deferrsMap.ref.get(id); | |
if (defer) { | |
defer.resolve(successData.result); | |
deferrsMap.ref.delete(id); | |
} | |
} | |
} | |
}) | |
}); | |
sample({ | |
clock: operation.finished.failure, | |
target: attach({ | |
source: $deferrsMap, | |
effect: async (defersMap, failData: { params: Params; error: E }) => { | |
const id = getParamsId(failData.params); | |
if (id) { | |
const deffer = defersMap.ref.get(id); | |
if (deffer) { | |
deffer.reject(failData.error); | |
defersMap.ref.delete(id); | |
} | |
} | |
} | |
}) | |
}); | |
sample({ | |
clock: operation.finished.skip, | |
target: attach({ | |
source: $deferrsMap, | |
effect: async (defersMap, failData: { params: Params }) => { | |
const id = getParamsId(failData.params); | |
if (id) { | |
const deffer = defersMap.ref.get(id); | |
if (deffer) { | |
deffer.reject(new OperationSkipError('Operation was skipped')); | |
defersMap.ref.delete(id); | |
} | |
} | |
} | |
}) | |
}); | |
sample({ | |
clock: operation.aborted, | |
target: attach({ | |
source: $deferrsMap, | |
effect: async (defersMap, failData: { params: Params }) => { | |
const id = getParamsId(failData.params); | |
if (id) { | |
const deffer = defersMap.ref.get(id); | |
if (deffer) { | |
deffer.reject(new OperationAbortError('Operation was aborted')); | |
defersMap.ref.delete(id); | |
} | |
} | |
} | |
}) | |
}); | |
return [ | |
effect, | |
{ | |
__: { | |
$deferrsMap | |
} | |
} | |
] as const; | |
} | |
export class OperationSkipError extends Error {} | |
export class OperationAbortError extends Error {} | |
/** | |
* Convert farfetched Query or Mutation instance to effector effect | |
* @template Params - operation params with Record<string, unknown> constrains | |
* @template Data - operation success data | |
* @param operation farfethed Query or Mutation instance | |
* @returns effect with params of operation.start event | |
* | |
* @throws {OperationAbortError} if operation was aborted | |
* @throws {OperationSkipError} if operation was skipped | |
* @throws {Error} if params is not an object | |
*/ | |
export function toEffect<Params extends Record<string, unknown>, Data, Err>( | |
operation: Query<Params, Data, Err> | Mutation<Params, Data, Err> | |
) { | |
const [effect] = createEffectFromOperation(operation); | |
return effect; | |
} | |
toEffect.OperationAbortError = OperationAbortError; | |
toEffect.OperationSkipError = OperationSkipError; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment