chore(): Improve workflows sdk tooling (#13421)
RESOLVES CORE-1171 **What** - Reduced async overhead for objects with mixed sync/async properties - Lower memory pressure from eliminated promise allocations - Faster primitive value processing with early returns - Better concurrency through selective batching of truly async operations - Event loop friendly behavior preventing artificial delays - Reduced memory allocation from eliminated duplicate processing - Decreased GC pressure from WeakMap-based caching instead of map **note** Now, `resolveValue`always treat every resoltion as sync operation unless it is not, meaning we do not create promise overhead when not necessary and only when actually treating with promises
This commit is contained in:
1 parent
8e11998895
commit
8e5c22a8e8
10 files changed
+304
-110
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/orchestration": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
"@medusajs/workflows-sdk": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
chore(): Improve workflows sdk tooling
|
||||||
@@ -91,7 +91,7 @@ class DistributedTransaction extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
#temporaryStorage = new Map<string, unknown>()
|
#temporaryStorage = new WeakMap<{ key: string }, unknown>()
|
||||||
|
|
||||||
public static setStorage(storage: IDistributedTransactionStorage) {
|
public static setStorage(storage: IDistributedTransactionStorage) {
|
||||||
this.keyValueStore = storage
|
this.keyValueStore = storage
|
||||||
@@ -312,15 +312,15 @@ class DistributedTransaction extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setTemporaryData(key: string, value: unknown) {
|
public setTemporaryData(key: string, value: unknown) {
|
||||||
this.#temporaryStorage.set(key, value)
|
this.#temporaryStorage.set({ key }, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTemporaryData(key: string) {
|
public getTemporaryData(key: string) {
|
||||||
return this.#temporaryStorage.get(key)
|
return this.#temporaryStorage.get({ key })
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasTemporaryData(key: string) {
|
public hasTemporaryData(key: string) {
|
||||||
return this.#temporaryStorage.has(key)
|
return this.#temporaryStorage.has({ key })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ export function deepCopy<
|
|||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (util.types.isProxy(obj)) {
|
||||||
|
return obj as unknown as TOutput
|
||||||
|
}
|
||||||
|
|
||||||
// Handle objects
|
// Handle objects
|
||||||
if (isObject(obj)) {
|
if (isObject(obj)) {
|
||||||
if (util.types.isProxy(obj)) {
|
|
||||||
return obj as unknown as TOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
copy = {} as TOutput
|
copy = {} as TOutput
|
||||||
cache.set(obj, copy) // Add to cache before recursing
|
cache.set(obj, copy) // Add to cache before recursing
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { isDefined } from "./is-defined"
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function parseStringifyIfNecessary(result: unknown) {
|
export function parseStringifyIfNecessary(result: unknown) {
|
||||||
if (typeof result !== "object") {
|
if (typeof result == null || typeof result !== "object") {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,18 @@ import {
|
|||||||
WorkflowResult,
|
WorkflowResult,
|
||||||
} from "./type"
|
} from "./type"
|
||||||
|
|
||||||
|
// Cache for loaded modules to avoid repeated traversal
|
||||||
|
let cachedLoadedModules: LoadedModule[] | null = null
|
||||||
|
|
||||||
|
function getCachedLoadedModules(): LoadedModule[] {
|
||||||
|
if (!cachedLoadedModules) {
|
||||||
|
cachedLoadedModules = MedusaModule.getLoadedModules().map(
|
||||||
|
(mod) => Object.values(mod)[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return cachedLoadedModules
|
||||||
|
}
|
||||||
|
|
||||||
function createContextualWorkflowRunner<
|
function createContextualWorkflowRunner<
|
||||||
TData = unknown,
|
TData = unknown,
|
||||||
TResult = unknown,
|
TResult = unknown,
|
||||||
@@ -91,9 +103,7 @@ function createContextualWorkflowRunner<
|
|||||||
const container_ = flow.container as MedusaContainer
|
const container_ = flow.container as MedusaContainer
|
||||||
|
|
||||||
if (!container_ || !isPresent(container_?.registrations)) {
|
if (!container_ || !isPresent(container_?.registrations)) {
|
||||||
executionContainer = MedusaModule.getLoadedModules().map(
|
executionContainer = getCachedLoadedModules()
|
||||||
(mod) => Object.values(mod)[0]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,8 +213,10 @@ function createContextualWorkflowRunner<
|
|||||||
__type: MedusaContextType as Context["__type"],
|
__type: MedusaContextType as Context["__type"],
|
||||||
}
|
}
|
||||||
|
|
||||||
context.transactionId ??= "auto-" + ulid()
|
const uniqId = ulid()
|
||||||
context.eventGroupId ??= ulid()
|
|
||||||
|
context.transactionId ??= "auto-" + uniqId
|
||||||
|
context.eventGroupId ??= uniqId
|
||||||
|
|
||||||
return await originalExecution(
|
return await originalExecution(
|
||||||
originalRun,
|
originalRun,
|
||||||
@@ -481,14 +493,11 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
> => {
|
> => {
|
||||||
const container = args?.container
|
const container = args?.container
|
||||||
delete args?.container
|
delete args?.container
|
||||||
const inputArgs = { ...args } as FlowRunOptions<
|
|
||||||
TDataOverride extends undefined ? TData : TDataOverride
|
|
||||||
>
|
|
||||||
|
|
||||||
return await buildRunnerFn<"run", TDataOverride, TResultOverride>(
|
return await buildRunnerFn<"run", TDataOverride, TResultOverride>(
|
||||||
"run",
|
"run",
|
||||||
container
|
container
|
||||||
)(inputArgs)
|
)(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
exportedWorkflow.registerStepSuccess = async <
|
exportedWorkflow.registerStepSuccess = async <
|
||||||
@@ -505,9 +514,6 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
> => {
|
> => {
|
||||||
const container = args?.container
|
const container = args?.container
|
||||||
delete args?.container
|
delete args?.container
|
||||||
const inputArgs = { ...args } as FlowRegisterStepSuccessOptions<
|
|
||||||
TDataOverride extends undefined ? TData : TDataOverride
|
|
||||||
>
|
|
||||||
|
|
||||||
return await buildRunnerFn<
|
return await buildRunnerFn<
|
||||||
"registerStepSuccess",
|
"registerStepSuccess",
|
||||||
@@ -516,7 +522,7 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
>(
|
>(
|
||||||
"registerStepSuccess",
|
"registerStepSuccess",
|
||||||
container
|
container
|
||||||
)(inputArgs)
|
)(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
exportedWorkflow.registerStepFailure = async <
|
exportedWorkflow.registerStepFailure = async <
|
||||||
@@ -533,9 +539,6 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
> => {
|
> => {
|
||||||
const container = args?.container
|
const container = args?.container
|
||||||
delete args?.container
|
delete args?.container
|
||||||
const inputArgs = { ...args } as FlowRegisterStepFailureOptions<
|
|
||||||
TDataOverride extends undefined ? TData : TDataOverride
|
|
||||||
>
|
|
||||||
|
|
||||||
return await buildRunnerFn<
|
return await buildRunnerFn<
|
||||||
"registerStepFailure",
|
"registerStepFailure",
|
||||||
@@ -544,7 +547,7 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
>(
|
>(
|
||||||
"registerStepFailure",
|
"registerStepFailure",
|
||||||
container
|
container
|
||||||
)(inputArgs)
|
)(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
exportedWorkflow.retryStep = async <
|
exportedWorkflow.retryStep = async <
|
||||||
@@ -568,12 +571,11 @@ export const exportWorkflow = <TData = unknown, TResult = unknown>(
|
|||||||
): Promise<WorkflowResult> => {
|
): Promise<WorkflowResult> => {
|
||||||
const container = args?.container
|
const container = args?.container
|
||||||
delete args?.container
|
delete args?.container
|
||||||
const inputArgs = { ...args } as FlowCancelOptions
|
|
||||||
|
|
||||||
return await buildRunnerFn<"cancel", unknown, unknown>(
|
return await buildRunnerFn<"cancel", unknown, unknown>(
|
||||||
"cancel",
|
"cancel",
|
||||||
container
|
container
|
||||||
)(inputArgs)
|
)(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
MedusaWorkflow.registerWorkflow(workflowId, exportedWorkflow)
|
MedusaWorkflow.registerWorkflow(workflowId, exportedWorkflow)
|
||||||
|
|||||||
@@ -101,6 +101,38 @@ export interface ApplyStepOptions<
|
|||||||
* @param invokeFn
|
* @param invokeFn
|
||||||
* @param compensateFn
|
* @param compensateFn
|
||||||
*/
|
*/
|
||||||
|
// Factory function to create and configure handlers
|
||||||
|
function createAndConfigureHandler<
|
||||||
|
TInvokeInput,
|
||||||
|
TStepInput extends {
|
||||||
|
[K in keyof TInvokeInput]: WorkflowData<TInvokeInput[K]>
|
||||||
|
},
|
||||||
|
TInvokeResultOutput,
|
||||||
|
TInvokeResultCompensateInput
|
||||||
|
>(
|
||||||
|
context: CreateWorkflowComposerContext,
|
||||||
|
stepName: string,
|
||||||
|
config: TransactionStepsDefinition,
|
||||||
|
input: TStepInput | undefined,
|
||||||
|
invokeFn: InvokeFn<
|
||||||
|
TInvokeInput,
|
||||||
|
TInvokeResultOutput,
|
||||||
|
TInvokeResultCompensateInput
|
||||||
|
>,
|
||||||
|
compensateFn?: CompensateFn<TInvokeResultCompensateInput>
|
||||||
|
) {
|
||||||
|
const handler = createStepHandler.bind(context)({
|
||||||
|
stepName,
|
||||||
|
input,
|
||||||
|
invokeFn,
|
||||||
|
compensateFn,
|
||||||
|
})
|
||||||
|
|
||||||
|
wrapAsyncHandler(config, handler)
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
export function applyStep<
|
export function applyStep<
|
||||||
TInvokeInput,
|
TInvokeInput,
|
||||||
TStepInput extends {
|
TStepInput extends {
|
||||||
@@ -127,14 +159,14 @@ export function applyStep<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = createStepHandler.bind(this)({
|
const handler = createAndConfigureHandler(
|
||||||
|
this,
|
||||||
stepName,
|
stepName,
|
||||||
|
stepConfig,
|
||||||
input,
|
input,
|
||||||
invokeFn,
|
invokeFn,
|
||||||
compensateFn,
|
compensateFn
|
||||||
})
|
)
|
||||||
|
|
||||||
wrapAsyncHandler(stepConfig, handler)
|
|
||||||
|
|
||||||
stepConfig.uuid = ulid()
|
stepConfig.uuid = ulid()
|
||||||
stepConfig.noCompensation = !compensateFn
|
stepConfig.noCompensation = !compensateFn
|
||||||
@@ -179,14 +211,14 @@ export function applyStep<
|
|||||||
|
|
||||||
delete newConfig.name
|
delete newConfig.name
|
||||||
|
|
||||||
const handler = createStepHandler.bind(this)({
|
const handler = createAndConfigureHandler(
|
||||||
stepName: newStepName,
|
this,
|
||||||
|
newStepName,
|
||||||
|
newConfig,
|
||||||
input,
|
input,
|
||||||
invokeFn,
|
invokeFn,
|
||||||
compensateFn,
|
compensateFn
|
||||||
})
|
)
|
||||||
|
|
||||||
wrapAsyncHandler(newConfig, handler)
|
|
||||||
|
|
||||||
this.handlers.set(stepName, this.overriddenHandler.get(stepName)!)
|
this.handlers.set(stepName, this.overriddenHandler.get(stepName)!)
|
||||||
this.overriddenHandler.delete(stepName)
|
this.overriddenHandler.delete(stepName)
|
||||||
@@ -319,7 +351,10 @@ export function wrapConditionalStep(
|
|||||||
) {
|
) {
|
||||||
const originalInvoke = handle.invoke
|
const originalInvoke = handle.invoke
|
||||||
handle.invoke = async (stepArguments: WorkflowStepHandlerArguments) => {
|
handle.invoke = async (stepArguments: WorkflowStepHandlerArguments) => {
|
||||||
const args = await resolveValue(input, stepArguments)
|
let args = resolveValue(input, stepArguments)
|
||||||
|
if (args instanceof Promise) {
|
||||||
|
args = await args
|
||||||
|
}
|
||||||
|
|
||||||
const canContinue = await condition(args, stepArguments)
|
const canContinue = await condition(args, stepArguments)
|
||||||
|
|
||||||
|
|||||||
@@ -80,14 +80,28 @@ export function createStepHandler<
|
|||||||
stepArguments,
|
stepArguments,
|
||||||
})
|
})
|
||||||
|
|
||||||
const argInput = input ? await resolveValue(input, stepArguments) : {}
|
let argInput = {}
|
||||||
|
if (input) {
|
||||||
|
argInput = resolveValue(input, stepArguments)
|
||||||
|
if (argInput instanceof Promise) {
|
||||||
|
argInput = await argInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const stepResponse: StepResponse<any, any> = await invokeFn.apply(this, [
|
const stepResponse: StepResponse<any, any> = await invokeFn.apply(this, [
|
||||||
argInput,
|
argInput,
|
||||||
executionContext,
|
executionContext,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if (!stepResponse || typeof stepResponse !== "object") {
|
||||||
|
return {
|
||||||
|
__type: OrchestrationUtils.SymbolWorkflowWorkflowData,
|
||||||
|
output: stepResponse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const stepResponseJSON =
|
const stepResponseJSON =
|
||||||
stepResponse?.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
stepResponse.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
||||||
? stepResponse.toJSON()
|
? stepResponse.toJSON()
|
||||||
: stepResponse
|
: stepResponse
|
||||||
|
|
||||||
@@ -104,13 +118,24 @@ export function createStepHandler<
|
|||||||
})
|
})
|
||||||
|
|
||||||
const stepOutput = (stepArguments.invoke[stepName] as any)?.output
|
const stepOutput = (stepArguments.invoke[stepName] as any)?.output
|
||||||
|
|
||||||
|
if (!stepOutput) {
|
||||||
|
const output = await compensateFn.apply(this, [
|
||||||
|
stepOutput,
|
||||||
|
executionContext,
|
||||||
|
])
|
||||||
|
return { output }
|
||||||
|
}
|
||||||
|
|
||||||
const invokeResult =
|
const invokeResult =
|
||||||
stepOutput?.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
stepOutput.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
||||||
? stepOutput.compensateInput
|
? stepOutput.compensateInput
|
||||||
: stepOutput
|
: stepOutput
|
||||||
|
|
||||||
const args = [invokeResult, executionContext]
|
const output = await compensateFn.apply(this, [
|
||||||
const output = await compensateFn.apply(this, args)
|
invokeResult,
|
||||||
|
executionContext,
|
||||||
|
])
|
||||||
return {
|
return {
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { transform } from "../transform"
|
import { transform } from "../transform"
|
||||||
import { WorkflowData, WorkflowTransactionContext } from "../type"
|
import { WorkflowData } from "../type"
|
||||||
import { OrchestrationUtils } from "@medusajs/utils"
|
|
||||||
import { resolveValue } from "./resolve-value"
|
|
||||||
|
|
||||||
export function proxify<T>(obj: WorkflowData<any>): T {
|
export function proxify<T>(obj: WorkflowData<any>): T {
|
||||||
return new Proxy(obj, {
|
return new Proxy(obj, {
|
||||||
@@ -10,18 +8,11 @@ export function proxify<T>(obj: WorkflowData<any>): T {
|
|||||||
return target[prop]
|
return target[prop]
|
||||||
}
|
}
|
||||||
|
|
||||||
return transform({}, async function (_, context) {
|
const transformer = transform({ target }, function (data) {
|
||||||
const { invoke } = context as WorkflowTransactionContext
|
return data.target?.[prop]
|
||||||
let output =
|
|
||||||
target.__type === OrchestrationUtils.SymbolInputReference ||
|
|
||||||
target.__type === OrchestrationUtils.SymbolWorkflowStepTransformer
|
|
||||||
? target
|
|
||||||
: invoke?.[obj.__step__]?.output
|
|
||||||
|
|
||||||
output = await resolveValue(output, context)
|
|
||||||
|
|
||||||
return output?.[prop]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return transformer
|
||||||
},
|
},
|
||||||
}) as unknown as T
|
}) as unknown as T
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,31 @@
|
|||||||
import {
|
import {
|
||||||
deepCopy,
|
deepCopy,
|
||||||
|
isObject,
|
||||||
OrchestrationUtils,
|
OrchestrationUtils,
|
||||||
parseStringifyIfNecessary,
|
parseStringifyIfNecessary,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
import * as util from "node:util"
|
||||||
|
|
||||||
async function resolveProperty(property, transactionContext) {
|
type InputPrimitive = string | Symbol
|
||||||
|
type InputObject = object & { __type?: string | Symbol; output?: any }
|
||||||
|
|
||||||
|
function resolveProperty(property, transactionContext) {
|
||||||
const { invoke: invokeRes } = transactionContext
|
const { invoke: invokeRes } = transactionContext
|
||||||
|
|
||||||
let res
|
let res
|
||||||
|
|
||||||
if (property?.__type === OrchestrationUtils.SymbolInputReference) {
|
if (property.__type === OrchestrationUtils.SymbolInputReference) {
|
||||||
res = transactionContext.payload
|
res = transactionContext.payload
|
||||||
} else if (
|
} else if (
|
||||||
property?.__type === OrchestrationUtils.SymbolMedusaWorkflowResponse
|
property.__type === OrchestrationUtils.SymbolMedusaWorkflowResponse
|
||||||
) {
|
) {
|
||||||
res = await resolveValue(property.$result, transactionContext)
|
res = resolveValue(property.$result, transactionContext)
|
||||||
} else if (
|
} else if (
|
||||||
property?.__type === OrchestrationUtils.SymbolWorkflowStepTransformer
|
property.__type === OrchestrationUtils.SymbolWorkflowStepTransformer
|
||||||
) {
|
) {
|
||||||
res = await property.__resolver(transactionContext)
|
res = property.__resolver(transactionContext)
|
||||||
} else if (property?.__type === OrchestrationUtils.SymbolWorkflowStep) {
|
} else if (property.__type === OrchestrationUtils.SymbolWorkflowStep) {
|
||||||
const output =
|
const output =
|
||||||
invokeRes[property.__step__]?.output ?? invokeRes[property.__step__]
|
invokeRes[property.__step__]?.output ?? invokeRes[property.__step__]
|
||||||
if (output?.__type === OrchestrationUtils.SymbolWorkflowStepResponse) {
|
if (output?.__type === OrchestrationUtils.SymbolWorkflowStepResponse) {
|
||||||
@@ -29,7 +34,7 @@ async function resolveProperty(property, transactionContext) {
|
|||||||
res = output
|
res = output
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
property?.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
property.__type === OrchestrationUtils.SymbolWorkflowStepResponse
|
||||||
) {
|
) {
|
||||||
res = property.output
|
res = property.output
|
||||||
} else {
|
} else {
|
||||||
@@ -39,49 +44,176 @@ async function resolveProperty(property, transactionContext) {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function unwrapInput({
|
||||||
* @internal
|
inputTOUnwrap,
|
||||||
*/
|
parentRef,
|
||||||
export async function resolveValue(input, transactionContext) {
|
transactionContext,
|
||||||
const unwrapInput = async (
|
}: {
|
||||||
inputTOUnwrap: Record<string, unknown>,
|
inputTOUnwrap: InputObject
|
||||||
parentRef: any
|
parentRef: any
|
||||||
) => {
|
transactionContext: any
|
||||||
if (inputTOUnwrap == null) {
|
}): any {
|
||||||
return inputTOUnwrap
|
if (inputTOUnwrap == null) {
|
||||||
}
|
return inputTOUnwrap
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(inputTOUnwrap)) {
|
if (Array.isArray(inputTOUnwrap)) {
|
||||||
return await promiseAll(
|
const promises: { promise: Promise<any>; index: number }[] = []
|
||||||
inputTOUnwrap.map((i) => resolveValue(i, transactionContext))
|
const resolvedItems: any[] = new Array(inputTOUnwrap.length)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof inputTOUnwrap !== "object") {
|
for (let i = 0; i < inputTOUnwrap.length; i++) {
|
||||||
return inputTOUnwrap
|
const item = inputTOUnwrap[i]
|
||||||
}
|
if (item == null || typeof item !== "object") {
|
||||||
|
resolvedItems[i] = item
|
||||||
for (const key of Object.keys(inputTOUnwrap)) {
|
} else {
|
||||||
parentRef[key] = deepCopy(
|
const resolved = resolveValue(item, transactionContext)
|
||||||
await resolveProperty(inputTOUnwrap[key], transactionContext)
|
if (resolved instanceof Promise) {
|
||||||
)
|
promises.push({ promise: resolved, index: i })
|
||||||
|
} else {
|
||||||
if (typeof parentRef[key] === "object") {
|
resolvedItems[i] = resolved
|
||||||
parentRef[key] = await unwrapInput(parentRef[key], parentRef[key])
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parentRef
|
if (promises.length > 0) {
|
||||||
|
return promiseAll(promises.map((p) => p.promise)).then(
|
||||||
|
(resolvedPromises) => {
|
||||||
|
for (let i = 0; i < promises.length; i++) {
|
||||||
|
resolvedItems[promises[i].index] = resolvedPromises[i]
|
||||||
|
}
|
||||||
|
return resolvedItems
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
const copiedInput =
|
if (util.types.isProxy(inputTOUnwrap)) {
|
||||||
input?.__type === OrchestrationUtils.SymbolWorkflowWorkflowData
|
const resolved = resolveProperty(inputTOUnwrap, transactionContext)
|
||||||
? input.output
|
if (resolved instanceof Promise) {
|
||||||
: input
|
return resolved.then((r) => {
|
||||||
|
inputTOUnwrap = r
|
||||||
|
if (!isObject(inputTOUnwrap)) {
|
||||||
|
return inputTOUnwrap
|
||||||
|
}
|
||||||
|
return unwrapInput({
|
||||||
|
inputTOUnwrap,
|
||||||
|
parentRef: {},
|
||||||
|
transactionContext,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
inputTOUnwrap = resolved
|
||||||
|
}
|
||||||
|
|
||||||
const result = copiedInput?.__type
|
if (!isObject(inputTOUnwrap)) {
|
||||||
? await resolveProperty(copiedInput, transactionContext)
|
return inputTOUnwrap
|
||||||
: await unwrapInput(copiedInput, {})
|
}
|
||||||
|
|
||||||
return parseStringifyIfNecessary(result)
|
const keys = Object.keys(inputTOUnwrap)
|
||||||
|
const promises: { promise: Promise<any>; keyIndex: number }[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i]
|
||||||
|
|
||||||
|
if (inputTOUnwrap[key] == null || typeof inputTOUnwrap[key] !== "object") {
|
||||||
|
parentRef[key] = inputTOUnwrap[key]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = resolveProperty(inputTOUnwrap[key], transactionContext)
|
||||||
|
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
promises.push({ promise: result, keyIndex: i })
|
||||||
|
} else {
|
||||||
|
parentRef[key] = result
|
||||||
|
|
||||||
|
if (result != null && typeof result === "object") {
|
||||||
|
const unwrapped = unwrapInput({
|
||||||
|
inputTOUnwrap: result,
|
||||||
|
parentRef: parentRef[key] || {},
|
||||||
|
transactionContext,
|
||||||
|
})
|
||||||
|
if (unwrapped instanceof Promise) {
|
||||||
|
promises.push({
|
||||||
|
promise: unwrapped.then((r) => ({ key, value: r })),
|
||||||
|
keyIndex: i,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
parentRef[key] = unwrapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (promises.length > 0) {
|
||||||
|
return promiseAll(promises.map((p) => p.promise)).then(
|
||||||
|
(resolvedPromises) => {
|
||||||
|
for (let i = 0; i < promises.length; i++) {
|
||||||
|
const resolved = resolvedPromises[i]
|
||||||
|
if (resolved && typeof resolved === "object" && "key" in resolved) {
|
||||||
|
parentRef[resolved.key] = resolved.value
|
||||||
|
} else {
|
||||||
|
const key = keys[promises[i].keyIndex]
|
||||||
|
parentRef[key] = resolved
|
||||||
|
|
||||||
|
if (resolved != null && typeof resolved === "object") {
|
||||||
|
const unwrapped = unwrapInput({
|
||||||
|
inputTOUnwrap: resolved,
|
||||||
|
parentRef: parentRef[key] || {},
|
||||||
|
transactionContext,
|
||||||
|
})
|
||||||
|
if (unwrapped instanceof Promise) {
|
||||||
|
return unwrapped.then((r) => {
|
||||||
|
parentRef[key] = r
|
||||||
|
return parentRef
|
||||||
|
})
|
||||||
|
}
|
||||||
|
parentRef[key] = unwrapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parentRef
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentRef
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveValue(
|
||||||
|
input: InputPrimitive | InputObject | unknown | undefined,
|
||||||
|
transactionContext
|
||||||
|
): Promise<any> | any {
|
||||||
|
if (input == null || typeof input !== "object") {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
const input_ = deepCopy(
|
||||||
|
(input as InputObject)?.__type ===
|
||||||
|
OrchestrationUtils.SymbolWorkflowWorkflowData
|
||||||
|
? (input as InputObject).output
|
||||||
|
: input
|
||||||
|
)
|
||||||
|
|
||||||
|
let result: any
|
||||||
|
|
||||||
|
if (input_?.__type) {
|
||||||
|
result = resolveProperty(input_, transactionContext)
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return result.then((r) => parseStringifyIfNecessary(r))
|
||||||
|
}
|
||||||
|
return parseStringifyIfNecessary(result)
|
||||||
|
} else {
|
||||||
|
result = unwrapInput({
|
||||||
|
inputTOUnwrap: input_,
|
||||||
|
parentRef: {},
|
||||||
|
transactionContext,
|
||||||
|
})
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return result.then((r) => parseStringifyIfNecessary(r))
|
||||||
|
}
|
||||||
|
return parseStringifyIfNecessary(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { resolveValue } from "./helpers"
|
|
||||||
import { StepExecutionContext, WorkflowData } from "./type"
|
|
||||||
import { proxify } from "./helpers/proxy"
|
|
||||||
import { OrchestrationUtils } from "@medusajs/utils"
|
|
||||||
import { ulid } from "ulid"
|
|
||||||
import {
|
import {
|
||||||
TransactionContext,
|
TransactionContext,
|
||||||
WorkflowStepHandlerArguments,
|
WorkflowStepHandlerArguments,
|
||||||
} from "@medusajs/orchestration"
|
} from "@medusajs/orchestration"
|
||||||
|
import { OrchestrationUtils } from "@medusajs/utils"
|
||||||
|
import { resolveValue } from "./helpers"
|
||||||
|
import { proxify } from "./helpers/proxy"
|
||||||
|
import { StepExecutionContext, WorkflowData } from "./type"
|
||||||
|
|
||||||
type Func1<T extends object | WorkflowData, U> = (
|
type Func1<T extends object | WorkflowData, U> = (
|
||||||
input: T extends WorkflowData<infer U>
|
input: T extends WorkflowData<infer U>
|
||||||
@@ -163,7 +162,7 @@ export function transform(
|
|||||||
values: any | any[],
|
values: any | any[],
|
||||||
...functions: Function[]
|
...functions: Function[]
|
||||||
): unknown {
|
): unknown {
|
||||||
const uniqId = ulid()
|
const uniqId = Math.random().toString(36).substring(2, 20)
|
||||||
|
|
||||||
const ret = {
|
const ret = {
|
||||||
__id: uniqId,
|
__id: uniqId,
|
||||||
@@ -182,7 +181,10 @@ export function transform(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const stepValue = await resolveValue(values, transactionContext)
|
let stepValue = resolveValue(values, transactionContext)
|
||||||
|
if (stepValue instanceof Promise) {
|
||||||
|
stepValue = await stepValue
|
||||||
|
}
|
||||||
|
|
||||||
let finalResult
|
let finalResult
|
||||||
for (let i = 0; i < functions.length; i++) {
|
for (let i = 0; i < functions.length; i++) {
|
||||||
|
|||||||
Reference in new issue
Block a user