Pulse

JS Interop

Transpiled JS and client execution

@javascript

Decorator that transpiles Python functions to JavaScript for client-side execution.

from pulse import javascript

Signature

@overload
def javascript(fn: Callable[[*Args], R]) -> JsFunction[*Args, R]: ...

@overload
def javascript(*, jsx: bool = False) -> Callable[[Callable], JsFunction | Jsx]: ...

Parameters

ParameterTypeDefaultDescription
fnCallable-Function to transpile (when used without arguments)
jsxboolFalseIf True, transpiles as React component with props destructuring

Usage

from pulse import javascript
from pulse.js import document, window

@javascript
def focus_element(selector: str):
    document.querySelector(selector).focus()

@javascript
def get_scroll():
    return {"x": window.scrollX, "y": window.scrollY}

# JSX mode for React components
@javascript(jsx=True)
def MyComponent(*children, title: str = ""):
    return div(h1(title), *children)

JsFunction

Wrapper for transpiled JavaScript functions. Returned by @javascript.

from pulse import JsFunction

Properties

PropertyTypeDescription
fnCallableOriginal Python function
idstrUnique identifier for deduplication
depsdict[str, Expr]Resolved dependencies
js_namestrUnique JS function name (e.g., "myFunc_1")

Methods

transpile

def transpile(self) -> Function

Transpile to a Function AST node. Result is cached.

imports

def imports(self) -> dict[str, Expr]

Get all Import dependencies.

functions

def functions(self) -> dict[str, JsFunction]

Get all JsFunction dependencies.


Import

JavaScript import declaration with auto-registration and deduplication.

from pulse import Import

Constructor

Import(
    name: str,
    src: str | None = None,
    *,
    kind: Literal["named", "default", "namespace", "side_effect"] = "named",
    is_type: bool = False,
    lazy: bool = False,
    version: str | None = None,
    before: tuple[str, ...] | list[str] = (),
)

Parameters

ParameterTypeDefaultDescription
namestrrequiredImport name (empty for side-effect imports)
srcstr | NoneNoneModule path or package name. If omitted, name is treated as the source and kind becomes "side_effect".
kindImportKind"named"Import type: "named", "default", "namespace", "side_effect"
is_typeboolFalseType-only import (TypeScript)
lazyboolFalseGenerate lazy import factory for code-splitting
versionstr | NoneNonePackage version constraint. Adds a dependency requirement used by the CLI
beforetuple[str, ...]()Imports that must come before this one

Usage

# Named import: import { useState } from "react"
useState = Import("useState", "react")

# Default import: import React from "react"
React = Import("React", "react", kind="default")

# Namespace import: import * as utils from "./utils"
utils = Import("utils", "./utils", kind="namespace")

# Side-effect import: import "./styles.css"
Import("", "./styles.css", kind="side_effect")

# Side-effect import (single-argument form)
Import("./styles.css")

# Lazy import for code-splitting
Chart = Import("Chart", "./Chart", kind="default", lazy=True)

Properties

PropertyTypeDescription
js_namestrUnique JS identifier
is_localboolTrue if local file import
is_lazyboolTrue if lazy import
is_defaultboolTrue if default import
is_namespaceboolTrue if namespace import

require

Register default npm package version constraints used by dependency resolution.

from pulse import require

Signature

def require(packages: Mapping[str, str]) -> None

Parameters

ParameterTypeDefaultDescription
packagesMapping[str, str]requiredPackage name to version constraint

Usage

import pulse as ps

ps.require(
    {
        "@mantine/core": ">=8.0.0",
        "@mantine/dates": ">=8.0.0",
        "dayjs": "^1",
    }
)

Call this at module import time to register requirements once. Import(..., version=...) also registers a requirement automatically.


react_component

Decorator that wraps an Import as a JSX component with a typed Python signature.

from pulse import react_component, Import, default_signature, Element

Signature

@overload
def react_component(expr: Expr) -> Callable[[Callable[P, Any]], Callable[P, Element]]

