<commit_before>import { action as mobxAction, isObservable } from "mobx"
import {isModel} from "./factories"
import {resolve} from "../top-level-api"
import {invariant, isPlainObject, isPrimitive, argsToArray} from "../utils"
import {Node, getNode, getRelativePath} from "./node"

export type IActionCall = {
    name: string;
    path?: string;
    args?: any[];
}

export type IActionHandler  = (actionCall: IActionCall, next: () => void) => void

export function createActionInvoker(name: string, fn: Function) {
    const action = mobxAction(name, fn)

    return function() {
        const adm = getNode(this)
        if (adm.isRunningAction()) {
            // an action is already running in this tree, invoking this action does not emit a new action
            return action.apply(this, arguments)
        } else {
            // start the action!
            const root = adm.root
            const args = arguments
            root._isRunningAction = true
            try {
                return adm.emitAction(
                    adm,
                    {
                        name: name,
                        path: "",
                        args: argsToArray(args).map((arg, index) => serializeArgument(adm, name, index, arg))
                    },
                    () => action.apply(this, args)
                )
            } finally {
                root._isRunningAction = false
            }
        }
    }
}


function serializeArgument(adm: Node, actionName: string, index: number, arg: any): any {
    if (isPrimitive(arg))<commit_msg>Better stacktraces for actions (longer, but with named Fn)<commit_after>import { action as mobxAction, isObservable } from "mobx"
import {isModel} from "./factories"
import {resolve} from "../top-level-api"
import {invariant, isPlainObject, isPrimitive, argsToArray} from "../utils"
import {Node, getNode, getRelativePath} from "./node"

export type IActionCall = {
    name: string;
    path?: string;
    args?: any[];
}

export type IActionHandler  = (actionCall: IActionCall, next: () => void) => void

export function createActionInvoker(name: string, fn: Function) {
    const action = mobxAction(name, fn)

    const actionInvoker = function () {
        const adm = getNode(this)
        if (adm.isRunningAction()) {
            // an action is already running in this tree, invoking this action does not emit a new action
            return action.apply(this, arguments)
        } else {
            // start the action!
            const root = adm.root
            const args = arguments
            root._isRunningAction = true
            try {
                return adm.emitAction(
                    adm,
                    {
                        name: name,
                        path: "",
                        args: argsToArray(args).map((arg, index) => serializeArgument(adm, name, index, arg))
                    },
                    () => action.apply(this, args)
                )
            } finally {
                root._isRunningAction = false
            }
        }
    }

    // This construction helps producing a better function name in the stack trace, but could be optimized
    // away in prod builds, and `invoker` be returned directly
    return new Function("f", `return function ${name}() { return f.apply(this, arguments)}`)(actionInvoker)
}


function serializeArgument(adm: Node, actionName: string, index: number, arg: any): any {
    if (isPrimitive(arg))
{'fraction_non_alphanumeric': 0.10435779816513761, 'fraction_numerical': 0.0, 'mean_word_length': 1.9393428812131424, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5351844', 'n_tokens_mistral': 928, 'n_tokens_neox': 895, 'n_words': 292}