Octonode Playbook

Studio TypeScript classes and services

Canonical Octonode repository documentation.

Studio TypeScript classes and services

The Studio presents project source as four concepts that do not require users to understand the TypeScript AST:

Studio conceptTypeScript authorityRuntime lifetime
Typesinterface, type, and enum declarationsCompile time only; compatible shapes become input forms.
Fixed valuesconst declarationsRecreated with the source node; JSON-safe values can bind graph inputs.
Process memorylet, var, static fields, and ordinary instance fieldsMemory only; it resets with the process or service lifetime.
Durable dataData tablesPersistent storage; class fields never imply durability.

Variables remain configuration owned by .octonode. The class builder maps constructor names to environment keys (apiUrl becomes API_URL) and generated services read those values from process.env; it never copies configured or secret values into source.

Class blueprint

The source index is the contract between the TypeScript compiler and Studio. A class symbol includes its full declaration, decorators, type parameters, extends, implements, explicit instances, and ordered members. Members retain constructors, parameter properties, fields, methods, getters/setters, auto-accessors, index signatures, static blocks, visibility, static, readonly, declare, definite assignment, abstract, override, async, generator, optional, decorator, initializer, parameter, and return-type metadata.

Studio groups that data into Construction, State, and Capabilities. Methods backed by an explicit top-level instance or exposed through defineService link directly to their workflow. Plain and abstract classes stay visible without being made executable accidentally.

Valid nested class forms are first-class source-index entries. A class expression stored in a field appears as Outer.Inner; a declaration or expression inside a method appears as Outer.createInner.Inner. Studio keeps those classes inside the containing blueprint, including their decorators, heritage, state, methods, services, and workflow links. An explicitly constructed addressable class such as new Outer.Inner() can expose its methods as workflows. Method-local classes run as part of the containing method because no stable instance exists at module scope. TypeScript's invalid class Outer { class Inner {} } form remains a syntax error.

The class builder can create a plain class, a workflow class with an explicit instance, or a service with constructor dependencies, lifecycle, state fields, and exposed methods. Advanced method bodies remain editable as TypeScript so the visual form does not become a second programming language.

Decorators

TypeScript 5 standard decorators can target classes and class members. Studio preserves their source text, arguments, and ordering and displays them on the corresponding blueprint item. Parameter decorators belong to the older experimentalDecorators model; standard decorators cannot decorate parameters and are incompatible with emitDecoratorMetadata. Studio still indexes and preserves legacy parameter decorators, labels them as legacy, and emits a SOURCE_LEGACY_PARAMETER_DECORATOR warning so compiler configuration is explicit.

Decorators are runtime code. A decorator declared locally or imported from another project module is included in the selected source-function dependency closure and stays an internal helper instead of becoming a workflow. Decorated classes bypass expression-level native lowering so construction and decorator effects cannot be erased. Cyclic runtime imports are rejected with their dependency chain before materialization.

Services, injection, and inheritance

defineService is the explicit runtime boundary:

  • create(context) constructs the class; generated services inject attached project Variables through their environment keys, while advanced services may use the invocation context;
  • expose is the allow-list of callable class methods;
  • lifecycle is invocation, workflow-run, or worker memory.

The scheduler routes every exposed method through one generated service host, passes one stable run ID, and closes workflow-scoped processes at the end of the workflow. Direct createNodeWorker callers can keep worker memory for the lifetime of their worker. invocation creates a fresh instance for each call; workflow-run and worker reuse the routed host at their documented scope.

Inheritance, constructor arguments, parameter properties, and this state execute within the same source-function process for explicit local instances. The JSON workflow boundary still rejects class instances: a method must return JSON-safe data, not the instance itself.

worker lifecycle is a cache, not persistence. Durable state belongs in a data table because a local process, container, or Cloudflare worker can restart at any time.

Two-way synchronization

TypeScript source -> compiler AST -> project source index -> Studio blueprint
Studio action -> expected revision -> declaration edit -> parse validation -> atomic write
              -> refreshed source index -> generated workflow adapters

Source offsets and symbol IDs select one declaration; the file revision prevents an older UI from overwriting newer code. Create, replace, export, move, and delete validate the resulting file before committing it. Compile and scan remain code-to-config operations and never invent source edits.

Two-way synchronization is declaration-granular. Native expressions and Promise topology can round-trip as graph structure. Structured control statements are indexed as nested regions with stable identities and can be replaced through the revision-safe source action without introducing DAG cycles. Mutation and other advanced statements remain inside one preserved code.source-function node rather than being rewritten lossily. Nested classes move with and are edited through their containing declaration, which prevents a local class from being accidentally rewritten as an invalid top-level export.

On this page