@overload
def react_component(
    name: str,
    src: str,
    *,
    lazy: bool = False,
    is_default: bool = False,
) -> Callable[[Callable[P, Any]], Callable[P, Element]]

Usage

from pulse import react_component, Import, default_signature, Element

@react_component(Import("Button", "@mantine/core"))
def Button(
    *children,
    variant: str = "filled",
    disabled: bool = False,
) -> Element:
    ...

@react_component("Calendar", "react-calendar", is_default=True)
def Calendar(*children, **props) -> Element:
    ...

# Use in components
Button("Click me", variant="outline")

default_signature

Default function signature for components without custom props:

def default_signature(
    *children: Node,
    key: str | None = None,
    **props: Any
) -> Element: ...

run_js

Execute JavaScript on the client during a callback.

from pulse import run_js

Signature

@overload
def run_js(expr: Expr, *, result: Literal[True]) -> asyncio.Future[Any]: ...

@overload
def run_js(expr: Expr, *, result: Literal[False] = ...) -> None: ...

Parameters

ParameterTypeDefaultDescription
exprExprrequiredExpression from calling a @javascript function
resultboolFalseIf True, returns Future with JS return value

Usage

from pulse import javascript, run_js
from pulse.js import document

@javascript
def focus_input(selector: str):
    document.querySelector(selector).focus()

@javascript
def get_value(selector: str):
    return document.querySelector(selector).value

# Fire and forget
def on_submit():
    run_js(focus_input("#next-field"))

# Await result
async def on_check():
    value = await run_js(get_value("#my-input"), result=True)
    print(value)

JsExecError

Exception raised when client-side JS execution fails.

from pulse import JsExecError
class JsExecError(Exception):
    pass

pulse.js Module

JavaScript builtin bindings for use in @javascript functions.

from pulse.js import (
    # Namespace objects (function-only modules)
    Math, JSON, console, window, document, navigator, crypto, Intl,

    # Classes (constructors + static methods)
    AbortController, AbortSignal,
    Array, ArrayBuffer, BigInt64Array, BigUint64Array, Blob, CustomEvent, DataView, Date,
    DOMParser, Error, File, FileReader, FormData, Headers, Int16Array, Int32Array, Int8Array,
    IntersectionObserver, Map, MutationObserver, Number, Object, PerformanceObserver, Promise,
    RegExp, Request, ResizeObserver, Response, Set, String, TextDecoder, TextEncoder,
    Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, URL, URLSearchParams,
    WeakMap, WeakSet, XMLSerializer,

    # Functions
    fetch,

    # Statement functions
    throw,

    # Object literal helper
    obj,

    # Primitive values
    undefined,
)

Namespace Objects

Access methods directly:

Math.floor(3.7)        # -> Math.floor(3.7)
JSON.stringify(data)   # -> JSON.stringify(data)
console.log("hi")      # -> console.log("hi")
window.scrollTo(0, 0)  # -> window.scrollTo(0, 0)
document.querySelector("#app")  # -> document.querySelector("#app")

Classes

Create instances with new:

Date()                 # -> new Date()
Set([1, 2, 3])         # -> new Set([1, 2, 3])
Map()                  # -> new Map()
Array(10)              # -> new Array(10)

# Static methods
Date.now()             # -> Date.now()
Array.isArray(x)       # -> Array.isArray(x)
Number.isFinite(42)    # -> Number.isFinite(42)

obj()

Create plain JavaScript object literals (not Maps):

from pulse.js import obj

obj(a=1, b=2)          # -> { a: 1, b: 2 }
obj(**base, c=3)       # -> { ...base, c: 3 }
obj()                  # -> {}

throw()

Emit a JavaScript throw statement:

from pulse.js import throw, Error

throw(Error("Something went wrong"))  # -> throw new Error("Something went wrong")

Node Types

Node

Base type for all VDOM nodes.

from pulse import Node

Node = Element | Primitive | None

Element

JSX element node.

from pulse import Element

PulseNode

Extended node type including components:

from pulse import PulseNode

PulseNode = Node | Component

On this page