<commit_before>import { Update, Effect, NodeDescriptor, ComponentContext, Result, ViewContext } from './contract'

export interface Render {
    (parentNode: Element, view: NodeDescriptor, oldView: NodeDescriptor | undefined, oldRootNode: Node | undefined): void
}

export type DebugOptions = {
    components?: string[]
}

let debugEnabled = false
const debugOptions: DebugOptions = {}

// TODO: enable custom logger
export function debug(debug: boolean = true, options?: DebugOptions) {
    debugEnabled = debug
    if (typeof options !== 'undefined') {
        debugOptions.components = options.components
    }
}

export function dispatch<TState, TArg>(context: ComponentContext<TState, any>, render: Render, fn: (state: TState, arg: TArg) => Result<TState>, arg: TArg) {

    if (context.isUpdating) {
        throw `${context.spec.name}: Dispatch error - the dispatch function may not be called during an update. Doing so would most likely corrupt the state.`
    }

    context.dispatchLevel++

    context.isUpdating = true

    const result = fn(context.state!, arg)

    if (typeof result !== 'object') {
        throw 'Invalid result.'
    }

    context.isUpdating = false

    const apply = <T>(fn: Update<TState, T>, arg: T) => dispatch(context, render, fn, arg)

    context.state = result.state

    if (debugEnabled) {
        if (typeof debugOptions.components === 'undefined' ||
            debugOptions.components!.indexOf(context.spec.name) !== -1) {

            console.group(context.spec.name)
            console.debug('State: ', context.state)
            console.groupEnd()
        }
    }

    if (typeof result.effects !== 'undefined' && result.effects.length) {
        for (let i = 0; i < result.effects.length; i++) {
            result.effects[i](apply)
        }
    }

    // Update view if the component was already mounted and the dispatchLevel
    // is at "lowest" level (i.e. 1).
    if (context.mounted && context.dispatchLevel === 1) {
        const ctx = {
            props: context.props,
            state: context.state!,
            apply: apply,
            invoke: (fn: Effect) => fn(apply),
            bind: (update: Update<TState, string>) => {<commit_msg>More debug info<commit_after>import { Update, Effect, NodeDescriptor, ComponentContext, Result, ViewContext } from './contract'

export interface Render {
    (parentNode: Element, view: NodeDescriptor, oldView: NodeDescriptor | undefined, oldRootNode: Node | undefined): void
}

export type DebugOptions = {
    components?: string[]
}

let debugEnabled = false
const debugOptions: DebugOptions = {}

// TODO: enable custom logger
export function debug(debug: boolean = true, options?: DebugOptions) {
    debugEnabled = debug
    if (typeof options !== 'undefined') {
        debugOptions.components = options.components
    }
}

export function dispatch<TState, TArg>(context: ComponentContext<TState, any>, render: Render, fn: (state: TState, arg: TArg) => Result<TState>, arg: TArg) {

    if (context.isUpdating) {
        throw `${context.spec.name}: Dispatch error - the dispatch function may not be called during an update. Doing so would most likely corrupt the state.`
    }

    context.dispatchLevel++

    context.isUpdating = true

    const result = fn(context.state!, arg)

    if (typeof result !== 'object') {
        throw 'Invalid result.'
    }

    context.isUpdating = false

    const apply = <T>(fn: Update<TState, T>, arg: T) => dispatch(context, render, fn, arg)

    if (debugEnabled) {
        if (typeof debugOptions.components === 'undefined' ||
            debugOptions.components!.indexOf(context.spec.name) !== -1) {

            console.group(context.spec.name)
            console.debug('State before update: ', context.state)
            console.debug(`Update '${(fn as any).name}' arguments: `, arg)
            console.debug('State after update: ', result.state)
            console.groupEnd()
        }
    }

    context.state = result.state

    if (typeof result.effects !== 'undefined' && result.effects.length) {
        for (let i = 0; i < result.effects.length; i++) {
            result.effects[i](apply)
        }
    }

    // Update view if the component was already mounted and the dispatchLevel
    // is at "lowest" level (i.e. 1).
    if (context.mounted && context.dispatchLevel === 1) {
        const ctx = {
            props: context.props,
            state: context.state!,
            apply: apply,
            invoke: (fn: Effect) => fn(apply),
            bind: (update: Update<TState, string>) => {
{'fraction_non_alphanumeric': 0.12617537721408265, 'fraction_numerical': 0.0017493986442160508, 'mean_word_length': 2.7895608947804473, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, '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': '7100908', 'n_tokens_mistral': 1284, 'n_tokens_neox': 1243, 'n_words': 382}